An opinionated LXC wrapper.
- LX-What?
- Motivation
- What it does
- Example session
- Why not docker?!
- Installation
- CLI Usage
- Requirements
- Limitations
- Development
- API
- License
LXC, aka Linux Containers. In case you don't know what they are, it's basically virtual machines without emulating the hardware. In technical details, it's chroot on steroids, because the filesystem, processes and networking are separated from the host. More information here.
This technology is getting a lot of traction since ~2 years, because it allows people to create isolated environments very quickly, very cheaply. Fedora 21, for example, will have each application run in a different container. Deploying containers on AWS lets you build multi-tier architectures for cheap, etc etc. There are many applications.
I personally use them as "VMs" for my projects (each project gets a VM). And lxc-wrapper is there to help me with that. I think this is a common usage though, so I thought it was worth sharing.
I use LXC in a very opinionated way, and has some manual maintenance to do every time I do something with LXCs. So I created this tool to automate what I do with them.
- I always forget the
--name
option. lxc-wrapper assumes that the argument is the name of the container. - Writing
--fancy
when I want an ls should be the default. No argument needed. - When creating a container, it assigns a static IP to it, adds an entry to the hosts file so that the container is reachable, and adds a symbolic link to the rootfs in a defined folder.
- Destroying a container cleans up behind itself.
Starting a new project "bar" based on the "foo" stack.
$ sudo lxc-wrapper --base foo create bar
Created container bar as copy of foo
$ sudo lxc-wrapper ls
NAME STATE IPV4 IPV6 GROUPS AUTOSTART
----------------------------------------------------
bar STOPPED - - - NO
foo STOPPED - - - NO
$ ls ~/lxc/
foo bar
$ ls ~/lxc/bar
bin boot dev etc home lib lib64 media mnt opt proc root run sbin srv sys tmp usr var
$ sudo lxc-wrapper start bar
$ sudo lxc-wrapper ls
NAME STATE IPV4 IPV6 GROUPS AUTOSTART
----------------------------------------------------
bar STARTED 10.0.3.4 - - NO
foo STOPPED - - - NO
$ ssh ubuntu@bar.lxc
When done with the project:
$ sudo lxc-wrapper stop bar
$ sudo lxc-wrapper destroy bar
$ sudo lxc-wrapper ls
NAME STATE IPV4 IPV6 GROUPS AUTOSTART
----------------------------------------------------
foo STOPPED - - - NO
Good question.
Several reasons:
- Docker is based on an overlayfs system all the time. My usage is simply having long-term projects, so docker's containers don't make sense.
- Docker's networking doesn't allow me to assign a static IP to a container. It makes it inconvenient, especially for long-term containers, to connect to them via ssh.
- Docker's containers' filesystems are hidden when they're not started. I don't want to start a container just to get some random file in it.
- Docker CLI are not a very beautiful API. lxc-wrapper is supposed to be simple to use.
You can:
- Download the sources and run
make && make install
. Onlysbcl
is needed. - Download and install the rpm
file
(pgp signature)
if you're on Fedora/CentOS/Red Hat (x86
64only) - Download and install the deb file (pgp signature) if you're on Ubuntu/Debian (amd64 only)
- Download and install the PKGBUILD if you're on ArchLinux
$ lxc-wrapper help
Usage: lxc-wrapper [OPTIONS] [COMMAND]
Wrapper around lxc for an opinionated workflow.
Commands:
help
Shows this help
create NAME
Creates a container named NAME
Options (must be BEFORE the command):
--base=BASE
Clones the BASE container
--template=TEMPLATE
Uses the TEMPLATE lxc template
Overridable variables and default values (must be BEFORE the command):
--lxc-default-folder=/var/lib/lxc/
--lxc-rootfs=rootfs/
--lxc-folder=~/lxc/
--lxc-host-extension=.lxc
--default-dns-nameserver=8.8.8.8
--hosts-file=/etc/hosts
--lxc-interfaces-file=etc/network/interfaces
start NAME
Starts the container named NAME
stop NAME
Stops the container named NAME
ls
Lists the containers
destroy NAME
Destroys the container named NAME
Overridable variables and default values (must be BEFORE the command):
--lxc-folder=~/lxc/
--lxc-host-extension=.lxc
--hosts-file=/etc/hosts
package NAME
Packages the container named NAME
Options (must be BEFORE the command):
--archive-path=PATH
the path of the archive
Overridable variables and default values (must be BEFORE the command):
--lxc-package-extension=.tar.gz
--lxc-default-folder=/var/lib/lxc/
deploy --archive ARCHIVE NAME
Deploys the ARCHIVE archive in a container named NAME
Overridable variables and default values (must be BEFORE the command):
--lxc-default-folder=/var/lib/lxc/
--lxc-config=config
--hosts-file=/etc/hosts
autostart NAME
Toggles the autostart status of the container named NAME
Overridable variables and default values (must be BEFORE the command):
--lxc-default-folder=/var/lib/lxc/
--lxc-config=config
Overridable variables and default values for all commands (must be BEFORE the c
ommand):
--default-shell=/bin/bash
Linux only.
If you just want to use the distributed package, that's all you need.
If you want to compile yourself, you need:
- sbcl
- lxc
And run:
$ git clone https://github.com/Ralt/lxc-wrapper
$ cd lxc-wrapper
$ make
$ make install
Eventually using sudo
for the make install
.
Tested on SBCL only. There is a requirement on sb-posix
to get the
version number.
The swank server or the CLI utility needs to be ran as root. (Ideally
with sudo, so that ~
matches your user folder)
Known limitations:
- Only /24 subnetworks supported. Which means you can only make 254 containers with lxc-wrapper on one host.
- Autostart management not supported yet.
You need:
- SBCL
- QuickLisp
To create a CLI utility, you need:
- buildapp
The Makefile supports the following tasks:
all
: builds the./dist/usr/bin/lxc-wrapper
binary, along with downloading dependencies in a local quicklisp environmentclean
: deletes the dependencies and the binaryinstall
: copies the./dist/usr/binlxc-wrapper
binary toDESTDIR
which is/usr/bin
by defaulttest
: runs tests; requires a functional lisp environment
(defcommand create (name args)
"Creates an LXC"
(destructuring-bind (&key base template)
args
Creates an LXC.
If a base LXC is provided, then it makes a clone of it.
If a template is provided, then it creates a new LXC based on this template.
The opinionated part of lxc-wrapper comes here. For every new LXC:
- It gives it a static IP
- It adds the static IP to the host's /etc/hosts
- It makes a symlink to the rootfs
(defcommand destroy (name args)
"Destroys an LXC and its leftovers"
(declare (ignore args))
Destroys an LXC.
The opinionated part of lxc-wrapper comes here too. When an LXC is destroyed:
- It destroys the entry in the host's /etc/hosts
- It deletes the symlink to the rootfs
(defcommand start (name args)
"Starts an LXC"
(declare (ignore args))
Starts an LXC. The argument can be a string or a symbol.
(defcommand stop (name args)
"Stops an LXC"
(declare (ignore args))
Stops an LXC. The argument can be a string or a symbol.
(defcommand ls (name args)
"Lists all the LXC"
(declare (ignore args))
Returns the fancy output of the list of LXCs.
(defcommand package (name args)
"Packages an LXC"
Packages an LXC into an shareable archive file.
(defcommand deploy (name args)
"Deploys an archive created by lxc-wrapper"
(destructuring-bind (&key archive)
args
Deploys an archive created by lxc-wrapper package
.
(defcommand autostart (name args)
"Toggles the autostart setting of a container"
Toggles the autostart setting of a container.
Variables are used throughout the code to be able to customize them through dynamic scoping.
Used by: create
Default value: /var/lib/lxc/
The folder where LXC stores its containers.
Used by: create
Default value: rootfs
The folder where the filesystem of the container lives.
Used by: create
, destroy
Default value: ~/lxc
The folder where symbolic links to the containers' filesystems are made.
Used by: create
, destroy
Default value: .lxc
The TLD of the container hostname.
Used by: create
Default value: 10.0.3.1
The gateway that the container uses.
Used by: create
Default value: 8.8.8.8
The DNS nameserver that the container uses.
Used by: create
, destroy
Default value: /etc/hosts
The host's hosts file.
Used by: create
, destroy
Default value: '(10 0 3 0)
The network of the container. Only /24 supported.
Used by: create
Default value: ^(\\d+)\\.(\\d+)\\.(\\d+)\\.(\\d+)
The regex used to find IPs in the hosts file.
Used by: create
Default value: etc/network/interfaces
The file where interfaces are written in the container.
Used by: create
, destroy
, start
, stop
, ls
Default value: /bin/bash
The shell used by the commands.
Used by: package
Default value: .tar.gz
The extension to give to archives created by package
.
Used by: deploy
Default value: #p"config"
The name of the configuration file of the containers.
MIT License.