Docker in OSX via boot2docker or Vagrant: getting over the hump

There are bucket loads of fantastic Docker related blog posts out there. This is yet another tldr guide to get the readers over the hump, particularly illustrating how you can mount local file systems onto a Docker container with Vagrant.

Before we begin, Docker is a client/daemon application. Docker daemon can listen to commands from the Docker client. The client is thin and all containers are hosted on the daemon. This means if you point Docker client to another daemon endpoint, the usual available list of images and containers will change. This is a good thing.

Installing Docker on OSX

As stated on the official Docker installation guide, install via Homebrew is highly recomended.

brew install boot2docker
brew install docker

Note that brew only installs the Docker client on OSX. boot2docker is the VirtualBox wrapper that will fire up Docker daemon. This is important as at the time of writing, commands performed on Docker clients are executed and takes place on the Docker daemon. For example, mounting files to a container really refers to mounting files from the server that the Docker daemon is running to the desired container. This is a cause of many confusion.

Running Docker with boot2docker

The installation guide is really good, and here is a tldr version. Note that the following script uses a different boot2docker port. This is to avoid port conflict with the Docker daemon via Vagrant later.

echo “export DOCKER_PORT=14243” >> ~/.boot2docker/profile 
boot2docker init
boot2docker start
export DOCKER_HOST=tcp://localhost:14243
docker version

If all goes well, doing docker version will yield the following.

docker_version. all good

docker_version. all good

Setting up Docker with Vagrant

The steps are based on John Zanchetta’s blog post, with all of the Vagrant steps trimmed and one network routing step found redundant. This is the tldr version and it assumes you are comfortable with Vagrant

Step 1 – Provision the Docker daemon box
Using the following Vagrant file, fire up the VM called docker

# -*- mode: ruby -*-
# vi: set ft=ruby :

VAGRANTFILE_API_VERSION = "2"

Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
    config.vm.define :docker do |docker|
        docker.vm.box = "precise64"
        docker.vm.box_url = "http://files.vagrantup.com/precise64.box"
        docker.vm.network "forwarded_port", guest: 80, host:58080
        docker.vm.network "forwarded_port", guest: 4243, host: 4243
        $script = <<SCRIPT
wget -q -O - https://get.docker.io/gpg | apt-key add -
echo deb http://get.docker.io/ubuntu docker main > /etc/apt/sources.list.d/docker.list
apt-get update -qq 
apt-get install -q -y --force-yes lxc-docker
usermod -a -G docker vagrant
sed -e 's/DOCKER_OPTS=/DOCKER_OPTS=\"-H 0.0.0.0:4243\"/g' /etc/init/docker.conf > /vagrant/docker.conf.sed
cp /vagrant/docker.conf.sed /etc/init/docker.conf
rm -f /vagrant/docker.conf.sed
service docker restart
SCRIPT
       docker.vm.provision :shell, :inline => $script
    end
 end

Step 2 – Vagrant or boot2docker, an easy switch
Point the DOCKER_HOST to the Vagrant-based Docker daemon. Note that by simply changing the DOCKER_HOST, you can point your Docker client to whichever Docker daemon you so desire; boot2docker or Vagrant-based for example.

export DOCKER_HOST=tcp://localhost:4243
docker version

If all goes well, you are ready to build!

Step 3 – Build your first Docker container and mount volumes in Docker with Vagrant
Once you have a container built, mounting the /vagrant is no different from mounting any other directories to the container from the Docker daemon. For example

docker run -t -i --name test --volume /vagrant:/vagrant:rw example:test

Easy!

Step 4 – Make your first push, my only advice
This blog post would not be complete without recommending you to perform your first Docker push.

If like me, you have created your container with “example” as the repository, and “test” as the tag based on a Dockerfile

docker build -t example:test .

And upon pushing (assuming the username is “yves”), for example

docker login
docker push yves/example

You will notice the following response:

The push refers to a repository [yves/example] (len: 0)
2014/05/31 00:46:35 No such id: yves/example

This is because the push command needs username, which has to be part of the repository name. However, the above example, the repository name is merely just “example”.

The work around here, based on scouring issues #720, is to create a new tag that includes the username as part of the repository name. For example:

docker tag  yves/example:test_tag
docker push yves/example

then watch the magic unfold in front of your very eyes.

The big take away is to always prefix your repository name with your docker.io username, followed by a /. For example, yves/ubuntu, yves/centos yves/whatever.

My docker image, yves/yves, is built based on the Vagrant file mentioned in https://macyves.wordpress.com/2014/05/20/release-management-part1-test-automation-with-vagrant-and-jenkins-build-pipeline/

Why Vagrant over boot2docker?

At the time of writing, mounting directories from your OSX machine to a container hosted by boot2docker is quite a mind field. See issues #3957. boot2docker is very well supported, but I feel more at ease with Vagrant.

Side note

Docker is really resilient. Simply retry the same command when encountering failures during image building or pushing to docker.io. This is in part thanks to the untagged images that floats around Docker daemon and that an image is really a repository containing a bunch of changesets performed, akin to Git.

However, if you wish to clean up all untagged images, see this blog post.

siege and TCP TIME_WAIT

Note to self form installing siege via brew on OSX.

brew install siege

Thanks clever people in brew and siege! 🙂

Mac OS X has only 16K ports available that won't be released until socket
TIME_WAIT is passed. The default timeout for TIME_WAIT is 15 seconds.
Consider reducing in case of available port bottleneck.

You can check whether this is a problem with netstat:

    # sysctl net.inet.tcp.msl
    net.inet.tcp.msl: 15000

    # sudo sysctl -w net.inet.tcp.msl=1000
    net.inet.tcp.msl: 15000 -> 1000

Run siege.config to create the ~/.siegerc config file.

/vagrant mount fix for Ubuntu VMs for Vagrant 1.5.1, Virtualbox 4.3.10 on OS X 10.9.2 Mavericks

When running Vagrant 1.5.1 with VirtualBox 4.3.10 on your shiny OS X 10.9.2 Mavericks, you may run into issue when trying to mount the /vagrant folder on Ubuntu VMs. The bug looks like

Failed to mount folders in Linux guest. This is usually because
the “vboxsf” file system is not available. Please verify that
the guest additions are properly installed in the guest and
can work properly. The command attempted was:

mount -t vboxsf -o uid=`id -u vagrant`,gid=`getent group vagrant | cut -d: -f3` /vagrant /vagrant
mount -t vboxsf -o uid=`id -u vagrant`,gid=`id -g vagrant` /vagrant /vagrant

This is a reported issue on both Vagrant and Virtualbox project and I believe the fault lies with VBoxGuestAdditions on the Virtualbox end. See Vagrant issue #3341 and Virtualbox issue #12879.

A fix was proposed to ssh into each and every Ubuntu VMs you got and perform the necessary symlink fix to get the VBoxGuestAdditions to work. This is quite clumsy in my opinion.

Here’s an alternative approach to the one liner symlink fix. This is based on the fixed VBoxGuestAdditions ISO as outlined in #12879.

Essentially boils down to copying the fixed ISO to the VirtualBox folder and letting vbguest plugin do its usual magic. Voila. Works for all ubuntu vms.


$ wget https://www.virtualbox.org/download/testcase/VBoxGuestAdditions_4.3.11-93070.iso

$ sudo cp VBoxGuestAdditions_4.3.11-93070.iso /Applications/VirtualBox.app/Contents/MacOS/VBoxGuestAdditions.iso

rst2pdf and rst2html5 on OS X 10.9.2 with XCode 5.1

This was almost the new nightmare on elm street scenario. But we got over it. Below are the complete steps to get rst2pdf and rst2html5 running on your sparkling new OS X 10.9.2 Mavericks.

Prerequisites

  • XCode 5.1
  • Homebrew 0.9.5

Steps


# install jpeg lib via homebrew
brew install libjpeg

# install pip via easy_install
sudo easy_install --upgrade pip

# install dependencies manually using the unused argument hack
sudo pip install --upgrade pdfrw

sudo pip install --upgrade Pygments

sudo ARCHFLAGS=-Wno-error=unused-command-line-argument-hard-error-in-future pip install --upgrade reportlab

sudo ARCHFLAGS=-Wno-error=unused-command-line-argument-hard-error-in-future pip install --allow-unverified PIL PIL

# install the bad boy
sudo pip install -U rst2pdf
sudo pip install -U rst2html5

Dirrty Hax0r in Perl 5.12 Pid.pm for varnish-agent

Right, so I am not particularly proud of this but again, more of a note to self than anything. Secondly, I am most certainly not a Perl expert. This little hack should probably never be used or put into any production Mac setup. If you know of a proper fix, please do let me know.

Lastly, this is varnish-agent and not varnish-agent2. The former is written in Perl while the latter is written in C and uses Varnish API directly.

OK! Now that the disclaimer is out of the way, onward with the dodgy! So when running varnish-agent on OSX 10.8.2, one may encounter the following issue:

Can't kill a non-numeric process ID at /Library/Perl/5.12/File/Pid.pm line 124.

The quick fix is proposed here involves adding this line to the the Pid.pm file on line 124.

return undef unless $pid > 0;

So your subroutine in Pid.pm will end up looking more like this.

sub running {
   my $self = shift;
   my $pid  = $self->_get_pid_from_file;
   return undef unless $pid > 0;
   return   kill(0, $pid)
      ? $pid
      : undef;
}

Building Varnish Cache 3.0.3 from source in Mountain Lion, OSX 10.8.2

If you want to install Varnish Cache in OSX, I highly recommend using Homebrew’s recipe for installing Varnish. Easy!

But if you want to build and run Varnish Cache from github, here is a little step-by-step checklist for you.

A shopping list of applications you’ll need for your Mountain Lion:

  • Homebrew 0.9.3
  • Xcode 4.5.2, via Appstore
  • Command Line Tool for Mountain Lion – November 1st 2012

You’ll need the following dependencies via Homebrew

  • automake, note that this has to be version 1.12. I have tested it with 1.13 and you will run into obsolete AM_CONFIG_HEADER. 
  • libtool
  • pcre
  • pkg-config

You’ll need docutils for rst2man

Run the following steps:

  1. brew install the above dependencies
  2. for automake, you will need to switch it to 1.12. See below
  3. install docutils
  4. git checkout 3.0.3 branch of Varnish Cache
  5. run ./autogen.sh
  6. run ./configure
  7. make install

To install another version of automake with Homebrew:

  1. Unlink the existing 1.13 version – brew unlink automake
  2. Get the list of versions available for automake – brew versions automake
  3. Copy the 1.12 information from git checkout…, for example git checkout 1e5eb62 /usr/local/Library/Formula/automake.rb
  4. cd into /usr/local/, or where ever else you have Homebrew storing your formula
  5. Paste the copied git information and this will check out the appropriate version of automake for you
  6. Install 1.12 – brew install automake
  7. Voila! You now have 1.12 version and you can switch between 1.13 or 1.12 by simply doing brew switch automake 1.13