systemd incorporates a container manager called systemd-nspawn [1]. In this post I'll show how to build and start containers on Debian Jessie.
First, lets install the prerequisites:
File: gistfile1.txt
-------------------
root@nspawn:~# apt-get install -y dbus debootstrap
Next, let's build the container file system:
File: gistfile1.txt
-------------------
root@nspawn:~# debootstrap --arch=amd64 jessie /var/lib/machines/container1/
I: Retrieving Release
I: Retrieving Release.gpg
I: Checking Release signature
I: Valid Release signature (key id 75DDC3C4A499F1A18CB5F3C8CBF8D6FD518E17E1)
I: Retrieving Packages
I: Validating Packages
I: Resolving dependencies of required packages...
I: Resolving dependencies of base packages...
I: Found additional required dependencies: acl adduser dmsetup insserv libaudit-common libaudit1 libbz2-1.0 libcap2 libcap2-bin libcryptsetup4 libdb5.3 libdebconfclient0 libdevmapper1.02.1 libgcrypt20 libgpg-error0 libkmod2 libncursesw5 libprocps3 libsemanage-common libsemanage1 libslang2 libsystemd0 libudev1 libustr-1.0-1 procps systemd systemd-sysv udev
I: Found additional base dependencies: libdns-export100 libffi6 libgmp10 libgnutls-deb0-28 libgnutls-openssl27 libhogweed2 libicu52 libidn11 libirs-export91 libisc-export95 libisccfg-export90 libmnl0 libnetfilter-acct1 libnettle4 libnfnetlink0 libp11-kit0 libpsl0 libtasn1-6
I: Checking component main on http://ftp.us.debian.org/debian...
...
I: Base system installed successfully.
To start the container and connect with console run:
File: gistfile1.txt
-------------------
root@nspawn:~# systemd-nspawn -D /var/lib/machines/container1/ --machine test_container -b
Spawning container test_container on /var/lib/machines/redis.
Press ^] three times within 1s to kill container.
Detected virtualization 'systemd-nspawn'.
Detected architecture 'x86-64'.
Welcome to Debian GNU/Linux 8 (jessie)!
[ OK ] Reached target Remote File Systems (Pre).
[ OK ] Reached target Paths.
[ OK ] Reached target Encrypted Volumes.
...
Debian GNU/Linux 8 cloud-server-02 console
nspawn login:
^]^]^]
The container is now running, sharing the network name space of the physical host.
To launch the container in its own network namespace with one interface connected to a bridge on the host first create the bridge:
File: gistfile1.txt
-------------------
root@nspawn:~# brctl addbr cont-bridge
root@nspawn:~# brctl show
bridge name bridge id STP enabled interfaces
cont-bridge 8000.000000000000 no
root@nspawn:~# systemd-nspawn -D /var/lib/machines/container1/ --machine test_container --network-bridge=cont-bridge -b
root@nspawn:~#
The container now has a network interface named host0:
File: gistfile1.txt
-------------------
root@nspawn:~# ip link set host0 up
root@nspawn:~# ip a s
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default
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
2: host0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
link/ether 32:2b:93:92:93:57 brd ff:ff:ff:ff:ff:ff
inet6 fe80::302b:93ff:fe92:9357/64 scope link
valid_lft forever preferred_lft forever
root@nspawn:~#
On the host a new virtual interface (the other end of the connections from the container) is connected to the bridge:
File: gistfile1.txt
-------------------
root@nspawn:~# ip a s
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default
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
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
link/ether bc:76:4e:10:25:2a brd ff:ff:ff:ff:ff:ff
inet 192.168.0.100/24 brd 192.168.0.255 scope global eth0
valid_lft forever preferred_lft forever
inet6 2001:4801:7822:103:be76:4eff:fe10:252a/64 scope global
valid_lft forever preferred_lft forever
inet6 fe80::be76:4eff:fe10:252a/64 scope link
valid_lft forever preferred_lft forever
3: br100: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN group default
link/ether 8a:0d:4a:34:a7:93 brd ff:ff:ff:ff:ff:ff
4: vb-test: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast master br100 state UP group default qlen 1000
link/ether 8a:0d:4a:34:a7:93 brd ff:ff:ff:ff:ff:ff
inet6 fe80::880d:4aff:fe34:a793/64 scope link
valid_lft forever preferred_lft forever
root@nspawn:~# brctl show
bridge name bridge id STP enabled interfaces
cont-bridge 8000.8a0d4a34a793 no vb-test
root@nspawn:~#
Systemd can control the containers just like any other service by specifying a unit file:
File: gistfile1.txt
-------------------
root@nspawn:~# cat /etc/systemd/system/test_container.service
[Unit]
Description=Test Container
[Service]
LimitNOFILE=100000
ExecStart=/usr/bin/systemd-nspawn --keep-unit --machine=test_container -j --directory=/var/lib/machines/container1/ --bind=/mnt
Restart=always
[Install]
Also=dbus.service
root@nspawn:~# systemctl start test_container
root@nspawn:~# machinectl
MACHINE CONTAINER SERVICE
test_container container nspawn
1 machines listed.
root@nspawn:~# systemctl list-machines
NAME STATE FAILED JOBS
â— test_container n/a 0 0
1 machines listed.
root@nspawn:~# machinectl status test_container
test_container
Since: Tue 2015-08-18 11:36:36 CDT; 33s ago
Leader: 23638 (bash)
Service: nspawn; class container
Root: /var/lib/machines/container1
Address: fe80::be76:4eff:fe10:32b3
fe80::be76:4eff:fe10:252a
2001:4801:7822:103:be76:4eff:fe10:252a
192.168.0.100
OS: Debian GNU/Linux 8 (jessie)
Unit: test_container.service
├─23637 /usr/bin/systemd-nspawn --keep-unit --machine=test_container -j --directory=/var/lib/machines/container1/ --bind=/mnt
└─23638 -bash
root@nspawn:~# machinectl login test_container
Connected to container test_container. Press ^] three times within 1s to exit session.
Here's a detailed example of how to start multiple containers running Redis on different ports:
File: gistfile1.txt
-------------------
root@nspawn:~# apt-get install -y build-essential git-core xfsprogs dbus debootstrap curl
root@nspawn:~# debootstrap --arch=amd64 jessie /var/lib/machines/redis/
root@nspawn:~# cd /usr/local/src
root@nspawn:/usr/local/src# git clone https://github.com/antirez/redis.git
root@nspawn:/usr/local/src/redis# cd redis
root@nspawn:/usr/local/src/redis# git fetch
root@nspawn:/usr/local/src/redis# git checkout -f 3.0.3
root@nspawn:/usr/local/src/redis# make distclean
root@nspawn:/usr/local/src/redis# CFLAGS=-march=native make -j4
root@nspawn:/usr/local/src/redis# mkdir -p /usr/local/bin/redis/3.0.3
root@nspawn:/usr/local/src/redis# cd src
root@nspawn:/usr/local/src/redis/src# ls redis-* | xargs file | grep executable | awk -F: '{print $1}' | xargs -I '{}' sudo /bin/mv '{}' /usr/local/bin/redis/3.0.3/
root@nspawn:/usr/local/src/redis/src# ln -s /etc/systemd/system/redistogo.include /etc/systemd/system/3.0.3\@.service
root@nspawn:/usr/local/src/redis# tee /etc/systemd/system/redis.include <<'EOF'
.include /etc/systemd/system/redis.service
EOF
root@nspawn:/usr/local/src/redis# tee /etc/systemd/system/redis.service <<'EOF'
[Unit]
Description=Redis in multiple containers
[Service]
LimitNOFILE=100000
ExecStart=/usr/bin/systemd-nspawn --user=redis --keep-unit --machine=redis-%p-%i -j --directory=/var/lib/machines/redis/ --bind=/usr/local/bin/redis/%p/:/bin/ --bind=/mnt --bind=/opt/null:/sbin --bind=/home/redis/%i redis-server /home/redis/%i/redis.conf
Restart=always
[Install]
Also=dbus.service
EOF
root@nspawn:~# useradd -s /bin/false -m redis
root@nspawn:~# useradd -s /bin/false -R /var/lib/machines/redis/ redis
root@nspawn:~# systemctl start 3.0.3@9000
root@nspawn:~# systemctl start 3.0.3@9001
root@nspawn:~# systemctl start 3.0.3@9002
Resources:
[1]. http://www.freedesktop.org/software/systemd/man/systemd-nspawn.html
[2]. https://coreos.com/docs/launching-containers/launching/getting-started-with-systemd/