Building Docker Images
Jump to navigation
Jump to search
Copyright Notice
Copyright © 2004-2023 by NobleProg Limited All rights reserved.
This publication is protected by copyright, and permission must be obtained from the publisher prior to any prohibited reproduction, storage in a retrieval system, or transmission in any form or by any means, electronic, mechanical, photocopying, recording, or likewise.
Author
Building images
There are two ways of building images:
- build an image based on existing container
- build an image from a Dockerfile
Committing an existing container
$ docker container run -it --name mc_test ubuntu
# inside of the container run: apt-get update && apt-get install mc
$ docker container diff mc_test
$ docker container commit -m "installed mc" mc_test training:mc1
Building an image from a Dockerfile
- Create a new directory and "Dockerfile" text file
- After that run the command below to build an image
$ docker image build [options] PATH | URL | -
$ docker image build -t training:mc2 .
- build context
- the PATH is a directory on your local filesystem.
- the URL is a the location of a Git repository.
- the build is run by the Docker daemon, not by the Client
- .dockerignore
- some selected options:
- -t - name and a tag in the repository/name:tag format
- -f or --file - name of the Dockerfile (default: PATH/Dockerfile)
FROM
- sets the base image for subsequent instructions
- from must be the first instruction in Dockerfile
FROM <image>
FROM <image>:<tag>
FROM <image>@<digest>
FROM ubuntu:trusty
RUN
- RUN instruction will execute any commands in a new layer on top of the current image and commit the results
- newly created image will be used for the next step in the Dockerfile
- RUN has 2 forms:
- exec form makes it possible to run commands using a base image that does not contain /bin/sh
- makes it possible to avoid shell string munging
RUN ["executable", "param1", "param2"]
RUN ["apt-get", "update"]
RUN ["apt-get", "install", "-y", "mc"]
- shell form - the command is run in a shell (/bin/sh -c)
- use a && (double ampersand) to combine many commands in one layer
- use a \ (backslash) to continue a single run instruction onto the next line
RUN <command>
RUN apt-get update
RUN apt-get install -y mc
RUN apt-get update && apt-get install -y mc
Layering RUN instructions and cache
- one of the key concepts of Docker: commits are cheap
- containers can be created from any point in an image’s history (docker images -a)
- cache and commands like apt-get update
- --no-cache - do not use cache when building the image
- --pull - always attempt to pull a newer version of the base image
COPY
- the COPY instruction copies files or directories from <src> to the filesystem of the container at the path <dest>
- <src> path must be inside the context of the build
- if <src> is a directory, the entire contents of the directory are copied but the directory itself is not copied
- slash after the directory name is important
- if <dest> doesn’t exist, it is created along with all missing directories in its path
- COPY has two forms:
- second one is required for paths containing whitespace
COPY <src> <src2>... <dest>
COPY ["<src>", "<src2>",... "<dest>"]
COPY file1 file1
COPY fil* /
COPY dir1/ /dest_dir/
COPY file1 dir1/ /dest_dir/
COPY ["file 1", "dir 1", "/dest/dir/"]
ADD
- ADD instruction can copy local files like COPY but has two extra features:
- remote URL support
- local tar extraction (only local)
WORKDIR
- WORKDIR instruction sets the working directory for any RUN, CMD, ENTRYPOINT, COPY and ADD instructions that follow it in the Dockerfile
CMD
- the main purpose of a CMD is to provide defaults for an executing container
- for example /bin/bash is default CMD for Ubuntu official image
- only the last CMD will take effect
- CMD has two forms:
- exec form, this is the preferred form
- skip executable to define default parameters to ENTRYPOINT
- does not invoke a command shell, so the variable substitution is not going to happen
CMD ["executable","param1","param2"]
CMD ["ping", "nobleprog.pl", "-c", "3"]
CMD ["param1","param2"]
CMD ["nobleprog.pl", "-c", "3"] # will work if ENTRYPOINT is set to ping
- shell form
- command is exectuted in /bin/sh -c
CMD command param1 param2
CMD ping nobleprog.pl -c 3
ENTRYPOINT
- allows you to configure a container that will run as an executable
- only the last ENTRYPOINT will take effect
- command line arguments to docker run will be appended after all elements in an exec form ENTRYPOINT, and will override all elements specified using CMD
- use docker run --entrypoint to override image ENTRYPOINT
- ENTRYPOINT has two forms:
- exec form, this is the preferred form
- this form allows to gracefully shut down using docker stop command
ENTRYPOINT ["executable", "param1", "param2"]
ENTRYPOINT ["ping", "-c", "5"]
CMD ["localhost"]
- shell form
- shell form prevents any CMD or run command line arguments from being used
- ENTRYPOINT will be started as a subcommand of /bin/sh -c (which does not pass signals)
ENTRYPOINT command param1 param2
ENTRYPOINT ping -c 5 localhost
Exercises
ENV
- sets the environment variable <key> to the value <value>
- ENV has two forms:
- first form - entire string after the first space will be treated as the <value> (including characters such as spaces and quotes)
- second form (uses equal sign) - allows for multiple variables to be set at once (quotes and backslashes can be used)
ENV <key> <value>
ENV <key1>=<value1> <key2>=<value2> \
<key3>=<value3>
ENV APACHE_LOCK_DIR="/var/lock/apache2" \
APACHE_PID_FILE="/var/run/apache2/apache2.pid" \
APACHE_RUN_USER="www-data" \
APACHE_RUN_GROUP="www-data" \
APACHE_LOG_DIR="/var/log/apache2/"
EXPOSE
- informs Docker that the container listens on the specified network ports at runtime
- it does not make the ports of the container accessible to the host
- use run -p flag to publish a range of ports
- use run -P flag to publish all of the exposed ports
EXPOSE 80 443
docker run -P image_name
docker run -p 8080:80 -p 443:443 image_name
Exercises
USER
- sets the user name or UID to use when running the image
- and for any RUN, CMD and ENTRYPOINT instructions that follow it in the Dockerfile
- try to avoid installing or using sudo since it has unpredictable TTY and signal-forwarding behaviour
- if you need functionality similar to sudo you should use gosu (https://github.com/tianon/gosu)
USER mongodb
VOLUME
- creates a mount point with the specified name and marks it as holding externally mounted volumes from native host or other containers
- docker run command initializes the newly created volume with any data that exists at the specified location within the base image
VOLUME ["/data"]
VOLUME ["/var/log"]
Exercises
Other commands
- LABEL - adds metadata to an image
LABEL <key>=<value> <key>=<value> <key>=<value> ...
LABEL version="1.0" description="Docker Training" maintainer="Kamil Baran"
- ARG - defines a variable that users can pass at build-time to the builder
- ONBUILD - adds to the image a trigger instruction to be executed at a later time, when the image is used as the base for another build
- STOPSIGNAL - sets the system call signal that will be sent to the container to exit.
Automated Docker builds
- https://github.com/KamilBaran/nobleprog_docker_training
- https://hub.docker.com/r/kamilbaran/nobleprog_training/
gosu helper tool
- Simple Go-based setuid+setgid+setgroups+exec
- https://github.com/tianon/gosu
RUN apt-get update \
&& apt-get install -y --no-install-recommends ca-certificates curl numactl \
&& rm -rf /var/lib/apt/lists/* \
&& gpg --keyserver ha.pool.sks-keyservers.net --recv-keys B42F6819007F00F88E364FD4036A9C25BF357DD4 \
&& curl -o /usr/local/bin/gosu -SL "https://github.com/tianon/gosu/releases/download/1.6/gosu-$(dpkg --print-architecture)" \
&& curl -o /usr/local/bin/gosu.asc -SL "https://github.com/tianon/gosu/releases/download/1.6/gosu-$(dpkg --print-architecture).asc" \
&& gpg --verify /usr/local/bin/gosu.asc \
&& rm /usr/local/bin/gosu.asc \
&& chmod +x /usr/local/bin/gosu
#!/bin/bash
# exit immediately if a command exits with a non-zero status.
set -e
# if the first character in arguments is -
if [ "${1:0:1}" = '-' ]; then
# set all arguments to mongod process
set -- mongod "$@"
fi
# if the cmd is mongod
if [ "$1" = 'mongod' ]; then
# disable numa
numa='numactl --interleave=all'
if $numa true &> /dev/null; then
set -- $numa "$@"
fi
# run mongod as mongodb user
exec gosu mongodb "$@"
fi
# run any other than mongod process
exec "$@"
Running more than one process in a container
FROM ubuntu:trusty
RUN apt-get update \
&& apt-get install -y openssh-server supervisor \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/* \
&& mkdir -p /var/run/sshd /var/log/supervisor
COPY supervisord.conf /etc/supervisor/conf.d/supervisord.conf
EXPOSE 22
CMD ["/usr/bin/supervisord"]
[supervisord]
nodaemon=true
[program:ping1]
command=/bin/ping nobleprog.pl
[program:ping2]
command=/bin/ping nobleprog.co.uk
[program:ping3]
command=/bin/ping nobleprog.cn
[program:sshd]
command=/usr/sbin/sshd -D
Exercises
General guidelines and recommendations
- Containers should be ephemeral
- Use a .dockerignore file
- Avoid installing unnecessary packages
- Run only one process per container
- Minimize the number of layers
- Sort multi-line arguments
- Build cache
Exercises