Sample OpenShift use-cases for a use in PoC
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 
Nicolas Massé 3bf46401db using docker builds 7 years ago
..
custom-base using docker builds 7 years ago
custom-tomcat using docker builds 7 years ago
target-app using docker builds 7 years ago
README.md using docker builds 7 years ago

README.md

Using Docker Builds

Context

Docker builds are the basis of the basis of every image construction in OpenShift. Even if there are other build types (S2I, Fabric8, Custom, etc.), Docker builds are the easiest and the most ubiquitous build type.

Possible usages of the Docker build encompasses:

  • Creating or modifying a base image
  • Creating an image of an application

To use Docker builds, you will need two Image Streams:

  • a source or input Image Stream
  • a target or output Image Stream

The Image Stream is an indirection level that let you manage your images in a very flexible way. An Image Stream contains tags and each tag can be:

  • a reference to an external image such as docker.io/centos:7 or registry.access.redhat.com/rhel7:7.5.
  • a reference to another tag in the same image stream
  • a reference to a tag in another image stream
  • a container image by itself

In addition to the Image Streams, we will also need a Dockerfile.

Practical example

In this guide, we will take a real world scenario and implement it. In this scenario, we would like to:

  • take the base image provided by Red Hat and add customizations to it
  • take this new base image and create a middleware image that includes Tomcat
  • take this middleware image and install a Tomcat application on it

Creating the source Image Stream

For this example, we will use a CentOS or RHEL base image as a basis for our subsequent builds.

First, we will create a new project:

oc new-project docker-builds

Import the RHEL 7.5 images in an image stream named rh-base:

oc import-image rh-base:7.5 --from=registry.access.redhat.com/rhel7:7.5 --scheduled --confirm

Alternativelly, if you run on OpenShift Origin you can import the CentOS 7.5 images instead:

oc import-image rh-base:7.5 --from=docker.io/centos:7.5.1804 --scheduled --confirm

Note: Although, there is a --all flag to the oc import-image command that can mirror the tags from the remote registry to the image stream, it has shortcomings and I would not recommend using it. Namely, it will not import all tags but just the first five. This behavior is configurable, see the maxImagesBulkImportedPerRepository parameter. Unless you really need to import a large number of tags, I would suggest importing them explicitely.

Creating the target Image Stream

In this example, we will create two target image streams: one for the corporate image we will build and one for the application we will build.

Create the custom-base image stream:

oc create imagestream custom-base

Then, the custom-tomcat image stream:

oc create imagestream custom-tomcat

And the target-app image stream:

oc create imagestream target-app

Create the first Docker build that builds the corporate base image

Review the Dockerfile of our corporate base image and try to build it locally:

docker build -t custom-base:dev ./custom-base

You can then run this new image locally and play around with it:

$ docker run -it custom-base:dev /bin/bash
[root@2329458e5159 /]# cat /etc/redhat-release
CentOS Linux release 7.5.1804 (Core)
[root@2329458e5159 /]# rsync --version
rsync  version 3.1.2  protocol version 31
Copyright (C) 1996-2015 by Andrew Tridgell, Wayne Davison, and others.
Web site: http://rsync.samba.org/
Capabilities:
    64-bit files, 64-bit inums, 64-bit timestamps, 64-bit long ints,
    socketpairs, hardlinks, symlinks, IPv6, batchfiles, inplace,
    append, ACLs, xattrs, iconv, symtimes, prealloc

rsync comes with ABSOLUTELY NO WARRANTY.  This is free software, and you
are welcome to redistribute it under certain conditions.  See the GNU
General Public Licence for details.

Once everything is fine, you can create the Docker build in OpenShift:

oc new-build -D - --name=custom-base --image-stream=rh-base:7.5 --to=custom-base:7.5 < custom-base/Dockerfile

OpenShift has created the build config and started a new build. Follow the build progression with:

oc logs -f bc/custom-base

Did you notice at the very beginning of the build log how OpenShift replaced the FROM centos:latest with the correct image stream reference ?

Step 1/4 : FROM registry.access.redhat.com/rhel7@sha256:135cbbec4581cd8b2f550dd90dea06affb55def73c7421e64091dc3f638d05e4

Now, create a dummy container based on this new image:

oc new-app --name custom-base --image-stream=custom-base:7.5
oc patch dc custom-base --type=json -p '[{"op": "add", "path": "/spec/template/spec/containers/0/command", "value": ["/bin/sh", "-c", "while :; do sleep 1; done" ]}]'

Watch the container being created:

oc get pods -w -l app=custom-base

Once created, get a shell and play around with it:

$ oc rsh $(oc get pods -l app=custom-base -o name|tail -n 1)
sh-4.2$ cat /etc/redhat-release
Red Hat Enterprise Linux Server release 7.5 (Maipo)
sh-4.2$ rsync --version
rsync  version 3.1.2  protocol version 31
Copyright (C) 1996-2015 by Andrew Tridgell, Wayne Davison, and others.
Web site: http://rsync.samba.org/
Capabilities:
    64-bit files, 64-bit inums, 64-bit timestamps, 64-bit long ints,
    socketpairs, hardlinks, symlinks, IPv6, batchfiles, inplace,
    append, ACLs, xattrs, iconv, symtimes, prealloc

rsync comes with ABSOLUTELY NO WARRANTY.  This is free software, and you
are welcome to redistribute it under certain conditions.  See the GNU
General Public Licence for details.

Although everything went smoothly, there is a catch in this setup: the Docker layer cache. If you run this build several times, you can see that subsequent builds are much faster.

$ oc get builds
NAME             TYPE      FROM         STATUS                       STARTED         DURATION
custom-base-1    Docker    Dockerfile   Complete                     1 hours ago     3m53s
custom-base-2    Docker    Dockerfile   Complete                     3 minutes ago   6s

This might seems a good point but if a new security patch is released for the rsync package we installed, it cannot be installed by triggering a new build.

There is a settings to enable to prevent this behavior:

oc patch bc custom-base -p '{ "spec": { "strategy": { "dockerStrategy": { "noCache": true } } } }'

You can trigger a new build and check that the layer cache is not used anymore:

oc start-build custom-base

The new build does not use the layer caching mechanism:

$ oc get builds
NAME             TYPE      FROM         STATUS                       STARTED         DURATION
custom-base-1    Docker    Dockerfile   Complete                     1 hours ago     3m53s
custom-base-2    Docker    Dockerfile   Complete                     15 minutes ago  6s
custom-base-3    Docker    Dockerfile   Complete                     4 minutes ago   3m37s

Create the second Docker build that builds the middleware image

In this example, we will build an image containing tomcat.

Start by reviewing the Dockerfile of our middleware image and try to build it locally:

docker build -t custom-tomcat:dev ./custom-tomcat --build-arg TOMCAT_URL=https://archive.apache.org/dist/tomcat/tomcat-9/v9.0.8/bin/apache-tomcat-9.0.8.tar.gz

You can then run this new image locally:

docker run --name tomcat -d -p 8080:8080 custom-tomcat:dev run
docker logs -f tomcat

And make sure tomcat is working properly:

$ curl -s http://localhost:8080/ |head -n 20



<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8" />
        <title>Apache Tomcat/9.0.8</title>
        <link href="favicon.ico" rel="icon" type="image/x-icon" />
        <link href="favicon.ico" rel="shortcut icon" type="image/x-icon" />
        <link href="tomcat.css" rel="stylesheet" type="text/css" />
    </head>

    <body>
        <div id="wrapper">
            <div id="navigation" class="curved container">
                <span id="nav-home"><a href="http://tomcat.apache.org/">Home</a></span>
                <span id="nav-hosts"><a href="/docs/">Documentation</a></span>
                <span id="nav-config"><a href="/docs/config/">Configuration</a></span>
                <span id="nav-examples"><a href="/examples/">Examples</a></span>

Once everything is fine, you can create the Docker build in OpenShift:

oc new-build -D - --name=custom-tomcat --image-stream=custom-base:7.5 --to=custom-tomcat:9.0.8 --build-arg=TOMCAT_URL=https://archive.apache.org/dist/tomcat/tomcat-9/v9.0.8/bin/apache-tomcat-9.0.8.tar.gz < custom-tomcat/Dockerfile

OpenShift has created the build config and started a new build. Follow the build progression with:

oc logs -f bc/custom-tomcat

You can then tag this image as "production":

oc tag custom-tomcat:9.0.8 custom-tomcat:production

Now, deploy a tomcat container based on this new image:

oc new-app --name custom-tomcat --image-stream=custom-tomcat:production
oc expose svc/custom-tomcat

Watch the container being created:

oc get pods -w -l app=custom-tomcat

Make sure your tomcat is working properly:

curl -s  http://$(oc get route custom-tomcat -o 'jsonpath={.spec.host}')/ |head -n 20

As with the previous example, it might be a good idea to disable the layer caching mechanism with:

oc patch bc custom-tomcat -p '{ "spec": { "strategy": { "dockerStrategy": { "noCache": true } } } }'

Create the third Docker build that builds the target application

In this example, we will build an image containing the tomcat sample app.

Start by reviewing the Dockerfile of our target app image and try to build it locally:

docker build -t target-app:dev ./target-app --build-arg ARTIFACT_URL=https://tomcat.apache.org/tomcat-7.0-doc/appdev/sample/sample.war

You can then run this new image locally:

docker run --name tomcat -d -p 8080:8080 target-app:dev run
docker logs -f tomcat

And make sure the target application is working properly:

$ curl -s http://localhost:8080/my-sample-app/ |head -n 10
<html>
<head>
<title>Sample "Hello, World" Application</title>
</head>
<body bgcolor=white>

<table border="0">
<tr>
<td>
<img src="images/tomcat.gif">

Once everything is fine, you can create the Docker build in OpenShift:

oc new-build -D - --name=target-app --image-stream=custom-tomcat:production --to=target-app:latest --build-arg=ARTIFACT_URL=https://tomcat.apache.org/tomcat-7.0-doc/appdev/sample/sample.war < target-app/Dockerfile

As with the previous example, it might be a good idea to disable the layer caching mechanism with:

oc patch bc target-app -p '{ "spec": { "strategy": { "dockerStrategy": { "noCache": true } } } }'

OpenShift has created the build config and started a new build. Follow the build progression with:

oc logs -f bc/target-app

Now, deploy your application:

oc new-app --name target-app --image-stream=target-app:latest
oc expose svc/target-app

Make sure your target application is working properly:

curl -s  http://$(oc get route target-app -o 'jsonpath={.spec.host}')/ |head -n 20

Congratulation ! You successfully deployed your application !

Update the middleware image

Now, let's say that a new version of tomcat has been released and you want to build this new version. To do so, we need to:

  • update the target imagestream reference (change the tag from 9.0.8 to 9.0.10)
  • update the build arg TOMCAT_URL with the correct url for this version
  • start a new build
  • tag the new image as "production"

Let's implement those modifications:

oc patch bc custom-tomcat --type=json -p '[ { "op": "replace", "path": "/spec/output/to/name", "value": "custom-tomcat:9.0.10" }, { "op": "replace", "path": "/spec/strategy/dockerStrategy/buildArgs/0/value", "value": "https://archive.apache.org/dist/tomcat/tomcat-9/v9.0.10/bin/apache-tomcat-9.0.10.tar.gz" } ]'

And trigger a new build with:

oc start-build custom-tomcat
oc logs -f bc/custom-tomcat

Tag the new version as "production" and watch OpenShift doing his magic:

oc tag custom-tomcat:9.0.10 custom-tomcat:production
oc get pods -w

You should get a similar output to this:

target-app-2-build       0/1       Init:0/1           0         1s
target-app-2-build       0/1       PodInitializing    0         5s
target-app-2-build       1/1       Running            0         6s
target-app-2-build       0/1       Completed          0         11s
target-app-2-deploy      0/1       Pending            0         0s
target-app-2-deploy      0/1       ContainerCreating  0         0s
target-app-2-deploy      1/1       Running            0         4s
target-app-2-6qrt8       0/1       Pending            0         0s
target-app-2-6qrt8       0/1       ContainerCreating  0         0s
target-app-2-6qrt8       1/1       Running            0         7s
target-app-1-bj9tg       1/1       Terminating        0         8m
target-app-1-bj9tg       0/1       Terminating        0         8m
target-app-2-deploy      0/1       Completed          0         16s
target-app-2-deploy      0/1       Terminating        0         16s

We could translate this OpenShift lingua to a similar monologue:

  • Oh ! The custom-tomcat:production image changed !
  • So, I need to trigger the target-app build that depends on this image.

[The target-app build finished.]

  • Oh ! The target-app:latest image changed !
  • So, I need to trigger a new deployment of the target-app

And finally, your target app is deployed automatically on the new tomcat image.

Commit your Dockerfile to a GIT repository

As you noticed, for each build we defined, the Dockerfile is stored inline in the OpenShift BuildConfig object. This is a convenience during prototyping but once your build is steady, you should commit the file to a GIT repository and reference it from the build config.

Update the three buildconfig to reference this GIT repository instead:

oc patch bc custom-base --type=json -p '[ { "op": "replace", "path": "/spec/source", "value": { "type": "Dockerfile", "git": { "uri": "https://github.com/nmasse-itix/OpenShift-Examples.git", "ref": "master" }, "contextDir": "Docker-Builds/custom-base/" } } ]'
oc patch bc custom-tomcat --type=json -p '[ { "op": "replace", "path": "/spec/source", "value": { "type": "Dockerfile", "git": { "uri": "https://github.com/nmasse-itix/OpenShift-Examples.git", "ref": "master" }, "contextDir": "Docker-Builds/custom-tomcat/" } } ]'
oc patch bc custom-tomcat --type=json -p '[ { "op": "replace", "path": "/spec/source", "value": { "type": "Dockerfile", "git": { "uri": "https://github.com/nmasse-itix/OpenShift-Examples.git", "ref": "master" }, "contextDir": "Docker-Builds/target-app/" } } ]'

Conclusion

In this guide we went through a real world example involving three chained builds to defined a custom applicative stack and a target application.

We also saw how OpenShift re-builds any image and re-deploy any application that depends on this image when this image changes.