Docker images are compiled in layers using a set of instructions contained in a text file called Dockerfile. Every container image starts with a base in many cases this base image is pulled from Dockerhub or your own repository. When creating your own base image you have two choices build one or use scratch.
Scratch is build into docker and is provided as a minimal linux environment that cannot do anything. If you have a compiled binary that will work in the container scratch may be a perfect minimal container. Do not expect scratch to have a package manager or even command line. For our example lets assume we have a basic c program called hello-world and is compiled:
FROM Scratch ADD hello / CMD ["/hello"]
This would start the container run the executable and end the container. My base container size with scratch alone is 1.84 kilobytes.
Building your own Image
Building your own image starts with install of the target operating system. Since Redhat and Ubuntu seem to be the most common operating systems available today I’ll provide instructions for both. In these instructions it’s possible to build minimal containers without package managers but these are multi-purpose base images. In both cases the process installs a minimal version of the operating system in a subdirectory then compiles the docker image from this directory.
Debian based systems make it really easy with the debootstrap command which is installed by default on Ubuntu. We will setup the image using Ubuntu 19.04 Disco Dingo.
sudo debootstrap disco disco > /dev/null sudo tar -C disco -c . | docker import - disco
You now have a docker image called disco that is a minimal Ubuntu 19.04.
Redhat / Centos
I’ll use centos since I don’t personally own any Redhat licenses but the process is exactly the same. This will build the same version of OS you are currently running. You will need to change the RPM-GPG-KEY with your version of Centos. I read about the CentOS process in this article.
# Create a folder for our new root structure export centos_root='/image/rootfs' mkdir -p $centos_root rpm --root $centos_root --initdb yum reinstall --downloadonly --downloaddir . centos-release rpm --root $centos_root -ivh --nodeps centos-release*.rpm rpm --root $centos_root --import $centos_root/etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7 yum -y --installroot=$centos_root --setopt=tsflags='nodocs' --setopt=override_install_langs=en_US.utf8 install yum sed -i "/distroverpkg=centos-release/a override_install_langs=en_US.utf8\ntsflags=nodocs" $centos_root/etc/yum.conf cp /etc/resolv.conf $centos_root/etc mount -o bind /dev $centos_root/dev #Enter the file system for our image to do a yum clean (remove cached stuff) type exit to leave the chroot chroot $centos_root /bin/bash # Run this command then type exit to leave the chroot yum clean all rm -f $centos_root/etc/resolv.conf umount $centos_root/dev #Create the docker image tar -C $centos_root -c . | docker import - centos
You now have a image called centos.
Put it together
Building your own images assures that no one can put something into your image that is unexpected. Scratch is a great way to run very minimal containers that are very small. If you need a fuller operating system you can use Ubuntu or CentOS.