Базові можливості LXD - системи контейнерів у Linux

Базові можливості LXD - системи контейнерів у Linux

LXD - Це системний менеджер контейнерів наступного покоління, так говорить джерело. Він пропонує інтерфейс користувача, схожий на віртуальні машини, але використовує натомість контейнери Linux.

Ядро LXD — це привілейований демон (сервіс запущений з правами root), який надає REST API через локальний сокет unix, а також через мережу, якщо встановлена ​​відповідна конфігурація. Клієнти, такі як інструмент командного рядка, що поставляється з LXD, посилають запити через цей REST API. Це означає, що незалежно від того, чи звертаєтесь ви до локального хоста або до віддаленого, все працює однаково.

У цій статті ми не будемо докладно зупинятися на концепціях LXD, не розглядатимемо всі доступні можливості, викладені в документації, в тому числі нещодавню реалізацію в останніх версіях LXD підтримки віртуальних машин QEMU паралельно з контейнерами. Натомість ми дізнаємося лише базові можливості управління контейнерами — налаштуємо пули сховищ, мережу, запустимо контейнер, застосуємо ліміти на ресурси, а також розглянемо як використовувати снепшоти, щоб ви змогли отримати базове уявлення про LXD та використовувати контейнери в Linux.

Для отримання повної інформації слід звернутися до офіційного джерела:

Навігація

Інсталяція LXD ^

Інсталяція LXD у дистрибутивах Ubuntu ^

У дистрибутиві Ubuntu 19.10 пакет lxd має трансляцію на snap-пакет:

apt search lxd

lxd/eoan 1:0.7 all
  Transitional package - lxd -> snap (lxd)

Це означає, що буде встановлено відразу два пакети, один системний, а інший як snap-пакет. Встановлення двох пакетів у системі може створити деяку проблему, коли системний пакет може стати сиротою, якщо видалити snap-пакет менеджером snap-пакетів.

Знайти пакет lxd у snap-репозиторії можна наступною командою:

snap find lxd

Name             Version        Summary
lxd              3.21           System container manager and API
lxd-demo-server  0+git.6d54658  Online software demo sessions using LXD
nova             ocata          OpenStack Compute Service (nova)
nova-hypervisor  ocata          OpenStack Compute Service - KVM Hypervisor (nova)
distrobuilder    1.0            Image builder for LXC and LXD
fabrica          0.1            Build snaps by simply pointing a web form to...
satellite        0.1.2          Advanced scalable Open source intelligence platform

Запустивши команду list можна переконатися, що пакет lxd ще не встановлено:

snap list

Name  Version    Rev   Tracking  Publisher   Notes
core  16-2.43.3  8689  stable    canonical✓  core

Незважаючи на те, що LXD є snap-пакетом, потрібно встановлювати його через системний пакет. lxd, який створить у системі відповідну групу, необхідні утиліти в /usr/bin тощо.

sudo apt update
sudo apt install lxd

Переконайтеся, що пакет встановлений як snap-пакет:

snap list

Name  Version    Rev    Tracking  Publisher   Notes
core  16-2.43.3  8689   stable    canonical✓  core
lxd   3.21       13474  stable/…  canonical✓  -

Інсталяція LXD у дистрибутивах Arch Linux ^

Для встановлення пакета LXD у системі необхідно запустити наступні команди, перша — актуалізує список пакетів у системі доступних у репозиторії, друга — безпосередньо встановить пакет:

sudo pacman -Syyu && sudo pacman -S lxd

Після встановлення пакета, для керування LXD звичайним користувачем, його необхідно додати до системної групи lxd:

sudo usermod -a -G lxd user1

Переконаємося, що користувач user1 доданий до групи lxd:

id -Gn user1

user1 adm dialout cdrom floppy sudo audio dip video plugdev netdev lxd

Якщо група lxd не видно у списку, тоді потрібно активувати сесію користувача заново. Для цього потрібно вийти і зайти в систему під цим користувачем.

Активуємо у systemd завантаження сервісу LXD при старті системи:

sudo systemctl enable lxd

Запускаємо сервіс:

sudo systemctl start lxd

Перевіряємо статус сервісу:

sudo systemctl status lxd

Сховище LXD (Storage) ^

До початку ініціалізації нам необхідно розібратися як логічно влаштоване сховище в LXD.

Сховище (зберігання) складається з одного чи кількох Пул зберігання який використовує одну з підтримуваних файлових систем, таких як ZFS, BTRFS, LVM або звичайні директорії. Кожен Пул зберігання поділяється на томи (Обсяг зберігання) які містять образи, контейнери або дані для інших цілей.

  • образи — це спеціально зібрані дистрибутиви без ядра Linux та доступні із зовнішніх джерел.
  • контейнери - це розгорнуті дистрибутиви з образів, готові до експлуатації
  • Сніпшоти — це знімки стану контейнерів, до яких можна повертатися.

Базові можливості LXD - системи контейнерів у Linux

Для управління сховищем у LXD служить команда lxc storage довідку за якою можна отримати вказавши ключ. lxc storage --help

Наступна команда виводить на екран список усіх Пул зберігання в LXD сховище:

lxc storage list

+---------+-------------+--------+--------------------------------+---------+
|  NAME   | DESCRIPTION | DRIVER |             SOURCE             | USED BY |
+---------+-------------+--------+--------------------------------+---------+
| hddpool |             | btrfs  | /dev/loop1                     | 2       |
+---------+-------------+--------+--------------------------------+---------+
| ssdpool |             | btrfs  | /var/lib/lxd/disks/ssdpool.img | 4       |
+---------+-------------+--------+--------------------------------+---------+

Для перегляду списку всіх Обсяг зберігання у вибраному Пул зберігання служить команда lxc storage volume list:

lxc storage volume list hddpool

+-------+----------------------------------+-------------+---------+
| TYPE  |          NAME                    | DESCRIPTION | USED BY |
+-------+----------------------------------+-------------+---------+
| image | ebd565585223487526ddb3607f515... |             | 1       |
+-------+----------------------------------+-------------+---------+

lxc storage volume list ssdpool

+-----------+----------------------------------+-------------+---------+
|   TYPE    |            NAME                  | DESCRIPTION | USED BY |
+-----------+----------------------------------+-------------+---------+
| container | alp3                             |             | 1       |
+-----------+----------------------------------+-------------+---------+
| container | jupyter                          |             | 1       |
+-----------+----------------------------------+-------------+---------+
| image     | ebd565585223487526ddb3607f515... |             | 1       |
+-----------+----------------------------------+-------------+---------+

Також, якщо для Пул зберігання при створенні була обрана файлова система BTRFS, то отримати список Обсяг зберігання або підпункти в інтерпретації BTRFS можна за допомогою інструментарію цієї файлової системи:

sudo btrfs subvolume list -p /var/lib/lxd/storage-pools/hddpool

ID 257 gen 818 parent 5 top level 5 path images/ebd565585223487526ddb3607f5156e875c15a89e21b61ef004132196da6a0a3

sudo btrfs subvolume list -p /var/lib/lxd/storage-pools/ssdpool

ID 257 gen 1820 parent 5 top level 5 path images/ebd565585223487526ddb3607f5156e875c15a89e21b61ef004132196da6a0a3
ID 260 gen 1819 parent 5 top level 5 path containers/jupyter
ID 263 gen 1820 parent 5 top level 5 path containers/alp3

Ініціалізація LXD ^

Перед створенням та використанням контейнерів необхідно виконати загальну ініціалізацію LXD, яка створює та налаштовує мережу, а також сховище. Це можна зробити вручну за допомогою стандартних команд клієнта, які доступні у списку за викликом команди lxc --help або за допомогою майстра ініціалізації lxd init відповівши на кілька запитань.

Вибір файлової системи для Storage Pool ^

Під час ініціалізації LXD ставить кілька питань, серед яких буде визначення типу файлової системи для дефолтного Пул зберігання. За промовчанням для нього вибирається файлова система BTRFS. Поміняти на іншу ФС після створення буде неможливо. Для вибору ФС пропонується таблиця порівняння можливостей:

особливість
Каталог
Btrfs
НВМ
ZFS
CEPH

Optimized image storage
немає
так
так
так
так

Optimized instance creation
немає
так
так
так
так

Optimized snapshot creation
немає
так
так
так
так

Optimized image transfer
немає
так
немає
так
так

Optimized instance transfer
немає
так
немає
так
так

Копіювати на запис
немає
так
так
так
так

Block based
немає
немає
так
немає
так

Instant cloning
немає
так
так
так
так

Storage driver usable inside a container
так
так
немає
немає
немає

Restore from older snapshots (not latest)
так
так
так
немає
так

Storage quotas
yes(*)
так
так
так
немає

Ініціалізація мережі та Storage Pool за допомогою майстра ^

Наступна команада, яку ми розглянемо, пропонує налаштувати основні компоненти LXD відповідями на прості питання за допомогою майстра ініціалізації.

Запустіть команду lxc init і введіть відповіді на запитання після знака двокрапки, так як показано в прикладі нижче або змініть їх відповідно до ваших умов:

lxd init

Would you like to use LXD clustering? (yes/no) [default=no]: 
Do you want to configure a new storage pool? (yes/no) [default=yes]: 
Name of the new storage pool [default=default]: ssdpool         
Name of the storage backend to use (lvm, btrfs, dir) [default=btrfs]: 
Create a new BTRFS pool? (yes/no) [default=yes]: 
Would you like to use an existing block device? (yes/no) [default=no]: 
Size in GB of the new loop device (1GB minimum) [default=15GB]: 10GB
Would you like to connect to a MAAS server? (yes/no) [default=no]: 
Would you like to create a new local network bridge? (yes/no) [default=yes]: 
What should the new bridge be called? [default=lxdbr0]: 
What IPv4 address should be used? (CIDR subnet notation, “auto” or “none”) [default=auto]: 10.0.5.1/24
Would you like LXD to NAT IPv4 traffic on your bridge? [default=yes]: 
What IPv6 address should be used? (CIDR subnet notation, “auto” or “none”) [default=auto]: none
Would you like LXD to be available over the network? (yes/no) [default=no]: 
Would you like stale cached images to be updated automatically? (yes/no) [default=yes] no
Would you like a YAML "lxd init" preseed to be printed? (yes/no) [default=no]: 

Створення додаткового Storage Pool ^

У попередньому кроці ми створили Пул зберігання якому дали назву ssdpool і файл якого розташувався у моїй системі за адресою /var/lib/lxd/disks/ssdpool.img. Ця адреса файлової системи відповідає фізичному SSD диску на моєму ПК.

Наступними діями для розширення розуміння того, яку роль відіграє Пул зберігання у сховищі, ми створимо другий Пул зберігання який фізично розташовуватиметься на іншому типі диску, на HDD. Проблема полягає в тому, що LXD не дозволяє створювати Пул зберігання поза адресою /var/lib/lxd/disks/ і навіть символічні посилання не працюватимуть, дивіться відповідь розробника. Минемо це обмеження ми можемо при ініціалізації/форматуванні Пул зберігання вказавши значення як блоковий пристрій замість шляху до loopback-файлу, вказавши це в ключі source.

Отже, до створення Пул зберігання необхідно визначити loopback-файл або існуючий розділ у файловій системі який він буде використовувати. Для цього ми створимо і будемо використовувати файл, який обмежимо розміром в 10GB:

dd if=/dev/zero of=/mnt/work/lxd/hddpool.img bs=1MB count=10000

10000+0 records in
10000+0 records out
10000000000 bytes (10 GB, 9,3 GiB) copied, 38,4414 s, 260 MB/s

Підключимо loopback-файл у вільний loopback-пристрій:

sudo losetup --find --show /mnt/work/lxd/hddpool.img

/dev/loop1

Завдяки ключу --show виконання команди повертає на екран ім'я пристрою, до якого підключився наш loopback-файл. За потреби, ми можемо вивести на екран список усіх зайнятих пристроїв цього типу, щоб переконатися у коректності наших дій:

losetup -l

NAME       SIZELIMIT OFFSET AUTOCLEAR RO BACK-FILE                      DIO LOG-SEC
/dev/loop1         0      0         0  0 /mnt/work/lxd/hddpool.img        0     512
/dev/loop0         0      0         1  0 /var/lib/lxd/disks/ssdpool.img   0     512

Зі списку можна виявити, що у пристрої /dev/loop1 підключений loopback-файл /mnt/work/lxd/hddpool.img, а у пристрої /dev/loop0 підключений loopback-файл /var/lib/lxd/disks/ssdpool.img який відповідає дефолтному Пул зберігання.

Наступна команда створює новий Пул зберігання в LXD на основі щойно підготовленого loopback-файлу. LXD відформатує файл loopback /mnt/work/lxd/hddpool.img у пристрої /dev/loop1 під файлову систему BTRFS:

lxc storage create hddpool btrfs size=10GB source=/dev/loop1

Виведемо список усіх Пул зберігання на екран:

lxc storage list

+---------+-------------+--------+--------------------------------+---------+
|  NAME   | DESCRIPTION | DRIVER |             SOURCE             | USED BY |
+---------+-------------+--------+--------------------------------+---------+
| hddpool |             | btrfs  | /dev/loop1                     | 0       |
+---------+-------------+--------+--------------------------------+---------+
| ssdpool |             | btrfs  | /var/lib/lxd/disks/ssdpool.img | 0       |
+---------+-------------+--------+--------------------------------+---------+

Збільшення розміру Storage Pool ^

після створення Пул зберігання, за потреби, його можна розширити. Для Пул зберігання На основі файлової системи BTRFS виконайте такі команди:

sudo truncate -s +5G /mnt/work/lxd/hddpool.img
sudo losetup -c /dev/loop1
sudo btrfs filesystem resize max /var/lib/lxd/storage-pools/hddpool

Автовставка loopback-файлу в слот loopback-пристрою ^

У нас є одна невелика проблема, при перезавантаженні системи хоста, файл /mnt/work/lxd/hddpool.img "вилетить" із пристрою /dev/loop1 і сервіс LXD впаде при завантаженні, оскільки не побачить його в цьому пристрої. Для вирішення цієї проблеми потрібно створити системний сервіс, який буде вставляти цей файл у пристрій /dev/loop1 під час завантаження системи хоста.

Створимо блок файл типу обслуговування в /etc/systemd/system/ для системи ініціалізації SystemD:

cat << EOF | sudo tee -a /etc/systemd/system/lxd-hddpool.service
[Unit]
Description=Losetup LXD Storage Pool (hddpool)
After=local-fs.target

[Service]
Type=oneshot
ExecStart=/sbin/losetup /dev/loop1 /mnt/work/lxd/hddpool.img
RemainAfterExit=true

[Install]
WantedBy=local-fs.target
EOF

Активуємо сервіс:

sudo systemctl enable lxd-hddpool

Created symlink /etc/systemd/system/local-fs.target.wants/lxd-hddpool.service → /etc/systemd/system/lxd-hddpool.service.

Після рестарту хостової системи перевіряємо статус сервісу:

systemctl status lxd-hddpool.service 

● lxd-hddpool.service - Losetup LXD Storage Pool (hddpool)
     Loaded: loaded (/etc/systemd/system/lxd-hddpool.service; enabled; vendor preset: disabled)
     Active: active (exited) since Wed 2020-04-08 03:43:53 MSK; 1min 37s ago
    Process: 711 ExecStart=/sbin/losetup /dev/loop1 /mnt/work/lxd/hddpool.img (code=exited, status=0/SUCCESS)
   Main PID: 711 (code=exited, status=0/SUCCESS)

апр 08 03:43:52 manjaro systemd[1]: Starting Losetup LXD Storage Pool (hddpool)...
апр 08 03:43:53 manjaro systemd[1]: Finished Losetup LXD Storage Pool (hddpool).

З висновку ми можемо переконатися, що стан сервісу дорівнює активнийнезважаючи на те, що виконання нашого скрипту з однієї команди завершилося, це дозволило нам зробити опцію RemainAfterExit=true.

Безпека. Привілеї контейнерів ^

Оскільки всі процеси контейнера фактично виконуються в ізоляції на хостовій системі, використовуючи його ядро, то для додаткового захисту доступу процесів контейнера до системи хоста LXD пропонує привілейованість процесів, де:

  • Привілейовані контейнери — це контейнери в яких процеси з UID та GID відповідають тому ж власнику, що й на хостовій системі. Наприклад, процес запущений у контейнері з UID рівним 0 має все ті ж права доступу, що і процес хостової системи з UID рівним 0. Іншими словами, root користувач у контейнері має всі права не тільки в контейнері, але і на хостовій системі якщо він зможе вийти за межі ізольованого простору імен контейнера.

  • Непривілейовані контейнери — це контейнери в яких процеси належать власнику UID і GID з номером від 0 до 65535, але для хостової системи власник маскується за допомогою біта SubUID і SubGID, що додається відповідно. Наприклад, користувач з UID=0 у контейнері буде помічений у хостовій системі як SubUID + UID. Це захищає хост-систему, оскільки якщо будь-який процес у контейнері зможе вийти зі свого ізольованого простору імен, він може взаємодіяти з хост-системою тільки як процес з невідомим, дуже високим UID/GID.

За замовчуванням, новостворені контейнери мають статус непривілейованих і тому ми повинні визначити SubUID та SubGID.

Створимо два конфігураційні файли в яких задамо маску для SubUID і SubGID відповідно:

sudo touch /etc{/subuid,/subgid}
sudo usermod --add-subuids 1000000-1065535 root 
sudo usermod --add-subgids 1000000-1065535 root

Для застосування змін, сервіс LXD має бути рестартований:

sudo systemctl restart lxd

Створення віртуального комутатора мережі ^

Оскільки ми раніше ініціалізували мережу за допомогою майстра ініціалізації lxd init та створили мережевий пристрій lxdbr0, то в цьому розділі ми просто ознайомимося з мережею LXD і з тим, як створювати віртуальний комутатор (мережевий міст, bridge) за допомогою команди клієнта.

Наступна схема демонструє як комутатор (мережевий міст, bridge) поєднує хост і контейнери в мережу:

Базові можливості LXD - системи контейнерів у Linux

Контейнери можуть взаємодіяти через мережу з іншими контейнерами або хостом на якому обслуговуються ці контейнери. Для цього необхідно з'єднати віртуальні мережеві карти контейнерів з віртуальним комутатором. На початку створимо комутатор, а мережні інтерфейси контейнера будуть слинковані в наступних розділах, після того, як буде створено сам контейнер.

Наступна команда створює комутатор із підмережею 10.0.5.0/24 та IPv4 адресою 10.0.5.1/24, а також включає ipv4.nat щоб контейнери змогли отримувати інтернет через хост за допомогою служби NAT:

lxc network create lxdbr0 ipv4.address=10.0.5.1/24 ipv4.nat=true ipv6.address=none

Перевіряємо список мережних пристроїв доступних LXD:

lxc network list

+--------+----------+---------+-------------+---------+
|  NAME  |   TYPE   | MANAGED | DESCRIPTION | USED BY |
+--------+----------+---------+-------------+---------+
| eno1   | physical | NO      |             | 0       |
+--------+----------+---------+-------------+---------+
| lxdbr0 | bridge   | YES     |             | 0       |
+--------+----------+---------+-------------+---------+

Також, переконатися у створенні мережевого пристрою можна за допомогою штатного засобу Linux-дистрибутиву. ip link або ip addr:

ip addr

1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host 
       valid_lft forever preferred_lft forever
2: eno1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
    link/ether bc:ee:7b:5a:6b:44 brd ff:ff:ff:ff:ff:ff
    altname enp0s25
    inet6 fe80::9571:11f3:6e0c:c07b/64 scope link noprefixroute 
       valid_lft forever preferred_lft forever
3: lxdbr0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
    link/ether c2:38:90:df:cb:59 brd ff:ff:ff:ff:ff:ff
    inet 10.0.5.1/24 scope global lxdbr0
       valid_lft forever preferred_lft forever
    inet6 fe80::c038:90ff:fedf:cb59/64 scope link 
       valid_lft forever preferred_lft forever
5: veth3ddab174@if4: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master lxdbr0 state UP group default qlen 1000
    link/ether ca:c3:5c:1d:22:26 brd ff:ff:ff:ff:ff:ff link-netnsid 0

Профіль конфігурації ^

Кожен контейнер в LXD має свою власну конфігурацію і може розширювати її за допомогою глобально декларованих конфігурацій, які називаються профілями конфігурації. Застосування конфігураційних профілів до контейнера має каскадну модель, наступний приклад демонструє це:

Базові можливості LXD - системи контейнерів у Linux

У цьому прикладі в системі LXD створено три профілю: default, hddpool и hostfs. Всі три профілю застосовані до контейнера, у якого є локальна конфігурація (сіра зона). Профіль default має пристрій root у якого параметр pool дорівнює ssdpool, але завдяки каскадній моделі застосування конфігурації ми можемо застосувати для контейнера профіль hddpool у якого параметр pool перекриє цей параметр із профілю default та контейнер отримає конфігурацію пристрою root з параметром pool рівним hddpool, а профіль hostfs просто додає новий пристрій у контейнер.

Для того, щоб побачити список доступних профілів конфігурації, служить наступна команда:

lxc profile list

+---------+---------+
|  NAME   | USED BY |
+---------+---------+
| default | 1       |
+---------+---------+
| hddroot | 0       |
+---------+---------+
| ssdroot | 1       |
+---------+---------+

Повний список доступних команд для роботи з профілем можна отримати додавши ключ --help:

lxc profile --help

Description:
  Manage profiles

Usage:
  lxc profile [command]

Available Commands:
  add         Add profiles to instances
  assign      Assign sets of profiles to instances
  copy        Copy profiles
  create      Create profiles
  delete      Delete profiles
  device      Manage instance devices
  edit        Edit profile configurations as YAML
  get         Get values for profile configuration keys
  list        List profiles
  remove      Remove profiles from instances
  rename      Rename profiles
  set         Set profile configuration keys
  show        Show profile configurations
  unset       Unset profile configuration keys

редагування профілю ^

Профіль конфігурації за промовчанням default не має конфігурації мережевої карти для контейнера і всі новостворені контейнери не мають мережу, для них необхідно створювати локальні (виділені) пристрої мережі окремою командою, але ми можемо створити у профілі конфігурації глобальний мережевий пристрій, який буде розділятися між усіма контейнерами, які використовують цей профіль. Таким чином, відразу після команди створення нового контейнера у них буде мережа з доступом до мережі. При цьому, немає обмежень, ми завжди можемо пізніше створити локальний мережевий пристрій, якщо буде в цьому необхідність.

Наступна команда додасть у профіль конфігурації пристрій eth0 типу nic що приєднується до мережі lxdbr0:

lxc profile device add default eth0 nic network=lxdbr0 name=eth0

Оскільки ми фактично додали пристрій у профіль конфігурації, то якби ми вказали в пристрої статичну IP адресу, то всі контейнери які будуть застосовувати цей профіль розділять одну і ту ж IP адресу. Якщо необхідно створити контейнер з виділеним для контейнера статичним IP адерсом, тоді слід створити конфігурацію мережного пристрою на рівні контейнера (локальна конфігурація) з параметром IP адреси, а не на рівні профілю.

Перевіримо профіль:

lxc profile show default

config: {}
description: Default LXD profile
devices:
  eth0:
    name: eth0
    network: lxdbr0
    type: nic
  root:
    path: /
    pool: ssdpool
    type: disk
name: default
used_by: []

У цьому профілі ми можемо побачити, що для всіх новостворених контейнерів буде створено два пристрої (devices):

  • eth0 - Пристрій типу nic з'єднане з комутатором (мережевим мостом) lxdbr0
  • root - Пристрій типу disk яке використовує пул сховища ssdpool

Створення нових профілів ^

Для використання раніше створених Пул зберігання контейнерами, створимо профіль конфігурації ssdroot в якому додамо пристрій типу disk з точкою монтування / (root) використовує раніше створене Пул зберігання - ssdpool:

lxc profile create ssdroot
lxc profile device add ssdroot root disk path=/ pool=ssdpool

Аналогічно, створюємо пристрій типу disk, але в цьому випадку використовує Пул зберігання - hddpool:

lxc profile create hddroot
lxc profile device add hddroot root disk path=/ pool=hddpool

Перевіряємо профілі конфігурації:

lxc profile show ssdroot

config: {}
description: ""
devices:
  root:
    path: /
    pool: ssdpool
    type: disk
name: ssdroot
used_by: []

lxc profile show hddroot

config: {}
description: ""
devices:
  root:
    path: /
    pool: hddpool
    type: disk
name: hddroot
used_by: []

Репозиторій образів ^

Контейнери створюються з образів, які є спеціально зібраними дистрибутивами, що не мають ядра Linux. Тому, перш ніж запустити контейнер, він має бути розгорнутий із цього образу. Джерелом образів служить локальний репозиторій, у який образи завантажуються із зовнішніх репозиторіїв.

Віддалені репозиторії образів ^

За умовчанням LXD налаштований на отримання образів із трьох віддалених джерел:

  • ubuntu: (for stable Ubuntu images)
  • ubuntu-daily: (for daily Ubuntu images)
  • зображення: (For bunch of other distros)

lxc remote list

+-----------------+------------------------------------------+--------+--------+
|      NAME       |                   URL                    | PUBLIC | STATIC |
+-----------------+------------------------------------------+--------+--------+
| images          | https://images.linuxcontainers.org       | YES    | NO     |
+-----------------+------------------------------------------+--------+--------+
| local (default) | unix://                                  | NO     | YES    |
+-----------------+------------------------------------------+--------+--------+
| ubuntu          | https://cloud-images.ubuntu.com/releases | YES    | YES    |
+-----------------+------------------------------------------+--------+--------+
| ubuntu-daily    | https://cloud-images.ubuntu.com/daily    | YES    | YES    |
+-----------------+------------------------------------------+--------+--------+

Наприклад, репозиторій ubuntu: має такі образи:

lxc image -c dasut list ubuntu: | head -n 11

+----------------------------------------------+--------------+----------+------------+
|                   DESCRIPTION                | ARCHITECTURE |   SIZE   |   TYPE     |
+----------------------------------------------+--------------+----------+------------+
| ubuntu 12.04 LTS amd64 (release) (20150728)  | x86_64       | 153.72MB | CONTAINER  |
+----------------------------------------------+--------------+----------+------------+
| ubuntu 12.04 LTS amd64 (release) (20150819)  | x86_64       | 152.91MB | CONTAINER  |
+----------------------------------------------+--------------+----------+------------+
| ubuntu 12.04 LTS amd64 (release) (20150906)  | x86_64       | 154.69MB | CONTAINER  |
+----------------------------------------------+--------------+----------+------------+
| ubuntu 12.04 LTS amd64 (release) (20150930)  | x86_64       | 153.86MB | CONTAINER  |
+----------------------------------------------+--------------+----------+------------+

Щоб вивести обмежену кількість колонок ми використали опцію -c з параметрами dasut, а також обмежили довжину списку командою head.

Для виведення списку образів доступна фільтрація. Наступна команда виведе список усіх доступних архітектур дистрибутива AlpineLinux:

lxc image -c ldast list images:alpine/3.11

+------------------------------+--------------------------------------+--------------+
|            ALIAS             |             DESCRIPTION              | ARCHITECTURE |
+------------------------------+--------------------------------------+--------------+
| alpine/3.11 (3 more)         | Alpine 3.11 amd64 (20200220_13:00)   | x86_64       |
+------------------------------+--------------------------------------+--------------+
| alpine/3.11/arm64 (1 more)   | Alpine 3.11 arm64 (20200220_13:00)   | aarch64      |
+------------------------------+--------------------------------------+--------------+
| alpine/3.11/armhf (1 more)   | Alpine 3.11 armhf (20200220_13:00)   | armv7l       |
+------------------------------+--------------------------------------+--------------+
| alpine/3.11/i386 (1 more)    | Alpine 3.11 i386 (20200220_13:01)    | i686         |
+------------------------------+--------------------------------------+--------------+
| alpine/3.11/ppc64el (1 more) | Alpine 3.11 ppc64el (20200220_13:00) | ppc64le      |
+------------------------------+--------------------------------------+--------------+
| alpine/3.11/s390x (1 more)   | Alpine 3.11 s390x (20200220_13:00)   | s390x        |
+------------------------------+--------------------------------------+--------------+

Локальний репозиторій образів ^

Для початку експлуатації контейнера необхідно додати образ із глобального репозиторію до локального local:. Наразі локальний репозиторій порожній, переконається в цьому нам дасть команда lxc image list. Якщо методом list не вказати репозиторій, то за умовчанням використовуватиметься локальний репозиторій. local:

lxc image list local:

+-------+-------------+--------+-------------+--------------+------+------+
| ALIAS | FINGERPRINT | PUBLIC | DESCRIPTION | ARCHITECTURE | TYPE | SIZE |
+-------+-------------+--------+-------------+--------------+------+------+

Управління образами у репозиторії проводиться такими методами:

Команда
Опис

lxc image псевдонім
Manage image aliases

lxc image скопіювати
Copy images between servers

lxc image видаляти
Видалити зображення

lxc image редагувати
Edit image properties

lxc image експорт
Export and download images

lxc image імпорт
Import images into the image store

lxc image інформація
Show useful information o images

lxc image список
Список зображень

lxc image оновлення
Refresh images

lxc image Показувати
Show image properties

Копіюємо образ у локальний репозиторій із глобального images::

lxc image copy images:alpine/3.11/amd64 local: --alias=alpine3

Image copied successfully!

Виведемо список всіх образів доступних зараз у локальному репозиторії local::

lxc image -c lfdatsu list local:

+---------+--------------+------------------------------------+--------------+
|  ALIAS  | FINGERPRINT  |            DESCRIPTION             | ARCHITECTURE |
+---------+--------------+------------------------------------+--------------+
| alpine3 | 73a3093d4a5c | Alpine 3.11 amd64 (20200220_13:00) | x86_64       |
+---------+--------------+------------------------------------+--------------+

Конфігурація LXD ^

Крім інтерактивного режиму, LXD підтримує також неінтерактивний режим установки конфігурації, це коли конфігурація задається у вигляді YAML-файлу, спеціального формату, який дозволяє встановити всю конфігурацію за один раз, минаючи виконання безлічі інтерактивних команд, які були розглянуті вище в цій статті. конфігурацію мережі, створення конфігураційних профілів і т.д. Тут ми не будемо розглядати цю область, ви можете самостійно ознайомитись із цим у документації.

Наступна інтерактивна команда lxc config яку ми розглянемо дозволяє встановлювати конфігурацію. Наприклад, щоб завантажені образи до локального репозиторію не оновлювалися автоматично з глобальних репозиторіїв, ми можемо включити цю поведінку наступною командою:

lxc config set images.auto_update_cached=false

Створення та керування контейнером ^

Для створення контейнера служить команда lxc init якій передаються значення репозиторий:образ і потім бажаний ідентифікатор контейнера. Репозиторій може бути вказаний як локальний local: так і будь-який глобальний. Якщо репозиторій не вказано, за замовчуванням, для пошуку образу використовується локальний репозиторій. Якщо образ вказаний із глобального репозиторію, то спочатку образ буде завантажений у локальний репозиторій, а потім використаний для створення контейнера.

Виконаємо наступну команду, щоб створити наш перший контейнер:

lxc init alpine3 alp --storage=hddpool --profile=default --profile=hddroot

Розберемо по порядку ключі команди, які ми тут використовуємо:

  • alpine3 — Вказується альяс (псевдонім) для образу, який раніше був завантажений у локальний репозиторій. Якби альяс був не створений для цього образу, то завжди можна послатися на образ за його відбиток пальця який виводиться у таблиці.
  • alp — Визначається ідентифікатор для контейнера
  • --storage — Цей ключ вказує у якому Пул зберігання буде створено контейнер
  • --profile — Ці ключі каскадно застосовують до контейнера конфігурацію із раніше створених профілів конфігурації

Запускаємо контейнер, який починає запускати init-систему дистрибутива:

lxc start alp

Також, можна скористатися командою lxc launch яка дозволяє об'єднати команди lxc init и lxc start за одну операцію.

Перевіряємо стан контейнера:

lxc list -c ns46tb
+------+---------+------------------+------+-----------+--------------+
| NAME |  STATE  |       IPV4       | IPV6 |   TYPE    | STORAGE POOL |
+------+---------+------------------+------+-----------+--------------+
| alp  | RUNNING | 10.0.5.46 (eth0) |      | CONTAINER | hddpool      |
+------+---------+------------------+------+-----------+--------------+

Перевіряємо конфігурацію контейнера:

lxc config show alp

architecture: x86_64
config:
  image.architecture: amd64
  image.description: Alpine 3.11 amd64 (20200326_13:39)
  image.os: Alpine
  image.release: "3.11"
  image.serial: "20200326_13:39"
  image.type: squashfs
  volatile.base_image: ebd565585223487526ddb3607f5156e875c15a89e21b61ef004132196da6a0a3
  volatile.eth0.host_name: vethb1fe71d8
  volatile.eth0.hwaddr: 00:16:3e:5f:73:3e
  volatile.idmap.base: "0"
  volatile.idmap.current: '[{"Isuid":true,"Isgid":false,"Hostid":1000000,"Nsid":0,"Maprange":65536},{"Isuid":false,"Isgid":true,"Hostid":1000000,"Nsid":0,"Maprange":65536}]'
  volatile.idmap.next: '[{"Isuid":true,"Isgid":false,"Hostid":1000000,"Nsid":0,"Maprange":65536},{"Isuid":false,"Isgid":true,"Hostid":1000000,"Nsid":0,"Maprange":65536}]'
  volatile.last_state.idmap: '[{"Isuid":true,"Isgid":false,"Hostid":1000000,"Nsid":0,"Maprange":65536},{"Isuid":false,"Isgid":true,"Hostid":1000000,"Nsid":0,"Maprange":65536}]'
  volatile.last_state.power: RUNNING
devices:
  root:
    path: /
    pool: hddpool
    type: disk
ephemeral: false
profiles:
- default
- hddroot
stateful: false
description: ""

У секції profiles ми можемо переконатися, що цей контейнер використовує два профілі конфігурації. default и hddroot. У секції devices ми можемо виявити лише один пристрій, оскільки мережний пристрій було створено на рівні профілю default. Для того, щоб побачити всі пристрої, що використовуються контейнером, необхідно додати ключ. --expanded:

lxc config show alp --expanded

architecture: x86_64
config:
  image.architecture: amd64
  image.description: Alpine 3.11 amd64 (20200326_13:39)
  image.os: Alpine
  image.release: "3.11"
  image.serial: "20200326_13:39"
  image.type: squashfs
  volatile.base_image: ebd565585223487526ddb3607f5156e875c15a89e21b61ef004132196da6a0a3
  volatile.eth0.host_name: vethb1fe71d8
  volatile.eth0.hwaddr: 00:16:3e:5f:73:3e
  volatile.idmap.base: "0"
  volatile.idmap.current: '[{"Isuid":true,"Isgid":false,"Hostid":1000000,"Nsid":0,"Maprange":65536},{"Isuid":false,"Isgid":true,"Hostid":1000000,"Nsid":0,"Maprange":65536}]'
  volatile.idmap.next: '[{"Isuid":true,"Isgid":false,"Hostid":1000000,"Nsid":0,"Maprange":65536},{"Isuid":false,"Isgid":true,"Hostid":1000000,"Nsid":0,"Maprange":65536}]'
  volatile.last_state.idmap: '[{"Isuid":true,"Isgid":false,"Hostid":1000000,"Nsid":0,"Maprange":65536},{"Isuid":false,"Isgid":true,"Hostid":1000000,"Nsid":0,"Maprange":65536}]'
  volatile.last_state.power: RUNNING
devices:
  eth0:
    name: eth0
    network: lxdbr0
    type: nic
  root:
    path: /
    pool: hddpool
    type: disk
ephemeral: false
profiles:
- default
- hddroot
stateful: false
description: ""

Встановлення статичної IP-адреси ^

Якщо ми спробуємо встановити IP-адресу для мережного пристрою eth0 командою lxc config device set alp призначеної для конфігурації контейнера, ми отримаємо помилку яка повідомить, що пристрій не існує тому що пристрій eth0 яке використовується контейнером належить профілю default:

lxc config device set alp eth0 ipv4.address 10.0.5.5

Error: The device doesn't exist

Ми можемо звичайно встановити статичну IP адресу для eth0 пристрої в профілі, але він буде єдиний для всіх контейнерів, які цей профіль будуть використовувати. Тому, додамо виділений для контейнера пристрій:

lxc config device add alp eth0 nic name=eth0 nictype=bridged parent=lxdbr0 ipv4.address=10.0.5.5

Потім потрібно рестартувати контейнер:

lxc restart alp

Якщо ми зараз подивимося на конфігурацію контейнера, то нам не потрібно використовувати опцію --expanded щоб побачити мережевий пристрій eth0, тому що ми створили його на рівні контейнера і воно каскадно перекрило цей же пристрій із профілю default:

lxc config show alp

architecture: x86_64
config:
  image.architecture: amd64
  image.description: Alpine 3.11 amd64 (20200326_13:39)
  image.os: Alpine
  image.release: "3.11"
  image.serial: "20200326_13:39"
  image.type: squashfs
  volatile.base_image: ebd565585223487526ddb3607f5156e875c15a89e21b61ef004132196da6a0a3
  volatile.eth0.host_name: veth2a1dc59d
  volatile.eth0.hwaddr: 00:16:3e:0e:e2:71
  volatile.idmap.base: "0"
  volatile.idmap.current: '[{"Isuid":true,"Isgid":false,"Hostid":1000000,"Nsid":0,"Maprange":65536},{"Isuid":false,"Isgid":true,"Hostid":1000000,"Nsid":0,"Maprange":65536}]'
  volatile.idmap.next: '[{"Isuid":true,"Isgid":false,"Hostid":1000000,"Nsid":0,"Maprange":65536},{"Isuid":false,"Isgid":true,"Hostid":1000000,"Nsid":0,"Maprange":65536}]'
  volatile.last_state.idmap: '[{"Isuid":true,"Isgid":false,"Hostid":1000000,"Nsid":0,"Maprange":65536},{"Isuid":false,"Isgid":true,"Hostid":1000000,"Nsid":0,"Maprange":65536}]'
  volatile.last_state.power: RUNNING
devices:
  eth0:
    ipv4.address: 10.0.5.5
    name: eth0
    nictype: bridged
    parent: lxdbr0
    type: nic
  root:
    path: /
    pool: hddpool
    type: disk
ephemeral: false
profiles:
- default
- hddroot
stateful: false
description: ""

Видалення контейнера ^

Для видалення контейнера служить команда lxc delete, але перш ніж видалити контейнер, він повинен бути зупинений за допомогою команди lxc stop:

lxc stop alp

lxc list

+------+---------+-------------------+------+-----------+-----------+
| NAME |  STATE  |       IPV4        | IPV6 |   TYPE    | SNAPSHOTS |
+------+---------+-------------------+------+-----------+-----------+
| alp  | STOPPED | 10.0.5.10 (eth0)  |      | CONTAINER | 0         |
+------+---------+-------------------+------+-----------+-----------+

Після того, як ми переконалися, що стан контейнера став ЗАСТАНОВА, його можна видалити з Пул зберігання:

lxc delete alp

Доступ до контейнера ^

Для виконання команд у контейнері, безпосередньо, минаючи мережеві з'єднання, служить команда lxc exec яка виконує команди у контейнері без запуску системної оболонки. Якщо вам потрібно виконати команду в оболонці, використовуючи shell патерни, такі як змінні, файлові перенаправлення (pipe) і т.д., необхідно явно запускати оболонку і передавати команду в якості ключа, наприклад:

lxc exec alp -- /bin/sh -c "echo $HOME"

У команді був задіяний спецсимвол екранування для спецсимволу $ щоб змінна $HOME не інтерпретувалася на хостовій машині, а була інтерпретована лише усередині контейнера.

Також, можна запустити інтерактивний режим оболонки, а потім завершити сеанс виконавши hotkey CTRL+D:

lxc exec alp -- /bin/sh

Управління ресурсами контейнера ^

У LXD можна управляти ресурсами контейнера за допомогою спеціального набору конфігурації. Повний список конфігураційних параметрів контейнера можна знайти у документації.

Обмеження ресурсів RAM (ОЗП) ^

Параметр limits.memory обмежує об'єм RAM, доступний для контейнера. Як значення вказується число та один з доступних суфіксів.

Задамо контейнеру обмеження на об'єм RAM 256 MB:

lxc config set alp limits.memory 256MB

Також, існують інші параметри обмеження пам'яті:

  • limits.memory.enforce
  • limits.memory.hugepages
  • limits.memory.swap
  • limits.memory.swap.priority

Команда lxc config show дозволяє вивести на екран всю конфігурацію контейнера, у тому числі застосоване обмеження ресурсів, яке було встановлено:

lxc config show alp

architecture: x86_64
config:
  image.architecture: amd64
  image.description: Alpine 3.11 amd64 (20200220_13:00)
  image.os: Alpine
  image.release: "3.11"
  image.serial: "20200220_13:00"
  image.type: squashfs
  limits.memory: 256MB
  volatile.base_image: 73a3093d4a5ce0148fd84b95369b3fbecd19a537ddfd2e2d20caa2eef0e8fd60
  volatile.eth0.host_name: veth75b6df07
  volatile.eth0.hwaddr: 00:16:3e:a1:e7:46
  volatile.idmap.base: "0"
  volatile.idmap.current: '[]'
  volatile.idmap.next: '[]'
  volatile.last_state.idmap: '[]'
  volatile.last_state.power: RUNNING
devices: {}
ephemeral: false
profiles:
- default
stateful: false
description: ""

Обмеження ресурсів CPU (ЦП) ^

Для обмеження ресурсів ЦП існує декілька типів обмежень:

  • limit.cpu - Прив'язує контейнер до одного або кількох ядрів ЦП
  • limits.cpu.allowance - Керує або квотами планувальника CFS, коли пройшло обмеження за часом, або універсальним механізмом спільного використання ресурсів CPU, коли пройшло відсоткове значення
  • limits.cpu.priority — пріоритет планувальника, коли для кількох примірників, які спільно використовують набір процесорів, призначений однаковий відсоток процесорів

lxc config set alp limits.cpu.allowance 40%

lxc config show alp

architecture: x86_64
config:
  image.architecture: amd64
  image.description: Alpine 3.11 amd64 (20200220_13:00)
  image.os: Alpine
  image.release: "3.11"
  image.serial: "20200220_13:00"
  image.type: squashfs
  limits.cpu.allowance: 40%
  limits.memory: 256MB
  volatile.base_image: 73a3093d4a5ce0148fd84b95369b3fbecd19a537ddfd2e2d20caa2eef0e8fd60
  volatile.eth0.host_name: veth75b6df07
  volatile.eth0.hwaddr: 00:16:3e:a1:e7:46
  volatile.idmap.base: "0"
  volatile.idmap.current: '[]'
  volatile.idmap.next: '[]'
  volatile.last_state.idmap: '[]'
  volatile.last_state.power: RUNNING
devices: {}
ephemeral: false
profiles:
- default
stateful: false
description: ""

Обмеження дискового простору ^

Крім таких обмежень як limits.read, limits.write ми також можемо обмежити обсяг споживання контейнером дискового простору (працює лише з ZFS або BTRFS):

lxc config device set alp root size=2GB

Після встановлення у параметрі devices.root.size ми можемо переконатися у встановленому обмеженні:

lxc config show alp
...
devices:
  root:
    path: /
    pool: hddpool
    size: 2GB
    type: disk
ephemeral: false
profiles:
- default
- hddroot
stateful: false
description: ""

Для перегляду використаних квот на диск ми можемо отримати з команди lxc info:

lxc info alp
...
Resources:
  Processes: 5
  Disk usage:
    root: 1.05GB
  CPU usage:
    CPU usage (in seconds): 1
  Memory usage:
    Memory (current): 5.46MB
  Network usage:
    eth0:
      Bytes received: 802B
      Bytes sent: 1.59kB
      Packets received: 4
      Packets sent: 14
    lo:
      Bytes received: 0B
      Bytes sent: 0B
      Packets received: 0
      Packets sent: 0

Незважаючи на те, що ми встановили обмеження для кореневого пристрою контейнера в 2GB, системні утиліти, такі як df не бачитимуть цього обмеження. Для цього ми проведемо невеликий тест і з'ясуємо, як це працює.

Створимо 2 нових однакових контейнери в тому самому Пул зберігання (hddpool):

lxc init alpine3 alp1 --storage=hddpool --profile=default --profile=hddroot
lxc init alpine3 alp2 --storage=hddpool --profile=default --profile=hddroot

lxc list
+------+---------+------------------+------+-----------+-----------+
| NAME |  STATE  |       IPV4       | IPV6 |   TYPE    | SNAPSHOTS |
+------+---------+------------------+------+-----------+-----------+
| alp1 | RUNNING | 10.0.5.46 (eth0) |      | CONTAINER | 0         |
+------+---------+------------------+------+-----------+-----------+
| alp2 | RUNNING | 10.0.5.30 (eth0) |      | CONTAINER | 0         |
+------+---------+------------------+------+-----------+-----------+

В одному з контейнерів створимо файл розміром 1GB:

lxc exec alp1 -- dd if=/dev/urandom of=file.img bs=1M count=1000

Переконаємося, що файл створено:

lxc exec alp1 -- ls -lh
total 1000M  
-rw-r--r--    1 root     root     1000.0M Mar 27 10:16 file.img

Якщо ми подивимося в другому контейнері, перевіримо існування файлу в тому самому місці, то цього файлу не буде, що очікувано, оскільки контейнери створюються у своїх власних Обсяг зберігання в цьому ж Пул зберігання:

lxc exec alp2 -- ls -lh
total 0

Але давайте порівняємо значення які видає df на одному та іншому контейнерах:

lxc exec alp1 -- df -hT
Filesystem           Type            Size      Used Available Use% Mounted on
/dev/loop1           btrfs           9.3G   1016.4M      7.8G  11% /
...

lxc exec alp2 -- df -hT
Filesystem           Type            Size      Used Available Use% Mounted on
/dev/loop1           btrfs           9.3G   1016.4M      7.8G  11% /
...

Пристрій /dev/loop1 змонтований як кореневий розділ є Пул зберігання яке ці контейнери використовують, тому вони поділяють його об'єм на двох.

Статистика споживання ресурсів ^

Переглянути статистику споживання ресурсів для контейнера можна за допомогою команди:

lxc info alp

Name: alp
Location: none
Remote: unix://
Architecture: x86_64
Created: 2020/04/08 18:05 UTC
Status: Running
Type: container
Profiles: default, hddroot
Pid: 19219
Ips:
  eth0: inet    10.0.5.5        veth2a1dc59d
  eth0: inet6   fe80::216:3eff:fe0e:e271        veth2a1dc59d
  lo:   inet    127.0.0.1
  lo:   inet6   ::1
Resources:
  Processes: 5
  Disk usage:
    root: 495.62kB
  CPU usage:
    CPU usage (in seconds): 1
  Memory usage:
    Memory (current): 4.79MB
  Network usage:
    eth0:
      Bytes received: 730B
      Bytes sent: 1.59kB
      Packets received: 3
      Packets sent: 14
    lo:
      Bytes received: 0B
      Bytes sent: 0B
      Packets received: 0
      Packets sent: 0

Робота зі снепшотами ^

У LXD є можливість створення снапшотів і відновлення стану контейнера.

Щоб створити снепшот, виконайте таку команду:

lxc snapshot alp snapshot1

У команди lxc snapshot немає ключа list, тому, щоб переглянути список снепшотів потрібно скористатися командою, яка виводить загальну інформацію про контейнер:

lxc info alp
...
...
Snapshots:
  snapshot1 (taken at 2020/04/08 18:18 UTC) (stateless)

Відновити контейнер із снепшоту можна командою lxc restore вказавши контейнер для якого буде зроблено відновлення та псевдонім снепшота:

lxc restore alp snapshot1

Наступна команда служить видалення снепшота. Зверніть увагу, що синтаксис команди не схожий на решту, тут необхідно вказати прямий слєш після імені контейнера. Якщо слід опустити, то команда видалення снепшота інтерпретується як команда видалення контейнера!

lxc delete alp/snapshot1

У наведеному вище прикладі ми розглянули так звані stateless-снапшоти. У LXD є інший тип снапшотів — stateful, у яких зберігається поточний стан всіх процесів у контейнері. Зі stateful-снапшотами пов'язані ряд цікавих і корисних функцій.

Що ще? ^

  • Для Python розробників доступний модуль PyLXD який надає API до LXD

UPDATE 10.04.2020 15:00 Додав навігацію

Джерело: habr.com

Додати коментар або відгук