Running nginx in LXD

After I got my LXD setup up and running, I wanted to run a web server inside it.

I chose nginx, just to expand my horizons (since I have plenty of experience using apache2).

How to determine if a container is running?

Determining if a container is running varies based on the type of container. (Most notably, which init system it is using.) For a container running systemd, such as a Xenial-based container, we can use this script (developed for MAAS):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#!/usr/bin/env bash
# Copyright 2016 Canonical Ltd.  This software is licensed under the
# GNU Affero General Public License version 3 (see the file LICENSE).

# Exit immediately if a command exits with a non-zero status.
set -o errexit
# Treat unset variables as an error when substituting.
set -o nounset

CONTAINER=$1
if [ "$CONTAINER" == "" ]; then
    echo "Required argument: LXD container name"
    exit 1
fi

t=0
while [ "$(lxc exec $CONTAINER -- systemctl is-system-running | \
        sed 's/degraded/running/')" != "running" ]; do 
    sleep 0.1
    let t=$t+1 || true
done
let q=$t%4 || true
let t=$t/10 || true
echo "(waited $t.$q seconds for container startup)"

I placed that in my $HOME/bin and wrote another script to bring up my nginx server.

Launching the Container

I wrote a relatively simple script to launch the container, install nginx (from a local mirror), add a bind mount so that I can update the web root from my $HOME on the local system, and add an iptables rule so that traffic is forwarded from the host to the container.

I tried to make it generic so that it would work on anyone's system, but it's a bit of a hack to find the proper parameters to add to iptables (and if you destroy the container, you'll have to manually remove the rule).

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
#!/bin/bash -e

CONTAINER=nginx
CONTAINER_IP=$(lxc list $CONTAINER -c 4 | grep eth0 | awk '{ print $2 }')
LOCAL_IP=$(ip route get 8.8.8.8 | grep src | awk '{ print $7 }')
LOCAL_IFACE=$(ip route get 8.8.8.8 | grep dev | awk '{ print $5 }')
LOCAL_PORT=80
CONTAINER_PORT=80

lxc launch ubuntu:x $CONTAINER
$HOME/bin/wait-for-systemd-container $CONTAINER

lxc exec $CONTAINER -- sed --in-place 's/archive.ubuntu.com/mirrors.linode.com/' \
                       /etc/apt/sources.list
lxc exec $CONTAINER -- apt-get update
lxc exec $CONTAINER -- apt-get dist-upgrade -y
lxc exec $CONTAINER -- apt-get install -y nginx
lxc exec $CONTAINER -- apt-get clean
lxc config device add $CONTAINER nginx-binding disk \
                      source=$HOME/html path=/var/www/html
sudo iptables -t nat -A PREROUTING -p tcp -i $LOCAL_IFACE -d $LOCAL_IP --dport $LOCAL_PORT -j DNAT \
    --to-destination $CONTAINER_IP:$CONTAINER_PORT

Now all I need to do is add the documents I want to serve to my $HOME/html and they'll automatically be seen in the container!