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.
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
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.
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.