There are three ways to create a new container image on your system.

  1. Interactively building a container that contains all the additions and changes one desire and then committing those changes into a new image.
  2. Use a Dockerfile to describe what’s in the new image and then build this image using that Dockerfile as a manifest.
  3. By importing it into the system from a tarball.

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.10/main/x86_64/APKINDEX.tar.gz fetch http://dl-cdn.alpinelinux.org/alpine/v3.10/community/x86_64/APKINDEX.tar.gz v3.10.2-189-g393dc02e8c [http://dl-cdn.alpinelinux.org/alpine/v3.10/main] v3.10.2-189-g393dc02e8c [http://dl-cdn.alpinelinux.org/alpine/v3.10/community] OK: 10337 distinct packages available (1/2) Installing libcap (2.27-r0) (2/2) Installing iputils (20180629-r1) Executing busybox-1.30.1-r2.trigger OK: 6 MiB in 16 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.020 ms 64 bytes from 127.0.0.1: icmp_seq=2 ttl=64 time=0.027 ms 64 bytes from 127.0.0.1: icmp_seq=3 ttl=64 time=0.039 ms 64 bytes from 127.0.0.1: icmp_seq=4 ttl=64 time=0.025 ms ^C — 127.0.0.1 ping statistics — 4 packets transmitted, 4 received, 0% packet loss, time 79ms rtt min/avg/max/mdev = 0.020/0.027/0.039/0.009 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 1c187b8e03a1 alpine “/bin/sh” 7 minutes ago Exited (0) 51 seconds ago sample

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 /etc C /etc/apk C /etc/apk/world C /root A /root/.ash_history C /usr C /usr/lib A /usr/lib/libcap.so.2 A /usr/lib/libcap.so.2.27 C /usr/sbin A /usr/sbin/getcap A /usr/sbin/rdisc A /usr/sbin/capsh A /usr/sbin/getpcaps A /usr/sbin/ipg A /usr/sbin/ninfod A /usr/sbin/clockdiff A /usr/sbin/tftpd A /usr/sbin/rarpd A /usr/sbin/tracepath A /usr/sbin/tracepath6 C /usr/sbin/arping A /usr/sbin/setcap C /bin A /bin/traceroute6 C /bin/ping C /bin/ping6

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:10c4a665426a41c98d2e8bd789ff0d7286ed3371184944d3f4f97a2f9824b604

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 10c4a665426a 2 minutes ago 7.34MB

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.

Leave a Reply

Your email address will not be published. Required fields are marked *