Source-to-Image is an excellent tool for building container images for applications in a fast, flexible, and reproducible way. Usually abbreviated as S2I, Source-to-Image takes a base "builder" image with all the libraries and build tools needed to compile an application or install dependencies (like Python's PIP or Ruby's Bundler) and a set of scripts in predefined locations that are used to build, test, and run the application. Once the builder image is created, S2I can take code from a repository, inject it into the build image, compile or install dependencies, and generate an application image with the final application ready to go.
I set out to learn how to build container images for applications written in Go (unofficially called Golang), and in over the next two articles, we will do so.
S2I provides a simple solution for the challenge of build reproducibility, for applications written in any programming language. That means I can reproduce consistent images to allow developers to focus on their applications rather than container images and orchestration. And, since the build environment is created ahead of time, builds take only as long as the application takes to compile or configure (which can be lightning fast with the technology behind the Go compiler).
The real beauty of S2I, in my opinion, is the ability to use builder images as templates, so that similar applications with similar configurations can be deployed without managing configuration files like Dockerfiles for every application—providing identical, reproducible environments for similar applications. Fewer templated images means less busy work on managing their updates, and a much greater likelihood that I can trust they stay up-to-date.
Only four files are required to make a build an S2I-compatible image, though a few more can come in handy. The following comes straight from the S2I README documentation:
|Dockerfile||Yes||Defines the base builder image|
|s2i/bin/assemble||Yes||Script that builds the application|
|s2i/bin/usage||No||Script that prints the usage of the builder|
|s2i/bin/run||Yes||Script that runs the application|
|s2i/bin/save-artifacts||No||Script for incremental builds that saves the built artifacts|
|test/run||No||Test script for the builder image|
|test/test-app||Yes||Tests application source code|
The builder image is created from the Dockerfile; therefore the Dockerfile will contain all the packages and libraries needed to compile, build, and run the source code. The Dockerfile will also need to copy the s2i/bin/* and test/* files into the resulting image to allow S2I to use them.
The s2i/bin/assemble script contains the logic to build the application or install its dependencies. For example, if the builder images were for Python applications, the assemble script would probably run pip install to install the dependencies from the requirements.txt file. For Go, the assemble script will run go get, among other things.
The s2i/bin/run script should be set as the CMD or ENTRYPOINT in the Dockerfile and is responsible for starting the application when the application image runs. In most cases, this script is required, because the image resulting from the S2I build is what runs the application. For a Go builder, it is not strictly necessary, but it can be helpful for testing an application.
The s2i/bin/save-artifacts script takes all the artifacts required for the application to run and streams them through the tar command to stdout. This allows the builder image to do incremental builds or enables us to extract the compiled binary so it can be included in a subsequent build.
These script files can be written in any language, as long as they can be executed in the container built from the Dockerfile.
Note: Despite what the documentation says, the test/test-app file is not required. If you're using the s2i create command to scaffold a new Source-to-Image builder, some blank tests are set up for you, but they are not strictly necessary. Additionally, the run script is required for most Source-to-Image builders, but for the Golang builder image we will create in this series, it is just a convenience.
We also need the Source-to-Image software to build the runtime or application images, but it doesn't necessarily have to have to be installed on the local system. We could create our entire build pipeline in OKD or OpenShift Container Platform and do all our builds there. It is just easier to develop and test images with the software installed locally.
Grab the latest release of Source-to-Image for your platform or install it with your distribution's package manager (e.g., dnf install s2i).
We now have S2I installed and a good understanding of what it takes to start designing our builder. In the next article, we will walk through good practices for the Dockerfile configuration (including avoiding root privileges) and see an example build.
As we continue the four-part series, we will work our way through using the S2I requirements, then building an image template for an application written in Go, and finally how to use S2I with OKD or OpenShift Container Platform buildConfigs to automate image build pipelines. Do you have a question on whether you should use S2I? Feel free to ask me in the comments.