Jul 172011
 

I’m using some lxc containers to offer services with less resources than full virtualization (like kvm, virtualbox, etc.) demands for.
At first I was creating the containers I needed basically following Chris Willing’s guide but, being jealous :P of the existing templates for the debian based distributions that allow creation of containers with a single command, I tried to assemble one for slackware 13.37, that I personally use at work and at home as a virtualization platform.

The concept is simply reapplying the steps that the slackware setup normally follows but with the necessary changes (mainly omissions) to fit our container environment: so I uncompressed the slackware setup initrd to check Patrick Volkerding’s work

gzip -dc /data/slackware/slackware64-13.37/isolinux/initrd.img | cpio -i -d -H newc --no-absolute-filenames

and had a look at the install shell scripts in /usr/lib/setup.
Then I took as a base the existing lxc-debian template and modified it to:
- download some slackware packages for a minimal installation (many thanks to Vincent Batts for the selection);
- install them in a folder;
- apply all the standard postinstall routines;
- apply the necessary container fixes (many thanks to Chris Willing that pioneered on this and documented it well :) ).

I published the result on github
https://github.com/Ponce/lxc-slackware

“…ok, that’s enough!
we want to play with containers too!”

I was getting to it :)

If you want to try them you have to decide which kind of networking you want for them: choices are basically between using:
- a network bridge on your network interface, with the containers in the same network segment as the host;
- a network bridge on a dummy interface used as gateway with NAT: the containers will be in a private network segment.

If you want to offer services and you have plenty of ips to use (like in an home network), maybe the first solution can be easier, as you don’t have to use iptables to redirect incoming connections to the private network of the containers.

Slackware-13.37 kernel already supports all you need, so let’s setup our interface, assuming our host eth0 (the primary ethernet interface, in the example) address is 192.168.1.5 and that we are in a 192.168.1.0/24 class C network with 192.168.1.1 as our gateway.
Here are the step to follow in each situation:

containers on the same network segment as the host

We have to put the interface down because we are going to add it to the bridge, so if you’are doing this through an ssh connection, I suggest you to run this as a script in a screen session, to avoid being cutted off ;)

/sbin/ifconfig eth0 down
/sbin/brctl addbr br0
/sbin/brctl setfd br0 0
/sbin/ifconfig br0 192.168.1.5 netmask 255.255.255.0 promisc up
/sbin/brctl addif br0 eth0
ifconfig eth0 0.0.0.0 up
route add default gw 192.168.1.1
echo 1 > /proc/sys/net/ipv4/ip_forward
echo 1 > /proc/sys/net/ipv4/conf/br0/proxy_arp

At the end the bridge setup should be ok: you can find a version of the script above (to be included in /etc/rc.d/rc.local) here; I also modify /etc/rc.d/rc.inet1.conf on my nodes where I use this configuration to omit eth0 config (the script is enough to bring up the interface on the host running the containers).

Next, we create a configuration file, let’s call it chuckd.config, for the chuckd virtual container that we are going to do: decide a custom hardware address (anything valid should do) and ip (as we said, in the same range)

lxc.network.type = veth
lxc.network.flags = up
lxc.network.link = br0
lxc.network.hwaddr = 00:aa:11:bb:22:cc
lxc.network.ipv4 = 192.168.1.10/24
lxc.network.name = eth0

containers on a natted private network

In this case the script to bring up the private network is this

/sbin/brctl addbr br0
/sbin/brctl setfd br0 0
/sbin/ifconfig br0 192.168.2.1 netmask 255.255.255.0 promisc up
echo 1 > /proc/sys/net/ipv4/ip_forward
echo 1 > /proc/sys/net/ipv4/conf/br0/proxy_arp

/usr/sbin/iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
# examples of redirections
iptables -t nat -A PREROUTING -p tcp --dport 51 -i eth0 -j DNAT --to 192.168.2.10:51
iptables -t nat -A PREROUTING -p tcp --dport 80 -i eth0 -j DNAT --to 192.168.2.10:80

at the end of the script you can see some example of redirection of incoming connections toward the containers in the private 192.168.2.0/24 network.

also the config file changes (with an ip on the same network)

lxc.network.type = veth
lxc.network.flags = up
lxc.network.link = br0
lxc.network.hwaddr = 00:aa:11:bb:22:cc
lxc.network.ipv4 = 192.168.2.10/24
lxc.network.name = eth0

- – -

Now we’ll setup our template so we can use it to create the container: change directory to /usr/lib/lxc/templates if you are on a 32 bit system

cd /usr/lib64/lxc/templates
wget --no-check-certificate https://raw.github.com/Ponce/lxc-slackware/master/lxc-slackware
chmod +x lxc-slackware

We are ready to create our first container, chuckd, using lxc-create and a slackware mirror: if not specified, defaults to the main one; here I’m using a local copy

MIRROR=http://192.168.1.2 lxc-create -f /path_to/chuckd.config -n chuckd -t slackware

wait some seconds (25, here) and you will have your slackware-13.37 minimal container in /var/lib/lxc/chuckd/rootfs :)

But you have to do another step before firing it up, and it’s setting the network parameters in /var/lib/lxc/$container/etc/rc.d/rc.inet1.conf to the one of your network, if you don’t have a dhcp server available

IPADDR[0]="192.168.1.10"
NETMASK[0]="255.255.255.0"
USE_DHCP[0]=""
...
GATEWAY="192.168.1.1"

(192.168.2.10, 255.255.255.0 and 192.168.2.1, in the nat example above) and a nameserver in /var/lib/lxc/$container/etc/resolv.conf (here I’m using google’s one)

nameserver 8.8.8.8

Then launch a screen session and fire up the container

screen
lxc-start -n chuckd

you’ll see something like this

INIT: version 2.86 booting
INIT: Entering runlevel: 3
Going multiuser...
Updating shared library links:  /sbin/ldconfig &
Starting sysklogd daemons:  /usr/sbin/syslogd /usr/sbin/klogd -c 3 -x
Starting OpenSSH SSH daemon:  /usr/sbin/sshd
Generating public/private rsa1 key pair.
Your identification has been saved in /etc/ssh/ssh_host_key.
Your public key has been saved in /etc/ssh/ssh_host_key.pub.
The key fingerprint is:
27:ac:44:47:c6:9d:a7:c3:e0:1a:5d:44:a5:d7:1e:69 root@chuckd
The key's randomart image is:
+--[RSA1 2048]----+
|       .o+oo.    |
|       oo +... . |
|      .o.+.o. E  |
|     ..oo +. o . |
|      .oS ..  .  |
|     ... o       |
|      .          |
|                 |
|                 |
+-----------------+
Generating public/private dsa key pair.
Your identification has been saved in /etc/ssh/ssh_host_dsa_key.
Your public key has been saved in /etc/ssh/ssh_host_dsa_key.pub.
The key fingerprint is:
dd:37:4d:fb:59:0f:06:da:e7:23:0a:55:3b:50:34:d0 root@chuckd
The key's randomart image is:
+--[ DSA 1024]----+
|          .++    |
|           .E.   |
|          . o   .|
|         . * o o.|
|        S + = *.o|
|         .   * o=|
|        .   . o.o|
|         . . . . |
|          .      |
+-----------------+
Generating public/private rsa key pair.
Your identification has been saved in /etc/ssh/ssh_host_rsa_key.
Your public key has been saved in /etc/ssh/ssh_host_rsa_key.pub.
The key fingerprint is:
ac:2e:04:67:ba:11:3a:17:fe:6b:4c:80:95:65:26:e9 root@chuckd
The key's randomart image is:
+--[ RSA 2048]----+
|  .++            |
|  ++             |
| +               |
|. E o  .         |
| o O    S        |
|o = o  .         |
| o B  .          |
|  . =.           |
|   ..o.          |
+-----------------+
Generating public/private ecdsa key pair.
Your identification has been saved in /etc/ssh/ssh_host_ecdsa_key.
Your public key has been saved in /etc/ssh/ssh_host_ecdsa_key.pub.
The key fingerprint is:
a5:e2:95:07:42:fc:ed:62:e7:c6:29:4a:45:54:17:2d root@chuckd
The key's randomart image is:
+--[ECDSA  256]---+
|     .. ... oo   |
|     ...   .E .  |
|      ..o..  .   |
|       o.=.      |
|      . S..      |
|     . +o.o      |
|      o. = .     |
|     .  . =      |
|      .. o       |
+-----------------+

* container chuckd started. *

and the console output will stop there: that means that the container fired up successfully! :)
Having it in a screen session means that you can detach the session (ctrl-A D) and reattach when needed (screen -D -r), it’s optional but useful.

After launching, you can connect to its ip via ssh or open a console with

lxc-console -n chuckd

You can install whatever package you use (also full package sets) with slackpkg, but you can alternatively use slapt-get, if you like.

Enjoy virtual containers!

Considerations: lxc aims to process isolation and at the moment it’s not full: the template sets in the default config to start the containers with lxc.cap.drop=sys_admin, but that line can be commented out from the template or per container, if needed.

P.S. if you want to specify your own set of packages for the container, just create a text file with a modified version of this list, following the same syntax

export arch=x86_64
export PACKAGES=" \
a/aaa_base-13.37-$arch-3.txz \
a/aaa_elflibs-13.37-$arch-7.txz \
a/aaa_terminfo-5.8-$arch-1.txz \
a/bash-4.1.010-$arch-1.txz \
a/bin-11.1-$arch-1.txz \
a/bzip2-1.0.6-$arch-1.txz \
a/coreutils-8.11-$arch-1.txz \
n/dhcpcd-5.2.11-$arch-1.txz \
a/dialog-1.1_20100428-$arch-2.txz \
ap/diffutils-3.0-$arch-1.txz \
a/e2fsprogs-1.41.14-$arch-1.txz \
a/elvis-2.2_0-$arch-2.txz \
a/etc-13.013-$arch-1.txz \
a/findutils-4.4.2-$arch-1.txz \
a/gawk-3.1.8-$arch-1.txz \
a/glibc-solibs-2.13-$arch-4.txz \
n/gnupg-1.4.11-$arch-1.txz \
a/grep-2.7-$arch-1.txz \
a/gzip-1.4-$arch-1.tgz \
n/iputils-s20101006-$arch-1.txz \
a/logrotate-3.7.8-$arch-1.txz \
n/net-tools-1.60-$arch-3.txz \
n/network-scripts-13.0-noarch-3.txz \
n/openssh-5.8p1-$arch-1.txz \
a/openssl-solibs-0.9.8r-$arch-3.txz \
a/pkgtools-13.37-noarch-9.tgz \
a/procps-3.2.8-$arch-3.txz \
a/sed-4.2.1-$arch-1.txz \
a/shadow-4.1.4.3-$arch-2.txz \
a/sharutils-4.11-$arch-1.txz \
ap/slackpkg-2.82.0-noarch-5.tgz \
a/sysklogd-1.5-$arch-1.txz \
a/sysvinit-2.86-$arch-6.txz \
a/sysvinit-functions-8.53-$arch-2.txz \
a/sysvinit-scripts-1.2-noarch-43.txz \
a/tar-1.26-$arch-1.tgz \
a/udev-165-$arch-2.txz \
a/util-linux-2.19-$arch-1.txz \
n/wget-1.12-$arch-1.txz \
a/which-2.20-$arch-1.txz \
a/xz-5.0.2-$arch-1.tgz"

then source it before launching lxc-create

. packages_I_need_list

you can also pass to the script a custom SUITE variable to use other versions than 13.37 (but this is untested, as PACKAGES has to be changed too for each SUITE).
P.P.S. 14.9.2011: edited the nat bridge script following Chris Willing’s hints (thanks again! :) ).

References:
- Linux Containers (LXC) on Slackware© 13.37 (Chris Willing)
- LXC HOWTO (Dwight Schauer)
- lxc-users mailing list
- lxc-devel mailing list

  • buda

    we are waiting for you at linux day :D

    • http://blog.ponce.cc/blog/ ponce

      sure, but you already know I will be lan-partying with ET all the time, don’t you? ;)

  • http://danixland.net danix

    Hi ponce, I don’t know if you like comments in italian, so I’ll write it in english.. :P
    what are the main differences between lxc and openvz?? why would you recomment one instead of another?

    • http://blog.ponce.cc/blog/ ponce

      - for OpenVZ you need kernel patches, LXC uses cgroups, already in vanilla kernels;
      - I don’t reccomend one or the other, as I use both :D but I personally prefeer what’s already available in slackware ;) ;
      - LXC has a smaller userbase as it’s younger than OpenVZ, it lacks some extensive documentation and some tools that OpenVZ already has, but the fun should be also getting into the internals and manage to do something in that direction :)

      you can write comments also in italian, but obviously posting in english will increase the chance that other people partecipate in the discussion with help and ideas ;)

  • Pingback: Chroot with X

  • FeyFre

    Good articel, and good template. At second or third retry I have managed to setup and run container.
    Now have some questions and notes:
    1. In first retry I build contain from unmodified container – all done good. As the second retry I decide to add some packages into template(particularly mc and nc), but it seem container builder does not noticed that, and built new container with old package set(cached). Is it possible add validation checks?
    2. I’m working in 32-bit environment. So template sets arch=i486 , but I have found 7 packages in slackware-13.37 with arch part equal i386 in package name(particularly n/nc-1.10-i386.txz which I tried to add). Just giving notice.
    3. I have local mirror of slackware packages so I want to target package source as local path i.e. MIRROR=/mnt/slackware lxc-create -n test -t slackware -f /path/to/config but template uses wget which does not understand such path. It would be good to have such ability.

    • http://blog.ponce.cc/blog/ ponce

      1: the old package set (the list, not the packages) shouldn’t be cached, it should use the PACKAGES variable as you export it, like in the example at the end of the post;

      2 and 3: this post has been done to let people start playing with lxc containers, but the installation procedure I come up with has to be revised.
      I thought about it and prepared another template that doesn’t use wget and installpkg but instead uses slackpkg to install the packages in a folder: slackpkg is the optimal choice for this, because it supports cleaner (and working) templates, it checks downloaded packages, it supports local mirrors, etc.
      But unfortunately, in its current version it doesn’t fully support installing packages in a $ROOT folder in the filesystem (installpkg can do it) nor the possibility to specify a custom folder for the configuration files: I wrote two small patches for let it do this that I proposed (hope they’re ok) to Piter Punk, author and maintainer of the utility.
      You can try an already patched slackpkg with this feature integrated, to use with the template above on the host where you create the containers.

      the correct syntax for using a local mirror with slackpkg is (example with slackware64-13.37)
      cdrom://path/to/slackware64-13.37/