Run LXC Containers on Raspberry Pi with Raspbian Buster

I'm super excited today, as I discovered you can run LXC containers on the LXD hypervisor hosted on a Raspberry Pi 4.

I'm using Raspbian Buster as the Operating System

What are we doing today?

I was searching for "lightweight virtual machines on raspberry pi" and found a couple of projects, but felt a bit unstable at the point.

Then I started to move to system containers, lxc, which is not a virtual machine, but they "feel" like one.

So in this tutorial I will show you how to install LXD on Raspbian Buster and how to launch a Alpine LXC container, install OpenSSH Server and SSH from our host to our Alpine LXC Container.

Install LXD

First we need to install snapd and bridge-utils:

$ sudo apt update
$ sudo apt install snapd bridge-utils -y

Install LXD with Snap:

$ sudo snap install core lxd

If you log out and back in, and you don't find lxd in your path, have a look at this issue

You can set your path temporarily:

$ export PATH=$PATH:/snap/bin

Or for a permanent solution append to your ~/.bashrc:

export PATH=$PATH:/snap/bin  

You should be able to see the following output:

$ lxd -h
Description:  
  The LXD container manager (daemon)

  This is the LXD daemon command line. It's typically started directly by your
  init system and interacted with through a tool like `lxc`.

  There are however a number of subcommands that let you interact directly with
  the local LXD daemon and which may not be performed through the REST API alone.

Usage:  
  lxd [flags]
  lxd [command]

If you are seeing ERROR: ld.so: object '/usr/lib/arm-linux-gnueabihf/libarmmem-${PLATFORM}.so' from /etc/ld.so.preload cannot be preloaded (cannot open shared object file): ignored.

I stumbled upon this post and removed /usr/lib/arm-linux-gnueabihf/libarmmem-${PLATFORM}.so from /etc/ld.so.preload

Initialize LXD

Initialize LXD and answer questions on how you want your setup customized, such as IPv4, cluster, storage etc:

$ lxd init

To list all arm based images:

$ lxc image  list images:armhf
(output edited for display reasons)

| ALIAS                | DESCRIPTION      |  ARCHITECTURE | SIZE |
| alpine/3.11 (3 more) | Alpine 3.11 armhf   | armv7l | 2.14MB   |
| alpine/edge (3 more) | Alpine edge armhf   | armv7l | 3.61MB   |  
| archlinux (5 more)   | Archlinux   armhf   | armv7l | 143.72MB |
| centos/7 (3 more)    | Centos 7    armhf   | armv7l | 78.95MB  |
| debian/10 (7 more)   | Debian buster armhf | armv7l | 69.02MB  |
...

Launch your first LXC Container

I will initialize a new container, called alpine-container using the alpine 3.11 image:

$ lxc init images:alpine/3.11 alpine-container
Creating alpine-container  

The image will now be pulled from the remote server then create the container and keep it in a stopped state, we can verify that by running:

$ lxc list
+-------------------+---------+---------------------+-----------+
|       NAME        |  STATE  |        IPV4         |   TYPE    |
+-------------------+---------+---------------------+-----------+
| alpine-container  | STOPPED |                     | CONTAINER |
+-------------------+---------+---------------------+-----------+

Let's start the container:

$ lxc start alpine-container

Let's list our containers:

$ lxc list
+-------------------+---------+---------------------+-----------+
|       NAME        |  STATE  |        IPV4         |   TYPE    |
+-------------------+---------+---------------------+-----------+
| alpine-container  | RUNNING | 10.6.192.196 (eth0) | CONTAINER |
+-------------------+---------+---------------------+-----------+

To exec into your LXC container:

$ lxc exec alpine-container -- sh
~ #

Install OpenSSH-Server on Alpine

Let's install the OpenSSH-Server on alpine:

$ apk update
$ apk add openssh-server

I'm creating a new user to test ssh:

$ adduser ruan
New password:  
Retype password:  
passwd: password for ruan changed by root  

Now we want to ssh to our container with our newly created user, using a ssh key. If you don't have a SSH key, create one:

$ ssh-keygen -f ./test -t rsa -C "Test" -q -N ""

Copy the contents of the public key, so that we can include it in the container's authorized_keys file of the target user:

$ cat ./test.pub

Let's switch to that user and create the authorized_keys file:

$ su - ruan
$ cat 'copied contents of ssh public key' >> ~/.ssh/authorized_keys
$ exit

Enable the OpenSSH Server on boot as the root user:

$ rc-update add sshd

Start the SSH Server:

$ /etc/init.d/sshd restart

SSH to the LXC Container

From the host, ssh to the container:

$ ssh ruan[email protected]

Welcome to Alpine!

The Alpine Wiki contains a large amount of how-to guides and general  
information about administrating Alpine systems.  
See <http://wiki.alpinelinux.org/>.

You can setup the system with the command: setup-alpine

You may change this message by editing /etc/motd.

alpine-container:~$  

Pretty neat right?

Resources

Some resources I stumbled upon: