Using S2i like buildpacks to deploy apps

Openshift’s S2i (Source-2-image) is a pretty cool replacement to buildpacks using Dockerfiles and Openshift

Objective

The goal is to be able to create a container image that contains the necessary application dependencies just by uploading the application code without having to write Dockerfiles.

Creating S2i builder images

S2i builder images are the base images that your deploying application will be injected its code/binary into to create a new deployment image.

An easy way to think about S2i builder images is that it is analogous to Buildpacks in which they set the environment for applications.

Directory Structure

S2i builder images have the following directory structure:

/
/s2i/
/s2i/assemble
/s2i/usage
/s2i/run
Dockerfile

Key details to note is that when you create a new build that uses an s2i builder image as its base layer, your source code will be injected into /tmp/src of the builder image. You will need to keep this in mind when you write your assemble scripts.

S2i Builder Image Default Files

/s2i/assemble

This is a bash script that contains instructions that will be run right after your deploying app source code is injected into /tmp/src of the S2i builder image.

#!/bin/sh

set -ex

echo "---> Preparing source..."

mkdir -p /tmp/myapp

cp -Rf /tmp/src/src/. /tmp/myapp

/s2i/usage

This script is run when the builder image is run w/o any additional commands. Its sole responsibility is to print out the usage of the builder image in text form.

/s2i/run

This script is run during runtime when the derived application image runs. Should contain the actual running of a long-lived process.

#!/bin/sh

set -e

echo "Running myapp..."
cat /tmp/myapp/*

echo Sleeping...
while true; do sleep 1; echo -n .;done

S2i Builder Image Dockerfile

A typical Dockerfile should contain at minimum the following steps:


# This image creates a very simple s2i builder image
# We are basing our builder image on openshift base-centos7 image

FROM openshift/base-centos7

# Set labels used in OpenShift to describe the builder images

LABEL io.k8s.description="simple s2i builder example" \
      io.k8s.display-name="simple s2i builder example" \
      io.openshift.s2i.scripts-url="image:///usr/libexec/s2i" \
      io.openshift.tags="builder"

# Copy the example S2I scripts to the expected location
COPY ./s2i/ /usr/libexec/s2i

# Set the default user for the image
USER 1001

# Set the default CMD to print the usage of the image, if somebody does docker run
CMD /usr/libexec/s2i/usage

Bonus: The io.openshift.s2i annotations can provide you with more configurable settings to help you with your s2i image creation. For example, you can use io.openshift.s2i.destination="/path/where/appcode/is/injected". If not your application code will be injected into /tmp/src of the base image.

Cheat Code

To quickly generate the necessary files to get your S2i builder image started (including the above directory structure), you can actually cheap by using the s2i cli. You can grab it at http://github.com/openshift/source-to-image

$ s2i create <image name> <relative path to name of directory to use to contain the files>

Deploying your Builder Image onto Openshift

oc new-build --name <name of builder image> --binary=true # Create the build configuration (using Docker build strategy)
oc start-build <name of builder image> --from-dir=.       # Start the build, using the current directory
oc logs bc/<name of builder image> -f                     # Check the build output for errors

When this is complete, this creates an image called <name of builder image> into Openshift which you can then use as the source base image.

Deploying your App onto Openshift

Now that you have your app, all you really need is to send an instruction to have your app baked into your builder image and return an output image which youc an then use to deploy.

In the directory of your app source code, create a new build configuration. Like the steps above, oc new build creates a build instruction to tell what is the expected output image.

$ oc new-build --binary=true -i <name of builder image> --name <name of output image/appname>

binary=true just instructs Openshift to expect the incoming files to be from a tarball/raw files

At this stage, you should have a working build configuration. Now let’s start to build an output image. The following command will push all the files in the current directory up start a build.

# make sure you're in your app source code directory
$ oc start-build <name of output image/appname> --from-dir=.
$ oc logs bc/<name of output image> -f

After this is completed, just do an oc new-app <name of output image/appname> and your app is deployed!

Bonus: If you want your application to overwrite any of the assemble/run/usage scripts in your builder image, you can add them in .s2i/bin/ of your application root. The S2i process will find these file and inject them into the builder image during build time.

References