The Docker Book
The Docker Book
The Docker Book
James Turnbull
April 24, 2016
Version: v1.10.3 (462f469)
Website: The Docker Book
Contents
Page
Chapter 1 Working with Docker images and repositories
What is a Docker image? . . . . . . . . . . . . . . . . . . . .
Listing Docker images . . . . . . . . . . . . . . . . . . . . . .
Pulling images . . . . . . . . . . . . . . . . . . . . . . . . . .
Searching for images . . . . . . . . . . . . . . . . . . . . . .
Building our own images . . . . . . . . . . . . . . . . . . . .
Creating a Docker Hub account . . . . . . . . . . . . . .
Using Docker commit to create images . . . . . . . . .
Building images with a Dockerfile . . . . . . . . . . . .
Building the image from our Dockerfile . . . . . . . . .
What happens if an instruction fails? . . . . . . . . . .
Dockerfiles and the build cache . . . . . . . . . . . . . .
Using the build cache for templating . . . . . . . . . .
Viewing our new image . . . . . . . . . . . . . . . . . .
Launching a container from our new image . . . . . .
Dockerfile instructions . . . . . . . . . . . . . . . . . . .
Pushing images to the Docker Hub . . . . . . . . . . . . . .
Automated Builds . . . . . . . . . . . . . . . . . . . . . .
Deleting an image . . . . . . . . . . . . . . . . . . . . . . . .
Running your own Docker registry . . . . . . . . . . . . . .
Running a registry from a container . . . . . . . . . . .
Testing the new registry . . . . . . . . . . . . . . . . . .
Alternative Indexes . . . . . . . . . . . . . . . . . . . . . . .
Quay . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
i
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
1
2
4
8
10
12
12
14
17
21
24
26
27
28
29
34
55
57
61
64
64
65
68
68
68
Contents
List of Figures
69
List of Listings
72
Index
73
ii
Chapter 1
Working with Docker images and
repositories
In Chapter 2, we learned how to install Docker. In Chapter 3, we learned how to
use a variety of commands to manage Docker containers, including the docker
run command.
Lets see the docker run command again.
Listing 1.1: Revisiting running a basic Docker container
IMAGE ID
ubuntu
latest
CREATED
VIRTUAL SIZE
225.4 MB
We see that weve got an image, from a repository called ubuntu. So where does
this image come from? Remember in Chapter 3, when we ran the docker run
command, that part of the process was downloading an image? In our case, its
the ubuntu image.
NOTE
Local images live on our local Docker host in the /var/lib/docker directory. Each image will be inside a directory named for your storage driver;
for example, aufs or devicemapper. Youll also find all your containers in the
/var/lib/docker/containers directory.
TIP
The Docker registry code is open source. You can also run your own registry, as well see later in this chapter. The Docker Hub product is also available
as a commercial behind the firewall product called Docker Trusted Registry, formerly Docker Enterprise Hub.
Here weve used the docker pull command to pull down the Ubuntu 12.04 image
from the ubuntu repository.
Lets see what our docker images command reveals now.
Listing 1.4: Listing the ubuntu Docker images
IMAGE ID
CREATED
VIRTUAL SIZE
ubuntu
latest
ubuntu
12.04
ubuntu
199.3 MB
You can see weve now got the latest Ubuntu image and the 12.04 image. This
show us that the ubuntu image is actually a series of images collected under a
single repository.
Version: v1.10.3 (462f469)
NOTE
We call it the Ubuntu operating system, but really it is not the full operating system. Its a cut-down version with the bare runtime required to run the
distribution.
We identify each image inside that repository by what Docker calls tags. Each
image is being listed by the tags applied to it, so, for example, 12.04, 12.10,
quantal, or precise and so on. Each tag marks together a series of image layers
that represent a specific image (e.g., the 12.04 tag collects together all the layers
of the Ubuntu 12.04 image). This allows us to store more than one image inside
a repository.
We can refer to a specific image inside a repository by suffixing the repository
name with a colon and a tag name, for example:
Listing 1.5: Running a tagged Docker image
This launches a container from the ubuntu:12.04 image, which is an Ubuntu 12.04
operating system.
We can also see that our new 12.04 images are listed twice with the same ID in our
docker images output. This is because images can have more than one tag. This
makes it easier to help label images and make them easier to find. In this case,
image ID 0b310e6bf058 is actually tagged with 12.04 and precise: the version
number and code name for that Ubuntu release, respectively.
Its always a good idea to build a container from specific tags. That way well know
exactly what the source of our container is. There are differences, for example,
between Ubuntu 12.04 and 14.04, so it would be useful to specifically state that
were using ubuntu:12.04 so we know exactly what were getting.
Version: v1.10.3 (462f469)
WARNING User-contributed images are built by members of the Docker community. You should use them at your own risk: they are not validated or verified
in any way by Docker Inc.
Pulling images
When we run a container from images with the docker run command, if the image
isnt present locally already then Docker will download it from the Docker Hub.
By default, if you dont specify a specific tag, Docker will download the latest
tag, for example:
Will download the ubuntu:latest image if it isnt already present on the host.
Alternatively, we can use the docker pull command to pull images down ourselves preemptively. Using docker pull saves us some time launching a container
from a new image. Lets see that now by pulling down the fedora:20 base image.
Listing 1.7: Pulling the fedora image
Lets see this new image on our Docker host using the docker images command.
This time, however, lets narrow our review of the images to only the fedora images. To do so, we can specify the image name after the docker images command.
Listing 1.8: Viewing the fedora image
IMAGE ID
fedora
20
CREATED
VIRTUAL SIZE
wfarr/puppet-module...
jamtur01/puppetmaster
. . .
TIP You can also browse the available images online at Docker Hub.
Here, weve searched the Docker Hub for the term puppet. Itll search images and
return:
Repository names
Version: v1.10.3 (462f469)
10
NOTE Well see more about Automated Builds later in this chapter.
Lets pull down one of these images.
Listing 1.11: Pulling down the jamtur01/puppetmaster image
This will pull down the jamtur01/puppetmaster image (which, by the way, contains a pre-installed Puppet master server).
We can then use this image to build a new container. Lets do that now using the
docker run command again.
Listing 1.12: Creating a Docker container from the puppetmaster image
11
NOTE
12
13
This command will log you into the Docker Hub and store your credentials for
future use. You can use the docker logout command to log out from a registry
server.
NOTE
14
Weve launched our container and then installed Apache within it. Were going
to use this container as a web server, so well want to save it in its current state.
That will save us from having to rebuild it with Apache every time we create a
new container. To do this we exit from the container, using the exit command,
and use the docker commit command.
Listing 1.16: Committing the custom container
You can see weve used the docker commit command and specified the ID of the
container weve just changed (to find that ID you could use the docker ps -l
-q command to return the ID of the last created container) as well as a target
repository and image name, here jamtur01/apache2. Of note is that the docker
commit command only commits the differences between the image the container
was created from and the current state of the container. This means updates are
lightweight.
Lets look at our new image.
15
latest
8ce0ea7a1528
13 seconds ago
90.63 MB
We can also provide some more data about our changes when committing our
image, including tags. For example:
Listing 1.18: Committing another custom container
Here, weve specified some more information while committing our new image.
Weve added the -m option which allows us to provide a commit message explaining our new image. Weve also specified the -a option to list the author of the
image. Weve then specified the ID of the container were committing. Finally,
weve specified the username and repository of the image, jamtur01/apache2, and
weve added a tag, webserver, to our image.
We can view this information about our image using the docker inspect command.
16
If we want to run a container from our new image, we can do so using the docker
run command.
Listing 1.20: Running a container from our committed image
Youll note that weve specified our image with the full tag: jamtur01/apache2:
webserver.
17
$ mkdir static_web
$ cd static_web
$ touch Dockerfile
Weve created a directory called static_web to hold our Dockerfile. This directory is our build environment, which is what Docker calls a context or build
context. Docker will upload the build context, as well as any files and directories
contained in it, to our Docker daemon when the build is run. This provides the
Docker daemon with direct access to any code, files or other data you might want
to include in the image.
Weve also created an empty Dockerfile file to get started. Now lets look at an
example of a Dockerfile to create a Docker image that will act as a Web server.
18
# Version: 0.0.1
FROM ubuntu:14.04
MAINTAINER James Turnbull "james@example.com"
RUN apt-get update && apt-get install -y nginx
RUN echo 'Hi, I am in your container' \
>/usr/share/nginx/html/index.html
EXPOSE 80
This means that if your Dockerfile stops for some reason (for example, if an
instruction fails to complete), you will be left with an image you can use. This is
highly useful for debugging: you can run a container from this image interactively
and then debug why your instruction failed using the last image created.
NOTE
The Dockerfile also supports comments. Any line that starts with a #
is considered a comment. You can see an example of this in the first line of our
Version: v1.10.3 (462f469)
19
20
NOTE Docker also uses the EXPOSE instruction to help link together containers,
which well see in Chapter 5. You can expose ports at run time with the docker
run command with the --expose option.
21
$ cd static_web
$ sudo docker build -t="jamtur01/static_web" .
Sending build context to Docker daemon
2.56 kB
>/usr/share/
nginx/html/index.html
---> Running in b51bacc46eb9
---> b584f4ac1def
Removing intermediate container b51bacc46eb9
Step 5 : EXPOSE 80
---> Running in 7ff423bd1f4d
---> 22d47c8cb6e5
Successfully built 22d47c8cb6e5
Weve used the docker build command to build our new image. Weve specified
the -t option to mark our resulting image with a repository and a name, here the
jamtur01 repository and the image name static_web. I strongly recommend you
Version: v1.10.3 (462f469)
22
TIP
If you dont specify any tag, Docker will automatically tag your image as
latest.
The trailing . tells Docker to look in the local directory to find the Dockerfile.
You can also specify a Git repository as a source for the Dockerfile as we see
here:
Listing 1.26: Building from a Git repository
Here Docker assumes that there is a Dockerfile located in the root of the Git
repository.
TIP Since Docker 1.5.0 and later you can also specify a path to a file to use as a
build source using the -f flag. For example, docker build -t "jamtur01/static_web" -f /path/to/file. The file specified doesnt need to be called Dockerfile
but must still be within the build context.
Version: v1.10.3 (462f469)
23
But back to our docker build process. You can see that the build context has
been uploaded to the Docker daemon.
Listing 1.27: Uploading the build context to the daemon
2.56 kB
TIP
If a file named .dockerignore exists in the root of the build context then
it is interpreted as a newline-separated list of exclusion patterns. Much like a
.gitignore file it excludes the listed files from being treated as part of the build
context, and therefore prevents them from being uploaded to the Docker daemon.
Globbing can be done using Gos filepath.
Next, you can see that each instruction in the Dockerfile has been executed with
the image ID, 22d47c8cb6e5, being returned as the final output of the build process. Each step and its associated instruction are run individually, and Docker has
committed the result of each operation before outputting that final image ID.
24
$ cd static_web
$ sudo docker build -t="jamtur01/static_web" .
Sending build context to Docker daemon
2.56 kB
Lets say I want to debug this failure. I can use the docker run command to create
a container from the last step that succeeded in my Docker build, in this example
using the image ID of 997485f46ec4.
25
I can then try to run the apt-get install -y ngin step again with the right package name or conduct some other debugging to determine what went wrong. Once
Ive identified the issue, I can exit the container, update my Dockerfile with the
right package name, and retry my build.
26
FROM ubuntu:14.04
MAINTAINER James Turnbull "james@example.com"
ENV REFRESHED_AT 2014-07-01
RUN apt-get -qq update
Lets step through this new Dockerfile. Firstly, Ive used the FROM instruction to
specify a base image of ubuntu:14.04. Next Ive added my MAINTAINER instruction to provide my contact details. Ive then specified a new instruction, ENV.
The ENV instruction sets environment variables in the image. In this case, Ive
specified the ENV instruction to set an environment variable called REFRESHED_AT,
showing when the template was last updated. Lastly, Ive specified the apt-get
-qq update command in a RUN instruction. This refreshes the APT package cache
when its run, ensuring that the latest packages are available to install.
With my template, when I want to refresh the build, I change the date in my ENV
instruction. Docker then resets the cache when it hits that ENV instruction and runs
every subsequent instruction anew without relying on the cache. This means my
RUN apt-get update instruction is rerun and my package cache is refreshed with
the latest content. You can extend this template example for your target platform
or to fit a variety of needs. For example, for a fedora image we might:
27
FROM fedora:20
MAINTAINER James Turnbull "james@example.com"
ENV REFRESHED_AT 2014-07-01
RUN yum -q makecache
TAG
jamtur01/static_web latest
ID
CREATED
SIZE
22d47c8cb6e5
If we want to drill down into how our image was created, we can use the docker
history command.
28
CREATED
CREATED BY
SIZE
22d47c8cb6e5
:{}]
6 minutes ago
0 B
b584f4ac1def
container'
93fb180f3bc9
6 minutes ago
27 B
6 minutes ago
18.46 MB
9d938b9e0090
6 minutes ago
20.02 MB
4c66c9dcee35
6 minutes ago
Turnbull " 0 B
. . .
We see each of the image layers inside our new jamtur01/static_web image and
the Dockerfile instruction that created them.
29
Here Ive launched a new container called static_web using the docker run command and the name of the image weve just created. Weve specified the -d option,
which tells Docker to run detached in the background. This allows us to run longrunning processes like the Nginx daemon. Weve also specified a command for
the container to run: nginx -g "daemon off;". This will launch Nginx in the
foreground to run our web server.
Weve also specified a new flag, -p. The -p flag manages which network ports
Docker publishes at runtime. When you run a container, Docker has two methods
of assigning ports on the Docker host:
Docker can randomly assign a high port from the range 32768 to 61000 on
the Docker host that maps to port 80 on the container.
You can specify a specific port on the Docker host that maps to port 80 on
the container.
The docker run command will open a random port on the Docker host that will
connect to port 80 on the Docker container.
Lets look at what port has been assigned using the docker ps command.
30
$ sudo docker ps -l
CONTAINER ID
IMAGE
... PORTS
NAMES
6751b94bb5c0
tcp
static_web
We see that port 49154 is mapped to the container port of 80. We can get the
same information with the docker port command.
Listing 1.37: The docker port command
Weve specified the container ID and the container port for which wed like to see
the mapping, 80, and it has returned the mapped port, 49154.
Or we could use the container name too.
Listing 1.38: The docker port command with container name
The -p option also allows us to be flexible about how a port is published to the
host. For example, we can specify that Docker bind the port to a specific port:
31
This will bind port 80 on the container to port 80 on the local host. Its important to be wary of this direct binding: if youre running multiple containers, only
one container can bind a specific port on the local host. This can limit Dockers
flexibility.
To avoid this, we could bind to a different port.
Listing 1.40: Binding to a different port
This would bind port 80 on the container to port 8080 on the local host.
We can also bind to a specific interface.
Listing 1.41: Binding to a specific interface
Here weve bound port 80 of the container to port 80 on the 127.0.0.1 interface
on the local host. We can also bind to a random port using the same structure.
Version: v1.10.3 (462f469)
32
Here weve removed the specific port to bind to on 127.0.0.1. We would now
use the docker inspect or docker port command to see which random port was
assigned to port 80 on the container.
TIP You can bind UDP ports by adding the suffix /udp to the port binding.
Docker also has a shortcut, -P, that allows us to publish all ports weve exposed
via EXPOSE instructions in our Dockerfile.
Listing 1.43: Exposing a port with docker run
This would publish port 80 on a random port on our local host. It would also
publish any additional ports we had specified with other EXPOSE instructions in
the Dockerfile that built our image.
33
NOTE
You can find the IP address of your local host with the ifconfig or ip
addr command.
$ curl localhost:49154
Hi, I am in your container
Dockerfile instructions
Weve already seen some of the available Dockerfile instructions, like RUN and
EXPOSE. But there are also a variety of other instructions we can put in our
Dockerfile. These include CMD, ENTRYPOINT, ADD, COPY, VOLUME, WORKDIR, USER,
ONBUILD, LABEL, STOPSIGNAL, ARG and ENV. You can see a full list of the available
Dockerfile instructions here.
Well also see a lot more Dockerfiles in the next few chapters and see how to
build some cool applications into Docker containers.
CMD
The CMD instruction specifies the command to run when a container is launched. It
is similar to the RUN instruction, but rather than running the command when the
container is being built, it will specify the command to run when the container
is launched, much like specifying a command to run when launching a container
with the docker run command, for example:
34
CMD ["/bin/true"]
WARNING
35
NOTE Its also important to understand the interaction between the CMD instruction and the ENTRYPOINT instruction. Well see some more details of this below.
Lets look at this process a little more closely. Lets say our Dockerfile contains
the CMD:
Listing 1.48: Overriding CMD instructions in the Dockerfile
CMD [ "/bin/bash" ]
We can build a new image (lets call it jamtur01/test) using the docker build
command and then launch a new container from this image.
Listing 1.49: Launching a container with a CMD instruction
36
TIME CMD
00:00:00 ps
You can see here that we have specified the /bin/ps command to list running
processes. Instead of launching a shell, the container merely returned the list
of running processes and stopped, overriding the command specified in the CMD
instruction.
TIP You can only specify one CMD instruction in a Dockerfile. If more than one
is specified, then the last CMD instruction will be used. If you need to run multiple
processes or commands as part of starting a container you should use a service
management tool like Supervisor.
ENTRYPOINT
Closely related to the CMD instruction, and often confused with it, is the ENTRYPOINT
instruction. So whats the difference between the two, and why are they both
needed? As weve just discovered, we can override the CMD instruction on the
docker run command line. Sometimes this isnt great when we want a container
to behave in a certain way. The ENTRYPOINT instruction provides a command
that isnt as easily overridden. Instead, any arguments we specify on the docker
run command line will be passed as arguments to the command specified in the
ENTRYPOINT. Lets see an example of an ENTRYPOINT instruction.
37
ENTRYPOINT ["/usr/sbin/nginx"]
Like the CMD instruction, we also specify parameters by adding to the array. For
example:
Listing 1.52: Specifying an ENTRYPOINT parameter
NOTE
As with the CMD instruction above, you can see that weve specified the
ENTRYPOINT command in an array to avoid any issues with the command being
prepended with /bin/sh -c.
38
Weve rebuilt our image and then launched an interactive container. We specified
the argument -g "daemon off;". This argument will be passed to the command
specified in the ENTRYPOINT instruction, which will thus become /usr/sbin/nginx
-g "daemon off;". This command would then launch the Nginx daemon in the
foreground and leave the container running as a web server.
We can also combine ENTRYPOINT and CMD to do some neat things. For example,
we might want to specify the following in our Dockerfile.
Listing 1.55: Using ENTRYPOINT and CMD together
ENTRYPOINT ["/usr/sbin/nginx"]
CMD ["-h"]
Now when we launch a container, any option we specify will be passed to the
Nginx daemon; for example, we could specify -g "daemon off"; as we did above
to run the daemon in the foreground. If we dont specify anything to pass to the
container, then the -h is passed by the CMD instruction and returns the Nginx help
text: /usr/sbin/nginx -h.
This allows us to build in a default command to execute when our container is run
combined with overridable options and flags on the docker run command line.
TIP
39
WORKDIR /opt/webapp/db
RUN bundle install
WORKDIR /opt/webapp
ENTRYPOINT [ "rackup" ]
Here weve changed into the /opt/webapp/db directory to run bundle install
and then changed into the /opt/webapp directory prior to specifying our
ENTRYPOINT instruction of rackup.
You can override the working directory at runtime with the -w flag, for example:
Listing 1.57: Overriding the working directory
40
This new environment variable will be used for any subsequent RUN instructions,
as if we had specified an environment variable prefix to a command like so:
Listing 1.59: Prefixing a RUN instruction
You can specify single environment variables in an ENV instruction or since Docker
1.4 you can specify multiple variables like so:
Listing 1.61: Setting multiple environment variables using ENV
41
Here weve specified a new environment variable, TARGET_DIR, and then used its
value in a WORKDIR instruction. Our WORKDIR instruction would now be set to /opt
/app.
NOTE
These environment variables will also be persisted into any containers created
from your image. So, if we were to run the env command in a container built
with the ENV RVM_PATH /home/rvm/ instruction wed see:
Listing 1.63: Persistent environment variables in Docker containers
root@bf42aadc7f09:~# env
. . .
RVM_PATH=/home/rvm/
. . .
You can also pass environment variables on the docker run command line using
the -e flag. These variables will only apply at runtime, for example:
42
Now our container has the WEB_PORT environment variable set to 8080.
USER
The USER instruction specifies a user that the image should be run as; for example:
Listing 1.65: Using the USER instruction
USER nginx
This will cause containers created from the image to be run by the nginx user. We
can specify a username or a UID and group or GID. Or even a combination thereof,
for example:
43
USER user
USER user:group
USER uid
USER uid:gid
USER user:gid
USER uid:group
You can also override this at runtime by specifying the -u flag with the docker run
command.
TIP The default user if you dont specify the USER instruction is root.
VOLUME
The VOLUME instruction adds volumes to any container created from the image.
A volume is a specially designated directory within one or more containers that
bypasses the Union File System to provide several useful features for persistent or
shared data:
This allows us to add data (like source code), a database, or other content into an
image without committing it to the image and allows us to share that data between
containers. This can be used to do testing with containers and an applications
Version: v1.10.3 (462f469)
44
VOLUME ["/opt/project"]
This would attempt to create a mount point /opt/project to any container created
from the image.
TIP Well see a lot more about volumes and how to use them in Chapters 5 and
6. If youre curious you can read more about volumes in the Docker volumes
documentation.
45
This ADD instruction will copy the file software.lic from the build directory to /
opt/application/software.lic in the image. The source of the file can be a URL,
filename, or directory as long as it is inside the build context or environment. You
cannot ADD files from outside the build directory or context.
When ADDing files Docker uses the ending character of the destination to determine what the source is. If the destination ends in a /, then it considers the source
a directory. If it doesnt end in a /, it considers the source a file.
The source of the file can also be a URL; for example:
Listing 1.70: URL as the source of an ADD instruction
Lastly, the ADD instruction has some special magic for taking care of local tar
archives. If a tar archive (valid archive types include gzip, bzip2, xz) is specified
as the source file, then Docker will automatically unpack it for you:
46
This will unpack the latest.tar.gz archive into the /var/www/wordpress/ directory. The archive is unpacked with the same behavior as running tar with the
-x option: the output is the union of whatever exists in the destination plus the
contents of the archive. If a file or directory with the same name already exists in
the destination, it will not be overwritten.
WARNING Currently this will not work with a tar archive specified in a URL.
This is somewhat inconsistent behavior and may change in a future release.
Finally, if the destination doesnt exist, Docker will create the full path for us,
including any directories. New files and directories will be created with a mode
of 0755 and a UID and GID of 0.
NOTE Its also important to note that the build cache can be invalidated by ADD
instructions. If the files or directories added by an ADD instruction change then
this will invalidate the cache for all following instructions in the Dockerfile.
COPY
The COPY instruction is closely related to the ADD instruction. The key difference
is that the COPY instruction is purely focused on copying local files from the build
context and does not have any extraction or decompression capabilities.
47
This will copy files from the conf.d directory to the /etc/apache2/ directory.
The source of the files must be the path to a file or directory relative to the build
context, the local source directory in which your Dockerfile resides. You cannot
copy anything that is outside of this directory, because the build context is uploaded to the Docker daemon, and the copy takes place there. Anything outside
of the build context is not available. The destination should be an absolute path
inside the container.
Any files and directories created by the copy will have a UID and GID of 0.
If the source is a directory, the entire directory is copied, including filesystem
metadata; if the source is any other kind of file, it is copied individually along
with its metadata. In our example, the destination ends with a trailing slash /, so
it will be considered a directory and copied to the destination directory.
If the destination doesnt exist, it is created along with all missing directories in
its path, much like how the mkdir -p command works.
LABEL
The LABEL instruction adds metadata to a Docker image. The metadata is in the
form of key/value pairs. Lets see an example.
Listing 1.73: Adding LABEL instructions
LABEL version="1.0"
LABEL location="New York" type="Data Center" role="Web Server"
48
Here we see the metadata we just defined using the LABEL instruction.
49
ARG build
ARG webapp_user=user
The second ARG instruction sets a default, if no value is specified for the argument
at build-time then the default is used. Lets use one of these arguments in a docker
build now.
Listing 1.76: Using an ARG instruction
As the jamtur01/webapp image is built the build variable will be set to 1234 and
the webapp_user variable will inherit the default value of user.
WARNING At this point youre probably thinking - this is a great way to pass
secrets like credentials or keys. Dont do this. Your credentials will be exposed
during the build process and in the build history of the image.
50
HTTP_PROXY
http_proxy
HTTPS_PROXY
https_proxy
FTP_PROXY
ftp_proxy
NO_PROXY
no_proxy
To use these predefined variables, pass them using the --build-arg <variable
>=<value> flag to the docker build command.
NOTE The ARG instruction was introduced in Docker 1.9 and you can read more
about it in the Docker documentation.
ONBUILD
The ONBUILD instruction adds triggers to images. A trigger is executed when the
image is used as the basis of another image (e.g., if you have an image that needs
source code added from a specific location that might not yet be available, or if
you need to execute a build script that is specific to the environment in which the
image is built).
The trigger inserts a new instruction in the build process, as if it were specified
right after the FROM instruction. The trigger can be any build instruction. For
example:
Version: v1.10.3 (462f469)
51
This would add an ONBUILD trigger to the image being created, which we see when
we run docker inspect on the image.
Listing 1.79: Showing ONBUILD instructions with docker inspect
For example, well build a new Dockerfile for an Apache2 image that well call
jamtur01/apache2.
52
FROM ubuntu:14.04
MAINTAINER James Turnbull "james@example.com"
RUN apt-get update && apt-get install -y apache2
ENV APACHE_RUN_USER www-data
ENV APACHE_RUN_GROUP www-data
ENV APACHE_LOG_DIR /var/log/apache2
ONBUILD ADD . /var/www/
EXPOSE 80
ENTRYPOINT ["/usr/sbin/apache2"]
CMD ["-D", "FOREGROUND"]
We now have an image with an ONBUILD instruction that uses the ADD instruction
to add the contents of the directory were building from to the /var/www/ directory
in our image. This could readily be our generic web application template from
which I build web applications.
Lets try this now by building a new image called webapp from the following
Dockerfile:
Version: v1.10.3 (462f469)
53
FROM jamtur01/apache2
MAINTAINER James Turnbull "james@example.com"
ENV APPLICATION_NAME webapp
ENV ENVIRONMENT development
We see that straight after the FROM instruction, Docker has inserted the ADD instruction, specified by the ONBUILD trigger, and then proceeded to execute the
remaining steps. This would allow me to always add the local source and, as Ive
done here, specify some configuration or build information for each application;
hence, this becomes a useful template image.
The ONBUILD triggers are executed in the order specified in the parent image and
are only inherited once (i.e., by children and not grandchildren). If we built another image from this new image, a grandchild of the jamtur01/apache2 image,
then the triggers would not be executed when that image is built.
Version: v1.10.3 (462f469)
54
NOTE There are several instructions you cant ONBUILD: FROM, MAINTAINER, and
NOTE The Docker Hub also has the option of private repositories. These are a
paid-for feature that allows you to store an image in a private repository that is
only available to you or anyone with whom you share it. This allows you to have
private images containing proprietary information or code you might not want to
share publicly.
We push images to the Docker Hub using the docker push command.
Lets try a push now.
Listing 1.84: Trying to push a root image
managed only by the Docker, Inc., team and will reject our attempt to write to
them. Lets try again.
Listing 1.85: Pushing a Docker image
This time, our push has worked, and weve written to a user repository, jamtur01
/static_web. We would write to your own user ID, which we created earlier, and
to an appropriately named image (e.g., youruser/yourimage).
We can now see our uploaded image on the Docker Hub.
56
TIP
You can find documentation and more information on the features of the
Docker Hub here.
Automated Builds
In addition to being able to build and push our images from the command line,
the Docker Hub also allows us to define Automated Builds. We can do so by connecting a GitHub or BitBucket repository containing a Dockerfile to the Docker
Hub. When we push to this repository, an image build will be triggered and a new
image created. This was previously also known as a Trusted Build.
Version: v1.10.3 (462f469)
57
NOTE Automated Builds also work for private GitHub and BitBucket repositories.
The first step in adding an Automated Build to the Docker Hub is to connect your
GitHub account or BitBucket to your Docker Hub account. To do this, navigate to
Docker Hub, sign in, click on your profile link, then click the Add Repository ->
Automated Build button.
58
59
60
push command.
You can only update it by pushing updates to your GitHub or BitBucket repository.
Deleting an image
We can also delete images when we dont need them anymore. To do this, well
use the docker rmi command.
61
Here weve deleted the jamtur01/static_web image. You can see Dockers layer
filesystem at work here: each of the Deleted: lines represents an image layer
being deleted.
NOTE
This only deletes the image locally. If youve previously pushed that
image to the Docker Hub, itll still exist there.
If you want to delete an images repository on the Docker Hub, youll need to sign
in and delete it there using the Delete repository link.
62
or, like the docker rm command cheat we saw in Chapter 3, we can do the same
with the docker rmi command:
63
TIP If youre running Docker behind a proxy or corporate firewall you can also
use the HTTPS_PROXY, HTTP_PROXY, NO_PROXY options to control how Docker connects.
64
NOTE
This will launch a container running version 2.0 of the registry application and
bind port 5000 to the local host.
TIP
If youre running an older version of the Docker Registry, prior to 2.0, you
can use the Migrator tool to upgrade to a new registry.
65
TAG
ID
CREATED
SIZE
jamtur01/static_web
latest
22d47c8cb6e5
24 seconds ago
12.29
Next we take our image ID, 22d47c8cb6e5, and tag it for our new registry. To
specify the new registry destination, we prefix the image name with the hostname
and port of our new registry. In our case, our new registry has a hostname of
docker.example.com.
Listing 1.91: Tagging our image for our new registry
After tagging our image, we can then push it to the new registry using the docker
push command:
66
The image is then posted in the local registry and available for us to build new
containers using the docker run command.
Listing 1.93: Building a container from our local registry
This is the simplest deployment of the Docker registry behind your firewall. It
doesnt explain how to configure the registry or manage it. To find out details
like configuring authentication, how to manage the backend storage for your images and how to manage your registry see the full configuration and deployments
details in the Docker Registry deployment documentation.
67
Alternative Indexes
There are a variety of other services and companies out there starting to provide
custom Docker registry services.
Quay
The Quay service provides a private hosted registry that allows you to upload both
public and private containers. Unlimited public repositories are currently free.
Private repositories are available in a series of scaled plans. The Quay product
has recently been acquired by CoreOS and will be integrated into that product.
Summary
In this chapter, weve seen how to use and interact with Docker images and the
basics of modifying, updating, and uploading images to the Docker Hub. Weve
also learned about using a Dockerfile to construct our own custom images. Finally, weve discovered how to run our own local Docker registry and some hosted
alternatives. This gives us the basis for starting to build services with Docker.
Well use this knowledge in the next chapter to see how we can integrate Docker
into a testing workflow and into a Continuous Integration lifecycle.
68
List of Figures
1.1 The Docker filesystem layers . . . . .
1.2 Docker Hub . . . . . . . . . . . . . . .
1.3 Creating a Docker Hub account. . . .
1.4 Your image on the Docker Hub. . . .
1.5 The Add Repository button. . . . . .
1.6 Selecting your repository. . . . . . .
1.7 Configuring your Automated Build.
1.8 Creating your Automated Build. . . .
1.9 Deleting a repository. . . . . . . . . .
69
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
3
5
13
57
58
59
60
61
63
Listings
1.1 Revisiting running a basic Docker container . . . . . . . . . .
1.2 Listing Docker images . . . . . . . . . . . . . . . . . . . . . . .
1.3 Pulling the Ubuntu 12.04 image . . . . . . . . . . . . . . . . .
1.4 Listing the ubuntu Docker images . . . . . . . . . . . . . . . .
1.5 Running a tagged Docker image . . . . . . . . . . . . . . . . .
1.6 Docker run and the default latest tag . . . . . . . . . . . . . .
1.7 Pulling the fedora image . . . . . . . . . . . . . . . . . . . . . .
1.8 Viewing the fedora image . . . . . . . . . . . . . . . . . . . . .
1.9 Pulling a tagged fedora image . . . . . . . . . . . . . . . . . .
1.10 Searching for images . . . . . . . . . . . . . . . . . . . . . . .
1.11 Pulling down the jamtur01/puppetmaster image . . . . . .
1.12 Creating a Docker container from the puppetmaster image
1.13 Logging into the Docker Hub . . . . . . . . . . . . . . . . . .
1.14 Creating a custom container to modify . . . . . . . . . . . .
1.15 Adding the Apache package . . . . . . . . . . . . . . . . . . .
1.16 Committing the custom container . . . . . . . . . . . . . . .
1.17 Reviewing our new image . . . . . . . . . . . . . . . . . . . .
1.18 Committing another custom container . . . . . . . . . . . . .
1.19 Inspecting our committed image . . . . . . . . . . . . . . . .
1.20 Running a container from our committed image . . . . . . .
1.21 Creating a sample repository . . . . . . . . . . . . . . . . . .
1.22 Our first Dockerfile . . . . . . . . . . . . . . . . . . . . . . . .
1.23 A RUN instruction in exec form . . . . . . . . . . . . . . . . .
1.24 Running the Dockerfile . . . . . . . . . . . . . . . . . . . . . .
1.25 Tagging a build . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.26 Building from a Git repository . . . . . . . . . . . . . . . . . .
70
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
1
4
6
6
7
9
9
9
10
10
11
11
14
14
15
15
16
16
17
17
18
19
20
22
23
23
Listings
1.27 Uploading the build context to the daemon . . . . .
1.28 Managing a failed instruction . . . . . . . . . . . . .
1.29 Creating a container from the last successful step .
1.30 Bypassing the Dockerfile build cache . . . . . . . .
1.31 A template Ubuntu Dockerfile . . . . . . . . . . . . .
1.32 A template Fedora Dockerfile . . . . . . . . . . . . .
1.33 Listing our new Docker image . . . . . . . . . . . . .
1.34 Using the docker history command . . . . . . . . . .
1.35 Launching a container from our new image . . . .
1.36 Viewing the Docker port mapping . . . . . . . . . .
1.37 The docker port command . . . . . . . . . . . . . . .
1.38 The docker port command with container name . .
1.39 Exposing a specific port with -p . . . . . . . . . . . .
1.40 Binding to a different port . . . . . . . . . . . . . . .
1.41 Binding to a specific interface . . . . . . . . . . . . .
1.42 Binding to a random port on a specific interface . .
1.43 Exposing a port with docker run . . . . . . . . . . .
1.44 Connecting to the container via curl . . . . . . . . .
1.45 Specifying a specific command to run . . . . . . . .
1.46 Using the CMD instruction . . . . . . . . . . . . . . .
1.47 Passing parameters to the CMD instruction . . . . .
1.48 Overriding CMD instructions in the Dockerfile . . .
1.49 Launching a container with a CMD instruction . .
1.50 Overriding a command locally . . . . . . . . . . . .
1.51 Specifying an ENTRYPOINT . . . . . . . . . . . . . .
1.52 Specifying an ENTRYPOINT parameter . . . . . . .
1.53 Rebuilding static_web with a new ENTRYPOINT .
1.54 Using docker run with ENTRYPOINT . . . . . . . .
1.55 Using ENTRYPOINT and CMD together . . . . . . .
1.56 Using the WORKDIR instruction . . . . . . . . . . .
1.57 Overriding the working directory . . . . . . . . . . .
1.58 Setting an environment variable in Dockerfile . . .
1.59 Prefixing a RUN instruction . . . . . . . . . . . . . .
1.60 Executing with an ENV prefix . . . . . . . . . . . . .
1.61 Setting multiple environment variables using ENV
Version: v1.10.3 (462f469)
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
24
25
26
26
27
28
28
29
30
31
31
31
32
32
32
33
33
34
35
35
35
36
36
37
38
38
38
39
39
40
40
41
41
41
41
71
Listings
1.62 Using an environment variable in other Dockerfile instructions
1.63 Persistent environment variables in Docker containers . . . . . .
1.64 Runtime environment variables . . . . . . . . . . . . . . . . . . . .
1.65 Using the USER instruction . . . . . . . . . . . . . . . . . . . . . .
1.66 Specifying USER and GROUP variants . . . . . . . . . . . . . . . .
1.67 Using the VOLUME instruction . . . . . . . . . . . . . . . . . . . .
1.68 Using multiple VOLUME instructions . . . . . . . . . . . . . . . .
1.69 Using the ADD instruction . . . . . . . . . . . . . . . . . . . . . . .
1.70 URL as the source of an ADD instruction . . . . . . . . . . . . . .
1.71 Archive as the source of an ADD instruction . . . . . . . . . . . .
1.72 Using the COPY instruction . . . . . . . . . . . . . . . . . . . . . .
1.73 Adding LABEL instructions . . . . . . . . . . . . . . . . . . . . . . .
1.74 Using docker inspect to view labels . . . . . . . . . . . . . . . . .
1.75 Adding ARG instructions . . . . . . . . . . . . . . . . . . . . . . . .
1.76 Using an ARG instruction . . . . . . . . . . . . . . . . . . . . . . .
1.77 The predefined ARG variables . . . . . . . . . . . . . . . . . . . . .
1.78 Adding ONBUILD instructions . . . . . . . . . . . . . . . . . . . . .
1.79 Showing ONBUILD instructions with docker inspect . . . . . . .
1.80 A new ONBUILD image Dockerfile . . . . . . . . . . . . . . . . . .
1.81 Building the apache2 image . . . . . . . . . . . . . . . . . . . . . .
1.82 The webapp Dockerfile . . . . . . . . . . . . . . . . . . . . . . . . .
1.83 Building our webapp image . . . . . . . . . . . . . . . . . . . . . .
1.84 Trying to push a root image . . . . . . . . . . . . . . . . . . . . . .
1.85 Pushing a Docker image . . . . . . . . . . . . . . . . . . . . . . . .
1.86 Deleting a Docker image . . . . . . . . . . . . . . . . . . . . . . . .
1.87 Deleting multiple Docker images . . . . . . . . . . . . . . . . . . .
1.88 Deleting all images . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.89 Running a container-based registry . . . . . . . . . . . . . . . . . .
1.90 Listing the jamtur01 static_web Docker image . . . . . . . . . . .
1.91 Tagging our image for our new registry . . . . . . . . . . . . . . .
1.92 Pushing an image to our new registry . . . . . . . . . . . . . . . .
1.93 Building a container from our local registry . . . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
42
42
43
43
44
45
45
46
46
47
48
48
49
50
50
51
52
52
53
53
54
54
55
56
62
63
64
65
66
66
67
67
72
Index
.dockerignore, 24
/var/lib/docker, 4
Automated Builds, 57
Build content, 47
Build context, 18, 24
.dockerignore, 24
Building images, 17
Bypassing the Dockerfile cache, 26
Context, 18
Debugging Dockerfiles, 25
Docker
Bind UDP ports, 33
Docker Hub, 5
Dockerfile
ADD, 46
ARG, 50
CMD, 34
COPY, 47
ENTRYPOINT, 37
ENV, 41
EXPOSE, 20, 33
FROM, 20
LABEL, 48
MAINTAINER, 20
ONBUILD, 51
RUN, 20
STOPSIGNAL, 49
USER, 43
VOLUME, 44
WORKDIR, 40
Running your own registry, 64
Security, 8
setting the working directory, 40
specifying a Docker build source, 23
tags, 7
docker
build, 17, 18, 21, 22, 50
no-cache, 26
-f, 23
context, 18
commit, 15
history, 28
images, 4, 9, 28, 65
inspect, 16, 33, 49, 52
login, 13
logout, 14
port, 31, 33
ps, 30
pull, 6, 9
push, 55, 61, 66
73
Index
rm, 63
rmi, 61, 63
run, 1, 8, 11, 21, 25, 30, 34, 35, 67
entrypoint, 39
expose, 21
-P, 33
-e, 42
-u, 44
-w, 40
set environment variables, 42
search, 10
tag, 66
Docker Content Trust, 8
Docker Hub, 5, 6, 10, 55, 57
Logging in, 13
Private repositories, 55
Docker Hub Enterprise, 5
Docker Inc, 8
Docker Trusted Registry, 5
Dockerfile, 17, 52, 57, 68
DSL, 17
exec format, 20
template, 27
Registry
private, 64
Supervisor, 37
tags, 7
Trusted builds, 57
Union mount, 2
exec format, 20
GitHub, 57
HTTP_PROXY, 64
HTTPS_PROXY, 64
NO_PROXY, 64
Port mapping, 20
Private repositories, 55
proxy, 64
Version: v1.10.3 (462f469)
74