building a vagrant ready docker image
Vagrant is a tool which creates virtual development environments using various virtualization platforms. These platforms are called “providers” in the Vagrant universe. It supports VirtualBox, VMware, Hyper-V and as of version 1.6 it also offers built-in support for using Docker as a provider.
Docker can be very helpful when creating virtual environments because containers are spawned way faster than virtual machines and additionaly you don’t have the virtual machine overhead.
Vagrant has the notion of base boxes. Base boxes are OS images with bare minimum packages installed. Vagrant can use these to run any supplied provisioner such as Puppet, Chef, SaltStack etc. to create the desired virtual environment. However, Vagrant has some requirements in order to be able communicate and run commands in the virtual machines, such as properly configured ssh and sudo access.
If we have our Puppet/Chef/whatever code already in place and want to switch from, let’s say VirtualBox, to docker, we need a docker image which supports these Vagrant requirements. We can use a Dockerfile to define the Vagrant requirements and then build our image. We will create a Vagrant-ready docker image starting off the debian base image.
First, we need to create a file named Dockerfile
. A Dockerfile is a text file which describes all
the instructions/commands that docker needs to run in order to build a docker image.
For starters we add the following lines:
The first line defines which debian base image we are going to use to build ours.
We will be using the image tagged as 7.8
. The maintainer line is informative
showing the author of the generated images.
Next we are going to create the vagrant
user and set the password to “vagrant”:
Also, setting the root password to “vagrant” is considered a good practice if we are going to distribute our image/Dockerfile, so let’s do that too:
Next we need to configure proper ssh access for the vagrant user. First we add the vagrant public
key to the authorized_keys file and then apply the approrpiate permissions.
The ADD
instruction will retrieve the vagrant.pub
key from github and add it in the authorized_keys
file of the vagrant user.
Next we are going to install the minimum required packages. This is a good time to also install puppet if you are going to use it to provision the container. Feel free to ignore it if you don’t need it or install chef or whatever tool you need. We also run apt-get clean to free some space from the resulting image.
The vagrant user should have root access to the system. We are going
to take advantage of the sudoers.d
folder. Sudo parses files in the /etc/sudoers.d
folder so that we don’t have to edit /etc/sudoers directly. The format for the files in the
sudoers.d folder is the same as for /etc/sudoers.
Create a folder next to the docker file named sudoers.d
and place the file 01_vagrant
inside
with the following contents:
Then we use the ADD
instruction to add it to the system and also ensure it has the proper permissions.
Finally we need to have the ssh server running in order for Vagrant to ssh into the mahine. First we create
the /var/run/sshd folder because we are going to skip running the ssh server init script and run it
using the CMD
instruction. Also we expose the port 22 using the EXPOSE
instruction.
Building the docker image is pretty straightforward, running:
will create the image with name vagrant-debian
. In order to spawn a docker container from that
image we run the docker run
command. We can see that the default action of this container
is to run an ssh server.
However the whole point is to run this through Vagrant. First stop the running container using the container id or the container name:
Then let’s create a Vagrantfile
with the following contents:
and run vagrant up --provider=docker
.
Now we can ssh in the running container through Vagrant using vagrant ssh
. We can use this
base image to create other images that work with Vagrant (use the image name in the FROM
instruction). Or we can use this image together with Vagrant + puppet, or any other configuration
management tool using the --provision-with
flag. If we just wanted to run a specific service/application we might be better off
putting the service installation/setup in the Dockerfile in the first place and skip the provisioning part.
However, this setup is super helpful if we already have a working puppet configuration and use Vagrant with
VirtualBox et. al. You can find my Vagrant-ready debian images in my dockerhub repo
and the Dockerfiles in my github repo.