DEBOPS.LXD(5)

NAME
debops.lxd - Configure and manage LXD service using Ansible

SYNOPSIS

debops run service/lxd [--limit group,host,...] [--diff] [--check] [--tags tag1,tag2,...] [--skip-tags tag1,tag2,...] [<ansible-playbook options>] ...

DESCRIPTION

Linux Containers <https://en.wikipedia.org/wiki/LXC> or LXC provide a way to partition existing Linux hosts into separate environments using Linux cgroups, namespace isolation, POSIX capabilities and chrooted filesystems.

LXD <https://linuxcontainers.org/lxd/introduction/>, or a Linux Container Daemon, is a service written in Go which provides a REST API and CLI interface for Linux Container management.

The debops.lxd Ansible role can be used to install and configure the LXD service on a Debian or Ubuntu hosts. The role will use the debops.golang role to install the lxd and lxc binaries from upstream on Debian hosts.

GETTING STARTED

  LXD installation details

At the time of writing this role (December 2019) LXD was not available natively in Debian. Packaging efforts are ongoing <https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=768073>, however there's no telling if LXD will be included in the next Debian release (Bullseye). The upstream developers suggest installation on Debian via Snap <https://stgraber.org/2017/01/18/lxd-on-debian/>, however this brings its own set of issues which are offtopic here.

Instead, on Debian hosts the debops.lxd role utilizes the debops.golang role to install the lxd and lxc binaries from upstream git repository by compiling them from source. The role will configure the rest of the needed infrastructure (systemd unit files, logrotate and sysctl configuration, POSIX groups, log directory, etc.) so that the LXD service should work out of the box on Debian without the need of a Snap installation.

The debops.golang configuration for building and installing LXD is defined in the lxd__golang__dependent_packages variable.

Due to the build dependency on the lxc-dev APT package, which pulls the lxc APT package automatically, the debops.lxc role and its dependencies will be used to configure the LXC environment. The lxcbr0 network bridge will be automatically disabled in this case.

WARNING: Merge commits in the lxc/lxd <https://github.com/lxc/lxd> GitHub repository might be signed with the GPG key issued by GitHub <https://help.github.com/articles/about-gpg/>, used for signing commits done in the web interface <https://security.stackexchange.com/a/173494>. It has to be done, because tagged LXD releases have problems with their dependency chains <https://github.com/lxc/lxd/issues/8293> and due to that the debops.lxd role relies on stable branches in the LXD repository. The trust is limited to the _golang UNIX account and might have an impact for any Go applications built in that specific environment.

  Example inventory

To enable LXD support on a host, it needs to be added to the [debops_service_lxd] Ansible inventory group:

[debops_all_hosts:children]
lxd_hosts
lxd_containers
[debops_service_lxd:children]
lxd_hosts
[lxd_hosts]
lxd-host    ansible_host=lxd-host.example.org
[lxd_containers]
webserver   ansible_host=webserver.example.org

By default, containers will use the lxdbr0 bridge managed by the LXD service, with their own internal lxd subdomain. You can use the debops.ifupdown Ansible role to configure additional network bridges on the LXD host, if you want to attach the containers to the public network.

  Example playbook

If you are using this role without DebOps, here's an example Ansible playbook that uses the debops.lxd role:

---
- name: Manage LXD service
  collections: [ 'debops.debops', 'debops.roles01',
                 'debops.roles02', 'debops.roles03' ]
  hosts: [ 'debops_service_lxd' ]
  become: True
  environment: '{{ inventory__environment | d({})
                   | combine(inventory__group_environment | d({}))
                   | combine(inventory__host_environment  | d({})) }}'
  roles:
    - role: root_account
      tags: [ 'role::root_account', 'skip::root_account' ]
    - role: keyring
      tags: [ 'role::keyring', 'skip::keyring', 'role::golang' ]
      keyring__dependent_gpg_user: '{{ golang__keyring__dependent_gpg_user }}'
      keyring__dependent_gpg_keys:
        - '{{ golang__keyring__dependent_gpg_keys }}'
      golang__dependent_packages:  # noqa var-naming[no-role-prefix]
        - '{{ lxd__golang__dependent_packages }}'
    - role: apt_preferences
      tags: [ 'role::apt_preferences', 'skip::apt_preferences' ]
      apt_preferences__dependent_list:
        - '{{ golang__apt_preferences__dependent_list }}'
        - '{{ lxc__apt_preferences__dependent_list }}'
    - role: golang
      tags: [ 'role::golang', 'skip::golang' ]
      golang__dependent_packages:
        - '{{ lxd__golang__dependent_packages }}'
    - role: cron
      tags: [ 'role::cron', 'skip::cron' ]
    - role: logrotate
      tags: [ 'role::logrotate', 'skip::logrotate' ]
      logrotate__dependent_config:
        - '{{ lxd__logrotate__dependent_config }}'
    - role: ferm
      tags: [ 'role::ferm', 'skip::ferm' ]
      ferm__dependent_rules:
        - '{{ lxc__ferm__dependent_rules }}'
    - role: python
      tags: [ 'role::python', 'skip::python', 'role::lxc' ]
      python__dependent_packages3:
        - '{{ lxc__python__dependent_packages3 }}'
      python__dependent_packages2:
        - '{{ lxc__python__dependent_packages2 }}'
    - role: sysctl
      tags: [ 'role::sysctl', 'skip::sysctl' ]
      sysctl__dependent_parameters:
        - '{{ lxc__sysctl__dependent_parameters }}'
        - '{{ lxd__sysctl__dependent_parameters }}'
    - role: lxc
      tags: [ 'role::lxc', 'skip::lxc' ]
    - role: lxd
      tags: [ 'role::lxd', 'skip::lxd' ]
# If a host has 'debops.dnsmasq' or 'debops.unbound' roles configured, execute
# its playbook in case that configuration applied by the 'lxd' role needs to be
# applied to 'dnsmasq' or 'unbound' services. This should ensure that the
# '*.lxd' subdomain for internal LXD containers is resolvable on the LXD host.
#
# If the host is not in the Ansible inventory groups required by the
# 'dnsmasq.yml' or the 'unbound.yml' playbooks, this should not impact
# anything.
- name: Configure dnsmasq service
  import_playbook: 'dnsmasq.yml'
- name: Configure unbound service
  import_playbook: 'unbound.yml'

  Ansible tags

You can use Ansible --tags or --skip-tags parameters to limit what tasks are performed during Ansible run. This can be used after a host was first configured to speed up playbook execution, when you are sure that most of the configuration is already in the desired state.

Available role tags:

role::lxd
    Main role tag, should be used in the playbook to execute all of the role tasks as well as role dependencies.

role::lxd:init
    Re-apply the LXD preseeding configuration. Requires the lxd__init_preseed variable to be set to True to be effective.

  Other resources

List of other useful resources related to the debops.lxd Ansible role:

•   Manual pages: lxc(7) <https://manpages.debian.org/lxc(7)>

•   LXD <https://wiki.debian.org/LXD> page in Debian Wiki, with packaging information and current progress

•   LXD <https://wiki.archlinux.org/index.php/LXD> page in Arch Linux Wiki

•   LXD <https://help.ubuntu.com/lts/serverguide/lxd.html> page in Ubuntu Wiki

•   LXD 2.0 blog post series <https://stgraber.org/2016/03/11/lxd-2-0-blog-post-series-012/> written by Stéphane Graber

•   LXD documentation page <https://lxd.readthedocs.io/en/latest/>

DEFAULT VARIABLE DETAILS

Some of the debops.lxd default variables have more extensive configuration than simple strings or lists, here you can find documentation and examples for them.

  lxd__preseed

The lxd__*_preseed variables define a set of LXD "preseed" configuration entries which are merged into one YAML document and passed to the lxd init --preseed command via stdin on first installation. These configuration entries can be used to configure various aspects of the LXD environment, like network interfaces, storage pools, clustered operation, profiles, and so on.

You can read the Non-interactive configuration via preseed YAML <https://lxd.readthedocs.io/en/latest/preseed/> LXD documentation page for more details about the preseeding process.

  Examples

See the lxd__default_preseed for the default configuration entries used to initialize the LXD service.

To see the current LXD configuration on a host, you can run the command:

lxd init --dump

This will print out the configuration in a YAML format which can then be split into separate configuration entries and put under the seed parameters.

To re-apply the preseed configuration via Ansible you can execute the command:

debops run service/lxd -l <host> -t role::lxd:init -e 'lxd__init_preseed=true'

This will re-run the command and apply the current preseed configuration again.

  Syntax

The preseed is defined using lists of YAML dictionaries, each dictionary defines a configuration entry using specific paraneters:

name
    Required. A string that identifies a configuration entry, not used otherwise. Multiple configuration entries with the same name parameter are merged together, overriding the seed parameter each time - this can be used to replace specific configuration entry in the Ansible inventory.

seed
    Required. YAML dictionary with the contents of the preseed configuration. After the final list of configuration entries is generated, contents of the seed parameters are combined recursively using the Ansible combine() filter. This can be used to override specific YAML keys in the preseed via different configuration entries.

state
    Optional. If not specified or present, a given configuration entry will be included in the final preseed document. If absent, a given configuration entry will not be included in the YAML document. If ignore, a given configuration entry will not be evaluated by the role during execution.

AUTHOR
Maciej Delmanowski

COPYRIGHT
2014-2024, Maciej Delmanowski, Nick Janetakis, Robin Schneider and others