Interactive image creation

The first way we can create a custom image is by interactively building a container. That is, we start with a base image that we want to use as a template and run a container of it interactively. Let's say that this is the alpine image. The command to run the container would then be as follows:

$ docker container run -it --name sample alpine /bin/sh

By default, the alpine container does not have the ping tool installed. Let's assume we want to create a new custom image that has ping installed. Inside the container, we can then run the following command:

/ # apk update && apk add iputils

This uses the Alpine package manager apk to install the iputils library, of which ping is a part. The output of the preceding command should look as follows:

fetch http://dl-cdn.alpinelinux.org/alpine/v3.7/main/x86_64/APKINDEX.tar.gz
fetch http://dl-cdn.alpinelinux.org/alpine/v3.7/community/x86_64/APKINDEX.tar.gz
v3.7.0-50-gc8da5122a4 [http://dl-cdn.alpinelinux.org/alpine/v3.7/main]
v3.7.0-49-g06d6ae04c3 [http://dl-cdn.alpinelinux.org/alpine/v3.7/community]
OK: 9046 distinct packages available
(1/2) Installing libcap (2.25-r1)
(2/2) Installing iputils (20121221-r8)
Executing busybox-1.27.2-r6.trigger
OK: 4 MiB in 13 packages

Now, we can indeed use ping, as the following snippet shows:

/ # ping 127.0.0.1
PING 127.0.0.1 (127.0.0.1) 56(84) bytes of data.
64 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.028 ms
64 bytes from 127.0.0.1: icmp_seq=2 ttl=64 time=0.044 ms
64 bytes from 127.0.0.1: icmp_seq=3 ttl=64 time=0.049 ms
^C
--- 127.0.0.1 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2108ms
rtt min/avg/max/mdev = 0.028/0.040/0.049/0.010 ms

Once we have finished our customization, we can quit the container by typing exit at the prompt. If we now list all containers with docker container ls -a, we can see that our sample container has a status of Exited, but still exists on the system:

$ docker container ls -a | grep sample
eff7c92a1b98 alpine "/bin/sh" 2 minutes ago Exited (0) ...

If we want to see what has changed in our container in relation to the base image, we can use the docker container diff command as follows:

$ docker container diff sample

The output should present a list of all modifications done on the filesystem of the container:

C /bin
C /bin/ping
C /bin/ping6
A /bin/traceroute6
C /etc/apk
C /etc/apk/world
C /lib/apk/db
C /lib/apk/db/installed
C /lib/apk/db/lock
C /lib/apk/db/scripts.tar
C /lib/apk/db/triggers
C /root
A /root/.ash_history
C /usr/lib
A /usr/lib/libcap.so.2
A /usr/lib/libcap.so.2.25
C /usr/sbin
C /usr/sbin/arping
A /usr/sbin/capsh
A /usr/sbin/clockdiff
A /usr/sbin/getcap
A /usr/sbin/getpcaps
A /usr/sbin/ipg
A /usr/sbin/rarpd
A /usr/sbin/rdisc
A /usr/sbin/setcap
A /usr/sbin/tftpd
A /usr/sbin/tracepath
A /usr/sbin/tracepath6
C /var/cache/apk
A /var/cache/apk/APKINDEX.5022a8a2.tar.gz
A /var/cache/apk/APKINDEX.70c88391.tar.gz
C /var/cache/misc

In the preceding list, A stands for added, and C for changed. If we had any deleted files, then those would be prefixed with D.

We can now use the docker container commit command to persist our modifications and create a new image from them:

$ docker container commit sample my-alpine
sha256:44bca4141130ee8702e8e8efd1beb3cf4fe5aadb62a0c69a6995afd49c2e7419

With the preceding command, we have specified that the new image shall be called my-alpine. The output generated by the preceding command corresponds to the ID of the newly generated image. We can verify this by listing all images on our system, as follows:

$ docker image ls

We can see this image ID (shortened) as follows:

REPOSITORY TAG      IMAGE ID        CREATED               SIZE
my-alpine latest 44bca4141130 About a minute ago 5.64MB
...

We can see that the image  named my-alpine, has the expected ID of 44bca4141130 and automatically got a tag latest assigned. This happens since we did not explicitly define a tag ourselves. In this case, Docker always defaults to the tag latest.

If we want to see how our custom image has been built, we can use the history command as follows:

$ docker image history my-alpine

This will print the list of layers our image consists of:

IMAGE            CREATED         CREATED BY          SIZE       COMMENT
44bca4141130 3 minutes ago /bin/sh 1.5MB
e21c333399e0 6 weeks ago /bin/sh -c #... 0B
<missing> 6 weeks ago /bin/sh -c #... 4.14MB

The first layer in the preceding list is the one that we just created by adding the iputils package.