Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Docker meets Vagrant (registry-less mode)

DZone's Guide to

Docker meets Vagrant (registry-less mode)

· DevOps Zone
Free Resource

“Automated Testing: The Glue That Holds DevOps Together” to learn about the key role automated testing plays in a DevOps workflow, brought to you in partnership with Sauce Labs.

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


`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
 - `ENTRYPOINT` - `command` 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 "-d -p 8162:8162 -p 61616:61616 --name activemq myrepo/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.

Learn about the importance of automated testing as part of a healthy DevOps practice, brought to you in partnership with Sauce Labs.

Topics:

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}