LXC

LXC is a userspace interface for the Linux kernel containment features. Through a powerful API and simple tools, it lets Linux users easily create and manage system or application containers.

LXC containers are often considered as something in the middle between a chroot and a full fledged virtual machine. The goal of LXC is to create an environment as close as possible to a standard Linux installation but without the need for a separate kernel.

source: linuxcontainers.org


Install

$ prt-get depinst lxc


Configure

To use unprivileged containers edit /etc/lxc/default.conf replace USERNAME with user name that will launch containers;

# Template used to create this container: /usr/share/lxc/templates/lxc-local
# Parameters passed to the template: --fstree crux-3.5-x86_64-20190419-rootfs.tar.xz --metadata crux-3.5-x86_64-20190419-lxc.tar.xz
# Template script checksum (SHA-1): c4910e305a7e8fc8bd5ce18d2254f36fac84a207
# For additional config options, please look at lxc.container.conf(5)

# Uncomment the following line to support nesting containers:
#lxc.include = /usr/share/lxc/config/nesting.conf
# (Be aware this has security implications)

# Uncomment to create unprivileged containers
# Network configuration

# Distribution configuration
lxc.include = /usr/share/lxc/config/common.conf
lxc.include = /usr/share/lxc/config/userns.conf
lxc.arch = x86_64
lxc.tty.max = 6

# Container specific configuration
lxc.idmap = u 0 100000 65536
lxc.idmap = g 0 100000 65536
lxcuser veth lxcbr0 10
lxc.rootfs.path = dir:/srv/lxc/rootfs
lxc.uts.name = LXC_NAME

# Network configuration
lxc.net.0.link = lxcbr0
lxc.net.0.type = veth
lxc.net.0.flags = up
lxc.net.0.name = eth0
lxc.net.0.hwaddr = 00:16:3e:00:00:00

# uncomment the next two lines if static IP addresses are needed
# leaving these commented will imply DHCP networking
#
#lxc.net.0.ipv4.address = 10.0.0.20/8
#lxc.net.0.ipv4.gateway = 10.0.0.1

# populate /dev
lxc.autodev = 1
lxc.mount.entry = none dev/shm tmpfs nodev,nosuid,noexec,mode=1777,create=dir 0 0

Edit /etc/lxc/lxc-usernet and add;

lxcuser veth lxcbr0 10

Check if kernel supports user namespaces;

$ grep CONFIG_USER_NS /boot/config-4.19.116-gnu
CONFIG_USER_NS=y

Allow normal users to run unprivileged containers;

# sysctl kernel.unprivileged_userns_clone=1

Edit /etc/sysctl.conf and add following to make it permanent;

kernel.unprivileged_userns_clone = 1

Create /etc/subuid with root and desired user that will launch containers, in this case we will create and use lxcuser;

lxcuser:100000:65536

And /etc/subgid;

lxcuser:100000:65536

Create user;

# useradd -s /bin/bash -c "lxc unprivileged user" -U -G users -d /srv/lxc -m lxcuser

Set password;

# passwd lxcuser

Mount cgroups filesystems;

Check this script cgroupfs-mount the easy way but not correct way is to;

# mount -t cgroup cgroup /sys/fs/cgroup

Add to /etc/fstab;

cgroup /sys/fs/cgroup cgroup defaults

Run lxc-cgroups rc script and add it to /etc/rc.conf;

# sh /etc/rc.d/lxc-cgroups

Check if there is missing requirements;

$ lxc-checkconfig

To manually setup network add bridge;

# ip link add name lxcbr0 type bridge
# ip link set dev lxcbr0 up
# ip link set enp8s0 master lxcbr0

This steps are not enough to allow network to containers but will allow to run a simple test. Check Qemu network, change example step described to fit desired configuration.

Setup the bridge, dnsmasq is required, run script rc lxc-net

# sh /etc/rc.d/lxc-net

Add above script to /etc/rc.conf.


Create image

Pacman provides script that creates images with given iso and tarballs ready to use at nullvoid.de. To create your own image download and mount iso on a directory then install packages into rootfs.

$ mkdir -p ~/lxc-image/iso
$ cd ~/lxc-image/

Download setup-iso.sh scrit and execute it;

$ chmod +x setup-iso.sh
$ ./setup-iso.sh -r ~/lxc-image/iso -d
$ sudo ./setup-iso.sh -r ~/lxc-image/iso -m /media

Install core packages into rootfs directory, download install-core.sh script and execute it;

$ sudo ./install-core.sh -r ~/lxc-image/rootfs -p /media/crux -o

Create create-container.sh, this script is based on pacman lxc.sh script with few changes to match the steps described above.

#!/bin/sh

set -e

if test $(id -u) -ne 0
then
        echo 'This must be run as root.'
            exit 1
fi

DIR="$(pwd)"
ARCH="x86_64"
RELEASE="$(cat /media/crux-media)"
VERSION="${RELEASE%-*}"
DATE="${RELEASE#*-}"

# Insert hostname templates
sed -i -e '/HOSTNAME=/cHOSTNAME=LXC_NAME' rootfs/etc/rc.conf
sed -i -e 's;localhost.*;& LXC_NAME;' rootfs/etc/hosts

# Fix default networking
cat > rootfs/etc/rc.d/net << 'EOF'
#!/bin/sh
#
# /etc/rc.d/net: start/stop network interface
#

case $1 in
    start)
        /sbin/dhcpcd -4 eth0
        ;;
    stop)
        /sbin/dhcpcd -x eth0
        ;;
    restart)
        $0 stop
        $0 start
        ;;
    *)
        echo "Usage: $0 [start|stop|restart]"
        ;;
esac
EOF

# Remove noclear from agettys
# Replace linux with xterm on agettys
# Remove serial console
# Remove existing powerfail entries
# Add an entry to allow LXC/LXD to shutdown the container
sed -i \
    -e 's;--noclear ;;' \
    -e 's;linux;xterm;' \
    -e '/s1:2:/d' \
    -e '/:powerfail:/d' \
    -e '/ctrlaltdel/cpf::powerfail:/sbin/telinit 0' \
    rootfs/etc/inittab

# Disable klogd in containers as it doesn't work
sed -i -e '/KLOG/d' -e 's;and klog ;;' rootfs/etc/rc.d/sysklogd

# Export a default LANG
sed -i -e '/LESS=/iexport LANG="C"' rootfs/etc/profile

# Disable startup functionality that doesn't work in a container
sed -i \
    -e '/# Start udev/,/^ *$/d' \
    -e '/# Create device-mapper device nodes and scan for LVM volume groups/,/^ *$/d' \
    -e '/# Mount root read-only/,/^ *$/d' \
    -e '/-f \/forcefsck/,/^ *$/d' \
    -e '/# Check filesystems/,/^ *$/d' \
    -e '/# Mount local filesystems/,/^ *$/d' \
    -e '/# Activate swap/,/^ *$/d' \
    -e '/hwclock/d' \
    -e '/# Load console font/,/^ *$/d' \
    -e '/# Load console keymap/,/^ *$/d' \
    -e '/# Screen blanks after 15 minutes idle time/,/^ *$/d' \
    -e '/# Run module initialization script/,/^ *$/d' \
    rootfs/etc/rc

# Disable shutdown functionality that doesn't work in a container
sed -i \
    -e '/# Set linefeed mode to avoid staircase effect/,/^ *$/d' \
    -e '/# Save system clock/,/^ *$/d' \
    -e '/# Turn off swap/,/^ *$/d' \
    -e '/# Unmount file systems/,/^ *$/d' \
    -e '/# Remount root filesystem read-only/,/^ *$/d' \
    rootfs/etc/rc.shutdown

# Disable certain functionality in single user login
sed -i \
    -e '/# Start udev/,/^ *$/d' \
    rootfs/etc/rc.single

# Remove variables that are not used in containers
sed -i -e '/FONT=/d' -e '/KEYMAP=/d' rootfs/etc/rc.conf

# Create the LXC metadata tarball
cat > config << EOF
lxc.include = LXC_TEMPLATE_CONFIG/common.conf
lxc.arch = $ARCH
lxc.tty.max = 6
EOF
cat > excludes << 'EOF'
dev/*
EOF
cat > config-user << EOF
lxc.include = LXC_TEMPLATE_CONFIG/common.conf
lxc.include = LXC_TEMPLATE_CONFIG/userns.conf
lxc.arch = $ARCH
lxc.tty.max = 6
EOF
cat > excludes-user << 'EOF'
dev/*
EOF
cat > templates << 'EOF'
/etc/rc.conf
/etc/hosts
EOF
cat > create-message << EOF
You just created a CRUX $VERSION $ARCH ($DATE) container.
EOF
tar -c -f - config excludes config-user excludes-user templates create-message | xz -9 > "$DIR/crux-$VERSION-$ARCH-$DATE-lxc.tar.xz"
rm -rf config config-user create-message excludes-user templates

# Create the LXD metadata tarball
mkdir -p templates
sed -e 's;LXC_NAME;{{ container.name }};' rootfs/etc/rc.conf > templates/rc.conf.tpl
sed -e 's;LXC_NAME;{{ container.name }};' rootfs/etc/hosts > templates/hosts.tpl
sed -e '/c1:2:/,/^ *$/c{% for i in config_get("user.ttys","")|make_list %}c{{ i }}:2:respawn:/sbin/agetty 38400 tty{{ i }} xterm\
{% endfor %}' rootfs/etc/inittab > templates/inittab.tpl
cat > metadata.yaml << EOF
architecture: $ARCH
creation_date: $(date +%s)
properties:
  architecture: $ARCH
  description: CRUX $VERSION $ARCH ($DATE)
  name: crux-$VERSION-$ARCH-$DATE
  os: crux
  release: "$VERSION"
  serial: "$DATE"
templates:
  /etc/rc.conf:
    when:
    - create
    - copy
    create_only: false
    template: rc.conf.tpl
  /etc/hosts:
    when:
    - create
    - copy
    create_only: false
    template: hosts.tpl
  /etc/inittab:
    when:
    - create
    - copy
    create_only: false
    template: inittab.tpl
EOF
tar -c -f - templates/ metadata.yaml | xz -9 > "$DIR/crux-$VERSION-$ARCH-$DATE-lxd.tar.xz"
#rm -rf templates/ metadata.yaml

# Create the root tarball
(cd rootfs; tar -c -f - *) | xz -9 > "$DIR/crux-$VERSION-$ARCH-$DATE-rootfs.tar.xz"
#rm -rf rootfs

Run script;

$ chmod +x create-container.sh
$ sudo ./create-container.sh

Output of above script creates three archives;

crux-3.5-x86_64-20190611-lxc.tar.xz
crux-3.5-x86_64-20190611-lxd.tar.xz
crux-3.5-x86_64-20190611-rootfs.tar.xz

Manage images

First become the user previoulsy created;

$ su - lxcuser

Create configuration file;

$ mkdir -p ~/.config/lxc
$ cp /etc/lxc/default.conf ~/.config/lxc/

From the man page;

"lxc-create creates a system object where is stored the configuration information and where can be stored user information. The identifier name is used to specify the container to be used with the different lxc commands."

At the time of this writing to directories are created; /var/lib/lxc/name_of_container and /usr/var/lib/lxc/name_of_container. Man page says; "The object is a directory created in /usr/var/lib/lxc and identified by its name."

$ lxc-create -n test -t local -- --metadata crux-3.5-x86_64-20190611-lxc.tar.xz --fstree crux-3.5-x86_64-20190611-rootfs.tar.xz
Unpacking the rootfs

---
You just created a CRUX 3.5 x86_64 (20190611) container.

To destroy a container run;

$ lxc-destroy -n container_name -s


Using containers

Become user created to run containers;

$ su - lxcuser

Start container in daemon mode, this will run it in background;

$ lxc-start -n test -d

When necessary to run in foreground to end up with a login shell use -F, this is useful when doing debugging;

$ lxc-start -n test -F

When running in foreground you should see the output of container starting and it's services, to get information about container run;

$ lxc-info -n test
Name:           test
State:          RUNNING
PID:            4946
IP:             10.0.3.95
Link:           veth101_AEYSE
 TX bytes:      1.87 KiB
 RX bytes:      5.31 KiB
 Total bytes:   7.18 KiB
$

To list running containers run;

$ lxc-ls -f
NAME STATE   AUTOSTART GROUPS IPV4      IPV6 UNPRIVILEGED
test RUNNING 0         -      10.0.3.95 -    true

To configure users and sshd access container using lxc-attach, add /usr/sbin to path and add user in this case test;

$ lxc-attach -n test
# export PATH="/usr/sbin:$PATH"
# passwd
# useradd -U -m -k /etc/skel -s /bin/bash test
# usermod -G adm,wheel,audio,input,video,users test
# passwd test
# exit

Now is possible to login using console;

$ lxc-console -n test

To exit lxc-console session, type Ctrl-A followed by Q. To stop a container;

$ lxc-stop -n test


Notes