docker on a diet
TRANSCRIPT
Docker
"Rena ship 07" by New Zealand Defence Force from Wellington, New Zealand - NZ Defence Force assistance to OP Rena. Licensed under CC BY 2.0 via Wikimedia Commons
on a diet
Motivation• Let’s download our base image!
• ZZZzzzz……
Sample image sizes
debian:wheezy 85.1 MB
ubuntu:trusty 188.3 MB
phusion/baseimage:0.9.16 279.7 MB
wordpress:4.1.0 470 MB
google/golang:latest 611.3 MB
python:2.7.9 744.9 MB
Docker containers
This
Not this
But wait!
– phusion/baseimage README
“Docker only needs to download the base image once: during the first deploy. On every subsequent deploys [sic], only the changes you make on top of the base image are downloaded.”
So why lean containers?• Continuous integration / automated testing
• Should you test your containers? (Hint: yes) • Third party CI services boot up a fresh
environment each time
• Fast bootstrapping • A new host in e.g. an autoscaling cluster has to
download all images from scratch
• Bandwidth / transfer • Especially if you’re running a private registry
Trim the fatMethod 1
Removing artifacts
• Example from phusion/baseimage:
• Just typical housekeeping • Without: 334.6 MB • With: 314 MB (-20.6 MB)
apt-get clean rm -rf /tmp/* /var/tmp/* rm -rf /var/lib/apt/lists/* rm -f /etc/ssh/ssh_host_* rm -rf /usr/share/man/?? rm -rf /usr/share/man/??_*
You ain’t gonna need it• phusion/baseimage installs syslog-ng, logrotate and
openssh-server (sshd) • SSH isn’t needed now that we have docker exec
(addressed in a blog post) • Log management: dump process logs to stdout and
use a collection container like progrium/logspout • Alternatively mount /dev/log into your container • With log management + sshd: 314 MB • Without: 279 MB (-35 MB)
Reducing dependencies• For example: a frontend app that uses a Gulp
pipeline with gulp-ruby-sass • This requires “gem install sass”, which requires
“apt-get install ruby-full rubygems-integration” • OR you could switch to gulp-sass and use
native bindings to libsass (C implementation) • With gulp-ruby-sass: 487.2 MB • With gulp-sass: 386 MB (-101.2 MB)
Delegate rolesMethod 2
Splitting your containers
• Differentiate between “build” and “runtime” • Compilation tools and libraries should not be
present in your production environment • Build your app in a “dev” or “builder” container
and transfer it to a “runtime” container • Specialized utility containers as standalone
binaries
Build pipeline
• How do I write my Dockerfile now that I need another container (or more) to build my app?
• Script a build pipeline! • Process your source files in a shared volume
with your build container(s) before loading it into your base runtime image as the final step
• A popular approach is to use Makefiles
Docker Makefiles
GIT = pie/git BUILD = pie/builder IMAGE = pie/hubot
hubot: docker run --rm -v $(pwd):/opt:rw -e GPG=$$GPG $(GIT) /bin/bash -c “[…]”
hubot.tar: | hubot docker run --rm -v $(pwd):/opt:rw $(BUILD) /bin/bash -c “npm […] && tar […]”
build: hubot.tar Dockerfile docker build -t $(IMAGE):latest --rm --no-cache .
clean: rm -rf hubot && rm -f hubot.tar
Credentials
Git container
Builder container
Shared folder (alternatively, make a data container)
Change your imageMethod 3
Switching the base image
• Basing your image off Debian instead of Ubuntu results in >100 MB savings off the bat
• Some tweaks needed: different packages, python3 not installed by default, etc
• Example: olberger/baseimage-docker • Before: 279 MB • After: 166.8 MB (-112.2 MB)
Reducing dependencies II• phusion/baseimage relies on a Python 3
my_init script which bootstraps runit • Replace runit with s6, a process supervisor
suite designed to run as PID 1, which removes the need for certain workarounds (e.g. environment variables)
• Eliminates python3 as a dependency • Before: 166.8 MB • After: 144.3 MB (-22.5 MB)
How low can you go?
• Build Linux from scratch! (LFS) • The hard work has been done for you:
Buildroot and BusyBox • Of course, you could also compile a statically
linked binary, e.g. a Golang app and load it into the scratch image (0 MB) but that’s just crazy talk
BusyBox
– busybox README
“BusyBox combines tiny versions of many common UNIX utilities into a single small executable. It provides replacements for most of the utilities you usually find in GNU fileutils, shellutils, etc. […] BusyBox provides a fairly complete environment for any small or embedded system.”
Here be dragons
Switching the base image II• BusyBox weighs in at 2.4 MB (!!) • Seriously barebones • A popular setup is to include opkg and
piggyback on the OpenWRT package index • An example being progrium/busybox (4.8 MB) • Roll your own using progrium/rootbuilder
• Before: 183 MB • After: 56 MB (-127 MB)
Caveats• OpenWRT packages are intended for routers
and embedded systems, hence it has a rather limited selection
• Packages not available in OpenWRT (nodejs, redis, nginx, etc) usually have to be compiled from source, often with manual tweaks
• There is a new project, docker-alpine based on Alpine Linux that has a more general purpose package index (using apk)
A tiny baseimage• https://registry.hub.docker.com/u/gigablah/baseimage/
• Result: 5.8 MB
FROM progrium/busybox MAINTAINER Chris Heng <[email protected]>
ADD s6-2.0.0.1.tar.gz / ADD service /etc/service
RUN mkdir -p /var/spool/cron/crontabs
ENTRYPOINT ["/usr/bin/s6-svscan", "-t0"] CMD ["/etc/service"]
In short…
Original image (nodejs app) 426 MB
Without ruby dependency 325 MB
Without sshd and syslog-ng 290 MB
With Debian as base 183 MB
With s6 as init system 166 MB
With BusyBox as base 56 MB
References
• http://phusion.github.io/baseimage-docker/ • https://blog.phusion.nl/2015/01/20/baseimage-docker-fat-
containers-treating-containers-vms/ • http://buildroot.uclibc.org/ • http://www.busybox.net/ • http://skarnet.org/software/s6/ • http://blog.tutum.co/2014/12/02/docker-and-s6-my-new-
favorite-process-supervisor/ • https://registry.hub.docker.com/u/gigablah/baseimage/ • http://gliderlabs.viewdocs.io/docker-alpine