There are three ways to create a new container image on your system.
- Interactively building a container that contains all the additions and changes one desire and then committing
those changes into a new image.
- Use a Dockerfile to describe what’s in the new image and then build this image using that Dockerfile as a
manifest.
- 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.