The Dockerfile build instructions

So far, we have looked at the integrated build system, the Dockerfile syntax and a sample lifecycle, wherein how a sample Dockerfile is leveraged for generating an image and how a container gets spun off from that image was discussed. In this section, we will introduce the Dockerfile instructions, their syntax, and a few befitting examples.

The FROM instruction

The FROM instruction is the most important one and it is the first valid instruction of a Dockerfile. It sets the base image for the build process. The subsequent instructions will use this base image and build on top of it. The Docker build system lets you flexibly use the images built by anyone. You can also extend them by adding more precise and practical features to them. By default, the Docker build system looks in the Docker host for the images.

However, if the image is not found in the Docker host, then the Docker build system will pull the image from the publicly available Docker Hub Registry. The Docker build system will return an error if it cannot find the specified image in the Docker host and the Docker Hub Registry.

The FROM instruction has the following syntax:

FROM <image>[:<tag>|@<digest>]

In the preceding code statement, note the following:

  • <image>: This is the name of the image which will be used as the base image.
  • <tag> or<digest>: Both tag and digest are optional attributes and you could qualify a particular Docker image version using either a tag or a digest. Tag latest is assumed by default if both tag and digest are not present.

Here is an example of the FROM instruction with the image name centos:

FROM centos

In the above example, the Docker build system would implicitly default to tag latest because neither a tag nor a digest is explicitly added to the image name.

You should be strongly discouraged from using multiple FROM instructions in a single Dockerfile, as damaging conflicts could arise.

The MAINTAINER instruction

All the MAINTAINER instruction does is enables the authors' details to set the in an image. Docker does not place any restrictions on placing the MAINTAINER instruction in a Dockerfile. However, it is strongly recommended that you should place it after the FROM instruction.

The following is the syntax of the MAINTAINER instruction, where <author's detail> can be in any text. However, it is strongly recommended that you should use the image, author's name and the e-mail address as shown in this code syntax:

MAINTAINER <author's detail>

There is an example of the MAINTAINER instruction with the author name, and the e-mail address at /chapter02/build_01_maintainer/ in the repo:

# Example Dockerfile showing MAINTAINER

FROM alpine:latest
MAINTAINER Russ McKendrick<[email protected]>

The RUN instruction

The RUN instruction is the real workhorse during the build time, and it can run any command. The general recommendation is to execute the multiple commands by using one RUN instruction. This reduces the layers in the resulting Docker image because the Docker system inherently creates a layer for each time an instruction is called in Dockerfile.

The RUN instruction has two types of syntax:

  • The first is the shell type, as shown here:
    RUN <command>

    Here, the <command> is the shell command that has to be executed during the build time. If this type of syntax is to be used, then the command is always executed by using /bin/sh -c.

  • The second syntax type is either exec or the JSON array, as shown here:
    RUN ["<exec>", "<arg-1>", ..., "<arg-n>"]

    Wherein, the code terms mean the following:

    • <exec>: This is the executable to run during the build time.
    • <arg-1>, ..., <arg-n>: These are the variables (zero or more) number of the arguments for the executable.

Unlike the first type of syntax, this type does not invoke /bin/sh -c. Hence, the types of shell processing, such as the variable substitution ($USER) and the wild card substitution (*, ?), do not happen in this type. If shell processing is critical for you, then you are encouraged to use the shell type. However, if you still prefer the exec (JSON array type) type, then use your preferred shell as the executable and supply the command as an argument.

For example, RUN ["bash", "-c", "rm", "-rf", "/tmp/abc"].

Let's add a few RUN instructions to our Dockerfile to install NGINX using apk and then set some permissions:

# Example Dockerfile showing RUN

FROM alpine:latest
MAINTAINER Russ McKendrick<[email protected]>

RUN apk add --update supervisor nginx&&rm -rf /var/cache/apk/*

As you can see, we are installing NGINX and Supervisor. The && has been added so that we can string several commands together on a single line, as each line within the Dockerfile creates a layer within the image stringing commands together like this streamlines your image file.

The COPY instruction

The COPY instruction enables you to copy the files from your Docker host to the filesystem of the image you are building. The following is the syntax of the COPY instruction:

COPY <src> ... <dst>

The preceding code terms bear the explanations shown here:

  • <src>: This is the source directory, the file in the build context, or the directory from where the docker build subcommand was invoked.
  • ...: This indicates that multiple source files can either be specified directly or be specified by wildcards.
  • <dst>: This is the destination path for the new image into which the source file or directory will get copied. If multiple files have been specified, then the destination path must be a directory and it must end with a slash /.

Using an absolute path for the destination directory or a file has been recommended. In the absence of an absolute path, the COPY instruction will assume that the destination path will start from root /. The COPY instruction is powerful enough for creating a new directory and for overwriting the filesystem in the newly created image.

An example of the copy command can be found in the repo (https://github.com/russmckendrick/bootcamp) at /chapter02/build_03_copy/:

# Example Dockerfile showing COPY

FROM alpine:latest
MAINTAINER Russ McKendrick<[email protected]>

RUN apk add --update supervisor nginx&&rm -rf /var/cache/apk/*

COPY start.sh /script/
COPY files/default.conf /etc/nginx/conf.d/
COPY files/nginx.conf /etc/nginx/nginx.conf
COPY files/supervisord.conf /etc/supervisord.conf

This copies the start.sh file to the folder in the Docker image at/script/and the configuration file from the files folder to in place on the image.

The ADD instruction

The ADD instruction is like the COPY instruction. However, in addition to the functionality supported by the COPY instruction, the ADD instruction can handle the TAR files and the remote URLs. We can annotate the ADD instruction as COPY on steroids.

The following is the syntax of the ADD instruction:

ADD <src> ... <dst>

The arguments of the ADD instruction are very similar to those of the COPY instruction, as shown here:

  • <src>: This is either the source directory or the file that is in the build context or in the directory from where the docker build subcommand will be invoked. However, the noteworthy difference is that the source can either be a tar file stored in the build context or be a remote URL.
  • ...: This indicates that the multiple source files can either be specified directly or be specified by using wildcards.
  • <dst>: This is the destination path for the new image into which the source file or directory will be copied.

Here is an example for demonstrating the procedure for copying multiple source files to the various destination directories in the target image filesystem. In this example, we have taken a TAR file (webroot.tar) in the source build context with the http daemon configuration file and the files for the web pages are stored in the appropriate directory structure, as shown here:

The ADD instruction

The next line in the Dockerfile content has an ADD instruction for copying the TAR file (webroot.tar) to the target image and extracting the TAR file from the root directory (/) of the target image, as shown here in the example you can find in the repo at /chapter02/build_04_add/:

# Example Dockerfile showing ADD

FROM alpine:latest
MAINTAINER Russ McKendrick<[email protected]>

RUN apk add --update supervisor nginx&&rm -rf /var/cache/apk/*

COPY start.sh /script/
COPY files/default.conf /etc/nginx/conf.d/
COPY files/nginx.conf /etc/nginx/nginx.conf
COPY files/supervisord.conf /etc/supervisord.conf

ADD webroot.tar /
RUN chown -R nginx:nginx /var/www/html

Thus, the TAR option of the ADD instruction can be used for copying multiples files to the target image, also note we have added a second RUN instruction to set the permissions on the folder we have just created using ADD.

The EXPOSE instruction

The EXPOSE instruction opens up a container network port for communicating between the container and the rest of the network.

The syntax of the EXPOSE instruction is as follows:

EXPOSE <port>[/<proto>] [<port>[/<proto>]...]

Here, the code terms mean the following:

  • <port>: This is the network port that has to be exposed to the outside world.
  • <proto>: This is an optional field provided for a specific transport protocol, such as TCP and UDP. If no transport protocol has been specified, then TCP is assumed to be the transport protocol.

The EXPOSE instruction allows you to specify multiple ports in a single line.

The following is an example of the EXPOSE instruction inside a Dockerfile exposing port 80:

# Example Dockerfile showing EXPOSE

FROM alpine:latest
MAINTAINER Russ McKendrick<[email protected]>

RUN apk add --update supervisor nginx&&rm -rf /var/cache/apk/*

COPY start.sh /script/
COPY files/default.conf /etc/nginx/conf.d/
COPY files/nginx.conf /etc/nginx/nginx.conf
COPY files/supervisord.conf /etc/supervisord.conf

ADD webroot.tar /

RUN chown -R nginx:nginx /var/www/html

EXPOSE 80/tcp

The ENTRYPOINT instruction

The ENTRYPOINT instruction will help in crafting an image for running an application (entry point) during the complete lifecycle of the container, which would have been spun out of the image. When the entry point application is terminated, the container would also be terminated along with the application and vice versa.

Thus, the ENTRYPOINT instruction would make the container function like an executable. Functionally, ENTRYPOINT is akin to the CMD instruction which we will look at next, but the major difference between the two is that the entry point application is launched by using the ENTRYPOINT instruction, which cannot be overridden by using the docker run subcommand arguments.

However, these docker container run subcommand arguments will be passed as additional arguments to the entry point application. Having said this, Docker provides a mechanism for overriding the entry point application through the --entrypoint option in the docker container run subcommand. The --entrypoint option can accept only word as its argument and hence, it has limited functionality.

Syntactically, the ENTRYPOINT instruction is very similar to the RUN, and the CMD instructions, and it has two types of syntax, as shown here:

  • The first type of syntax is the shell type, as shown here:
    ENTRYPOINT <command>

    Here, <command> is the shell command, which is executed during the launch of the container. If this type of syntax is used, then the command is always executed by using /bin/sh -c.

  • The second type of syntax is exec or the JSON array, as shown here:
    ENTRYPOINT ["<exec>", "<arg-1>", ..., "<arg-n>"]

    Wherein, the code terms mean the following:

    • <exec>: This is the executable, which has to be run during the container launch time.
    • <arg-1>, ..., <arg-n>: These are the variable (zero or more) number of arguments for the executable.

Syntactically, you can have more than one ENTRYPOINT instruction in a Dockerfile. However, the build system will ignore all the ENTRYPOINT instructions except the last one. In other words, in the case of multiple ENTRYPOINT instructions, only the last ENTRYPOINT instruction be effective.

As you may recall from when we covered the RUN instruction we installed a service called supervisord, we will be using this for the entry point in our image meaning that our Dockerfile now looks like the following:

# Example Dockerfile showing ENTRYPOINT

FROM alpine:latest
MAINTAINER Russ McKendrick<[email protected]>

RUN apk add --update supervisor nginx&&rm -rf /var/cache/apk/*

COPY start.sh /script/
COPY files/default.conf /etc/nginx/conf.d/
COPY files/nginx.conf /etc/nginx/nginx.conf
COPY files/supervisord.conf /etc/supervisord.conf

ADD webroot.tar /

RUN chown -R nginx:nginx /var/www/html

EXPOSE 80/tcp

ENTRYPOINT ["supervisord"]

Now we could leave it here and the image would be functional, however there is one instruction we should pass to our image.

The CMD instruction

The CMD instruction can run any command (or application), which is similar to the RUN instruction. However, the major difference between those two is the time of execution. The command supplied through the RUN instruction is executed during the build time, whereas the command specified through the CMD instruction is executed when the container is launched from the newly created image. Thus, the CMD instruction provides a default execution for this container. However, it can be overridden by the docker run subcommand arguments. When the application terminates, the container will also terminate along with the application and vice versa.

On the face of it the CMD instruction is very similar to the RUN instruction in that it can run any command passed to it, however there is a major difference between the two instructions.

The command passed to the RUN instruction is executed at build time and commands passed using the CMD instruction are executed at run time meaning you can define the default execution for the container. This means if no command is passed during the docker container run command then the CMD will executed.

The CMD instruction has three types of syntax, as shown here:

  • The first syntax type is the shell type, as shown here:
    CMD <command>
    

    Wherein, the <command> is the shell command, which has to be executed during the launch of the container. If this type of syntax is used, then the command is always executed by using /bin/sh -c.

  • The second type of syntax is exec or the JSON array, as shown here:
    CMD ["<exec>", "<arg-1>", ..., "<arg-n>"]
    

    Wherein, the code terms mean the following:

    • <exec>: This is the executable, which is to be run during the container launch time
    • <arg-1>, ..., <arg-n>: These are the variable (zero or more) number of the arguments for the executable
  • The third type of syntax is also exec or the JSON array, which is similar to the previous type. However, this type is used for setting the default parameters to the ENTRYPOINT instruction, as shown here:
    CMD ["<arg-1>", ..., "<arg-n>"]
    

    Wherein, the code terms mean the following:

    • <arg-1>, ..., <arg-n>: These are the variables (zero or more) number of the arguments for the ENTRYPOINT instruction, which will be explained in the next section.

Syntactically, you can add more than one CMD instruction in Dockerfile. However the build system would ignore all the CMD instructions except for the last one. In other words, in the case of multiple CMD instructions, only the last CMD instruction would be effective.

As mentioned in the previous section, our Dockerfile could have been run with just the ENTRYPOINT instruction defined, however that would give a non-breaking error when supervisiord starts up so let's pass a flag which defines where our supervisor configuration file is using the CMD instruction:

# Example Dockerfile showing CMD

FROM alpine:latest
MAINTAINER Russ McKendrick<[email protected]>

RUN apk add --update supervisor nginx&&rm -rf /var/cache/apk/*

COPY start.sh /script/
COPY files/default.conf /etc/nginx/conf.d/
COPY files/nginx.conf /etc/nginx/nginx.conf
COPY files/supervisord.conf /etc/supervisord.conf

ADD webroot.tar /

RUN chown -R nginx:nginx /var/www/html

EXPOSE 80/tcp

ENTRYPOINT ["supervisord"]
CMD ["-c","/etc/supervisord.conf"]

We are now in a position where we can build our image, you can find our completed Dockerfile in the /chapter02/ build_07_cmd/ folder in the repo, to build the image simple run the following command:

docker image build -t cluster

This will kick of the build, as you can see from the following terminal:

The CMD instruction

There are 12 steps in the build, it will take a minute or two, but once compete you should see something like the following terminal output:

The CMD instruction

Once you have your image built, you can check and then run it by using the following commands:

docker image ls
docker container run -d -p 8080:80 cluster
The CMD instruction

Now the container is running, opening your browser and going http://localho st:8080/ should show you something like the following page:

The CMD instruction

There you have it, we have created an image:

  • Using the Alpine Linux base (FROM)
  • Installed NGINX and supervisord using apk (RUN)
  • Copied the configuration from our Docker host to the image (COPY)
  • Uploaded and extracting our web root (ADD)
  • Set the correct ownership of our web root (RUN)
  • Ensured that port 80 on the container is open (EXPOSE)
  • Made sure that supervisord is the default process (ENTRYPOINT)
  • Passed the configuration file flag to supervisord (CMD)

Before moving onto the next section you can stop and remove the container by running the following command making sure you replace the container ID with that of yours:

docker container ps
docker container stop de9a26a1d149
docker container rm de9a26a1d149

Then remove the image we created by running:

docker image rm cluster

Next, we are going to go back to our WordPress image and customize it.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset