After couple of people asking me about complete LXC setup on Ubuntu, I decided to write an ultimate guide on how to start with these containers on your home network.
Advantages of LXC over heavy virtualization or docker:
- They're light, as light as docker
- They're slightly more persistent than docker, meaning you can run services inside them
This guide will show you how to:
- Install and configure LXC container system
- Bootstrap a base image
- Setup basic network bridge with your local network
- Clone this image to create more containers
Here are prerequisites:
- AMD64/ARM64/ARM32 machine (yes, it works on Odroid and Raspberry too)
- Ubuntu 15.10/16.04 installed
- Local network, meaning your WiFi router for DHCP and DNS
Let's get started then!
The best and the most up to date way to get current packages would be to add ppa:ubuntu-lxc/lxd-stable to your apt repos:
If you don't have add-apt-repository, then you will need to apt-get python-software-properties and software-properties-common packages first.
Now we can pull package updates and actually install LXC:
apt-get update apt-get install lxc
Easy, we'll leave it at this for a moment, let's get to setting up our network.
The easiest way to use LXC is to use the builtin lxcbr0 bridge interface that will be created upon install, but this has some side effects:
- It uses dnsmasq to setup DNS and DHCP addresses
- Containers are isolated in a host-only network, so one needs to create port redirects to pull services out to host IP
The most common way that I'm using for my everyday experimentation is to create a network bridge directly to my home network. This way you don't need to worry about redirecting anything, as you will get DNS/DHCP configured by your home router.
To setup a network bridge in Ubuntu, you need to install bridge-utils package and have a look at the following tutorial:
In short, you need to type following in your /etc/network/interfaces:
# The loopback network interface auto lo iface lo inet loopback auto eno1 iface eno1 inet manual auto br0 iface br0 inet dhcp bridge-ifaces eno1 bridge-ports eno1 up ifconfig eno1 up
Remember to replace eno1 with your locally detected interface, obtained by looking at the output of ifconfig. Restart after making changes to this file.
Once your system rebooted, you should be able to see a br0 interface added, with an IP address on it.
There are far more advanced networking options available, including GRE tunnels, VPN, multicasts and so on, for that you need to go and look at Flockport's articles:
Okay, basic network setup is completed now, time to setup our base OS image.
Setting up first LXC image (the template)
Let's setup our first debootstrapped image on LXC, it's as easy as invoking this command:
lxc-create -n template-ubuntu-xenial -t ubuntu -- -r xenial
After a moment of deboostrap running, we'll have a fresh image of Ubuntu Xenial pulled, we can check that with a following command:
lxc-ls --fancy NAME STATE AUTOSTART GROUPS IPV4 IPV6 template-ubuntu-xenial STOPPED 0 - - -
Now let's change the default network interface to point to our br0 bridge that we created earlier. For that, we'll need to edit the /var/lib/lxc/template-ubuntu-xenial/config file, and change the line with interfaces to look like this:
lxc.network.link = br0
For a moment we'll leave it at this. Now you need to start your container:
lxc-start -d -n template-ubuntu-xenial
Check what IP address does it have:
lxc-ls --fancy NAME STATE AUTOSTART GROUPS IPV4 IPV6 template-ubuntu-xenial STOPPED 0 - 10.21.34.188 -
Great, we have an IP address from our local network.
This is the moment when you can SSH into the new container, remember that Ubuntu has a default ubuntu/ubuntu user.
After customising the template container, we need to shut it down:
lxc-stop -d -n template-ubuntu-xenial
That's it, our template is done.
Cloning the template into further containers
Now we can begin stamping the template into clones:
lxc-clone -s -B overlayfs -n child1-xenial-$(date +%s) template-ubuntu-xenial Created container child1-xenial-1463170628 as snapshot of template-ubuntu-xenial lxc-start -d -n child1-xenial-1463170628
What we did here, is we've created a child1-xenial-1463170628 container that is based on our template and uses a differential storage backend (overlayfs).
Let's check whether the child is started properly:
lxc-ls --fancy NAME STATE AUTOSTART GROUPS IPV4 IPV6 template-ubuntu-xenial STOPPED 0 - - - child1-xenial-1463170628 RUNNING 0 - 10.21.34.189 -
That's it, you can now SSH into the child container and do your stuff, it's like another brand new Ubuntu.
To destroy a container, you need to stop it first:
lxc-stop -n child1-xenial-1463170628 lxc-destroy -n child1-xenial-1463170628 Destroyed container child1-xenial-1463170628