Fixing the Docker Ubuntu image on Rackspace Cloud
Published October 15th, 2013
Docker is an awesome tool to build, manage and operate Linux containers. Docker has been gaining momentum for various use cases, and if you want to learn more I recommend checking out the Docker.io website.
I have been using Docker on and off for months, but recently started to deploy services using it onto the Rackspace Public Cloud. Unfortunately, not everything went smoothly, and this is the story of getting it all working.
tl;dr: Updated Ubuntu Images.
I have published updated Ubuntu 12.04 LTS images for Docker under the racker/precise-with-updates repository. Get the images by running docker pull racker/precise-with-updates
. The latest
tag is automatically updated every day with the latest Ubuntu updates and security patches. The Dockerfile
for building this is on racker: docker-ubuntu-with-updates if you want to tweak it for your own uses.
Illegal instruction!?
Following the basic Docker instructions to get it running on Ubuntu 12.04, I installed a new kernel, rebooted, and dockerd
was running successfully.
I started building a new Dockerfile
from the ubuntu:12.04
base image. Everything was going OK, but then apt-get upgrade
crashed with:
Illegal instruction
After some prodding, I find Docker issue #1984 — at least I’m not alone in my sorrow.
Digging through the links, you come to LP: eglibc/+bug/956051, a wonderful bug about glibc. glibc made incorrect the assumptions that if the FMA4 instruction set is available, that the Advanced Vector Extensions (AVX) would also be available. In LP: eglibc/+bug/979003 a patch for this bug is pushed to all recent Ubuntu versions, so why did it crash with Docker?
This affects Docker on Rackspace for two reasons:
- Docker’s base Ubuntu 12.04 image has not applied any patches.
- Many of Rackspace’s Public Cloud servers run an
AMD Opteron(tm) 4332
processor with Xen which hasFMA4
instructions but theAVX
instructions do not work.
Since the bug has already been fixed upstream, creating an updated Ubuntu image to use as a base seems like the easiest way to fix this. Little did I know that creating an updated image was another rabbit hole.
Building an updated Ubuntu image.
I naively started by trying to just run apt-get dist-upgrade
from the ubuntu:12.04
base image. It didn’t work at all. Packages tried to mess with upstart
, and something went horribly wrong with /dev/shm
.
After taking a break to propose to my fiancé and vacation in Hawaii, I built up the following Dockerfile:
FROM ubuntu:12.04
I started with the ubuntu:12.04
base image; It is possible to start from scratch using debootstrap
, but it takes much longer to build and doesn’t provide any advantages.
To allow automated installation of new packages, we set the debconf(7) frontend to noninteractive
, which never prompts the user for choices on installation/configuration of packages:
ENV DEBIAN_FRONTEND noninteractive
One of the updated packages is initramfs-tools. In the post-install hooks, it tries to update the initramfs for the machine and even try to run grub
or lilo
. Since we are inside docker we don’t want to do this. Debian bug #594189 contains many more details about these issues, but by setting the INITRD
environment variable we can skip these steps:
ENV INITRD No
ischroot
is a command used by many post install scripts in Debian to determine how to treat the machine. Unfortunately as discussed in in Debian bug #685034 it tends to not work correctly. Inside a Docker container we almost always want ischroot
to return true. Because of this, I made a new ischroot which if the FAKE_CHROOT
environment variable is set, always exits 0:
ENV FAKE_CHROOT 1
RUN mv /usr/bin/ischroot /usr/bin/ischroot.original
ADD src/ischroot /usr/bin/ischroot
Using this replacement ischroot
, it allows updates to the initscripts
package to successfully install without breaking /dev/shm
, which works around LP Bug #974584.
policy-rc.d provides a method of controlling of the init scripts of all packages. By exiting with a 101
, the init scripts for a package will not be ran, since 101
stands for action forbidden by policy
:
ADD src/policy-rc.d /usr/sbin/policy-rc.d
Before installing any packages, I added a new sources.list that has -updates
and -security
repositories enabled and to use mirror.rackspace.com. Rackspace maintains mirror.rackspace.com
as a GeoDNS address to pull from the nearest Rackspace data center, and it is generally much faster than archive.ubuntu.com
.
ADD src/sources.list /etc/apt/sources.list
Normally dpkg
will call fsync after every package is installed, but when building an image we don’t need to worry about individual fsyncs, so we can use the force-unsafe-io
option:
RUN echo 'force-unsafe-io' | tee /etc/dpkg/dpkg.cfg.d/02apt-speedup
Since we want make the image as small as possible, we add a Post-Invoke
hook to dpkg which deletes cached deb
files after installation:
RUN echo 'DPkg::Post-Invoke {"/bin/rm -f /var/cache/apt/archives/*.deb || true";};' | tee /etc/apt/apt.conf.d/no-cache
Finally we run dist-upgrade
:
RUN apt-get update -y && apt-get dist-upgrade -y
After all the upgrades have been applied, we want to clean out a few more cached files:
RUN apt-get clean
RUN rm -rf /var/cache/apt/* && rm -rf /var/lib/apt/lists/mirror.rackspace.com*
These final steps bring the image down to under 90 megabytes, which is smaller than the default ubuntu:12.04
image.
As one last trick, I flatten the image into a single layer by export and importing the image.
Automatically updated every day.
Ubuntu’s gets many updates and security patches on a regular basis. Rather than building a one off image, I hooked up Docker to a builder in Jenkins. The latest
tag is rebuilt every day and pushed to the public registry under racker/precise-with-updates.
This means you can use racker/precise-with-updates:latest
in a Dockerfile
, or in your interactive terminals:
docker run -i -t racker/precise-with-updates /bin/bash
Using docker pull
you can bring the images down locally for other operations:
docker pull racker/precise-with-updates
docker images racker/precise-with-updates
I have also published the source for the image on Github, and would welcome any PRs or feedback.
Written by Paul Querna, CTO @ ScaleFT. @pquerna