Building Docker Images

From Training Material
Jump to navigation Jump to search
The printable version is no longer supported and may have rendering errors. Please update your browser bookmarks and please use the default browser print function instead.


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
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



gosu helper tool


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