In this article I intend to go over the different choices for a docker parent image. Choosing a good parent image for your service can be a difficult decision. The factors that I am going to consider are:
As a complement to this article I recommend reading:
The images that I will evaulate are:
To me convenience implies having the ability to run your software easily on the base image with little to zero hacks in order to run. For example if my service depends on the imagemagick library, adding this package to the image should be simple.
When building an image for a ruby service it would make sense to have a minimal ruby installation on the image. If additional steps are needed to install the necessary software then this decreases the convenience factor. If less effort is needed than this increases the convenience factor.
Let’s take a look at the centos base image.
も docker run -it centos:7 /bin/bash Unable to find image 'centos:7' locally 7: Pulling from library/centos Digest: sha256:6f6d986d425aeabdc3a02cb61c02abb2e78e57357e92417d6d58332856024faf Status: Downloaded newer image for centos:7 [root@16351ffd32af /]# ls / anaconda-post.log bin dev etc home lib lib64 media mnt opt proc root run sbin srv sys tmp usr var [root@16351ffd32af /]# ruby -v bash: ruby: command not found [root@16351ffd32af /]# yum install ruby -y [root@16351ffd32af /]# ruby -v ruby 2.0.0p648 (2015-12-16) [x86_64-linux]
Install ruby in CentOS is pretty easy. However, the current version of ruby in the default repos offers an out of date ruby that is no longer officially supported. This means if you want to use a supported ruby you will need to find other ways to install it yourself. In most cases, this means adding your own custom rpm repo’s and pulling the ruby from there.
Let’s take a look at Alpine linux.
も docker run -it alpine:latest /bin/sh Unable to find image 'alpine:latest' locally latest: Pulling from library/alpine 4fe2ade4980c: Pull complete Digest: sha256:621c2f39f8133acb8e64023a94dbdf0d5ca81896102b9e57c0dc184cadaf5528 Status: Downloaded newer image for alpine:latest / # / # apk add ruby fetch http://dl-cdn.alpinelinux.org/alpine/v3.8/main/x86_64/APKINDEX.tar.gz fetch http://dl-cdn.alpinelinux.org/alpine/v3.8/community/x86_64/APKINDEX.tar.gz (1/9) Installing ca-certificates (20171114-r3) (2/9) Installing gmp (6.1.2-r1) (3/9) Installing ncurses-terminfo-base (6.1_p20180818-r1) (4/9) Installing ncurses-terminfo (6.1_p20180818-r1) (5/9) Installing ncurses-libs (6.1_p20180818-r1) (6/9) Installing readline (7.0.003-r0) (7/9) Installing yaml (0.1.7-r0) (8/9) Installing ruby-libs (2.5.1-r2) (9/9) Installing ruby (2.5.1-r2) Executing busybox-1.28.4-r1.trigger Executing ca-certificates-20171114-r3.trigger OK: 25 MiB in 22 packages / # ruby -v ruby 2.5.1p57 (2018-03-29 revision 63029) [x86_64-linux-musl]
With very little effort a modern ruby was installed and available to an alpine image. CentOS makes it easy to install an unsupported version of ruby. Alpine makes it easy to install a modern supported version of ruby.
Alpine is the winner for convenience.
も sudo docker images | grep centos centos 7 5182e96772bf 8 weeks ago 200MB centos latest 5182e96772bf 8 weeks ago 200MB
The vanilla centos:7 image is 200MB.
も docker images | grep alpine alpine latest 196d12cf6ab1 2 weeks ago 4.41MB
The alpine image starts at 4.41MB. The CentOS image is 200 times larger than the alpine image.
Alpine is the winner in size.
The Alpine linux about page says:
Alpine Linux was designed with security in mind. The kernel is patched with an unofficial port of grsecurity/PaX, and all userland binaries are compiled as Position Independent Executables (PIE) with stack smashing protection. These proactive security features prevent exploitation of entire classes of zero-day and other vulnerabilities.
The minimal size of Alpine linux gives a smaller footprint to attack. However, flaws have been discovered in the alpine package manager.
Flaws will be found in any distribution but with less packages installed by default this decreases the attack surface for vulnerabilities that can lead to exploits.
Alpine uses musl libc instead of GNU libc. This can sometimes pose problems because musl libc doesn’t always exhibit the same behaviour as GNU libc and can lead to issues down the road. (Example: https://www.elastic.co/blog/docker-base-centos7)
Ships with GNU libc that is pretty well trusted and dependable. CentOS is the Community Enterprise OS which is the binary compatible version of RHEL. This means all security updates released by RHEL are picked up by CentOS. This allows for a larger community to respond and resolve security issues.
In terms of security, this one is difficult for me to judge. Alpine is a smaller distro but also has a smaller community that supports it. It uses a different libc that isn’t as hardened as GNU libc. In terms of security, I think CentOS is the winner due to it’s larger community and patches provided by RedHat.
In conclusion, I think CentOS is probably the safer choice. Alpine will likely work for most teams shipping services that don’t depend heavily on libc. Depending on how large your software is the final image may or may not be that different even if you start with the smaller Alpine image.
For the time being I will continue to use alpine for personal projects and wont push back to hard if we’re required to use CentOS at work.