Docker meets Vagrant (registry-less mode)

In very common web application I’m using MongoDB as a database, memcached to store session data and caches, ActiveMQ for messaging and Solr for full text search.

And every time there is a very common team is working on a very common app. And every developer in my team is changing stuff in databases. And every QA wants to have they own junk in databases. Finally I need up to 7 different environments (for every developer, and for development testing, and for staging). And I remember ‘Doing a task more than twice? Then, Automate it.’ rule.

In this article I’ll describe how to use Docker and Vagrant locally w/o remote registry. And how I can easily solve all my problems with all thous modern DevOps technologies. I’m going to use Vagrant to automate virtual machine preparation on developer computer. And Docker to do a container management. Every system component (like MongoDB or ActiveMQ) will be described as a docker container. For this step Docker will do the same job as Chef or Puppet but in the next article I’ll explain how to use it w/ remote registry to maintain non-virtual environment.

Ok, I have Vagrant installed on my system and I need to install ActiveMQ into Vagrant box using Docker. Let’s start with Vagrant file.

VAGRANTFILE_API_VERSION = "2"
Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
  config.vm.box = "base"
  config.vm.network "forwarded_port", :guest => 8162, :host => 8162
  config.vm.network "forwarded_port", :guest => 61616, :host => 61616  
  config.vm.provision "docker"
end

After running vagrant up I’ve got a VM with Docker installed. I’ve also mapped ActiveMQ ports from VM into Host. The next step is creating a Dockerfile for ActiveMQ container. The Dockerfile describes which commands should be executed to prepare container. But I have no idea which commands should be executed to install ActiveMQ. But that’s ok. I gonna figure it out inside.

Before I start few words about Docker terminology:

Dockerfile > (build) > image > (run) > container 
  • Dockerfile – file which describes image (parent image, shell commands, FS manipulation, entry point).
  • Image – it’s actually a result of Dockerfile build. Image contains all files for next containers. You can use image as a parent for other images and you can pull and push images into repository.
  • Container – when you run an image with specific command it creates new container. The command can be defined in Dockerfile or in command line.

Back to my ActiveMQ. Inside Vagrant (vagrant ssh) I’m executing docker run -a stdin -a stdout -i -t ubuntu /bin/bash. It’s running bash shell into ubuntu container (downloads ubuntu image, and creates bash container). Ok, after few mins thats what I’ve got:

apt-get update --fix-missing # first, update the system
apt-get install -y software-properties-common python-software-properties #install add-apt-repository
add-apt-repository -y ppa:webupd8team/java # add java
apt-get update # one more update for java
echo debconf shared/accepted-oracle-license-v1-1 select true | sudo debconf-set-selections # agree to java license
echo debconf shared/accepted-oracle-license-v1-1 seen true | sudo debconf-set-selections
apt-get install -y oracle-java8-installer # install java
cd /tmp #load ActiveMQ
wget http://mirror.netcologne.de/apache.org/activemq/5.10.0/apache-activemq-5.10.0-bin.tar.gz
tar -xvzf apache-activemq-5.10.0-bin.tar.gz
mv apache-activemq-5.10.0 /var/lib/activemq

And also I’ve changed ActiveMQ web console port from 8161 to 8162 (because I’m crazzzy). And I’m able to run /var/lib/activemq/bin/activemq console inside that ubuntu container (it’s not reachable from outside of container). Time to say exit for the container.

Few Dockers commands which might help:

  • docker ps -a – shows all containers
  • docker images – all downloaded images
  • docker rm <container hash/name> – remove container
  • docker rmi <image hash> – remove image
  • docker build <path to Dockerfile dir> – build image from docker file
  • docker run <image name> <command> – run container from image (every run runs new container)
  • docker start/stop/kill/attach <container hash/name> – starts/stops/kills/attaches to container

2014-07-10 11-33-20 1. vagrant@vagrant-ubuntu-precise-64: ~ (bash) sick_torvalds is autogenerated name for my bash container. And I’m deleting it docker rm sick_torvalds (thous names sounds horrible some times).

Let’s do my Dockerfile inside activemq dir.

FROM ubuntu:latest

RUN apt-get update --fix-missing
RUN apt-get install -y software-properties-common python-software-properties
RUN add-apt-repository -y ppa:webupd8team/java
RUN apt-get update
RUN echo debconf shared/accepted-oracle-license-v1-1 select true | sudo debconf-set-selections
RUN echo debconf shared/accepted-oracle-license-v1-1 seen true | sudo debconf-set-selections
RUN apt-get install -y oracle-java8-installer

RUN cd /tmp
RUN wget http://mirror.netcologne.de/apache.org/activemq/5.10.0/apache-activemq-5.10.0-bin.tar.gz
RUN tar -xvzf apache-activemq-5.10.0-bin.tar.gz
RUN mv apache-activemq-5.10.0 /var/lib/activemq
COPY jetty.xml /var/lib/activemq/conf/jetty.xml

EXPOSE 61616
EXPOSE 8162

ENV ACTIVEMQ_OPTS_MEMORY -Xms300m -Xmx300m

ENTRYPOINT /var/lib/activemq/bin/activemq console

Pretty same, right. I’m using jetty.xml to override web console port to 8162 (just an example here). So I need to put jetty.xml into activemq folder. Few words about Dockerfile notation:

  • FROM – name of parent image
  • RUN – execute a shell command (docker will continue with execution in case of exception)
  • COPY (or ADD) – copy from Dockerfile dir into image
  • EXPOSE – make port accessible for other containers (but not from outside of Docker infrastructure)
  • VOLUME – mount a Docker volume (see next article)
  • USER – change to user (root is default for container)
  • ONBUILD – execute only for child containers
  • ENTRYPOINTcommand or ['binary'] for execute. If you defined JSON array as argument than first element should be binary. If it’s a command /bin/sh -c <command> will be executed.
  • CMD – The default command for docker. If you are using ENTRYPOINT with binary you can define default arguments here. To be honest I don’t get the difference between ENTRYPOINT and CMD fully and this might be wrong.

Back to ActiveMQ stuff. Now you can build and run the container inside Vagrant box:

docker build -t myrepo/activemq /vagrant/activemq
docker run -d -p 8162:8162 -p 61616:61616 --name activemq myrepo/activemq
docker logs activemq # see the logs

-p 8162:8162 -p 61616:61616 says that I want to map host_port:container_port. I’m also mapping thous ports in Vagrant, remember? Open http://localhost:8162/ on your Host. Should work.

The last modification in Vagrantfile to make activemq build and start on VM creating time. Replace config.vm.provision "docker" to:

VAGRANTFILE_API_VERSION = "2"
Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
  config.vm.box = "base"
  config.vm.network "forwarded_port", :guest => 8162, :host => 8162
  config.vm.network "forwarded_port", :guest => 61616, :host => 61616  
  config.vm.provision "docker" do |d|
    d.build_image "-t myrepo/activemq /vagrant/activemq"
    d.run "myrepo/activemq", :auto_assign_name => false, :args => "-d -p 8162:8162 -p 61616:61616 -name activemq"
  end
end

That’s it. You can execute vagrant destroy and vagrant up to check it works.

Thank you for reading. Now you can easily automate developer machine preparation with Vagrant and Docker. The next step will be automation your staging env and (may be) delivery to production using Docker registry. Source on github.

One thought on “Docker meets Vagrant (registry-less mode)

Leave a Reply

Your email address will not be published. Required fields are marked *

To create code blocks or other preformatted text, indent by four spaces:

    This will be displayed in a monospaced font. The first four 
    spaces will be stripped off, but all other whitespace
    will be preserved.
    
    Markdown is turned off in code blocks:
     [This is not a link](http://example.com)

To create not a block, but an inline code span, use backticks:

Here is some inline `code`.

For more help see http://daringfireball.net/projects/markdown/syntax