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
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
runan 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
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
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)
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 (
rootis default for container)
ONBUILD– execute only for child containers
['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.