Introduction to Linux Containers (LXC)


Bhaskar S 09/20/2014


Overview

Linux Containers or LXC, is a lightweight operating-system level virtualization that allows a user to run one or more virtualized operating environments on a single host.

Think of the virtualized operating environment as a self-contained and isolated container with its own file system, networking stack, processes, etc and with controlled access to the shared physical resources of the host such as CPU, Memory, I/O, etc.

The users of a virtual machine like VirtualBox may ask - what is the difference between a virtual machine and a linux container ?

A Virtual Machine requires a Hypervisor on top of the host operating system and each virtualized operating environment runs on top of the hypervisor with its own guest operating system.

The following Figure-1 illustrates the high-level overview of a Virtual Machine:

Virtual Machine
Figure-1

A Linux Container (LXC), on the other hand, does not require a Hypervisor and each virtualized operating environment runs on top of the shared host operating system.

Currently, LXC is a Linux only virtualization solution. In other words, one cannot run other operating systems like Windows.

The following Figure-2 illustrates the high-level overview of a Linux Container:

Linux Container
Figure-2

Linux Containers (LXC) provide an operating system level virtualization that are much more lightweight, efficient and faster to launch compared to the Virtual Machines.

Installation

The installation is on a Ubuntu 14.04 LTS based Linux desktop.

To install the LXC, issue the following command:

$ sudo apt-get install lxc lxctl lxc-templates

Once the install completes, issue the following command to check everything was ok:

$ lxc-checkconfig

If LXC was installed correctly, the following would be a typical output:

Output.1

Kernel configuration not found at /proc/config.gz; searching...
Kernel configuration found at /boot/config-3.13.0-24-generic
--- Namespaces ---
Namespaces: enabled
Utsname namespace: enabled
Ipc namespace: enabled
Pid namespace: enabled
User namespace: enabled
Network namespace: enabled
Multiple /dev/pts instances: enabled

--- Control groups ---
Cgroup: enabled
Cgroup clone_children flag: enabled
Cgroup device: enabled
Cgroup sched: enabled
Cgroup cpu account: enabled
Cgroup memory controller: enabled
Cgroup cpuset: enabled

--- Misc ---
Veth pair device: enabled
Macvlan: enabled
Vlan: enabled
File capabilities: enabled

Note : Before booting a new kernel, you can check its configuration
usage : CONFIG=/path/to/config /usr/bin/lxc-checkconfig

Hands-on with LXC

Linux containers LXC can be operated in two modes - privileged or unprivileged.

In the privileged mode, all the commands are issued in the root user context using the sudo command.

We will operate in the unprivileged mode as it is more secure and shields the host from any harm.

To get started, we need to first create an LXC container. To create a container, use the lxc-create as follows:

$ lxc-create -n my_lxc_ubuntu -t ubuntu

Using the option -n, we specify the container name and using the option -t, we specify the type of the container. In our case we want a ubuntu based container.

The following would be a typical output:

Output.2

lxc_container: No mapping for container root
lxc_container: Error chowning /home/mytest/.local/share/lxc/my_lxc_ubuntu/rootfs to container root
lxc_container: Error creating backing store type (none) for my_lxc_ubuntu
lxc_container: Error creating container my_lxc_ubuntu

Hmm - we got an error. The reason for this error is that we are trying to create an unprivileged container and we are missing some setup.

Execute the following commands so that we are able to create unprivileged containers:

$ mkdir -p ~/.config/lxc
$ echo "lxc.id_map = u 0 100000 65536" > ~/.config/lxc/default.conf
$ echo "lxc.id_map = g 0 100000 65536" >> ~/.config/lxc/default.conf
$ echo "lxc.network.type = veth" >> ~/.config/lxc/default.conf
$ echo "lxc.network.link = lxcbr0" >> ~/.config/lxc/default.conf
$ echo "$USER veth lxcbr0 2" | sudo tee -a /etc/lxc/lxc-usernet

Let us again try to create a container, using lxc-create as follows:

$ lxc-create -n my_lxc_ubuntu -t ubuntu

The following would be a typical output:

Output.3

lxc: Missing newuidmap/newgidmap
error mapping child
setgid: Invalid argument
lxc_container: Error chowning /home/mytest/.local/share/lxc/my_lxc_ubuntu/rootfs to container root
lxc_container: Error creating backing store type (none) for my_lxc_ubuntu
lxc_container: Error creating container my_lxc_ubuntu

Again, we got an error. The reason for this error is that the Linux container (LXC) depends on the commands newuidmap and newgidmap.

Execute the following command to install newuidmap and newgidmap so that we are able to create unprivileged containers:

$ sudo apt-get install uidmap

Once again, let us again try to create a container, using lxc-create as follows:

$ lxc-create -n my_lxc_ubuntu -t ubuntu

The following would be a typical output:

Output.4

This template can't be used for unprivileged containers.
You may want to try the "download" template instead.
lxc_container: container creation template for my_lxc_ubuntu failed
lxc_container: Error creating container my_lxc_ubuntu

Once again, we got an error. The reason for this error is that the unprivileged container is not able to use the default pre-built templates that are located in the directory /usr/share/lxc/templates/. These pre-built templates need root user access to perform some setup (create device nodes).

Hence the suggestion to use the special template called download which downloads images from a central repository maintained by the LXC community.

Finally, let us again attempt to create a container, using lxc-create as follows:

$ lxc-create -n my_lxc_ubuntu -t download

The following would be a typical output:

Output.5

Setting up the GPG keyring
Downloading the image index

---
DIST	RELEASE	ARCH	VARIANT	BUILD
---
centos	6	amd64	default	20140829_02:16
centos	6	i386	default	20140829_02:16
debian	jessie	amd64	default	20140828_22:42
debian	jessie	armel	default	20140828_22:42
debian	jessie	armhf	default	20140828_22:42
debian	jessie	i386	default	20140828_22:42
debian	sid	amd64	default	20140828_22:42
debian	sid	armel	default	20140828_22:42
debian	sid	armhf	default	20140827_01:23
debian	sid	i386	default	20140828_22:42
debian	wheezy	amd64	default	20140828_22:42
debian	wheezy	armel	default	20140828_22:42
debian	wheezy	armhf	default	20140828_22:42
debian	wheezy	i386	default	20140828_22:42
oracle	6.5	amd64	default	20140828_11:40
oracle	6.5	i386	default	20140828_11:40
plamo	5.x	amd64	default	20140828_21:36
plamo	5.x	i386	default	20140828_21:36
ubuntu	lucid	amd64	default	20140828_03:49
ubuntu	lucid	i386	default	20140828_03:49
ubuntu	precise	amd64	default	20140828_03:49
ubuntu	precise	armel	default	20140828_03:49
ubuntu	precise	armhf	default	20140828_03:49
ubuntu	precise	i386	default	20140828_03:49
ubuntu	trusty	amd64	default	20140828_03:49
ubuntu	trusty	arm64	default	20140828_03:49
ubuntu	trusty	armhf	default	20140828_03:49
ubuntu	trusty	i386	default	20140828_03:49
ubuntu	trusty	ppc64el	default	20140825_03:49
ubuntu	utopic	amd64	default	20140828_03:49
ubuntu	utopic	arm64	default	20140828_03:49
ubuntu	utopic	armhf	default	20140828_03:49
ubuntu	utopic	i386	default	20140828_03:49
ubuntu	utopic	ppc64el	default	20140825_03:49
---
      

Bingo !!!. We are now prompted to type the Distribution we desire. We will type in ubuntu. Next, we will be prompted for the Release. We will type in trusty. Finally, we will be prompted to type the Architecture. We will type in i386.

The following would be a typical output:

Output.6

Distribution: ubuntu
Release: trusty
Architecture: i386

Downloading the image index
Downloading the rootfs
Downloading the metadata
The image cache is now ready
Unpacking the rootfs

---
You just created an Ubuntu container (release=trusty, arch=i386, variant=default)
The default username/password is: ubuntu / ubuntu
To gain root privileges, please use sudo.

Now that we have successfully created a ubuntu based container, we need to get it up and running in order to use it. To start the container as a daemon, use the lxc-start command as follows:

$ lxc-start -n my_lxc_ubuntu -d

Typical there is no output when we start a container in the daemon mode.

To check the status of the container(s), use the lxc-ls command as follows:

$ lxc-ls -f

The following would be a typical output:

Output.7

NAME           STATE    IPV4        IPV6  AUTOSTART  
---------------------------------------------------
my_lxc_ubuntu  RUNNING  10.0.3.140  -     NO

To get more information about a container, use the lxc-info command as follows:

$ lxc-info -n my_lxc_ubuntu

The following would be a typical output:

Output.8

Name:           my_lxc_ubuntu
State:          RUNNING
PID:            5362
IP:             10.0.3.140
CPU use:        1.55 seconds
BlkIO use:      18.66 MiB
Memory use:     23.36 MiB
Link:           vethTO4SY6
 TX bytes:      3.45 KiB
 RX bytes:      9.51 KiB
 Total bytes:   12.96 KiB

To use a container that has been started, use the lxc-console command as follows:

$ lxc-console -n my_lxc_ubuntu

The following would be a typical output:

Output.9

Ubuntu 14.04.1 LTS my_lxc_ubuntu tty1

my_lxc_ubuntu login: 

Login to the container using the user-id ubuntu and the default password ubuntu.

The following would be a typical output:

Output.10

my_lxc_ubuntu login: ubuntu
Password: 
Last login: Sat Sep 20 18:45:03 UTC 2014 on tty1
Welcome to Ubuntu 14.04.1 LTS (GNU/Linux 3.13.0-24-generic i686)

 * Documentation:  https://help.ubuntu.com/
ubuntu@my_lxc_ubuntu:~$ 

We can use the container normally like any other virtual machine or a real machine. One can manage software packages (add, upgrade, remove) on the container just like a regular host.

To shutdown a running container, use the lxc-stop command as follows:

$ lxc-stop -n my_lxc_ubuntu

Typical there is no output when we stop a container.

To check the status of the container(s), let us once again use the lxc-ls command as follows:

$ lxc-ls -f

The following would be a typical output:

Output.11

NAME           STATE    IPV4  IPV6  AUTOSTART  
---------------------------------------------
my_lxc_ubuntu  STOPPED  -     -     NO

To completely delete a container from a host, use the lxc-destroy command as follows:

$ lxc-destroy -n my_lxc_ubuntu

Typical there is no output when we remove a container.

To check the status of the container(s), let us once again use the lxc-ls command as follows:

$ lxc-ls -f

The following would be a typical output:

Output.12

NAME  STATE  IPV4  IPV6  AUTOSTART  
----------------------------------

Linux Containers (LXC) provide lightweight virtualization that allow one to create isolated and secure environment(s) for experimenting with new or different versions of software, which is useful for application developers.

References

Official Ubuntu LXC Documentation