LXD / LXC — A Quick Introduction to The Linux Containers

Vishal Raj
The Startup
Published in
9 min readSep 28, 2019
Photo by Moritz Kindler on Unsplash

Similar to docker and rkt, LXD (pronounced Lex-dee) is also relatively new container orchestration engine. The first version LXD 0.1 was release on Feb 2015 and the latest version, 3.17, has been in September 2019 (See LXD release notes). Today we will peek into the basics of LXD and how it works.

As I mentioned earlier, LXD is a container orchestration engine. But mind it that, it is quite different from Docker. While docker containers are built for application containerization, LXD containers can run only Linux operating systems. It may seem like the the LXD containers are similar to virtual machines. Well you may say so, but the differnce is that, a virtual machine has full blown installation of the guest operating system and it emulates every device of the host device (networks, sound card, mouse, keyboard etc) for every guest operating system installation. While LXD containers share all the devices with the host operating system. You may compare LXD with Hypervisor for that matter. So lets begin exploring LXD

LXD Release cycle

The LXD project puts out monthly feature releases which while backward compatible at an API and CLI level, will contain some behavior change and potentially require manual intervention during an upgrade. In addition to those, every 2 years a LTS release is made which comes with 5 years of support through frequent bugfix-only releases. The LXD team recommends you pick “3.0” for production environments and use “latest” if you’re interested in getting the latest LXD features.

— LXD snap track

Installing LXD / LXC on Ubuntu

It must be noted that even though LXD and its APIs have been developed in OS agnostic way, currently it is supported only on Linux installations. To install, open a new terminal window and run

vishal@demo:~>sudo apt-get update -qq
vishal@demo:~>sudo apt-get install lxd lxd-client -y
Reading package lists... Done
Building dependency tree
Reading state information... Done
The following NEW packages will be installed:
lxd lxd-client
0 upgraded, 2 newly installed, 0 to remove and 42 not upgraded.
Need to get 10.7 kB of archives.
After this operation, 142 kB of additional disk space will be used.
Get:1 http://archive.ubuntu.com/ubuntu disco/main amd64 lxd all 1:0.7 [9,108 B]
Get:2 http://archive.ubuntu.com/ubuntu disco/universe amd64 lxd-client all 1:0.7 [1,612 B]
Fetched 10.7 kB in 1s (9,859 B/s)
Preconfiguring packages ...
Selecting previously unselected package lxd.
(Reading database ... 102903 files and directories currently installed.)
Preparing to unpack .../archives/lxd_1%3a0.7_all.deb ...
=> Installing the LXD snap
==> Checking connectivity with the snap store
==> Installing the LXD snap from the latest track for ubuntu-19.04
2019-09-23T04:45:39Z INFO Waiting for restart...
Warning: /snap/bin was not found in your $PATH. If you've not restarted your session since you
installed snapd, try doing that. Please see https://forum.snapcraft.io/t/9469 for more
details.
lxd 3.17 from Canonical✓ installed
=> Snap installation complete
==> Cleaning up leftovers
Failed to stop lxd.socket: Unit lxd.socket not loaded.
Failed to stop lxd.service: Unit lxd.service not loaded.
Failed to stop lxd-containers.service: Unit lxd-containers.service not loaded.
Failed to disable unit: Unit file lxd.socket does not exist.
Unpacking lxd (1:0.7) ...
Selecting previously unselected package lxd-client.
Preparing to unpack .../lxd-client_1%3a0.7_all.deb ...
Unpacking lxd-client (1:0.7) ...
Setting up lxd (1:0.7) ...
Setting up lxd-client (1:0.7) ...
vishal@demo:~>

Now that the LXD has been installed, we need to do a bit of setup, before we can start using it. Continuing on the terminal, run the following commands

With the first command, we are creating a virtual network bridge interface which will be used by the containes to communicate with the host operating system and other containers. The second command is to do the first time setup for LXD. This is going to ask you a series of question. As of now, with version 3.1x, the following questions are asked

vishal@demo:~>lxc network create lxdbr0
If this is your first time running LXD on this machine, you should also run: lxd init
To start your first container, try: lxc launch ubuntu:18.04
Network lxdbr0 created
vishal@demo:~>
vishal@demo:~>lxd init
Would you like to use LXD clustering? (yes/no) [default=no]: no
What name should be used to identify this node in the cluster? [default=mylxdhost]: lxdhost
What IP address or DNS name should be used to reach this node? [default=127.0.0.1]: 127.0.0.1
Are you joining an existing cluster? (yes/no) [default=no]: no
Setup password authentication on the cluster? (yes/no) [default=yes]: yes
Trust password for new clients: ******
Again: ******
Do you want to configure a new local storage pool? (yes/no) [default=yes]: yes
Do you want to configure a new local storage pool? (yes/no) [default=yes]: yes
Name of the storage backend to use (btrfs, dir, lvm, zfs) [default=zfs]: zfs
Create a new ZFS pool? (yes/no) [default=yes]: yes
Would you like to use an existing block device? (yes/no) [default=no]: no
Size in GB of the new loop device (1GB minimum) [default=15GB]: 1GB
Do you want to configure a new remote storage pool? (yes/no) [default=no]: no
Would you like to connect to a MAAS server? (yes/no) [default=no]: no
Would you like to configure LXD to use an existing bridge or host interface? (yes/no) [default=no]: yes
Name of the existing bridge or host interface: lxdbr0
Would you like stale cached images to be updated automatically? (yes/no) [default=yes]: yes
Would you like a YAML "lxd init" preseed to be printed? (yes/no) [default=no]: no
vishal@demo:~>

Now are are ready to explore LXD. It must be noted that LXC, the client for LXD, is what will be used from command line, to interact with LXD. Lets first see the list of running containers (of course, there will be none)

vishal@demo:~> lxc list
+------+-------+------+------+------+-----------+----------+
| NAME | STATE | IPV4 | IPV6 | TYPE | SNAPSHOTS | LOCATION |
+------+-------+------+------+------+-----------+----------+
vishal@demo:~>

Lets launch an Ubuntu 18.04 container. Please note that, like Docker, LXD also has publicly available operating system images which can be used to launch containers. You may also create a separate image repository of your own to pull custom images from.

vishal@demo:~> lxc remote list
+-----------------+------------------------------------------+---------------+-------------+--------+--------+
| NAME | URL | PROTOCOL | AUTH TYPE | PUBLIC | STATIC |
+-----------------+------------------------------------------+---------------+-------------+--------+--------+
| images | https://images.linuxcontainers.org | simplestreams | none | YES | NO |
+-----------------+------------------------------------------+---------------+-------------+--------+--------+
| local (default) | unix:// | lxd | file access | NO | YES |
+-----------------+------------------------------------------+---------------+-------------+--------+--------+
| ubuntu | https://cloud-images.ubuntu.com/releases | simplestreams | none | YES | YES |
+-----------------+------------------------------------------+---------------+-------------+--------+--------+
| ubuntu-daily | https://cloud-images.ubuntu.com/daily | simplestreams | none | YES | YES |
+-----------------+------------------------------------------+---------------+-------------+--------+--------+
vishal@demo:~> lxc launch ubuntu:18.04 my-ubuntu-1804
Creating the container
Container name is: my-ubuntu-1804
Starting my-ubuntu-1804
vishal@demo:~>lxc list
+----------------+---------+---------------------+-----------------------------------------------+------------+-----------+--------------+
| NAME | STATE | IPV4 | IPV6 | TYPE | SNAPSHOTS | LOCATION |
+----------------+---------+---------------------+-----------------------------------------------+------------+-----------+--------------+
| my-ubuntu-1804 | RUNNING | 10.17.28.218 (eth0) | fd42:3e11:8204:f076:216:3eff:fee8:c988 (eth0) | PERSISTENT | 0 | lxdhost |
+----------------+---------+---------------------+-----------------------------------------------+------------+-----------+--------------+
vishal@demo:~>

Well, now we have a container running Ubuntu 18.04 version. This process may look pretty simple and small, but behind the scenes much is happening. LXD will first scan the local cache dir to see if the OS image is available locally. If not, it will check https://images.linuxcontainers.org or https://cloud-images.ubuntu.com/daily based on the image requested.

Now that the container is up, we would like to interact with the container. You can either get inside the container and run command like any other normal distribution.

vishal@demo:~>lxc exec my-ubuntu-1804 -- /bin/bash
root@my-ubuntu-1804 $ apt-get update -qq
roto@my-ubuntu-1804 $

or you can directly tell LXC what command to pass to the container.

vishal@demo:~>lxc exec my-ubuntu-1804 -- apt-get update -qq
vishal@demo:~>

Similarly stoppping and deleting contianers can be done in the following way

vishal@demo:~>lxc stop my-ubuntu-1804
vishal@demo:~>lxc delete my-ubuntu-1804
For running container, force it to delete, but not a good practice
vishal@demo:~>lxc delete my-ubuntu-1804 --force

While a container is in running state, you can create a snapshot to create a point in time backup. This snapshot can be used to restore the existing container to the time when snapshot was taken. Alternatively it can also be used to launch fresh containers. When you request information for a container, the snapshot information is displayed at the end (See the output below). When you create a container from a snapshot, it is by default created in stopped state. Lets explore a bit.

vishal@demo:~>lxc snapshot my-ubuntu-1804 default-installation
vishal@demo:~>lxc info my-ubuntu-1804
Name: my-ubuntu-1804
Location: lxdhost
Remote: unix://
Architecture: x86_64
Created: 2019/09/27 17:05 UTC
Status: Running
Type: persistent
Profiles: default
Pid: 3978
Ips:
eth0: inet 10.0.132.131 vethF1KQAX
eth0: inet6 fd42:f131:8fbc:8a85:216:3eff:fedf:ef88 vethF1KQAX
eth0: inet6 fe80::216:3eff:fedf:ef88 vethF1KQAX
lo: inet 127.0.0.1
lo: inet6 ::1
Resources:
Processes: 5
CPU usage:
CPU usage (in seconds): 0
Memory usage:
Memory (current): 3.77MB
Memory (peak): 6.38MB
Network usage:
eth0:
Bytes received: 3.63kB
Bytes sent: 2.07kB
Packets received: 29
Packets sent: 16
lo:
Bytes received: 0B
Bytes sent: 0B
Packets received: 0
Packets sent: 0
Snapshots:
default-installation (taken at 2019/09/27 17:05 UTC) (stateless)
vishal@demo:~>lxc restore my-ubuntu-1804 default-installation
vishal@demo:~>lxc copy my-ubuntu-1804/default-installation ubuntu-02
vishal@demo:~>lxc list
lxc list
+----------------+---------+---------------------+-----------------------------------------------+------------+-----------+----------+
| NAME | STATE | IPV4 | IPV6 | TYPE | SNAPSHOTS | LOCATION |
+----------------+---------+---------------------+-----------------------------------------------+------------+-----------+----------+
| my-ubuntu-1804 | RUNNING | 10.0.132.126 (eth0) | fd42:f131:8fbc:8a85:216:3eff:fe0b:3ee9 (eth0) | PERSISTENT | 1 | lxdhost |
+----------------+---------+---------------------+-----------------------------------------------+------------+-----------+----------+
| ubuntu-02 | STOPPED | | | PERSISTENT | 0 | lxdhost |
+----------------+---------+---------------------+-----------------------------------------------+------------+-----------+----------+
vishal@demo:~>

An LXD image is made up of three parts

  • The root file system (as squashfs or tar.xz file) — The root file system
  • templates — Contains the data which will be put into root file system before the container fires up
  • metadata.yaml — contains information to be put into root filesystem before the container fires up

In order to see these contents, you can export an image to a new directory. This would export a tar zipped file. Unzipping the file will show the contents mentioned above. Lets see

vishal@demo:~>mkdir ubu-img
vishal@demo:~>lxc image export ubuntu:18.04 ubu-img/
Image exported successfully!
vishal@demo:~> ls -al ubu-img/
total 182256
-rw-r--r-- 1 vishal vishal 784 Sep 27 18:34 ubuntu-18.04-server-cloudimg-amd64-lxd.tar.xz
-rw-r--r-- 1 vishal vishal 186626048 Sep 27 18:34 ubuntu-18.04-server-cloudimg-amd64.squashfs
vishal@demo:~> mkdir ubu-img/rootfs
vishal@demo:~> tar -xzf ubu-img/ubuntu-18.04-server-cloudimg-amd64-lxd.tar.xz -C ubu-img/
vishal@demo:~>mkdir ubu-img/rootfs
vishal@demo:~>mount -t squashfs ubu-img/ubuntu-18.04-server-cloudimg-amd64.squashfs ubu-img/rootfs
vishal@demo:~>ls -al ubu-img/rootfs
total 0
0 drwxr-xr-x 2 root root 2590 Sep 18 15:51 bin
0 drwxr-xr-x 2 root root 3 Sep 18 15:55 boot
0 drwxr-xr-x 4 root root 176 Sep 18 15:51 dev
0 drwxr-xr-x 88 root root 2932 Sep 18 15:55 etc
0 drwxr-xr-x 2 root root 3 Apr 24 2018 home
0 drwxr-xr-x 20 root root 438 Sep 18 15:50 lib
0 drwxr-xr-x 2 root root 43 Sep 18 15:48 lib64
0 drwxr-xr-x 2 root root 3 Sep 18 15:48 media
0 drwxr-xr-x 2 root root 3 Sep 18 15:48 mnt
0 drwxr-xr-x 2 root root 3 Sep 18 15:48 opt
0 drwxr-xr-x 2 root root 3 Apr 24 2018 proc
0 drwx------ 2 root root 46 Sep 18 15:51 root
0 drwxr-xr-x 4 root root 39 Sep 18 15:55 run
0 drwxr-xr-x 2 root root 3626 Sep 18 15:51 sbin
0 drwxr-xr-x 2 root root 3 Jul 12 08:40 snap
0 drwxr-xr-x 2 root root 3 Sep 18 15:48 srv
0 drwxr-xr-x 2 root root 3 Apr 24 2018 sys
0 drwxrwxrwt 2 root root 3 Sep 18 15:51 tmp
0 drwxr-xr-x 10 root root 138 Sep 18 15:48 usr
0 drwxr-xr-x 13 root root 209 Sep 18 15:51 var
vishal@demo:~>

Similarly, a custom LXD image can be contructed and exported for reuse, from a folder containing the afore mentioned contents.

vishal@demo:~>lxc image import ubu-img/ --alias my-custom-ubuntu
Image imported with fingerprint: eb8a5da2e50752ada231c0fddc70a74d5370fda0f444ff7d457fbf2bacb527d0
vishal@demo:~>lxc image list
+------------------+--------------+--------+---------------------------------------------+--------+----------+------------------------------+
| ALIAS | FINGERPRINT | PUBLIC | DESCRIPTION | ARCH | SIZE | UPLOAD DATE |
+------------------+--------------+--------+---------------------------------------------+--------+----------+------------------------------+
| my-custom-ubuntu | eb8a5da2e507 | no | Ubuntu 18.04 LTS server (20190918) | x86_64 | 0.00MB | Sep 28, 2019 at 5:57am (UTC) |
+------------------+--------------+--------+---------------------------------------------+--------+----------+------------------------------+
| | 9ff5784302bf | no | ubuntu 18.04 LTS amd64 (release) (20190918) | x86_64 | 177.98MB | Sep 27, 2019 at 4:56pm (UTC) |
+------------------+--------------+--------+---------------------------------------------+--------+----------+------------------------------+
vishal@demo:~>

So the export functionality can be used to build your custom image and publish for usage by others. For example, you can do the setup of your web application and then export the image for use by other team members.

That’s all folks. Refer to the links below to know LXD in detail.

PS: I highly recommend the free course LXD/LXC on linuxacademy.com. It has been so fantastic, that I felt capable of writing this article.

PPS: I am in no way promoting LinuxAcademy. My only association with LA is that I am using their free courses to gain knowledge.

Bibliography:

--

--