.. index:: pair: Dockerfile ; Images .. _images_with_docker: ==================================================== Building Docker images with a Dockerfile ==================================================== .. seealso:: - https://avril2018.container.training/intro.yml.html#161 - :ref:`petazzoni` .. contents:: :depth: 3 Objectives ============= .. seealso:: - https://avril2018.container.training/intro.yml.html#163 We will build a container image automatically, with a Dockerfile. At the end of this lesson, you will be able to: - Write a Dockerfile. - Build an image from a Dockerfile. Dockerfile overview ====================== .. seealso:: - https://avril2018.container.training/intro.yml.html#164 - A Dockerfile is a build recipe for a Docker image. - It contains a series of instructions telling Docker how an image is constructed. - The :ref:`docker build command ` builds an image from a Dockerfile. Writing our first Dockerfile ============================== .. seealso:: - https://avril2018.container.training/intro.yml.html#165 Our Dockerfile must be in a new, empty directory. 1. Create a directory to hold our Dockerfile. - $ mkdir myimage 2. Create a Dockerfile inside this directory. - $ cd myimage - $ vim Dockerfile Of course, you can use any other editor of your choice. Type this into our Dockerfile... =================================== .. seealso:: - https://avril2018.container.training/intro.yml.html#166 :: FROM ubuntu RUN apt-get update RUN apt-get install figlet - FROM indicates the base image for our build. - Each RUN line will be executed by Docker during the build. - Our RUN commands must be non-interactive. (No input can be provided to Docker during the build.) In many cases, we will add the -y flag to apt-get. Build it! ============ .. seealso:: - https://avril2018.container.training/intro.yml.html#167 Save our file, then execute: $ docker build -t figlet . - -t indicates the tag to apply to the image. - . indicates the location of the build context. We will talk more about the build context later. To keep things simple for now: this is the directory where our Dockerfile is located. What happens when we build the image ? ========================================= The output of docker build looks like this: :: # docker build -t figlet . :: Sending build context to Docker daemon 2.048kB Step 1/3 : FROM ubuntu latest: Pulling from library/ubuntu a48c500ed24e: Already exists 1e1de00ff7e1: Already exists 0330ca45a200: Already exists 471db38bcfbf: Already exists 0b4aba487617: Already exists Digest: sha256:c8c275751219dadad8fa56b3ac41ca6cb22219ff117ca98fe82b42f24e1ba64e Status: Downloaded newer image for ubuntu:latest ---> 452a96d81c30 Step 2/3 : RUN apt-get update ---> Running in 81dab184c747 Get:1 http://archive.ubuntu.com/ubuntu bionic InRelease [242 kB] Get:2 http://archive.ubuntu.com/ubuntu bionic-updates InRelease [83.2 kB] Get:3 http://archive.ubuntu.com/ubuntu bionic-backports InRelease [65.5 kB] Get:4 http://archive.ubuntu.com/ubuntu bionic/universe Sources [11.5 MB] Get:5 http://security.ubuntu.com/ubuntu bionic-security InRelease [83.2 kB] Get:6 http://security.ubuntu.com/ubuntu bionic-security/universe Sources [3786 B] Get:7 http://security.ubuntu.com/ubuntu bionic-security/universe amd64 Packages [18.8 kB] Get:8 http://security.ubuntu.com/ubuntu bionic-security/main amd64 Packages [88.6 kB] Get:9 http://security.ubuntu.com/ubuntu bionic-security/multiverse amd64 Packages [1066 B] Get:10 http://archive.ubuntu.com/ubuntu bionic/main amd64 Packages [1344 kB] Get:11 http://archive.ubuntu.com/ubuntu bionic/multiverse amd64 Packages [186 kB] Get:12 http://archive.ubuntu.com/ubuntu bionic/restricted amd64 Packages [13.5 kB] Get:13 http://archive.ubuntu.com/ubuntu bionic/universe amd64 Packages [11.3 MB] Get:14 http://archive.ubuntu.com/ubuntu bionic-updates/universe Sources [28.7 kB] Get:15 http://archive.ubuntu.com/ubuntu bionic-updates/main amd64 Packages [127 kB] Get:16 http://archive.ubuntu.com/ubuntu bionic-updates/multiverse amd64 Packages [1660 B] Get:17 http://archive.ubuntu.com/ubuntu bionic-updates/universe amd64 Packages [79.3 kB] Fetched 25.2 MB in 17s (1496 kB/s) Reading package lists... Removing intermediate container 81dab184c747 ---> 01e04143b340 Step 3/3 : RUN apt-get install figlet ---> Running in 2dea10299bd1 Reading package lists... Building dependency tree... Reading state information... The following NEW packages will be installed: figlet 0 upgraded, 1 newly installed, 0 to remove and 11 not upgraded. Need to get 133 kB of archives. After this operation, 752 kB of additional disk space will be used. Get:1 http://archive.ubuntu.com/ubuntu bionic/universe amd64 figlet amd64 2.2.5-3 [133 kB] debconf: delaying package configuration, since apt-utils is not installed Fetched 133 kB in 22s (6148 B/s) Selecting previously unselected package figlet. (Reading database ... 4035 files and directories currently installed.) Preparing to unpack .../figlet_2.2.5-3_amd64.deb ... Unpacking figlet (2.2.5-3) ... Setting up figlet (2.2.5-3) ... update-alternatives: using /usr/bin/figlet-figlet to provide /usr/bin/figlet (figlet) in auto mode update-alternatives: warning: skip creation of /usr/share/man/man6/figlet.6.gz because associated file /usr/share/man/man6/figlet-figlet.6.gz (of link group figlet) doesn't exist Removing intermediate container 2dea10299bd1 ---> e8fd21b0252b Successfully built e8fd21b0252b Successfully tagged figlet:latest Sending the build context to Docker ======================================= .. seealso:: - https://avril2018.container.training/intro.yml.html#169 Sending build context to Docker daemon 2.048 kB - The build context is the . directory given to :ref:`docker build ` - It is sent (as an archive) by the Docker client to the Docker daemon. - This allows to use a remote machine to build using local files. - Be careful (or patient) if that directory is big and your link is slow. Executing each step ==================== .. seealso:: - https://avril2018.container.training/intro.yml.html#169 The caching system ===================== .. seealso:: - https://avril2018.container.training/intro.yml.html#171 If you run the same build again, it will be instantaneous. Why ? After each build step, Docker takes a snapshot of the resulting image. Before executing a step, Docker checks if it has already built the same sequence. Docker uses the exact strings defined in your Dockerfile, so: - RUN apt-get install figlet cowsay is different from RUN apt-get install cowsay figlet - RUN apt-get update is not re-executed when the mirrors are updated You can force a rebuild with docker build --no-cache .... :: # docker images :: REPOSITORY TAG IMAGE ID CREATED SIZE figlet latest e8fd21b0252b 10 minutes ago 121MB ubuntu latest 452a96d81c30 4 weeks ago 79.6MB Running the image ==================== .. seealso:: - https://avril2018.container.training/intro.yml.html#172 The resulting image is not different from the one produced manually. .. _viewing_history: Using image and viewing history ================================= .. seealso:: - https://avril2018.container.training/intro.yml.html#173 The :ref:`history ` command lists all the layers composing an image. For each layer, it shows its creation time, size, and creation command. When an image was built with a Dockerfile, each layer corresponds to a line of the Dockerfile. Introducing JSON syntax ========================== .. seealso:: - https://avril2018.container.training/intro.yml.html#174 Most Dockerfile arguments can be passed in two forms: - plain string: RUN apt-get install figlet - JSON list: RUN ["apt-get", "install", "figlet"] We are going to change our Dockerfile to see how it affects the resulting image. JSON syntax vs string syntax =============================== .. seealso:: - https://avril2018.container.training/intro.yml.html#176 Compare the new history: :: IMAGE CREATED CREATED BY SIZE COMMENT ba8d944adee0 39 seconds ago apt-get install figlet 992kB 01e04143b340 18 minutes ago /bin/sh -c apt-get update 40.5MB 452a96d81c30 4 weeks ago /bin/sh -c #(nop) CMD ["/bin/bash"] 0B 4 weeks ago /bin/sh -c mkdir -p /run/systemd && echo 'do… 7B 4 weeks ago /bin/sh -c sed -i 's/^#\s*\(deb.*universe\)$… 2.76kB 4 weeks ago /bin/sh -c rm -rf /var/lib/apt/lists/* 0B 4 weeks ago /bin/sh -c set -xe && echo '#!/bin/sh' > /… 745B 4 weeks ago /bin/sh -c #(nop) ADD file:81813d6023adb66b8… 79.6MB - JSON syntax specifies an exact command to execute. - String syntax specifies a command to be wrapped within /bin/sh -c "...". When to use JSON syntax and string syntax =========================================== .. seealso:: - https://avril2018.container.training/intro.yml.html#177 String syntax --------------- - is easier to write - interpolates environment variables and other shell expressions - creates an extra process (/bin/sh -c ...) to parse the string - requires /bin/sh to exist in the container JSON syntax ------------- - is harder to write (and read!) - passes all arguments without extra processing - doesn't create an extra process - doesn't require /bin/sh to exist in the container