How to create a LXD container for snap development

Deprecation notice

Snapcraft now supports the --use-lxd command-line option, which can create a clean, compatible LXD container for snap building instead of using a Multipass VM.

While the instructions in this topic should still work to a degree we recommend using the built-in feature whenever possible to benefit from the upstream’s enhancements.

License

Creative Commons — Attribution-ShareAlike 4.0 International — CC BY-SA 4.0+, with an exception of explicitly licensed to be compatible with the snapcraft.io documentation’s licensing.

Benefits of snap development in a LXD container

Multipass builds are essentially virtual machines, which suffer from the limitations of a VM:

  • Limited memory capacity, which is not shared with the host
  • Limited computation resources as only a subset of CPU cores are used in the VM(but configurable)
  • Lower efficiency as certain level of emulation is required
  • Requires some extra time to boot from power-off state

Building snaps withing LXD containers will not affected by these limitations.

Demonstration-specific details

  • The name of the container to-be-created is my-snapcraft-playground
  • Your regular account UID is 12345

Basic operations

Install LXD

Follow Linux Containers - LXD - Getting started - command line for the instructions. Note that the latest LXD distribution is provided via snaps.

(OPTIONAL) APT optimizations

By default Ubuntu containers use the non-localized archive.ubuntu.com software source, which can be replaced by a faster mirror in your region.

NOTE: LXD profile modifications only apply to containers created in the future.

Before creating the container, run lxc profile edit default to edit the default profile, customize then merge the following config to the config key:

config:
  user.user-data: |
    #cloud-config
    apt:
      disable_suites: $RELEASE-proposed
      primary:
        - arches: [default]
          uri: http://my.favorite.ubuntu.mirror/ubuntu/
      conf: |
        APT {
            Get {
                Assume-Yes "true";
            }
            Periodic {
                Enable "0";
            }
        }

NOTE: The #cloud-config line is NOT a comment, but an identifier of Cloud-Init’s cloud config data, which CANNOT be removed.

This cloud-init config sets the Ubuntu software archive to the my.favorite.ubuntu.mirror site, and setup a APT configuration at /etc/apt/apt.conf.d/94cloud-init-config to by default --assume-yes in apt-get(8) and conforming utilities, this promises faster download speeds and smaller download sizes during package installation. It also disables APT’s periodic package management system maintenance as it will occasionally disturb with our package management operations.

References

Create a Ubuntu 16.04/18.04 LXD Container

If your snap is targeting the core snap, run lxc launch ubuntu:16.04 my-snapcraft-playground in the terminal to create a Ubuntu 16.04 container.

If your snap is targeting the core18 snap, run lxc launch ubuntu:18.04 my-snapcraft-playground in the terminal to create a Ubuntu 18.04 container.

NOTE:

  • By default LXD creates unprivileged containers.
  • If you’re targeting the core snap create a Ubuntu 16.04 container instead.

Install Snapcraft

Run lxc exec my-snapcraft-playground -- snap install snapcraft --classic to install snapcraft.

$ lxc exec my-snapcraft-playground -- snap install snapcraft --classic
2019-03-26T09:50:32Z INFO Waiting for restart...
snapcraft 3.2 from Canonical✓ installed

Set SNAPCRAFT_BUILD_ENVIRONMENT environment variable to host

Currently Snapcraft builds the snap in a Multipass VM by default and will refuse to operate in the host environment, to tell Snapcraft to build snaps directly in the container we need to set the SNAPCRAFT_BUILD_ENVIRONMENT environment variable to the value host.

Run the lxc exec my-snapcraft-playground -- editor /etc/profile and add the following line at the end of the file:

export SNAPCRAFT_BUILD_ENVIRONMENT=host

Maintain a clean Snapshot

Run the lxc stop my-snapcraft-playground command to stop the container, and run lxc snapshot my-snapcraft-playground clean to create a clean snapshot of the container.

NOTE: It is possible to create a snapshot while the container is running, the writer just prefers not to do so.

Whenever you want to restore the container to the clean state, run lxc restore my-snapcraft-playground clean.

You can re-create the snapshot by running the lxc delete my-snapcraft-playground/clean command to delete the old snapshot, make changes to the container(like upgrading the packages and snaps), and recreate a new one using the aforementioned lxc snapshot command.

Conclusion

You may now access the shell of the default ubuntu user by running lxc exec my-snapcraft-playground -- sudo --login --user ubuntu

username@hostname$ lxc exec my-snapcraft-playground -- sudo --login --user ubuntu
ubuntu@my-snapcraft-playground:~$

Now you can push the snapcraft recipe into the container via various VCS utilities or the lxc file push command and run snapcraft to build the snap inside the container. You can pull the snap package back to the host by using the lxc file pull command.

The process can be more simplified and straight-forward by using the advance features of the LXD containers, refer the following section for info.

Advanced usage

Mount the source tree in the host system directly into the container

It is possible to reuse the source tree in the host to avoid pulling them manually in container. To achieve this a disk device configuration is required.

Run lxc config device add my-snapcraft-playground 'Source Tree of Foo Software' disk source=/path/to/foo/software/source/tree path=/home/ubuntu/foo to setup the mountpoint.

NOTE:

  • The destination directory(e.g. foo in /home/ubuntu/foo) need not to be created in advance.
  • Without user/group ID mapping, contents in mounted folders can not be modified within the container.

Reference

Setup user/group ID mapping for writable host source tree

The user and group IDs in unprivileged containers are shifted from the host ID range for security reasons, the result is the disk device mounted host source tree is not writable in the container:

ubuntu@my-snapcraft-playground:~$ ls --format=long
total 4
drwxr-xr-x 27 nobody nogroup 4096 Mar 14 19:54 foo

An ID mapping configuration is required to make the ownership of files in host appears to be the same of the user in container so that regular write access is possible. This configuration is the lxc.idmap key in LXC, and is exposed by LXD via the raw.idmap key, which has the following format:

both 1000 1000
uid 50 500
gid 10000 10000

Run lxc config edit my-snapcraft-playground and merge the following either config to map both host UID/GID of 12345 to container’s 1000 (The UID/GID of the default ubuntu user)

config:
  raw.idmap: both 12345 1000

or

config:
  raw.idmap: |-
    uid 12345 1000
    gid 12345 1000

You must also edit the /etc/subuid and /etc/subgid host config to allow LXD using your UID & GID, the following one is the config I’m using:

lxd:100000:100000
root:100000:100000
lxd:12345:1
root:12345:1

WARNING: This particular config works for me™ but I AM TOTALLY NO IDEA WHY IT WORKS, it should be improved to be making sense

A container restart is required to apply the configuration, run lxc restart my-snapcraft-playground to do so. If everything goes properly the mounted disk device directory /home/ubuntu/foo should now be owned by ubuntu in the container and regular write access is available.

ubuntu@my-snapcraft-playground:~$ ls --format=long
total 4
drwxrwxr-x 14 ubuntu ubuntu 4096 Mar 26 02:58 foo

References

Conclusion

Now you can map your source tree directly into the container and run snapcraft within it. You can create containers specific for certain snap’s building by running the lxc copy my-snapcraft-playground snapcraft-_snap_name_ command so that you can ensure it can be built cleanly from scratch.


Happy snapcrafting!

2 Likes

There is now an easier way of doing this:

1 Like

Hi @admins, I can no longer edit this topic now, can you help me make this topic wiki, or dump its Markdown source so I can revive it elsewhere?

Thanks in advance!

Hello! Not sure why this became un-editable, but I’ve made it a wiki now. Let me know if you still have problems.

1 Like

I got the access now, thanks!

Is there a version of this recipe that uses the newer feature of idmapped mounts to R/W mount a host directory within the LXD container?

The integrated --use-lxd feature in Snapcraft should meet the requirement, I suggest using it instead of the instructions mentioned here.

I’ve updated the topic to reflect the status.