Virtualization technologies have been around for a while in various forms and over the last several years the container style of virtualization has become popular as a complement (or replacement) to VMs (Virtual Machines / hypervisors). The first half of this article does a nice job of summarizing the differences between containers and hypervisors. In the containerization space Docker is probably a name many would think of first. While we've found Docker useful in some contexts, we've also been increasingly employing the LXD/LXC suite of technologies in our development workflows with good results.

How does LXD relate to LXC or Docker?

LXC provides the base level interface for containerization on the Linux kernel, and LXD builds on the stable LXC API to provide client/server tools for managing containers on your network. Accessible via both command line and a REST API it provides a simple, consistent developer experience. Working with LXC containers feels similar to working with VMs but you don't have the extra abstraction layer running to slow things down. From their description:

LXC containers are often considered as something in the middle between a chroot and a full fledged virtual machine. The goal of LXC is to create an environment as close as possible to a standard Linux installation but without the need for a separate kernel.

Alternatively, Docker or Rocket (which are application container managers as opposed to system container managers) focus on distributing apps as containers. Docker is a single application container.

While Docker has become wildly popular over the past few years, here at Lincoln Loop, we find that there are a few down sides with the adoption of this technology.

If you want to learn more about how the two approaches stack up, you can read this article.

LXD Vs. Docker from a DevOps Perspective

On one hand, with LXC and the LXD tools you get a containerized Linux box that you can launch/pause/stop/snapshot in a matter of seconds. It can be seen as a completely isolated Linux system. You can have dozen of them running in parallel and moving existing infrastructure to it is a breeze because it integrates seamlessly with existing tooling.

On the other hand, Docker lets you containerize your infrastructure by breaking it up into one container per application. If there are some benefits to this breakdown, it's a total shift in paradigm. It requires a new infrastructure, a new skill-set, and causes a new class of issues. LXC can also be used to containerize per-application but we have only experimented with the per-system approach thus far.

Use Cases

Each of the containers can be built manually or by using your configuration management of choice. During this process, you can create a snapshot to persist any important state to disk. You'll be able to restore any of them.

Once you reach the state you'd like to use as a starting point, you can preserve it as an image. Then you'll be able to launch a new container from it. Images can be imported and exported in a variety of ways:

  • Tarball
  • Online /Remote Registry

When you mix and match the features above you can easily create network topology to reproduce or approximate production environment.

LXC in Action


Note

The naming of things can get confusing here. A quick review:

  • LXC - the Linux containerization interface
  • LXD - the set of tools for using LXC
  • lxc - the name of the command-line client tool to LXD

Pull an image from the Ubuntu remote repository and create a container from it.

lxc launch ubuntu:16.04 yml-ubuntu-16-04

You can execute a bash session into this newly created container and install the packages you're interested in as well as performing the necessary configuration:

  • packages: open ssh server, vim, tmux
  • your personal dotfiles
  • ...

As you can see below, our container is running.

yml@carbon$ lxc list
+------------------+--------+---------------------+-----------+-----------+
|          NAME    | STATE  |        IPV4         |   TYPE    | SNAPSHOTS |
+------------------+--------+---------------------+-----------+-----------+
| yml-ubuntu-16-04 |RUNNING |10.245.115.248 (eth0)|PERSISTENT | 0         |
+------------------+--------+---------------------+-----------+-----------+

exec will get you a root terminal:

lxc exec yml-ubuntu-16-04 bash
root@yml-ubuntu-16-04:~# id
uid=0(root) gid=0(root) groups=0(root)
root@yml-ubuntu-16-04:~# echo "Configure this machine"

Once you reach a state that you want to preserve, you can turn your container into an image that could be used as a blueprint to create more containers.

yml@carbon$ lxc publish yml-ubuntu-16-04 --alias yml-16-04
Container published with fingerprint: cb771e76ff5a8cf306fa1b5a2a6e035e8d4dd1cb6000007f3427a5330e25f6ae
yml@carbon$ lxc image list
+-----------+--------------+-------------------------------------------+---------+
|    ALIAS  | FINGERPRINT  |                DESCRIPTION                |  SIZE   |
+-----------+--------------+-------------------------------------------+---------+
| yml-16-04 | cb771e76ff5a |                                           |215.24MB |
+-----------+--------------+-------------------------------------------+---------+
|           | f452cda3bccb |ubuntu 16.04 LTS amd64 (release) (20160627)|138.23MB |
+-----------+--------------+-------------------------------------------+---------+

The last entry is the original image that I pulled from the remote Ubuntu repository (FINGERPRINT: f452cda3bccb), and the one above is my personalized Ubuntu 16.04 image.

How to Get Started

Below I have compiled a list of links that you can use to get started with this new way of working with containers.