Translate

неділя, 22 листопада 2015 р.

LXC Сontainers. Part I: Overview

LXC (англ. LinuX Containers) — система віртуалізації на рівні операційної системи для запуску декількох ізольованих примірників ОС Linux на одному комп'ютері. LXC не використовує віртуальні машини, а створює віртуальне оточення (контейнер) з власним простором процесів і мережевим стеком. Тобто у разі контейнеризації хост-ситема не емулює хардову частину (відсутній гіпервізор), а просто обмежує процеси та ресурси гостей внутрішніми підсистемами.

У разі LXC, ці підсистеми - це cgroups (додана у версії 2.6.29), namespaces (для ізоляції процесів, мережевого стека і т.д.); для обмеження доступу задіяні такі можливості ядра, як профілі Apparmor і SELinux, політики Seccomp, Chroots (pivot_root) і capabilities. Усі контейнери LXC використовують один примірник ядра ОС.

Ця система схожа на системи OpenVZ (LXC прийняв багато патчів та ідей з цього проекту) і Linux-VServer для ОС Linux, jail для FreeBSD, Solaris Containers для Solaris. Проте на відміну від OpenVZ, LXC не потребує окремого ядра для своєї роботи: необхідні для роботи LXC підсистеми одразу знаходяться в основній вітці ядра.


До складу інструментарію LXC входить бібліотека liblxc, набір утиліт (lxc-create, lxc-start, lxc-stop, lxc-ls і тому подібне), шаблони для побудови контейнерів і набір біндінгів для різних мов програмування.
У якості хосту (системи на якій будемо створювати контейнери) я обрав Ubuntu 15.04 та версію LXC 1.1.5 з стабільного ppa-репозиторію. По можливості, тести краще проводити на справжньому залізі, а не емульованому, адже на віртуальній машині можуть проявитись, наприклад, проблеми з мережевим мостом.

Отже, додамо репозиторій з останнім стабільним релізом:

# add-apt-repository ppa:ubuntu-lxc/stable

Та встановимо LXC:

# apt-get update
# apt-get install lxc

Для створення нового контейнеру можна скористуватись інтерактивним режимом:

# lxc-create --template download --name u1

Або з короткими ключами:

# lxc-create -t download -n u1

Setting up the GPG keyring
Downloading the image index

---
DIST    RELEASE ARCH    VARIANT BUILD
---
centos  6       amd64   default 20151107_02:16
centos  6       i386    default 20151107_02:16
centos  7       amd64   default 20151107_02:16
debian  jessie  amd64   default 20151106_22:42
debian  jessie  armel   default 20151106_22:42
debian  jessie  armhf   default 20151106_22:42
...
ubuntu  wily    ppc64el default 20151107_03:49
---

Distribution: ubuntu
Release: wily
Architecture: amd64

Downloading the image index
Downloading the rootfs
Downloading the metadata
The image cache is now ready
Unpacking the rootfs

---
You just created an Ubuntu container (release=wily, arch=amd64, variant=default)

To enable sshd, run: apt-get install openssh-server

For security reason, container images ship without user accounts
and without a root password.

Use lxc-attach or chroot directly into the rootfs to set a root password
or create user accounts.

Або ж передавши всі параметри одразу:

# lxc-create -t download -n u1 -- --dist ubuntu --release trusty --arch amd64

Чи короткий варіант:

# lxc-create -t download -n u1 -- -d ubuntu -r trusty -a amd64

-t - скорочене ім’я lxc-темплейт скрипту, що буде використовуватись для створення контейнеру. Переглянути всі наявні темплейти, тобто всі дистрибутиви, що можуть використовуватись у якості контейнерів, можна за адресою /usr/share/lxc/templates.

lxc-download (-t download) - теплейт-скрипт, що завантажує попередньо зібраний контейнер, що також може бути використаний для unprivileged контейнерів. Універсальний для всіх дистрибутивів, використання його вже приведено вище. Попередньо зібрані образи контейнерів генеруються щоденно на https://jenkins.linuxcontainers.org/

Інші темплейт-скрипти, lxc-distro_name, в процесі створення контейнера одразу підвантажать всі необхідні для роботи пакети + оновлення. Це зазвичай займає більше часу за lxc-download (як мінімум, при створенні першого контейнеру). Наприклад, створити контейнер з ОС Centos 6.7 можна наступним чином:

# lxc-create -t centos -n c2 -- -a i386 -R 6.7

-a - архітектура
-R - реліз

Для rpm-дистрибутивів попередньо необхідно встановити на lxc-host yum та rpm (тепер я знаю навіщо вони існують в репозиторіях deb-подібних дистрибутивів), це в залежить від того, якою операційною системою виступає хост. Для створення контейнеру з CentOS також можна скористатись темплейтом lxc-download.

Більш того, LXC може запускати не лише контейнери з архітектурами, що може підтримувати хост-система. Після інсталяції пакету емуляції "qemu-user-static", можна встановлювати контейнери з іншими архітектурами, наприклад armv7:

# aptitude install qemu-user-static
# lxc-create -t ubuntu -n uarm -- -a armhf --release trusty
# lxc-attach -n uarm -- file /bin/ls

/bin/ls: ELF 32-bit LSB  executable, ARM, EABI5 version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.32, BuildID[sha1]=74480e1a4f4c0aebebd4db4f5405e720cf4b8de5, stripped

Мабуть наразі буде доречно поговорити про те, в яких директоріях LXC зберігає дані контейнерів та конфігураційні файли:

* /var/lib/lxc - місце знаходження всіх контейнерів.
* /var/lib/lxcsnap - тут розміщуються снепшоти (про них ми поговоримо трохи далі)
* /var/cache/lxc - місцезнаходження кешу темплейтів для створення контейнерів.

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

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

# lxc-ls --fancy
NAME  STATE    IPV4  IPV6  GROUPS  AUTOSTART
--------------------------------------------
u1    STOPPED  -     -     -       NO
c2    STOPPED  -     -     -       NO

Запустимо контейнер:

# lxc-start --name u1

# lxc-ls --fancy
NAME  STATE    IPV4          IPV6  GROUPS  AUTOSTART
----------------------------------------------
u1    RUNNING  10.0.3.139     -     -       NO
c2    STOPPED  -              -     -       NO

Побачити базову інформацію про контейнер можна таким чином:

# lxc-info --name u1
Name:           u1
State:          RUNNING
PID:            4019
IP:             10.0.3.139
CPU use:        0.69 seconds
BlkIO use:      12.83 MiB
Memory use:     25.54 MiB
KMem use:       0 bytes
Link:           vethR5TEL6
 TX bytes:      1.30 KiB
 RX bytes:      1.48 KiB
 Total bytes:   2.79 KiB

Перехід в контейнер:

# lxc-attach -n u1
root@u1:~#

lxc-attach може бути також використаний для виконання команд в контейнері, не переходячи в нього:

# lxc-attach -n u1 -- service sshd restart
# lxc-attach -n u1 -- ping i.ua

До версії ядра 3.8 перейти в контейнер можна було лише таким чином:

# lxc-console -n u1

Різниця між lxc-attach та lxc-console в тому, що перший породжує одразу bash-сесію, пропускаючи процес логіну до tty-консолі.

Зупинка контейнера відбувається так:

# lxc-stop --name u1     # shutdown
# lxc-stop -r --name u1  # reboot
# lxc-stop -k --name u1  # kill

lxc-stop без указання ключа на конкретну дію буде просто завершувати роботу (shutdown), надіславши сигнал SIGPWR до контейнера.

Видалення контейнеру проходить наступним чином:

# lxc-destroy --name u1

Для того, щоб контейнер стартував автоматично у разі перезавантаження хост-системи, варто додати в /var/lib/lxc/u1/config наступне:

# vim /var/lib/lxc/u1/config
...
lxc.start.auto = 1
lxc.start.delay = 5
...

lxc.start.delay - пауза, перед стартом наступного контейнера.

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

# vim /var/lib/lxc/u1/config
...
lxc.group = web-servers
...

А потім, наприклад, перезапустити групу таким чином:

# lxc-autostart -r -g web-servers

Між іншим, стартувати всі контейнери, в яких установлена опція lxc.start.auto можна так:

# lxc-autostart -a

"Заморозити" контейнер можна виконавши наступну команду:

# lxc-freeze -n u1
# lxc-ls --fancy
NAME  STATE    IPV4          IPV6                                 GROUPS       AUTOSTART
----------------------------------------------------------------------------------------
c2    STOPPED  -             -                                    -            NO
u1    FROZEN   10.0.3.139    fdee:cbcd:a595:0:216:3eff:feca:fd06  web-servers  BY-GROUP

Після чого контейнер u1 і надалі буде займати аналогічну к-ть пам’яті, проте процеси в ньому не будуть використовувати потужності процесора.

"Розморозити" контейнер можна наступною командою:

# lxc-unfreeze -n u1

Скористуймося попереднім робочим контейнером для створення нового:

# lxc-clone -o u1 -n u2

Проте існуючий, u1, необхідно спочатку зупинити. Також треба змінити IP-адресу нового контейнера в /var/lib/lxc/u2/config у разі, якщо вона задається статично. Зауважу, що мак-адресу lxc-clone змінює самостійно для контейнера-клону.

У останніх версіях LXC lxc-clone був оголошений устарівшим (але поки працює) і замість нього рекомендують використовувати lxc-copy (u2 - новий контейнер):

# lxc-copy -n u1 -N u2

LXC підтримує снепшоти, у разі якщо файловою системою було обрано btrfs, zfs, overlayfs чи файлова система встановлена поверх lvm. Інакше, команда lxc-snapshot просто створить контейнер-копію. Тому спочатку контейнер можна перетворити в overlayfs, а вже потім робити снепшоти:

# lxc-clone -o u1 -n u1-test -B overlayfs -s

# echo "before installing htop" > snap-comment
# lxc-snapshot -n u1-test -c snap-comment
# lxc-snapshot -n u1-test -L -C
snap0 (/var/lib/lxc/u1-test/snaps) 2015:11:15 01:30:02
before installing htop

І потім відповідно відновити контейнер на попередню точку:

# lxc-snapshot -n u1-test -r snap0

Або відновити снепшот в новий контейнер:

# lxc-snapshot -n u1-test -r snap0 u1-test-snap0

Проте, на жаль, всі ці дії можливі лише на вимкнених контейнерах. На відміну від OpenVZ, де контейнери можна снепшотити на ходу за допомогою утиліти vzrestore із мінімальним простоєм. Але це вже інша розмова.

Цікавою можливістю є так званий ephemeral контейнер. Це тип контейнера, який існує лише короткий термін, допоки не відбудеться вихід із його консолі. Створюється він на базі існуючого і відповідно буде корисний для короткотермінової роботи, тестів:

# lxc-start-ephemeral -o u1
setting rootfs to .%s. /var/lib/lxc/u1-t1hb_zxg/rootfs

Connected to tty 1
Type <Ctrl+a q> to exit the console, <Ctrl+a Ctrl+a> to enter Ctrl+a itself

Ubuntu 15.04 u1-t1hb_zxg pts/0

u1-t1hb_zxg login: ipeacocks
Password:
Last login: Wed Nov 11 21:36:10 UTC 2015 from 192.168.1.34 on pts/4
ipeacocks@u1-t1hb_zxg:~$

Також LXC надає можливість перебросу девайсів, доступних на хост-машині, прямо в контейнер:

# lxc-device add -n u1 /dev/ttyUSB0 /dev/ttyS0

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

# ls -l /var/lib/lxc/c2/rootfs/
total 80
dr-xr-xr-x  2 root root  4096 лис 16 00:31 bin
dr-xr-xr-x  2 root root  4096 вер 23  2011 boot
...
drwxr-xr-x 12 root root  4096 лис 16 00:31 usr
drwxr-xr-x 17 root root  4096 лис 16 00:31 var

До розділів, змонтованих як tmpfs, можна звернутись наступним чином:

# ls -lh /proc/$(sudo lxc-info -n u1 -p -H)/root/run/
total 16K
-rw-r--r--  1 root root    4 лис 18 00:47 crond.pid
----------  1 root root    0 лис 18 00:47 crond.reboot
...
-rw-r--r--  1 root root    5 лис 18 01:19 sshd.pid
drwxr-xr-x 16 root root  420 лис 18 01:17 systemd
drwxr-xr-x  2 root root   40 лис 18 00:47 user
-rw-rw-r--  1 root utmp 2,7K лис 18 00:47 utmp

LXC підтримує можливість створення nested контейнерів (контейнерів в контейнерах) проте, мабуть, писати про це буде зайвим. Не хочу роздувати ще більше цю саттю.

LXC "з коробки" вміє запускати скрипти, що можуть бути прив’язані до певних етапів чи дій з контейнером: наприклад, на старті контейнера чи перед монтуванням дисків, перед клонування контейнера, підняттям чи вимкненням мережевого інтерфейсу та ін.

* lxc.hook.pre-start - скрипт буде запускатись перед створенням контейнеру
* lxc.hook.pre-mount - скрипт виконається перед монтуванням
* lxc.hook.mount - скрипт виконається після монтування
* lxc.hook.autodev - теж саме, але якщо монтуванням займається autodev
* lxc.hook.start - скрипт запускається перед стартом контейнера (перед /sbin/init)
* lxc.hook.post-stop - скрипт виконається, після вимкнення контейнера
* lxc.hook.clone - скрипт виконається, під час клонування контейнера в новий
* lxc.network.script.up - запуск скрипту відбудеться після підняттям мережевого інтерфейсу
* lxc.network.script.down - аналогічно, але після вимкнення мережевого інтерфейсу

Для демонстрації цієї можливості, напишемо простий скрипт:

# vim /var/lib/lxc/u1/pre-start.sh

#!/bin/sh
echo "arguments: $*" > /tmp/test
echo "environment:" >> /tmp/test
env | grep LXC >> /tmp/test

Зробимо його виконуваним:

# chmod +x /var/lib/lxc/u1/pre-start.sh

Та відповідно активуємо його для контейнера u1:

# vim /var/lib/lxc/u1/config
...
lxc.hook.pre-start = /var/lib/lxc/u1/pre-start.sh
...

Після старту контейнера u1, на хост машині з’явиться файл test з таким змістом:

# cat /tmp/test
arguments: u1 lxc pre-start
environment:
LXC_ROOTFS_MOUNT=/usr/lib/x86_64-linux-gnu/lxc
LXC_CONFIG_FILE=/var/lib/lxc/u1/config
LXC_ROOTFS_PATH=/var/lib/lxc/u1/rootfs
LXC_NAME=u1

LXC-хост звісно може лімітувати контейнери по залізу, завдяки cgroup-підсистемі. Наприклад, так можна встановити к-ть активних ядер для контейнеру:

# lxc-cgroup -n u1 cpuset.cpus 0,1
# lxc-cgroup -n u1 cpuset.cpus
0-1

Так - к-ть оперативної пам’яті:

# lxc-cgroup -n u1 memory.limit_in_bytes 128M

Лімітування часу використання CPU для контейнера:

# lxc-cgroup -n u1 cpu.shares 100

Це значення відносне. Тобто якщо першому контейнеру лімітувати використання cpu.shares до 100, а другому - до 200, то другий контейнер отримуватиме вдвічі більше часу роботи центрального процесора. Аналогічно можно було б виставити значення 1000 для першого і 2000 для другого.

Обмеження Block I/O (I/O для блочних пристроїв):

# lxc-cgroup -n u1 blkio.weight 500

Як я розумію, це працює по аналогічній схемі з cpu.shares.

Щоб зробити ці обмеження також активними після рестарту контейнера, необхідно додати параметри до конфігураційного файлу:

# vim /var/lib/lxc/u1/config
...
lxc.cgroup.memory.limit_in_bytes = 128M
lxc.cgroup.cpuset.cpus = 0,1
lxc.cgroup.cpu.shares = 100
lxc.cgroup.blkio.weight = 500
...

Наскільки я розумію, квоти в LXC достатньо обмежені в порівнянні зі знову ж таки OpenVZ. Але проект дуже жваво розвивається, тому сумнівів щодо розширення даного функціоналу в майбутньому в мене немає.

З LXC є чудова можливість створювати непривілейовані контейнери, тобто ті що працюватимуть в просторі користувача. Для цього необхідно виконати декілька кроків. Спочатку відредагуємо/створимо default.conf у домашній директорії звичайного користувача (тобто, будь-якого не root):

$ vim ~/.config/lxc/default.conf
lxc.network.type = veth
lxc.network.link = lxcbr0
lxc.network.flags = up
lxc.network.hwaddr = 00:16:3e:xx:xx:xx
lxc.id_map = u 0 100000 65536
lxc.id_map = g 0 100000 65536

lxc.id_map вказує в яких межах будуть лежати id процесів в користувацькому контейнері.
lxc.network.link - брідж, що буде використовуватись по замовчуванню для контейнерів.

Далі необхідно призначити непривілейованому користувачу діапазон UIDs та GIDs з якими він зможе працювати:

# usermod --add-subuids 100000-165536 $USER
# usermod --add-subgids 100000-165536 $USER
# chmod +x $HOME

Остання команда необхідна адже, LXC потребує доступ до ~/.local/share/lxc/. І нарешті відредагуємо lxc-usernet:

# vim /etc/lxc/lxc-usernet
<your username> veth lxcbr0 10

<your username> - їм’я користувача, від якого будуть запускатись непривілейовані контейнери.

Наразі все - можна запускати контейнер від звичайного користувача:

$ lxc-create -t download -n p1 -- -d ubuntu -r trusty -a amd64

Шляхи для непривілейованих контейнерів такі:

* $HOME/.local/share/lxc - розміщення всіх контейнерів
* $HOME/.local/share/lxcsnap - снепшоти контейнерів
* $HOME/.cache/lxc - кеші темплейтів

На цьому поки все. У наступних статтях про LXC я планую також розповісти про налаштування мережевого стеку та, можливо, про LXC Web Panel (проте, здається, поточна версія LXC не підтримується).

Якщо справді цікавлять теми контейнерної віртуалізації, дуже раджу до ознайомлення мої попередні статті про OpenVZ та Docker.

Посилання:
https://www.stgraber.org/2013/12/20/lxc-1-0-blog-post-series/
https://www.flockport.com/guides/
https://www.digitalocean.com/community/tutorials/getting-started-with-lxc-on-an-ubuntu-13-04-vps
http://ru-openvz.livejournal.com/1970.html
https://uk.wikipedia.org/wiki/LXC
http://www.janoszen.com/2013/01/22/lxc-vs-openvz/
http://en.community.dell.com/techcenter/os-applications/w/wiki/7440.lxc-containers-in-ubuntu-server-14-04-lts-part-2
https://linuxcontainers.org/ru/lxc/introduction/
http://linoxide.com/tools/manage-linux-containers-lxc/
https://docs.oracle.com/cd/E37670_01/E37355/html/ol_control_containers.html
http://www.janoszen.com/2013/05/14/lxc-tutorial/
http://stackoverflow.com/questions/17989306/what-does-docker-add-to-lxc-tools-the-userspace-lxc-tools
https://www.safematix.com/system/linux/ubuntu-lxc-memory-swap-limit/

Немає коментарів:

Дописати коментар