Building images interactively

Building images interactively

In this section, we will create our first container image.

It will be a basic distribution image, but we will pre-install the package figlet.

We will:

  • Create a container from a base image.

  • Install software manually in the container, and turn it into a new image.

  • Learn about new commands:

The plan

  • Create a container (with docker run) using our base distro of choice.

  • Run a bunch of commands to install and set up our software in the container.

  • (Optionally) review changes in the container with docker diff.

  • Turn the container into a new image with docker commit.

  • (Optionally) add tags to the image with docker tag.

Setting up our container

Start an Ubuntu container:

$ docker run -it ubuntu
root@5d5da832b81a:/#

root@<yourContainerId>:#/

root@5d5da832b81a:/# apt-get update
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/main amd64 Packages [88.6 kB]
Get:8 http://security.ubuntu.com/ubuntu bionic-security/universe amd64 Packages [18.8 kB]
Get:9 http://security.ubuntu.com/ubuntu bionic-security/multiverse amd64 Packages [1066 B]
Get:10 http://archive.ubuntu.com/ubuntu bionic/universe amd64 Packages [11.3 MB]
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/main amd64 Packages [1344 kB]
Get:14 http://archive.ubuntu.com/ubuntu bionic-updates/universe Sources [28.7 kB]
Get:15 http://archive.ubuntu.com/ubuntu bionic-updates/universe amd64 Packages [79.3 kB]
Get:16 http://archive.ubuntu.com/ubuntu bionic-updates/multiverse amd64 Packages [1660 B]
Get:17 http://archive.ubuntu.com/ubuntu bionic-updates/main amd64 Packages [127 kB]
Fetched 25.2 MB in 17s (1527 kB/s)
Reading package lists... Done
root@5d5da832b81a:/# apt-get install figlet
Reading package lists... Done
Building dependency tree
Reading state information... Done
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]
Fetched 133 kB in 0s (382 kB/s)
debconf: delaying package configuration, since apt-utils is not installed
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

Inspect the changes

  • Open a new session into the docker server

  • type docker ps to get the container id

[root@intranet-dev ~]# docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED              STATUS              PORTS               NAMES
814973c3cf61        ubuntu              "/bin/bash"         About a minute ago   Up About a minute                       sharp_minsky

Now let’s run docker diff to see the difference between the base image and our container.

[root@intranet-dev ~]# docker diff 814
C /usr
C /usr/share
A /usr/share/figlet
A /usr/share/figlet/646-cn.flc
A /usr/share/figlet/646-de.flc
A /usr/share/figlet/646-gb.flc
A /usr/share/figlet/646-pt.flc
A /usr/share/figlet/8859-3.flc
A /usr/share/figlet/8859-4.flc
A /usr/share/figlet/koi8r.flc
A /usr/share/figlet/script.flf
A /usr/share/figlet/ushebrew.flc
A /usr/share/figlet/646-es2.flc
A /usr/share/figlet/646-hu.flc
A /usr/share/figlet/646-no.flc
A /usr/share/figlet/646-yu.flc
A /usr/share/figlet/ivrit.flf
A /usr/share/figlet/646-irv.flc
A /usr/share/figlet/frango.flc

Docker tracks filesystem changes

As explained before:

  • An image is read-only.

  • When we make changes, they happen in a copy of the image.

  • Docker can show the difference between the image, and its copy.

For performance, Docker uses copy-on-write systems. (i.e. starting a container based on a big image doesn’t incur a huge copy.)

Copy-on-write security benefits

  • docker diff gives us an easy way to audit changes (à la Tripwire)

  • Containers can also be started in read-only mode (their root filesystem will be read-only, but they can still have read-write data volumes)

Commit our changes into a new image

The docker commit command will create a new layer with those changes, and a new image using this new layer.

$ docker commit 814

sha256:c10a9dbc718b49ba25af4fcd99d57c0fddd1dcd87d3ab8f878caaeb135b4521f

The output of the docker commit command will be the ID for your newly created image.

We can use it as an argument to docker run.

Testing our new image

Let’s run this image:

$ docker run -it c10a
root@d73eb40949d3:/# figlet hello
 _          _ _
| |__   ___| | | ___
| '_ \ / _ \ | |/ _ \
| | | |  __/ | | (_) |
|_| |_|\___|_|_|\___/

Tagging images

Referring to an image by its ID is not convenient. Let’s tag it instead.

We can use the tag command:

$ docker tag c10a figlet

But we can also specify the tag as an extra argument to commit:

$ docker commit c10a figlet

And then run it using its tag::

$ docker run -it figlet

What’s next ?

Manual process = bad. Automated process = good.

In the next chapter, we will learn how to automate the build process by writing a Dockerfile.