Working locally with Docker while on the road.

I just got back from a conference trip to Europe, DockerCon in Copenhagen, All-Systems-Go in Berlin, and then Open Source Summit in Prague.

While I was there, I needed to continue doing the development of some major refactoring of RancherOS for my LinuxKit talk – which meant I needed to be able to “docker pull” from both builds and temporary testing VM’s, from horrible hotel Wifi, and from overloaded conference Wifi – not great.

Because the bandwidth from Australia isn’t wonderful, I had already modified the builds and test tooling to use a local Docker registry mirror – so all I really needed to do was setup the same caching infrastructure I have on my home office network, but on a roaming notebook… and yup, its pretty straightforward:

sven@y260:~$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
cc4f1ff259f7 b8efb18f159b "nginx -g 'daemon ..." 2 weeks ago Up 13 days 0.0.0.0:80->80/tcp nginx
458c808f9218 3ebefe7c539b "/entrypoint.sh /e..." 2 weeks ago Up 13 days 0.0.0.0:5555->5000/tcp mirror
e09411b42128 3ebefe7c539b "/entrypoint.sh /e..." 2 weeks ago Up 13 days 0.0.0.0:5000->5000/tcp registry
5d572c8e8658 46fc18186a54 "/bin/sh -c 'chmod..." 2 weeks ago Up 9 days 0.0.0.0:3142->3142/tcp apt-cacher

These containers were started using docker run:


#apt-cacher-ng
docker run -d --network host --restart=always --name apt-cacher -v /var/cache/apt-cacher-ng:/var/cache/apt-cacher-ng svendowideit/apt-cacher-ng
#a docker registry
docker run -d --network host --name registry --restart always registry
#a hub registry mirror
docker run -d --name mirror --network host -v /var/lib/registry-mirror:/registry -e STORAGE_PATH=/registry -e STANDALONE=false -e MIRROR_SOURCE=https:/registry-1.docker.io -e MIRROR_SOURCE_INDEX=https://index.docker.io registry
docker exec mirror sh -c "echo 'proxy:' >> /etc/docker/registry/config.yml"
docker exec mirror sh -c "echo ' remoteurl: https://registry-1.docker.io' >> /etc/docker/registry/config.yml"
docker exec mirror sh -c "sed -i~ 's/addr: :5000/addr: :5555/g' /etc/docker/registry/config.yml"
docker restart mirror

The docker daemon will automatically use the local registry, and the local registry mirror on the “localhost” port 5000&5555 – and so long as you can give your VM’s a valid IP to your host, that too will work..

I have the following in my .bashrc, and these environment vars are used by the build and test tooling to pass on to the VM’s (ok, so this works because I have VMWare workstation on my Linux box):


export APTPROXY=http://$(ip a | grep "inet " | grep global | grep vmnet1 | sed 's/ //' | cut -d " " -f 2 | sed 's/\/.//'):3142
export ENGINE_REGISTRY_MIRROR=http://$(ip a | grep "inet " | grep global | grep vmnet1 | sed 's/ //' | cut -d " " -f 2 | sed 's/\/.//'):5555
export RANCHER_REPO=http://$(ip a | grep "inet " | grep global | grep vmnet1 | sed 's/ //' | cut -d " " -f 2 | sed 's/\/.//')/

Slim application containers (using Docker)

Another talk I gave at Linux.conf.au, was about making slim containers (youtube) – ones that contain only the barest essentials needed to run an application.

In the same way that we don’t ship the VM / filesystem of our build server, you should not be shipping the container you’re building from source.

Another talk I gave at Linux.conf.au, was about making slim containers (youtube) –  ones that contain only the barest essentials needed to run an application.

And I thought I’d do it from source, as most “Built from source” images also contain the tools used to build the software.

1. Make the Docker base image you’re going to use to build the software

In January 2015, the main base images and their sizes looked like:

scratch             latest              511136ea3c5a        19 months ago       0 B
busybox             latest              4986bf8c1536        10 days ago         2.433 MB
debian              7.7                 479215127fa7        10 days ago         85.1 MB
ubuntu              15.04               b12dbb6f7084        10 days ago         117.2 MB
centos              centos7             acc1b23376ec        10 days ago         224 MB
fedora              21                  834629358fe2        10 days ago         250.2 MB
crux                3.1                 7a73a3cc03b3        10 days ago         313.5 MB

I’ll pick Debian, as I know it, and it has the fewest restrictions on what contents you’re permitted to redistribute (and because bootstrapping busybox would be an amazing talk on its own).

Because I’m experimenting, I’m starting by seeing how small I can make a new Debian base image –  starting with:

FROM debian:7.7

RUN rm -r /usr/share/doc /usr/share/doc-base \
          /usr/share/man /usr/share/locale /usr/share/zoneinfo

CMD ["/bin/sh"]

Then make a new single layer (squashed image) by running docker export and docker import

REPOSITORY          TAG                 IMAGE ID            CREATED             VIRTUAL SIZE
debian              7.7                 479215127fa7        10 days ago         85.1 MB
our/debian:jessie   latest              cba1d00c3dc0        1 seconds ago       46.6 MB

Ok, not quite half, but you get the idea.

Its well worth continuing this exercise using things like dpkg —get-selections to remove anything else you won’t need.

Importantly, once you’ve made your smaller base image, you should use it consistently for ALL the containers you use. This means that whenever there are important security fixes, that base image will be downloadable as quickly as possible –  and all your related images can be restarted quickly.

This also means that you do NOT want to squish your images to one or two layers, but rather into some logical set of layers that match your deployment update risks –  a common root base, and then layers based on common infrastructure, and lastly application and customisation layers.

2. Build static binaries –  or not

Building a static binary of your application (in typical Go style) makes some things simpler –  but in the end, I’m not really convinced it makes a useful difference.

But in my talk, I did it anyway.

Make a Dockerfile that installs all the tools needed, builds nginx, and then output’s a tar file that is a new build context for another Docker image (and contains the libraries ldd tells us we need):

cat Dockerfile.build-static-nginx | docker build -t build-nginx.static -
docker run --rm build-nginx.static cat /opt/nginx.tar > nginx.tar
cat nginx.tar | docker import - micronginx
docker run --rm -it -p 80:80 micronginx /opt/nginx/sbin/nginx -g "daemon off;"
nginx: [emerg] getpwnam("nobody") failed (2: No such file or directory)

oh. I need more than just libraries?

3. Use inotify to find out what files nginx actually needs!

Use the same image, but start it with Bash –  use that to install and run inotify, and then use docker exec to start nginx:

docker run --rm build-nginx.static bash
$ apt-get install -yq inotify-tools iwatch
# inotifywait -rm /etc /lib /usr/lib /var
Setting up watches.  Beware: since -r was given, this may take a while!
Watches established.
/lib/x86_64-linux-gnu/ CLOSE_NOWRITE,CLOSE libnss_files-2.13.so
/lib/x86_64-linux-gnu/ CLOSE_NOWRITE,CLOSE libnss_nis-2.13.so
/lib/x86_64-linux-gnu/ CLOSE_NOWRITE,CLOSE ld-2.13.so
/lib/x86_64-linux-gnu/ CLOSE_NOWRITE,CLOSE libc-2.13.so
/lib/x86_64-linux-gnu/ CLOSE_NOWRITE,CLOSE libnsl-2.13.so
/lib/x86_64-linux-gnu/ CLOSE_NOWRITE,CLOSE libnss_compat-2.13.so
/etc/ OPEN passwd
/etc/ OPEN group
/etc/ ACCESS passwd
/etc/ ACCESS group
/etc/ CLOSE_NOWRITE,CLOSE group
/etc/ CLOSE_NOWRITE,CLOSE passwd
/etc/ OPEN localtime
/etc/ ACCESS localtime
/etc/ CLOSE_NOWRITE,CLOSE localtime

Perhaps it shouldn’t be too surprising that nginx expects to rifle through your user password files when it starts 🙁

4. Generate a new minimal Dockerfile and tar file Docker build context, and pass that to a new `docker build`

The trick is that the build container Dockerfile can generate the minimal Dockerfile and tar context, which can then be used to build a new minimal Docker image.

The excerpt from the Dockerfile that does it looks like:


# Add a Dockerfile to the tar file
RUN echo "FROM busybox" > /Dockerfile \
    && echo "ADD * /" >> /Dockerfile \
    && echo "EXPOSE 80 443" >> /Dockerfile \
    && echo 'CMD ["/opt/nginx/sbin/nginx", "-g", "daemon off;"]' >> /Dockerfile

RUN tar cf /opt/nginx.tar \
           /Dockerfile \
           /opt/nginx \
           /etc/passwd /etc/group /etc/localtime /etc/nsswitch.conf /etc/ld.so.cache \
           /lib/x86_64-linux-gnu

This tar file can then be passed on using

cat nginx.tar | docker build -t busyboxnginx .

Result

Comparing the sizes, our build container is about 1.4GB, the Official nginx image about 100MB, and our minimal nginx container, 21MB to 24MB –  depending if we add busybox to it or not:

REPOSITORY          TAG            IMAGE ID            CREATED              VIRTUAL SIZE
micronginx          latest         52ec332b65fc        53 seconds ago       21.13 MB
nginxbusybox        latest         80a526b043fd        About a minute ago   23.56 MB
build-nginx.static  latest         4ecdd6aabaee        About a minute ago   1.392 GB
nginx               latest         1822529acbbf        8 days ago           91.75 MB

Its interesting to remember that we rely heavily on I know this, its a UNIX system –  application services can have all sorts of hidden assumptions that won’t be revealed without putting them into more constrained environments.

In the same way that we don’t ship the VM / filesystem of our build server, you should not be shipping the container you’re building from source.

This analysis doesn’t try to restrict nginx to only opening certain network ports, devices, or IPC mechanisms – so there’s more to be done…

Boot2Docker dom0 and more docker orchestration magic.

I’ve modifyed boot2docker to

Auto-start an image named ‘dom0:latest’. This image then orchestrates the remainder of the system.
This personal dom0 image starts sshd and the containers I want this system to auto-run.
I also set up a `home-volume` container, which I -volumes-from mount into all my development containers.

All kicked off by the sd-card boot2docker.

So, some concrete examples for my previous Boot2Docker rules post:dom0boot2docker

I’m modifying boot2docker to

  1. if present, auto-start an image named ‘dom0:latest’. This image then orchestrates the remainder of the system.
  2. my personal dom0 image starts sshd and the containers I want this system to auto-run.
  3. Set up a `home-volume` container, which I -volumes-from mount into all my development containers.

When I do some development, testing or production, it happens in containers, the base OS is pristine, and can be trivially updated (atm, i’m using boot from USB flash and SD Card).

Similarly, the dom0 container is also a bare busybox container, cloned from the filesystem of the boot2docker image itself.. I’m not ready for my end goal of doing this to my notebook and desktop – but then, this setup is only a few days old :).

This setup uses my detect existing /var/lib/docker on HD pull request , and the dom0-rootfs, dom0-base and dom0 images, and then from there, and initial dev image.

2 customisations I’ve made to the boot2docker are persisted on the HD – /var/lib/boot2docker/etc/hostname is set to something useful to me, and the optional /var/lib/boot2docker/bootlocal.sh script starts the dom0 container at boot.

When I need a set of containers started, I can create a tiny orchestration container that can talk to the docker daemon and thus start more containers, controlling how they interact with each other and the outside world.

 

Docker 0.7 is here – welcome RPM distros (and anyone else that lacks AUFS)

The Docker project has continued its mostly-monthly releases with the long anticipated 0.7 release, this time making the storage backend pluggable, so fedora/redhat based users can use it without building a custom kernel.

The Docker project has continued its mostly-monthly releases with the long anticipated 0.7 release, this time making the storage backend pluggable, so fedora/redhat based users can use it without building a custom kernel.

I’m curious to see the performance differences between the 3 storage backends we have now – but I need to assimilate the wonders of Linking containers for adhoc scaling first.

Try it out – I’m even more convinced that Docker containers have an interesting future 🙂

easy install of Docker.io on Debian

UPDATE: for Docker 0.6.5, the ubuntu debian package also installs on Debian. You still need to enable IPv4 forwarding as below – then re-start the docker daemon

I’ve been doing some work on Docker – learning golang, Docker internals, and just some of the command line options that I didn’t know I needed to know about.

Because I was in a hurry, I threw an old unused disk into one of my old laptops and installed ubuntu. That was enough for me to learn that I wanted to know alot more about Docker.

So, I’m back to using the loaner T530 with my 128GB SSD in it – its been running Debian since the day I got the SSD, over 2 years ago.

it turns out that on Debian testing (with the 3.10-3-amd64 kernel), its incredibly easy to run docker:

sudo apt-get install lxc wget bsdtar curl golang git aufs-tools mercurial iptables
wget --output-document=docker https://get.docker.io/builds/Linux/x86_64/docker-latest
chmod +x docker
sudo su -
#enable IPv4 forwarding
echo "net.ipv4.ip_forward = 1" >> /etc/sysctl.conf
sysctl -p /etc/sysctl.conf
# set up and mount the cgroup mountpoint
echo 'none /sys/fs/cgroup cgroup defaults 0 0' | sudo tee -a /etc/fstab
mount /sys/fs/cgroup
#OK, you might need to reboot if it fails to mount?
./docker -d &

done.
from there, you can run the docker cli like normal (except that its not in your path yet).

I’m going to pull over the apt pinning installation documentation I wrote for publican the other week and re-write it (and test) for installing docker on Debian Stable, and we’ll all be much happier.

I wonder if Docker can replace Puppet.

I’m curious to see how hard it would be to push out Docker versioned configuration changesets over ssh to ‘anywhere’, with some kind of idempotency via system ‘tags’.

I’ve finally spent a little time playing with Docker, and to be honest, the really simple

here’s a list of commands that get run to set up the image

feels awesome.

to test it out, I wrote the simplest steps I could think of to create a working foswiki installation into a Dockerfile:

FROM ubuntu
MAINTAINER    Sven Dowideit <svendowideit@home.org.au>

RUN echo deb http://fosiki.com/Foswiki_debian/ stable main contrib > /etc/apt/sources.
list.d/fosiki.list
RUN echo deb http://archive.ubuntu.com/ubuntu precise main restricted universe multive
rse >> /etc/apt/sources.list
RUN gpg –keyserver the.earth.li –recv-keys 379393E0AAEE96F6
RUN apt-key add //.gnupg/pubring.gpg
RUN apt-get update
RUN apt-get install -y foswiki

#create the tmp dir
RUN mkdir /var/lib/foswiki/working/tmp
RUN chmod 777 /var/lib/foswiki/working/tmp
#TODO: randomise the admin pwd..
RUN htpasswd -cb /var/lib/foswiki/data/.htpasswd admin admin
RUN mv /etc/foswiki/LocalSite.cfg /etc/foswiki/LocalSite.cfg.orig
RUN grep –invert-match {Password} /etc/foswiki/LocalSite.cfg.orig > /etc/foswiki/Loca
lSite.cfg
RUN chown www-data:www-data /etc/foswiki/LocalSite.cfg

RUN bash -c ‘echo “/usr/sbin/apachectl start” >> /.bashrc’
RUN bash -c ‘echo “echo foswiki configure admin user password is ‘admin'” >> /.bashrc’

EXPOSE 80

and then I can create the image with a simple:

docker build -t svendowideit/ubuntu-foswiki .

and run that image by calling:

docker run -t -i -p 8888:80 svendowideit/ubuntu-foswiki /bin/bash

Which (assuming that port 8888 is unused on my host computer) means I can do some testing by pointing my web client to http://localhost:8888/foswiki

When I exit the bash shell, which allows me to debug what is happening, everything is shutdown, and all changes are lost. If I make changes, I can commit them, but at this point, I prefer to make a new Dockerfile.

The interesting thing is that Docker seems to create an image tag for every command, so if I make add some RUN lines, or make changes, it doesn’t need to re-do steps that it has done before…. which sounds to me just like Rex, Puppet, Ansible etc – but more re-useable.

And so, I’m curious to see how hard it would be to push out Docker versioned configuration changesets over ssh to ‘anywhere’, with some kind of impotency via system ‘tags’.

 

PS, the docker image is available from https://index.docker.io/u/svendowideit/ubuntu-foswiki/ , and uses my debian packages, so you should install new extensions using apt-get install

Foswiki 1.1.5 released – rpms, debs and usbstick ready

Foswiki 1.1.5 released – rpms, debs and usbstick ready

George has been leading the charge to a major bug fixing release of foswiki – we’ve resolved over 120 issues, and worked hard to improve security – dealing with some interesting cross site scripting issues found by ‘SonyStyles’, and then pushing on to harden the registration process to deal with spammers.

foswiki’s password system can now migrate your user’s password store to more modern encryption methods – the default that we shipped with Twiki can thus move from crypt to md5-apache.

4 days after the release, the installation and maintenance options for 1.1.5 have improved too:

  1. my yum package repository (extensions too)
  2. my debian package repository (extensions too)
  3. my Foswiki on a USB stick for Windows
  4. Oliver’s VirtualMachine

Centos yum install foswiki and Debian apt-get install foswiki

Thats right, on Redhat Enterprise and Centos, its now just as easy to install foswiki and its ~300 plugins as it is to do so on Debian.

That’s right, on Redhat Enterprise and Centos, it’s now just as easy to install foswiki and its ~300 plugins as it is on Debian.

This means that you can now manage your Enterprise Foswiki using the same package management tools as the rest of the operating system.

For example, I just installed a demo system with:

yum install foswiki-jhotdrawplugin foswiki-ldapcontrib foswiki-newuserplugin foswiki-glueplugin foswiki-ldapngplugin foswiki-calendarplugin foswiki-edittableplugin foswiki-interwikiplugin foswiki-renderlistplugin foswiki-smiliesplugin foswiki-tableplugin foswiki-directedgraphplugin

and when yum finished, I browse to http://server/foswiki/ and its up and running.

These packages are built by a script that downloads the latest packages from http://foswiki.org/Extensions, generates an EPM manifest and then builds rpm packages – every night. I have not yet tested them with Redhat Enterprise 6 or fedora

 

To try it out, you’ll need to add the EPEL repository, and then this one to your yum config:

 

su
rpm -Uvh http://download.fedoraproject.org/pub/epel/6/i386/epel-release-6-5.noarch.rpm
cd /etc/yum.repo.d/
wget http://fosiki.com/Foswiki_rpms/foswiki.repo

and then run

yum makecache

 

To see what foswiki extensions are available, run
yum search foswiki

To install foswiki, and some plugins:

yum install foswiki foswiki-workflowplugin foswiki-jscalendarcontrib foswiki-ldapcontrib

then browse to http://servername/foswiki/bin/configure to enable the plugin and configure settings.

 

Centos Foswiki repository on its way

Just a short teaser from my testing virtual machine: I’m still working on a few wrinkles – hopefully It’ll be releaseable for foswiki 1.1.5

yum install foswiki-widgetsskin

Just a short teaser from my testing virtual machine: I’m still working on a few wrinkles – hopefully It’ll be releaseable for foswiki 1.1.5

yum install foswiki-widgetsskin
========================================================================
 Package Arch Version Repository Size
========================================================================Installing:
 foswiki-widgetsskin i386 110415-226 foswiki 9.6 M
Installing for dependencies:
 apr i686 1.3.9-3.el6_1.2 base 129 k
 apr-util i686 1.3.9-3.el6_0.1 base 89 k
 apr-util-ldap i686 1.3.9-3.el6_0.1 base 15 k
 foswiki i386 1.1.4-231 foswiki 2.6 M
 foswiki-autoviewtemplateplugin i386 111217-226 foswiki 9.1 k
 foswiki-commentplugin i386 111217-226 foswiki 23 k
 foswiki-comparerevisionsaddon i386 111217-226 foswiki 95 k
 foswiki-edittableplugin i386 111217-226 foswiki 78 k
 foswiki-emptyplugin i386 111217-226 foswiki 15 k
 foswiki-famfamfamcontrib i386 111217-226 foswiki 3.0 M
 foswiki-historyplugin i386 111217-226 foswiki 76 k
 foswiki-interwikiplugin i386 111217-226 foswiki 11 k
 foswiki-jqueryplugin i386 111221-226 foswiki 6.1 M
 foswiki-jscalendarcontrib i386 111217-226 foswiki 383 k
 foswiki-mailercontrib i386 111217-226 foswiki 44 k
 foswiki-patternskin i386 111217-226 foswiki 725 k
 foswiki-preferencesplugin i386 111217-226 foswiki 12 k
 foswiki-renderlistplugin i386 111217-226 foswiki 20 k
 foswiki-slideshowplugin i386 111217-226 foswiki 23 k
 foswiki-smiliesplugin i386 111217-226 foswiki 14 k
 foswiki-spreadsheetplugin i386 120118-226 foswiki 37 k
 foswiki-tableplugin i386 111217-226 foswiki 29 k
 foswiki-tinymceplugin i386 111222-226 foswiki 1.4 M
 foswiki-tipscontrib i386 111217-226 foswiki 18 k
 foswiki-topicusermappingcontrib i386 111221-226 foswiki 41 k
 foswiki-twikicompatibilityplugin i386 111217-226 foswiki 140 k
 foswiki-twistyplugin i386 111217-226 foswiki 28 k
 foswiki-wysiwygplugin i386 111217-226 foswiki 61 k
 httpd i686 2.2.15-15.el6.centos base 818 k
 httpd-tools i686 2.2.15-15.el6.centos base 70 k
 mailcap noarch 2.1.31-2.el6 base 27 k
 perl i686 4:5.10.1-119.el6_1.1 base 9.7 M
 perl-AppConfig noarch 1.66-6.el6 base 87 k
 perl-Archive-Tar i686 1.58-119.el6_1.1 base 70 k
 perl-CGI i686 3.51-119.el6_1.1 base 206 k
 perl-CGI-Session noarch 4.35-5.el6 base 120 k
 perl-Compress-Raw-Zlib i686 2.023-119.el6_1.1 base 67 k
 perl-Compress-Zlib i686 2.020-119.el6_1.1 base 43 k
 perl-DBD-Pg i686 2.15.1-3.el6 base 191 k
 perl-DBI i686 1.609-4.el6 base 705 k
 perl-ExtUtils-MakeMaker i686 6.55-119.el6_1.1 base 290 k
 perl-ExtUtils-ParseXS i686 1:2.2003.0-119.el6_1.1 base 42 k
 perl-FreezeThaw noarch 0.45-5.el6 base 19 k
 perl-IO-Compress-Base i686 2.020-119.el6_1.1 base 66 k
 perl-IO-Compress-Zlib i686 2.020-119.el6_1.1 base 133 k
 perl-IO-Zlib i686 1:1.09-119.el6_1.1 base 30 k
 perl-IO-stringy noarch 2.110-10.1.el6 base 68 k
 perl-Module-Pluggable i686 1:3.90-119.el6_1.1 base 37 k
 perl-Package-Constants i686 1:0.02-119.el6_1.1 base 24 k
 perl-Pod-Escapes i686 1:1.04-119.el6_1.1 base 30 k
 perl-Pod-Simple i686 1:3.13-119.el6_1.1 base 209 k
 perl-Test-Harness i686 3.17-119.el6_1.1 base 229 k
 perl-Test-Simple i686 0.92-119.el6_1.1 base 110 k
 perl-Text-Glob noarch 0.08-7.el6 base 11 k
 perl-devel i686 4:5.10.1-119.el6_1.1 base 420 k
 perl-libs i686 4:5.10.1-119.el6_1.1 base 590 k
 perl-version i686 3:0.77-119.el6_1.1 base 49 k
 postgresql-libs i686 8.4.9-1.el6_1.1 base 201 k
 rcs i686 5.7-37.el6 base 169 k
Transaction Summary
========================================================================
Install 60 Package(s)

Yes, I’m finally cutting the foswiki release package up into its constituent parts so that the packages can be updated separately like when using configure.

 

The same build script is used to generate debian packages, so I hope that will come in February too.