sbuild Debian packages with LXD

The canonical tool for building Debian packages is sbuild. This is what the Debian archive autobuilder network uses. Traditionally these builds are done in ephemeral schroot chroots. This does not provide a lot of isolation, just a different root file system. There are some serious security risks with that. But one can also run into trouble with sharing the host's network interfaces. Package tests sometimes need to run and use network services, they may interact with services on the host, unexpectedly.

We can provide more isolation than schroot with containers or VMs. Ubuntu's launchpad builders run in VMs for added security. Containers offer less isolation (and thus security), but can be a little more convenient. They share the host's file-system, so one can take advantage of filesystem-level copy-on-write from btrfs.

Previously I've used schroot on my laptop, but with a new machine I thought I'd experiment with using LXD containers. Debian doesn't have LXD in the archive yet. There is an ITP bug that shows recent progress, so hopefully we'll get it soon. But in the meantime, it's installable as a snap.

I use an apt-cacher-ng to keep a partial Debian archive cache on my laptop. This is entirely optional, but speeds up builds and saves on network traffic when you're on a metered connection.

Initial set-up (once-off):

  1. Install snapd:
    $ sudo apt install snapd
  2. Install the LXD snap:
    $ snap install lxd
  3. Add yourself to the lxd group:
    $ sudo adduser $USER lxd
  4. Initialize LXD:
    $ sudo lxd init
    If you get an error calculating IPv6 routes, retry this step offline. 1
  5. Log in again, so that /snap/bin is on $PATH, and you have your lxd group membership.
  6. HACK: sbuild runs autopkgtest-virt-lxd with a stripped $PATH, it then fails to find the lxc binary. My workaround is:
    $ sudo ln -s /snap/bin/lxc /usr/bin.
  7. Configure sbuild. The relevant bits of ~/.sbuild.rc are:

    # LXD build containers are ephemeral, no need to clean-up.
    $purge_build_deps = 'never';
    
    # Build with LXD (via the autopkgtest backend)
    $chroot_mode = 'autopkgtest';
    $autopkgtest_virt_server = 'lxd';
    $autopkgtest_virt_server_options = ['autopkgtest/debian/%r/%a'];
    
    # Run autopkgtest in LXD too
    $run_autopkgtest = 1;
    $autopkgtest_opts = ['--apt-upgrade', '--', 'lxd', 'autopkgtest/debian/%r/%a'];
    $autopkgtest_root_args = [''];
    

Build image setup:

For each architecture and release that you want to build for, you'll need to create a base image. I am using the images:debian/sid base images built by LXD upstream, but you can use your own, locally created images too.

  1. Create a base image to use:
    $ AUTOPKGTEST_APT_PROXY=http://10.233.1.1:3142/ autopkgtest-build-lxd images:debian/sid
  2. The resulting image is called autopkgtest/debian/sid/amd64. Debian packages are built against the unstable name, not sid, so this needs to be renamed:
    $ lxc image alias rename autopkgtest/debian/sid/amd64 autopkgtest/debian/unstable/amd64
  3. systemd-journald-audit.socket can't activate because audit namespaces aren't a thing yet (systemd#6519).
    This is not critical, it can just be masked:
    # systemctl mask systemd-journald-audit.socket
  4. systemd-login.service can't start because ProtectProc and ProtectControlGroups don't work in LXC (systemd#17866).
    These can be disabled:
    # mkdir /etc/systemd/system/systemd-logind.service.d
    # cat <<EOF > /etc/systemd/system/systemd-logind.service.d/lxc.conf
    [Service]
    ProtectProc=default
    ProtectControlGroups=off
    EOF
    

Build packages:

  1. Build packages from source, as you would with a schroot-backed sbuild:
    $ sbuild -Ad unstable hello

Caveats:

  1. SBUILD_SHELL

  2. No convenient way to use eatmydata for sbuild. Autopkgtests get it automatically.

Footnotes:

1

NetworkManager creates a default IPv6 route to fc00::/7, encompassing the entire ULA address space. LXD looks for unrouted ULA address space for its internal IPv6 network.