Book Image

Learn OpenShift

By : Denis Zuev, Artemii Kropachev, Aleksey Usov
Book Image

Learn OpenShift

By: Denis Zuev, Artemii Kropachev, Aleksey Usov

Overview of this book

Docker containers transform application delivery technologies to make them faster and more reproducible, and to reduce the amount of time wasted on configuration. Managing Docker containers in the multi-node or multi-datacenter environment is a big challenge, which is why container management platforms are required. OpenShift is a new generation of container management platforms built on top of both Docker and Kubernetes. It brings additional functionality to the table, something that is lacking in Kubernetes. This new functionality significantly helps software development teams to bring software development processes to a whole new level. In this book, we’ll start by explaining the container architecture, Docker, and CRI-O overviews. Then, we'll look at container orchestration and Kubernetes. We’ll cover OpenShift installation, and its basic and advanced components. Moving on, we’ll deep dive into concepts such as deploying application OpenShift. You’ll learn how to set up an end-to-end delivery pipeline while working with applications in OpenShift as a developer or DevOps. Finally, you’ll discover how to properly design OpenShift in production environments. This book gives you hands-on experience of designing, building, and operating OpenShift Origin 3.9, as well as building new applications or migrating existing applications to OpenShift.
Table of Contents (24 chapters)

Creating a custom Docker image

The Docker community has Docker images for most popular software applications. These include, for example, images for web servers (Apache, Nginx, and so on), enterprise application platforms (JBoss EAP, Tomcat), images with programming languages (Perl, PHP, Python), and so on.

In most cases, you do not need to build your own Docker images to run standard software. But if you have a business need that requires having a custom application, you probably need to create your own Docker image.

There are a number of ways to create a new docker image:

  • Commit: Creating a Docker image from a running container. Docker allows you to convert a working container to a Docker image using the docker commit command. This means that image layers will be stored as a separate docker image. This approach is the easiest way to create a new image.
  • Import/Export: This is similar to the first one but uses another Docker command. Running container layers will be saved to a filesystem using docker export and then the image will be recreated using docker import. We do not recommend this method for creating a new image since the first one is simpler.
  • Dockerfile: Building a Docker image using a Dockerfile. Dockerfile is a plain text file that contains a number of steps sometimes called instructions. These instructions can run a particular command inside a container or copy files to a container. A user can initiate a build process using Dockerfile and the Docker daemon will run all instructions in the Dockerfile in a temporary container. Then this container is converted to a docker image. This is the most common way to create a new docker image. Building custom docker images from Dockerfile will be described in details in a later chapter.
  • From scratch: Building a base Docker image. In the two previous methods, Docker images are created using Docker images, and these docker images were created from a base Docker image. You cannot modify this base image unless you create one yourself. If you want to know what is inside your image, you might want to create a base image instead. There are two ways to do so:
    • Create a base image layer using the tar command.
    • Use special Dockerfile instructions (from scratch). Both methods will be described in later chapters.

Customizing images using docker commit

The general recommendation is that all Docker images should be built from a Dockerfile to create clean and proper image layers without unwanted temporary and log files, despite the fact that some vendors deliver their Docker images without an available Dockerfile . If there is a need to modify that existing image, you can use the standard docker commit functionality to convert an existing container to a new image.

As an example, we will try to modify our existing httpd container and make an image from it.

First, we need to get the httpd image:

$ docker pull httpd
Using default tag: latest
Trying to pull repository docker.io/library/httpd ...
latest: Pulling from docker.io/library/httpd
...
output truncated for brevity
...
Digest: sha256:6e61d60e4142ea44e8e69b22f1e739d89e1dc8a2764182d7eecc83a5bb31181e

Next, we need a container to be running. That container will be used as a template for a future image

$ docker run -d --name httpd httpd
c725209cf0f89612dba981c9bed1f42ac3281f59e5489d41487938aed1e47641

Now we can connect to the container and modify its layers. As an example, we will update index.html:

$ docker exec -it httpd /bin/sh
# echo "This is a custom image" > htdocs/index.html
# exit

Let's see the changes we made using the docker diff command. This command shows you all files that were modified from the original image. The output looks like this:

$ docker diff httpd
C /usr
C /usr/local
C /usr/local/apache2
C /usr/local/apache2/htdocs
C /usr/local/apache2/htdocs/index.html
...
output truncated for brevity
...

The following table shows the file states of the docker diff command:

Symbol

Description

A

A file or directory was added

D

A file or directory was deleted

C

A file or directory was changed

In our case, docker diff httpd command shows that index.html was changed.

Create a new image from the running container:

$ docker commit httpd custom_image
sha256:ffd3a523f9848776d65de8302253de9dc78e4948a792569ee46fad5c099312f6

Verify that the new image has been created:

$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
custom_image latest ffd3a523f984 3 seconds ago 177.4 MB
docker.io/httpd latest 01154c38b473 2 weeks ago 177.4 MB

The final step is to verify that the image works properly:

$ docker run -d --name custom_httpd -p 80:8080 custom_image
78fc5731d62e5a6377a7de152c0ba25d350603e6d97fa26967e06a82c8257e71 $ curl localhost:8080
This is a custom image

Using Dockerfile build

Usually, those who use Docker containers expect to have a high-level of automation, and the docker commit command is difficult to automate. Luckily, Docker can build images automatically by reading instructions from a special file usually called a Dockerfile. A Dockerfile is a text document that contains all the commands a user can call on the command line to assemble an image. Using docker build, users can create an automated build that executes several command-line instructions in succession. On CentOS 7, you can learn a lot more using the Dockerfile built-in documentation page man Dockerfile.

A Dockerfile has a number of instructions that help Docker to build an image according to your requirements. Here is a Dockerfile example, which allows us to achieve the same result as in the previous section:

$ cat Dockerfile
FROM httpd
RUN echo "This is a custom image" > /usr/local/apache2/htdocs/index.html

Once this Dockerfile is created, we can build a custom image using the docker build command:

$ docker build -t custom_image2 . 
Sending build context to Docker daemon 2.048 kB
Step 1 : FROM httpd
---> 01154c38b473
Step 2 : RUN echo "This is a custom image" > /usr/local/apache2/htdocs/index.html
---> Using cache
---> 6b9be8efcb3a
Successfully built 6b9be8efcb3a
Please note that the . at the end of the first line is important as it specifies the working directory. Alternatively, you can use ./ or even $(pwd). So the full commands are going to be:

docker build -t custom_image2 .
or

docker build -t custom_image2 ./
or

docker build -t custom_image2 $(pwd)
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
custom_image2 latest 6b9be8efcb3a 2 minutes ago 177.4 MB
custom_image latest ffd3a523f984 19 minutes ago 177.4 MB
docker.io/httpd latest 01154c38b473 2 weeks ago 177.4 MB

Using Docker history

We can check the history of image modifications using docker history:

$ docker history custom_image2
IMAGE CREATED CREATED BY SIZE COMMENT
6b9be8efcb3a 21 hours ago /bin/sh -c echo "This is a custom image" > /u 23 B
01154c38b473 2 weeks ago /bin/sh -c #(nop) CMD ["httpd-foreground"] 0 B
...
output truncated for brevity
...

Note that a new layer, 6b9be8efcb3a, is added. This is where we change the content of the index.html file in comparison to the original httpd image.

Dockerfile instructions

Some Dockerfile instructions are shown in the table:

Instruction

Description and examples

FROM image[:tag]

It sets the base image used in the build process.

Examples:

FROM httpd

FROM httpd:2.2

RUN <command> <parameters>

The RUN instruction executes any commands in a new layer on top of the current image and commits the results.

Examples:

RUN yum install -y httpd &&\

echo "custom answer" >/var/www/html/index.html

RUN ["command", "param1", "param2"]

This is the same as the last one but in Docker format.

COPY <src> <dst>

The COPY instruction copies new files from <src> and adds them to the filesystem of the container at the path <dest>. The <src> must be the path to a file or directory relative to the source directory that is being built (the context of the build) or a remote file URL.

Examples:

COPY index.html /var/www/html/index.html

ENTRYPOINT ["executable", "param1", "param2"]

An ENTRYPOINT helps you configure a container that can be run as an executable. When you specify an ENTRYPOINT, the whole container runs as if it were only that executable.

Examples:

ENTRYPOINT ["/usr/sbin/httpd","-D","FOREGROUND"]

In most cases the default value of ENTRYPOINT is /bin/sh -c, which means that CMD will be interpreted as a command to run

EXPOSE <port>

This instruction informs a Docker daemon that an application will be listening on this port at runtime. This is not very useful when working with standalone Docker containers because port publishing is performed via the -p argument of the CLI, but it is used by OpenShift when creating a service for a new application deployed from a Docker image and by Docker itself when exporting default environment variables inside a container.

CMD ["executable", "param1", "param2"]


Provides arguments to an ENTRYPOINT command and can be overridden at runtime with the docker run command.

Example:

CMD ["/usr/sbin/httpd","-D","FOREGROUND"]

When the docker build command is run, Docker reads the provided Dockerfile from top to bottom, creating a separate layer for every instruction and placing it in the internal cache. If an instruction from Dockerfile is updated, it invalidates the respective caching layer and every subsequent one, forcing Docker to rebuild them when the docker build command is run again. Therefore, it's more effective to place the most malleable instructions at the end of Dockerfile, so that the number of invalidated layers is minimized and cache usage is maximized. For example, suppose we have a Dockerfile with the following contents:

$ cat Dockerfile
FROM centos:latest
RUN yum -y update
RUN yum -y install nginx, mariadb, php5, php5-mysql
RUN yum -y install httpd
CMD ["nginx", "-g", "daemon off;"]

In the example, if you choose to use MySQL instead of MariaDB, the layer created by the second RUN command, as well as the third one, will be invalidated, which for complex images means a noticeably longer build process.

Consider the following example. Docker includes images for minimal OSes. These base images can be used to build custom images on top of them. In the example, we will be using a CentOS 7 base image to create a web server container from scratch:

  1. First, we need to create a project directory:
$ mkdir custom_project; cd custom_project

Then, we create a Dockerfile with the following content:

$ cat Dockerfile
FROM centos:7
RUN yum install httpd -y
COPY index.html /var/www/html/index.html
ENTRYPOINT ["/usr/sbin/httpd","-D","FOREGROUND"]
  1. Create the index.html file:
$ echo "A new cool image" > index.html
  1. Build the image using docker build:
$ docker build -t new_httpd_image .
Sending build context to Docker daemon 3.072 kB
...
output truncated for brevity
...
Successfully built 4f2f77cd3026
  1. Finally, we can check that the new image exists and has all the required image layers:
$ docker history new_httpd_image
IMAGE CREATED CREATED BY SIZE COMMENT
4f2f77cd3026 20 hours ago /bin/sh -c #(nop) ENTRYPOINT ["/usr/sbin/htt 0 B
8f6eaacaae3c 20 hours ago /bin/sh -c #(nop) COPY file:318d7f73d4297ec33 17 B
e19d80cc688a 20 hours ago /bin/sh -c yum install httpd -y 129 MB
...
output truncated for brevity
...
The top three layers are the instructions we added in the Dockerfile.