Building Docker Images

From Training Material
Jump to navigation Jump to search


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