Detaching Resources: Architecture foundation
Detaching Resources is as series about my personal findings and experiments on building web applications in a service-oriented fashion. The title and content are based on “Backing Services” from the Twelve-Factor App methodology.
A Quick Introduction on Virtualization
Virtualization, as the name suggests, is the act of creating a virtual version of something. It can be used to create virtual hardware, operating system, memory (swap), network (VPN), storage (RAID), etc. We will focus on the first two.
Hardware virtualization is probably the most known type. You certainly have heard of virtual machines (VM) and softwares like Parallels, VMware and VirtualBox. It allows you run, for instance, Linux inside Mac OS X. Since it creates an entire virtual computer, it can become very heavyweight, CPU and memory hungry.
Operating system (OS) virtualization acts in the OS kernel layer. It creates lightweight virtual environments (VE or containers), because they use the host OS’ system call. There is no need of an intermediate VM; and still being completely isolated. On the other hand, it is not as flexible as the hardware virtualization, since the guest and host OS must be the same. Linux-VServer, OpenVZ and Linux Containers (LXC) are examples of implementations of this technology.
LXC is the newest implementation of OS virtualization in Linux. It is based on the Linux kernel cgroups (control groups) to limit, account and isolate hardware resources of process groups. LXC is considered as something between a “chroot on steroids” and a full VM. Its goal is to create an environment as close as possible to a standard Linux installation, but without the need of a separate kernel.
Docker provides a high level API on top of LXC. It automates the creation of containers based on a Dockerfile, making it repeatable and consistent. It allows container versioning with git-like capabilities. Allows reuse. Any container can be used as a base image to create new ones. It encourages sharing. Anyone can upload a container to their public registry. The registry can also be used in your private servers.
Hands on Docker
To install Docker, check its documentation for your OS. It is comprehensive and easy to follow.
If you are using a Mac OS X, you need Linux running in a VM. Docker already makes our lives easier by supporting Vagrant. All you need is to install VirtualBox and Vagrant and then
$ git clone https://github.com/dotcloud/docker.git
$ cd docker
$ vagrant up
$ vagrant ssh
The docker daemon should be already running as root. Since the user
vagrant
is in the group docker
, it is not necessary to run the
docker
CLI with sudo.
To get the basics on Docker, I suggest you follow their interactive
tutorial. You will
learn how to pull
container images from the index
repository, run
the container, commit
your changes and push
it back to the repository. I will skip straight
to the use of a Dockerfile
, where things get more interesting.
The Dockerfile
The Dockerfile
contains the set of
steps docker will
use to reproduce the system images.
# Dockerfile
FROM ubuntu:latest
MAINTAINER Vinicius Horewicz <vinicius@horewi.cz>
RUN sed -i.bak "s/main$/main universe/" /etc/apt/sources.list
RUN apt-get update
RUN apt-get upgrade -y
RUN locale-gen en_US en_US.UTF-8
RUN echo "root:root" | chpasswd
# initctl workaround
RUN dpkg-divert --local --rename --add /sbin/initctl
RUN ln -s /bin/true /sbin/initctl
# prevent services from starting automatically
RUN echo exit 101 > /usr/sbin/policy-rc.d
RUN chmod +x /usr/sbin/policy-rc.d
RUN apt-get install -y curl monit openssh-server rabbitmq-server
# disable upstart for SSH
RUN mv /etc/init/ssh.conf /etc/init/ssh.conf.dist
ADD ./etc/monit/sshd.conf /etc/monit/conf.d/sshd.conf
ADD ./etc/monit/rabbitmq.conf /etc/monit/conf.d/rabbitmq.conf
# run monit in foreground
CMD ["/usr/bin/monit", "-I"]
EXPOSE 22 :5672
Dockerfiles follow the format INSTRUCTION arguments
. The first
instruction must be FROM
. It specifies the base image from which we
will build.
Images names are in the format user/image:tag
. Where user
is the
username of its creator [in the repository]. When it is not specified,
means you are using official images from the Docker team. image
is the
image name. Images can be tagged, just like git. That is the reason of
the tag
argument. When omitted, the value latest
is assumed.
RUN
instruction runs a command and commits the result at the build
time. Every RUN
means a new image is committed, which serves as a base
for the next instruction. Thanks to the this
layered approach and to
AuFS, Docker can cache build steps,
making them blazing fast.
ADD
commands will copy files from <src>
to <dest>
. Source can be a
file or directory relative to the source directory being built or a
remote file URL. Destination is the path in the container. ADD
is not
cached (yet?).
CMD
specifies the command to be executed when the container is started.
There can exist only one in a Dockerfile. If there are more than one,
only the last will take effect.
EXPOSE
sets port to be publicly exposed when running the container. It
is specified as [PUBLIC:]PRIVATE
. When PUBLIC is omitted, a random port
is assigned. If you want to use the same port for public and private,
you can specify only :PRIVATE
.
Running the Container
You start the container with the docker run
command. It will run the
specified CMD
. When this process completes, the container is fully
stopped. Think about containers as “process in a box”. This is why we
must run the CMD
in foreground instead of using a daemon or running
/etc/init.d/monit start
in CMD
.
One might ask: Why monit? Remember a container run only one process and stops right after this process ends. Even though a container can start with /sbin/init, docker still has some issues with upstart, so we will use monit to start and keep our process running.
We still have to do some workarounds and disable upstart for some services to be able to use init.d scripts.
The Final Result
The final result is available in this repository. I have created basic monitrc files for SSH and RabbitMQ, so monit can start these services. If you want to give it a try:
$ git clone https://github.com/wicz/detaching-resources.git
$ cd detaching-resources/v1/docker-rabbitmq
$ docker build -t <yourusername>/rabbitmq .
$ docker run -t <yourusername>/rabbitmq
Finally, we have set up the architecture foundation. In the next post of the series we will start implementing services which will exchange messages through this message queue container.