Building Docker Images: Difference between revisions
												
				Jump to navigation
				Jump to search
				
(No difference) 
 | 
Latest revision as of 14:53, 10 September 2019
 
Copyright Notice
Copyright © 2004-2025 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