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 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.
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 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:
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
.
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 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 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 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 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 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:
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
.
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 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:
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
.
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 executableENTRYPOINT
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:
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:
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
Now the container is running, opening your browser and going http://localho
st:8080/
should show you something like the following page:
There you have it, we have created an image:
FROM
)NGINX
and supervisord
using apk
(RUN
)COPY
)ADD
)RUN
)80
on the container is open (EXPOSE
)supervisord
is the default process (ENTRYPOINT
)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.
18.217.80.88