Managing Containers with LXD: A Brief Introduction

Today we’d like to continue our blog series on containerization. As our first two articles (1 and 2) were dedicated to theory, this post will focus on one tool and its features: LXD.

LXD (short for Linux Container Daemon) was created by Stéphane Graber, who works for Canonical. His name is well known in the professional community as he is also one of the authors of another popular container solution: LXC. Naturally, LXD is built on LXC, which makes it easier to work with containers and adds a wide range of new abilities.

In this article, we can only give a brief introduction to LXD: we’ll compare it with Docker, give you installation and setup instructions, and also demonstrate its basic container management functions.

LXD and Docker

LXD is a relatively new instrument: the first version was released in 2014, when Docker had already been widely distributed and proven itself in practice.
Like Docker, LXD is built on LXC.

Despite this, they have absolutely different applications: if Docker is for launching applications in containers, LXD is for launching complete operating systems.

Using LXD, we can create containers not in the literal sense, but as lightweight virtual machines. To highlight this fact and also point out how it is different from other containerization tools, many authors have named LXD lightvisor.

In Canonical publications, they point out that LXD containers can work 10 times faster than traditional KVM virtual machines.

LXD is an attempt to solve a variety of problems that are encountered with other containerization tools: dynamic resource management, expanded container migration options (including in real-time), and removed security vulnerabilities. Compared to Docker, LXD has a lot more options for reconfiguring containers.

LXD is equipped with open API and has clients for different programming languages. A plugin for OpenStack has been created that lets you manage containers using Nova.

Installation and Setup

From here on out, we’ll be describing the installation and setup of LXD on Ubuntu 16.04. LXD is included in the official repository for this OS and is installed following the standard procedure:

apt-get install lxd

In one of his articles, Stéphane Graber recommends using a ZFS file system as the backend for storing containers. To use ZFS, we have to install the corresponding packets:

apt-get install zfsutils-linux

If you can’t or don’t want to use ZFS for any particular reason, you can also use BTRFS or LVM (see here for more details).

Once the installation is complete, we run the command

lxd init

The setup utility will prompt us with some simple questions and then automatically configure itself. For more information on setting up LXD, check out this article.

Creating Containers

All containers in LXD are created from images. You can use images from local and remote repositories. Let’s view a list of available repositories:

lxc remote list
 
+-----------------+------------------------------------------+---------------+--------+--------+
|  	NAME   	|               	URL                	|   PROTOCOL	| PUBLIC | STATIC |
+-----------------+------------------------------------------+---------------+--------+--------+
| images      	| https://images.linuxcontainers.org   	| lxd       	| YES	| NO 	|
+-----------------+------------------------------------------+---------------+--------+--------+
| local (default) | unix://                              	| lxd       	| NO 	| YES	|
+-----------------+------------------------------------------+---------------+--------+--------+
| ubuntu      	| https://cloud-images.ubuntu.com/releases | simplestreams | YES	| YES	|
+-----------------+------------------------------------------+---------------+--------+--------+
| ubuntu-daily	| https://cloud-images.ubuntu.com/daily	| simplestreams | YES	| YES	|
+-----------------+------------------------------------------+---------------+--------+--------+

For just trying out LXD, a local repository is fine. We’ll launch Ubuntu 16.04 in a container:

lxc launch ubuntu:16.04 container1

LXD will create a container from this image and launch it.

We can launch the command shell in this container with the command:

lxc exec container1 /bin/bash

If we want to create a container, but not launch it, we can just run the command:

lxc init ubuntu:16.04 container1

We can subsequently start and stop a container with the commands lxc start and lxc stop.

LXC is quite capable of managing containers on the fly. For example, to move a file from the main host to the container, we run:

lxc file push [path to file on host] [container]/[path]

To perform the opposite task and download a file from the container onto the host:

$ lxc file pull [container]/[path]

We can edit files directly in the container:

lxc edit [container]/[path]

We’ve looked at the main commands for creating and launching containers; for those interested in learning more, please look at this detailed article by Stéphan Graber.

Managing Resources

There’s no point in managing isolated environments if you cannot control the resources: we should provide containers with enough resources and at the same time, be sure that the container will not exceed those resources, which would interrupt the rest of the system.

In LXD, resources can be allocated to containers with a special set of commands:

# setting a memory limit
lxc config set container1 limits.memory 512M
 
# assigning processor cores to the container
lxc config set container1 limits.cpu 1,3
 
# limiting CPU consumption
lxc config set container1 cpu.allowance 10%
 
# limiting the container disk volume (works only for ZFS and btrfs)
lxc config set container1 root size 10GB

More information on managing resources can be found in this article.

Statistics on container resource consumption can be viewed with a simple command:

lxc info container1
 
Name: container1
Architecture: x86_64
Created: 2016/08/16 07:55 UTC
Status: Running
Type: persistent
Profiles: default
Pid: 4110
Ips:
lo: 	inet	127.0.0.1
lo: 	inet6   ::1
eth0:   inet6   fe80::216:3eff:fe18:faa9	vethA2SCMX
Resources:
Processes: 24
Memory usage:
Memory (current): 48.88MB
Memory (peak): 163.26MB
Network usage:
eth0:
Bytes received: 648 bytes
Bytes sent: 648 bytes
Packets received: 8
Packets sent: 8
lo:
Bytes received: 264 bytes
Bytes sent: 264 bytes
Packets received: 4
Packets sent: 4

Using Snapshots

In LXD, you can create snapshots and restore containers from them. We’ll take a look at how this works in practice (our example comes from the interactive LXD tutorial).

We’ll first make some changes to our previously created container1:

lxc exec container1 -- apt-get update
lxc exec container1 -- apt-get dist-upgrade -y
lxc exec container1 -- apt-get autoremove —purge -y

Then we make a screenshot of the container and save it as “new”:

lxc snapshot container1 new

We’ll try to “break” something in our first container:

lxc exec container1 -- rm -Rf /etc /usr

Afterwards, we launch the command shell in the container:

lxc exec container1 -- bash
I have no name!@container1:~#

We run the command exit and return to the main host. Now let’s restore container1 from the snapshot:

lxc restore container1 new

We’ll try launching the command shell in the now restored container:

lxc exec container1 -- bash
root@container1:#

Everything is back to normal and in working order!

In this example, we looked at what’s called a stateless snapshot. LXD has another type of snapshot, stateful snapshots, which saves the current state of all of the processes running in the container. There are a number of useful and interesting functions that related to snapshots.

To create stateful snapshots, we have to install CRIU (CheckPoint/Restore in Userspace) first. This is used for saving the current state of all of our processes and then restoring them on either the current machine or another one.
In Ubuntu 16.04, CRIU is installed from the standard package manager:

apt-get install criu

Now that we’ve installed it, we create a snapshot:

lxc snapshot container1 snapshot1 --stateful

These snapshots may prove particularly useful in some situations. Let’s say we need to restart a server running multiple containers. To avoid launching them from scratch and instead having them continue where they left off, we can run:

# before restarting
lxc stop container1 --stateful

# after restarting
lxc start container1

The “live” container migration mechanism relies on stateful snapshots, but this mechanism is still in development.

Conclusion

LXD is an easy-to-use container management system with a series of useful functions. We hope LXD will continue to be developed and earn a more prestigious place among other modern containerization tools.

If you have any experience using LXD, please share with us in the comments below.

We obviously can’t cover all of LXD’s functions in one article. For anyone interested in learning more, please check out the following links: