Table of Contents

Настраиваем хост контейнеров LXD с хранилищем zfs и web-интерфейсом.

Настройка LXD

https://habr.com/post/308400/
Если мы настраиваем физический хост или виртуальную машину на собственном сервере, то нам нужно удалить пакет cloud-init. Он предназначен для автоматического конфигурирования системы в различных облачных окружениях.

sudo apt-get -y purge cloud-init && sudo rm -rf /etc/cloud

Теперь ставим lxd:

sudo apt-get install lxd zfsutils-linux bridge-utils

Настраиваем ZFS-pool

Использование ZFS позволяет использовать снепшоты для бекапа, а также дедупликацию и компрессию для экономии места. Если на хосте LXС/LXD есть ZFS volume, то контейнеры можно создавать как в нем, так и по старому в директории. Просто при создании контейнера нужно указывать имя zfs pool, в котором будет создан dataset

sudo zpool create -f lxd /dev/xvdb
sudo zpool status
sudo zfs list
sudo zfs set dedup=on lxd
sudo zfs set compression=on lxd
sudo zdb -S lxd

Настройка network bridge для LXD

Мне нужно, чтобы контейнеры имели адреса в двух физических LAN. Таким образом, в netplan мне нужно настроить два bridge, в котором указать в качестве slave-интерфейсов мои физические интерфейсы.
Устанавливаем bridge-utils:

sudo apt-get install bridge-utils

Удаляем все ненужные yaml-файлики из /etc/netplan/ и оставляем единственный с таким вот содержимым:

network:
  version: 2
  renderer: networkd
  ethernets:
    eth0:
      dhcp4: no
      dhcp6: no
    eth1:
      dhcp4: no
      dhcp6: no
  bridges:
    br0:
      dhcp4: no
      dhcp6: no
      interfaces:
        - eth0
      addresses: [ 192.168.77.13/24 ]
      gateway4: 192.168.77.1
      nameservers:
          addresses:
              - 192.168.77.100
              - 192.168.77.1
          search: [autosys.tk]
      parameters:
          stp: false
          forward-delay: 0
    br1:
      dhcp4: no
      dhcp6: no
      interfaces:
        - eth1
      addresses: [ 192.168.100.113/24 ]
      parameters:
          stp: false
          forward-delay: 0

Первичная настройка LXD

Перед созданием или импортом контейнеров необходимо произвести первоначальное конфигурирование LXD. Выполняем команду:

sudo lxd init

Вот один из вариантов конфигурирования:

Would you like to use LXD clustering? (yes/no) [default=no]: n
Do you want to configure a new storage pool? (yes/no) [default=yes]: yes
Name of the new storage pool [default=default]: lxd
Name of the storage backend to use (btrfs, dir, lvm, zfs) [default=zfs]: zfs
Create a new ZFS pool? (yes/no) [default=yes]: n
Name of the existing ZFS pool or dataset: lxd
Would you like to connect to a MAAS server? (yes/no) [default=no]: n
Would you like to create a new local network bridge? (yes/no) [default=yes]: n
Would you like to configure LXD to use an existing bridge or host interface? (yes/no) [default=no]: yes
Name of the existing bridge or host interface: br0
Would you like LXD to be available over the network? (yes/no) [default=no]: n
Would you like stale cached images to be updated automatically? (yes/no) [default=yes]
Would you like a YAML "lxd init" preseed to be printed? (yes/no) [default=no]: y
config: {}
cluster: null
networks: []
storage_pools:
- config:
    source: lxd
  description: ""
  name: lxd
  driver: zfs
profiles:
- config: {}
  description: ""
  devices:
    eth0:
      name: eth0
      nictype: bridged
      parent: br0
      type: nic
    root:
      path: /
      pool: lxd
      type: disk
  name: default

Повторное конфигурирование можно запустить так:

dpkg-reconfigure -p medium lxd

Внесение изменений в дефолтный профиль

Для того, чтобы отредактировать дефолтный профиль, который будет применяться к новым контейнерам. Сохраним его в файл:

sudo lxc profile show default > default_lxd_profile.yaml

Отредактируем его, например - заменив lxdbr0 на имя нашего bridge - br0. И сохраним его в конфигурации LXD:

sudo lxc profile edit default < default_lxd_profile.yaml

Удалить дефолтный lxdbr0 можно командой:

sudo lxc network delete lxdbr0

Монтируем директорию хоста в контейнер LXD

sudo lxc config device add ContainerName DeviceName disk source=/tmp/share_on_host path=/tmp/share_on_guest

Тут ContainerName - имя контейнера, DeviceName - имя устройства (оно может быть произвольным и служит для идентификации этого ресурса в конфигурации).

Добавление storage pool

Для добавления хранилища выполняем:

sud mkdir -p /path/to/storage/pool
sudo lxc storage create PoolName dir source=/path/to/storage/pool

Данная команда создаст пул в директории. Для того, чтобы его впоследствии использовать нужно создать соответствующий профиль.

Создаем профиль для нового storage pool

Создаем пустой профиль:

sudo lxc profile create dir_storage

Открываем редактор:

sudo lxc profile edit dir_storage

И приводим его к такому виду:

config: {}
description: "Profile with Directory storage"
devices:
  eth0:
    name: eth0
    nictype: bridged
    parent: br0
    type: nic
  root:
    path: /
    pool: lxd_dir
    type: disk
name: dir_storage
used_by: []

Добавляем устройство ppp в профиль или контейнер LXD

Добавление профиля и устройства в него:

sudo lxc profile create device_ppp
sudo lxc profile device add device_ppp dev_ppp unix-char path=/dev/ppp

И назначаем профиль контейнеру:

sudo lxc profile assign pptp device_ppp,default

Тут важно, что нужно перечислить все профили, назначаемые контейнеру.

Добавление устройства непосредственно в контейнер

В этом случае профиль можно не создавать.

sudo lxc config device add <ctname> dev_ppp unix-char path=/dev/ppp

Смена пароля root в контейнере LXD

Допустим, у нас есть контейнер test1 и нам надо сменить в нем пароль root. Для этого выполняем:

sudo lxc exec test1 -- sh -c passwd

Установка web-интерфейса для LXD

Web-интерфейсов для LXD существует несколько. Покопавшись на github я выбрал LXDUI от AdaptiveScale. Его преимущества - довольно легкий (написан на python), обновляется, по крайней мере пока.
Устанавливаем запчасти:

sudo apt-get install -y python3 zfsutils-linux bridge-utils unzip python3-pip
sudo rm /usr/bin/python && sudo ln -s /usr/bin/python3.6 /usr/bin/python
sudo pip3 install --upgrade pip

Скачиваем:

wget https://github.com/AdaptiveScale/lxdui/archive/master.zip

Распаковываем:

unzip ./master.zip

Устанавливаем:

cd lxdui-master
sudo pip3 install .

Запускаем:

sudo lxdui start

Создаем файл сервиса systemd

sudo nano /lib/systemd/system/lxdui.service

Пишем туда вот что:

[Unit]
Description=LXC/LXD Web User Interface
After=network.target

[Service]
Type=simple
ExecStart=/usr/local/bin/lxdui start
ExecStop=/usr/local/bin/lxdui stop
ExecReload=/usr/local/bin/lxdui reload

[Install]
WantedBy=multi-user.target

Включаем сервис:

sudo systemctl enable lxdui

И запускаем его:

sudo service lxdui start

Аутентификация локальных и доменных пользователей

LXDUI от AdaptiveScale сам по себе прост и неплох, однако в нем нет аутентификации локальных или доменных пользователей.
Я решил это исправить и добавить аутентификацию пользователей, авторизовавшихся через pam-модули хоста.
Для этого нужно немного отредактировать файлик ./app/lib/auth.py
В моем случае он установился в /usr/local/lib/python3.6/dist-packages/app/lib/auth.py

sudo pip3 install python-pam

В самом конце это файлика есть функция authenticate, которую я привел вот к такому виду:

    def authenticate(self, username, password):
        #try to authenticate using pam
        pamobj = pam.pam()
        if pamobj.authenticate(username, password) is True:
           groups_gids = subprocess.check_output("id -G " + username, shell=True, universal_newlines=True).split()
           #for group in  [subprocess.check_output("getent group " + gid, shell=True, universal_newlines=True).split(':',maxsplit = 1)[0] for gid in groups_gids]:
           for group in  [grp.getgrgid(gid).gr_name for gid in groups_gids]:
                 if group.lower() == 'lxdui':
                     return True, 'Authenticated'

        #Authebticate using LXDUI database
        account, err = self.get(username)

        if account is None:
            return 'Error', err

        if account['password'] == self.sha_password(password):
            return True, 'Authenticated'
        else:
            return False, 'Incorrect password.'

Кроме того, в начало файла нужно добавить:

import pam, grp, subprocess

Мой хост присоединен к домену Active Directory и теперь пользователи, входящие в доменную или локальную группу lxdui будут нормально логиниться.

Экспорт/Импорт контейнеров со старого хоста LXC

На новом хосте создаем контейнер с таким же именем как и старый. Останавливаем его. Монтируем.

sudo lxc stop CONTAINERNAME
sudo zfs mount lxd/containers/CONTAINERNAME

Удаляем файлы:

sudo rm -Rf /var/lib/lxd/storage-pools/lxd/containers/CONTAINERNAME/rootfs/*

И теперь выполняем rsync. тут сложность в том, что rsync должен быть выполнен с правами sudo на обоих хостах:

sudo rsync -hXAavzP --stats --numeric-ids --rsync-path="echo PASSWORD | sudo -Sv && sudo rsync"  Username@Old_LXC_Host:/var/lib/lxc/CONTAINERNAME/rootfs/ /var/lib/lxd/storage-pools/lxd/containers/CONTAINERNAME/rootfs/

Контейнеры импортированные из LXC работали в privileged mode, поэтому нужно задать такой же режим импортированному контейнеру:

sudo lxc config set CONTAINERNAME security.privileged true

Также нужно отключить apparmor:

sudo lxc profile set default raw.lxc lxc.apparmor.profile=unconfined

В данном случае - отключаем apparmor (устанавливаем профиль unconfined) для профиля default.
Или можно отключить apparmor для заданного контейнера:

sudo lxc config set CONTAINERNAME raw.lxc lxc.apparmor.profile=unconfined

Миграция LXC-LXD с помощью lxd-tools

При попытке мигрировать с хоста Ubuntu 16.04.5 у меня толком ничего не вышло. При миграции переезжала файловая система, однако файл с настройками контейнера не создавался.

На хосте LXC ставим lxd-tools. Добавляем диск и делаем zfs-пул.
Затем настраиваем lxd с помощью

lxd init

Мигрируем контейнер с помощью

lxc-to-lxd

Затем делаем

export

переносим полученный image на новый хост и разворачиваем там.

Установка lxc-to-lxd на Ubuntu 16.04

При попытке установить lxd-tools из репозитория Ubuntu 16.04 оказалось, что ставится только версия lxd-tools (2.0.11-0ubuntu1~16.04.4), в которой отсутствует lxc-to-lxd.
Поэтому, ставим из backports:

sudo apt install -t xenial-backports lxd-tools  

В результате установится версия 3.0.1-0ubuntu1~16.04.4, в которой lxc-to-lxd есть!.

Конвертация

sudo lxc-to-lxd container_to_move

Миграция

Если контейнер удачно конвертировался в LXD. Экспортируем его в файл:

  lxc snapshot container_name backup
  lxc publish container_name/backup --alias container_name-backup
  lxc image export container_name-backup .
  lxc image delete container_name-backup

Импортируем на новом хосте:

  lxc image import TARBALL-NAME --alias container_name-backup
  lxc launch container_name-backup container_name
  lxc image delete container_name-backup