CentOS 7: Run containers with LXD/LXC

CentOS 7 has LXC 1.x while LXD/LXC is 2.x. Commands are different between 1.x and 2.x. And libvirt will not support LXC 1.0. This article will describe installing LXD/LXC on CentOS 7.

1 Install LXD

The following script will install LXD.

#!/bin/sh -e


FEDORA=https://dl.fedoraproject.org/pub/fedora/linux/releases/27
FEDORA_PACKAGES_L=${FEDORA}/Everything/source/tree/Packages/l
FEDORA_LXC=lxc-2.0.8-2.fc27.2.src.rpm

# Install Fedora's lxc package because centos's is too old.
sudo yum install -y wget epel-release rpmdevtools rpm-build
wget -q ${FEDORA_PACKAGES_L}/${FEDORA_LXC}
sudo yum-builddep -y ${FEDORA_LXC}
rpmbuild --rebuild ${FEDORA_LXC}
# shellcheck disable=SC2046
sudo yum localinstall -y \
     $(find ~/rpmbuild/RPMS -type f -a ! -name "*debuginfo*")

USER_ADDED_TO_LXD_GROUP="${USER}"
export GOPATH=${HOME}/go

# Setup subuid and subgid.
echo "root:100000:65536" | sudo tee -a /etc/subuid
echo "root:100000:65536" | sudo tee -a /etc/subgid

# LXD/LXC uses lxc-xxx pakcage.
sudo yum install -y lxc-devel

# The container-selinux has supported LXD.
sudo yum install -y container-selinux

# Add lxd group and add user to lxd group.
sudo /usr/sbin/groupadd -r lxd
sudo gpasswd -a "${USER_ADDED_TO_LXD_GROUP}" lxd
sudo mkdir /var/lib/lxd
sudo chown root:lxd /var/lib/lxd
sudo restorecon /var/lib/lxd

# Build LXD and install LXD.
sudo yum install -y git golang sqlite-devel dnsmasq squashfs-tools libacl-devel
go get -v -x github.com/lxc/lxd/lxc github.com/lxc/lxd/lxd
sudo cp "${GOPATH}"/bin/lxd /usr/bin
sudo chown root:lxd /usr/bin/lxd
sudo restorecon /usr/bin/lxd
sudo cp "${GOPATH}"/bin/lxc /usr/bin
sudo chown root:lxd /usr/bin/lxc
sudo restorecon /usr/bin/lxc

# Create LXD directory.
sudo mkdir -p /var/log/lxd
sudo chown root:lxd /var/log/lxd
sudo restorecon /var/log/lxd

# Setup systemd service.
cat <<EOF | sudo tee /usr/lib/systemd/system/lxd.service
[Unit]
Description=LXD - main daemon
After=network.target
Requires=network.target lxd.socket
Documentation=man:lxd(1)

[Service]
Environment="LXD_SOCKET=/var/run/lxd/unix.socket"
EnvironmentFile=-/etc/environment
ExecStart=/usr/bin/lxd --group lxd --logfile=/var/log/lxd/lxd.log
ExecStartPost=/usr/bin/lxd waitready --timeout=600
KillMode=process
TimeoutStartSec=600
TimeoutStopSec=40
Restart=on-failure
LimitNOFILE=infinity
LimitNPROC=infinity

[Install]
Also=lxd.socket
EOF
sudo restorecon /usr/lib/systemd/system/lxd.service
cat <<EOF | sudo tee /usr/lib/systemd/system/lxd.socket
[Unit]
Description=LXD - unix socket
Documentation=man:lxd(1)

[Socket]
ListenStream=/var/run/lxd/unix.socket
SocketGroup=lxd
SocketMode=0660
Service=lxd.service

[Install]
WantedBy=sockets.target
EOF
sudo restorecon /usr/lib/systemd/system/lxd.socket
sudo systemctl --system daemon-reload
sudo systemctl enable lxd.service

cat <<EOF | sudo tee /etc/profile.d/lxd.sh
export LXD_SOCKET=/var/run/lxd/unix.socket
EOF

# Setup kernel.
sudo yum install -y grub2-tools
# shellcheck disable=SC1091
. /etc/default/grub
C="user_namespace.enable=1 namespace.unpriv_enable=1"
C="$GRUB_CMDLINE_LINUX $C"
sudo sed -e "s;^GRUB_CMDLINE_LINUX=.*;GRUB_CMDLINE_LINUX=\"$C\";g" \
     -i /etc/default/grub
sudo /usr/sbin/grub2-mkconfig -o /boot/grub2/grub.cfg
cat <<EOF | sudo tee /etc/sysctl.d/lxd.conf
user.max_user_namespaces=15076
EOF

# LXD can be used after reboot.
sudo /sbin/reboot

2 Initialize LXD

The following script will initialize LXD.

#!/bin/sh


# Would you like to use LXD clustering? (yes/no) [default=no]: no
# Do you want to configure a new storage pool? (yes/no) [default=yes]: yes
# Name of the new storage pool [default=default]: default
# Name of the storage backend to use (dir, lvm, zfs) [default=zfs]: dir
# Would you like to connect to a MAAS server? (yes/no) [default=no]: no
# Would you like to create a new network bridge? (yes/no) [default=yes]: yes
# What should the new bridge be called? [default=lxdbr0]: lxdbr0
# What IPv4 address should be used? (CIDR subnet notation, “auto” or “none”)
# [default=auto]: auto
# What IPv6 address should be used? (CIDR subnet notation, “auto” or “none”)
# [default=auto]: none
# Would you like LXD to be available over the network? (yes/no)
# [default=no]: no
# 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

# Initialize LXD with NAT network.
lxd waitready
cat <<EOF | lxd init
no
yes
default
dir
no
yes
lxdbr0
auto
none
no
yes
no
EOF

3 Run container

This can download debian/stretch image and run it. IP address is assigned to container.

$ lxc launch images:debian/stretch debian
<snip>
$ lxc exec debian ip a s
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN
group default qlen 1
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host
       valid_lft forever preferred_lft forever
4: eth0@if5: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue
state UP group default qlen 1000
    link/ether 00:16:3e:29:1e:7d brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet6 fe80::216:3eff:fe29:1e7d/64 scope link tentative
       valid_lft forever preferred_lft forever