LXC

LXC

Контейнеры представляют из себя облегченную технологию виртуализации. Они больше похожи на chroot чем на полноценную виртуализацию типа Qemu или VMware, поскольку они не эмулируют оборудование, а также используют в разделяемом режиме ту же операционную систему, что и основная система. Поэтому контейнеры лучше сравнивать с зонами (zones) Solaris или изоляторами (jails) BSD. Linux-vserver и OpenVZ - это две предыдущие разработки контейнеро-подобной функциональности для Linux. На самом деле контейнеры получились как результат работы по слиянию функциональности vserver and OpenVZ. Некоторая функциональность vserver и OpenVZ все еще отсутствует в контейнерах, однако контейнеры могут загружаться множеством дистрибутивов Linux и имеют преимущество, что они могут запускаться на неизмененном ядре.

Существует две реализации пользовательского пространства контейнеров, каждая из которых использует те же самые возможности ядра. Libvirt позволяет использовать контейнеры через драйвер LXC, подсоединяясь к 'lxc:///'. Это очень удобно, поскольку поддерживается то же использование, что и для других драйверов. Другая реализация, называемая просто 'LXC', несовместима с libvirt, но более гибкая с использованием дополнительных утилит пользовательского пространства. Есть возможность переключаться с одной на другую, хотя существуют особенности, которые могут привести в замешательство.

В этом документе мы будем рассматривать в основном пакет lxc. Ближе к концу будет описано как использовать драйвер libvirt LXC.

В этом документе будут использоваться имена контейнеров CN, C1 и C2.

Установка

Пакет lxc может быть установлен с использованием команды:

sudo apt-get install lxc

Этот способ подтянет все требуемые и рекомендуемые зависимости, включая cgroup-lite, lvm2 и debootstrap. Для использования libvirt-lxc установите libvirt-bin. LXC и libvirt-lxc могут быть установлены и использоваться одновременно.

Настройка основной системы

Основное расположение файлов LXC

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

~~ Существует два задания отслеживания:
  ~~ **/etc/init/lxc-net.conf**: необязательное задание, которое запускается только если в **/etc/default/lxc** определено **USE_LXC_BRIDGE** (по умолчанию true). Оно устанавливает мост на основе NAT для использования контейнером.
  ~~ **/etc/init/lxc.conf**: запускается если **LXC_AUTO** (по умолчанию true) установлена в **/etc/default/lxc**. Оно отслеживает записи в **/etc/lxc/auto/**, которые являются символическими ссылками на файлы конфигураций для контейнеров, которые должны быть запущены при загрузке системы.
~~ **/etc/lxc/lxc.conf**: Это конфигурационный файл по умолчанию для создания контейнеров, который предписывает контейнерам использовать LXC мост, созданный отслеживающим заданием **lxc-net**. Если при создании контейнера не указывается конфигурационный файл, будет использоваться именно этот.
~~ Примеры других конфигурационных файлов создания контейнеров можно найти в каталоге **/usr/share/doc/lxc/examples**. Они показывают как создавать контейнеры без частной сети или с использованием **macvlan**, **vlan** или иных сетевых расположений.
~~ Различные инструменты администрирования контейнеров можно найти в каталоге **/usr/bin**.
~~ **/usr/lib/lxc/lxc-init**: минимальная очень легковесная инициализирующая программа, которая используется **lxc-execute**. Вместо того, чтобы загружать весь контейнер, она вручную монтирует несколько файловых систем, главным образом /proc, и отрабатывает их аргументы. Желательно, чтобы вам не пришлось обращаться к этому файлу.
~~ **/usr/lib/lxc/templates/** содержит шаблоны, которые могут быть использованы при создании новых контейнеров для различных дистрибутивов и их разновидностей. На данный момент не все шаблоны поддерживаются.
~~ **/etc/apparmor.d/lxc/lxc-default** содержит Apparmor профиль доступа по умолчанию, который обеспечивает защиту основной системы от контейнеров. Дополнительную информацию смотрите в разделе [[wiki:руководство_по_ubuntu_server:безопасность:apparmor|AppArmor]].
~~ **/etc/apparmor.d/usr.bin.lxc-start** содержит профиль для защиты основной системы от **lxc-start** пока он устанавливает контейнер.
~~ **/etc/apparmor.d/lxc-containers** заставляет все профили, определенные в **/etc/apparmor.d/lxc**, загружаться при старте системы.
~~ Существует множество страниц руководства **man** по средствам администрирования, таким как файл настроек контейнеров **lxc.conf**.
~~ **/var/lib/lxc** - место где хранятся контейнеры и информация по их настройкам.
~~ **/var/cache/lxc** - место кеширования данных дистрибутивов для ускорения создания множества контейнеров.

lxcbr0

Когда USE_LXC_BRIDGE установлена в true в файле /etc/default/lxc (как устанавливается по умолчанию), мост с именем lxcbr0 создается в процессе старта. Этот мост получает частный адрес 10.0.3.1, а контейнеры его использующие получат адреса из диапазона 10.0.3.0/24. Экземпляр dnsmasq начинает прослушивать этот мост, поэтому если другой dnsmasq получает связь со всеми интерфейсами до запуска отслеживающего сервиса lxc-net, lxc-net рушится на старте и lxcbr0 не создается.

Если у вас есть другой мост, например, virbr0 по умолчанию от libvirt или br0 для вашего основного сетевого интерфейса, вы можете использовать его вместо lxcbr0 для ваших контейнеров.

Использование отдельной файловой системы для хранения контейнеров

LXC сохраняет информацию контейнеров и корневую файловую систему (с резервным хранилищем по умолчанию) в /var/lib/lxc. Также шаблоны создания контейнеров предпочитают хранить закешированную информацию по дистрибутивам в /var/cache/lxc.

Дефолтный путь, называемый lxcpath, может быть переопределён в командной строке ключом -P или навсегда через lxcpath = /новый/путь в /etc/lxc/lxc.conf Если вы переопределяете lxcpath, то каталог снимков snap добавляется к lxcpath и магическим образом за ним следует. Кэш шаблонов, к сожалению, жёстко вшит и не просто его сменить. Но можно всё сделать через символические ссылки. Например, если /srv является большой смонтированной файловой системой, создайте каталог и символьную ссылку на него:

sudo mkdir /srv/lxccache
sudo rm -rf /var/cache/lxc
sudo ln -s /srv/lxccache /var/cache/lxc

или, используя монтирование:

sudo mkdir /srv/lxccache
sudo sed -i '$a \
/srv/lxccache /var/cache/lxc none defaults,bind 0 0' /etc/fstab
sudo mount -a

Контейнеры с поддержкой lvm

Существует возможность использовать разделы LVM в качестве хранилища для контейнеров. Преимуществом этого является, кроме прочего, гибкость в управлении хранилищем и быстрое клонирование контейнеров. По умолчанию используется VG (группа томов) с именем lxc, но могут применяться и другие VG при использовании параметров командной строки. Когда LV (логический том) используется для хранения контейнеров, конфигурационный файл контейнера все еще /var/lib/lxc/CN/config, но корневая точка входа в этом файле (lxc.rootfs) будет указывать на имя блочного устройства логического тома, т.е. /dev/lxc/CN.

Контейнеры с деревом каталогов и LVM хранилищем могут сосуществовать вместе.

Btrfs

Если основная система имеет /var, размеченный как btrfs, средства администрирования LXC распознают это и автоматически будут использовать для клонирования контейнеров снимки btrfs.

ZFS

Подобно btrfs, использование zfs позволит применять возможности, но уже ZFS: подтом (subvolume) для контейнера, быстрое создание снимков и клонов, более эффективное использование места на диске за счёт дедупликации.

Apparmor

LXC поставляется с профилем Apparmor, предназначенным для защиты основной системы от случайного неправильного использования привилегий внутри контейнера. Например, контейнер не должен иметь возможности писать в каталог /proc/sysrq-trigger или менять большинство файлов в каталоге /sys.

Профиль usr.bin.lxc-start используется при запуске lxc-start. Этот профиль в основном предотвращает монтирование lxc-start новых файловых систем вне корневой файловой системы контейнера. Перед инициализацией контейнера LXC запрашивает переключение на профиль контейнера. По умолчанию используется профиль lxc-container-default, определенный в /etc/apparmor.d/lxc/lxc-default. Этот профиль запрещает контейнеру доступ к многим опасным каталогам и монтирование большинства файловых систем.

Если вы обнаружили, что lxc-start падает из-за попытки легитимного доступа, перекрытого политикой Apparmor, вы можете отключить профиль lxc-start следующим образом:

sudo apparmor_parser -R /etc/apparmor.d/usr.bin.lxc-start
sudo ln -s /etc/apparmor.d/usr.bin.lxc-start /etc/apparmor.d/disabled/

Это позволит запускать lxc-start без ограничений, но продолжит ограничивать собственно контейнер. Если вы хотите также снять ограничения с контейнера, в дополнение к блокировке использования профиля usr.bin.lxc-start, вам потребуется в файл настроек контейнера добавить:

lxc.aa_profile = unconfined

Если вы желаете запускать контейнер в собственном профиле, вы можете создать новый профиль в /etc/apparmor.d/lxc/. Его имя должно начинаться на lxc- чтобы lxc-start имел возможность переключения на этот профиль. После создания политики, загрузите ее используя команду:

sudo apparmor_parser -r /etc/apparmor.d/lxc-containers

Профиль автоматически загрузится после перезагрузки системы, поскольку его содержимое учтено в /etc/apparmor.d/lxc-containers. Наконец, чтобы заставить контейнер CN использовать новый профиль lxc-CN-profile, добавьте следующие строки в его файл настройки:

lxc.aa_profile = lxc-CN-profile

lxc-execute не просматривает профиль Apparmor, но контейнер, который он порождает, будет ограничен.

Группы управления

Группы управления (cgroups) - это способность ядра предоставлять возможность иерархически группировать задачи, а также назначать и ограничивать ресурсы для каждой cgroup. Они используются в контейнерах для ограничения доступа к блочным и посимвольным устройствам (block and character device) и для заморозки (приостановки) контейнеров. В дальнейшем их можно использовать для ограничения используемой памяти и блочного ввода/вывода, гарантированного минимума использования CPU и для фиксирования определенных CPU за отдельными контейнерами. По умолчанию LXC устанавливает зависимость на установку пакета cgroup-lite, который предоставляет надлежащую инициализацию cgroup при загрузке системы. Пакет cgroup-lite монтирует каждую cgroup подсистему отдельно в /sys/fs/cgroup/SS, где SS - название подсистемы. Например, подсистема freezer монтируется в /sys/fs/cgroup/freezer. Группы управления LXC хранятся в /sys/fs/cgroup/SS/INIT/lxc, где INIT - инициализирующая группа управления задачи. По умолчанию это /, поэтому группа управления freezer для контейнера CN будет /sys/fs/cgroup/freezer/lxc/CN.

Привилегии

Утилиты администрирования контейнерами должны запускаться с привилегиями суперпользователя. Утилита с названием lxc-setup была написана с намерением предоставлять инструменты с требуемыми правами доступа к файлам, позволяя обычным пользователям запускать эти инструменты с достаточными привилегиями. Однако, поскольку суперпользователь не может пока быть надежно погружен в контейнер, эта особенность бесполезна. Поэтому рекомендуется не использовать lxc-setup и предоставлять администраторам LXC требуемые привилегии sudo.

Пространство имен пользователя, которое предположительно будет доступно к следующему LTS выпуску, будет позволять ограничивать (сдерживать) суперпользователя контейнера, также как будут уменьшены привилегии, необходимые для создания и администрирования контейнеров.

Отслеживающие задания LXC

Как отмечалось выше, пакет lxc включает два отслеживающих задания. Первое, lxc-net, стартует всегда когда другое, lxc, собирается стартовать и останавливается, когда то останавливается. Если переменная USE_LXC_BRIDGE установлена в false в файле /etc/defaults/lxc, то оно завершится немедленно. Если переменная true и возникает ошибка организации моста, то задание lxc не стартует. lxc-net отключает мост, когда останавливается, хотя использующий его контейнер работает.

Задание lxc запускается на 2-5 уровне выполнения. Если переменная LXC_AUTO установлена в true, оно ищет в /etc/lxc контейнеры, которые должны запускаться автоматически. Когда задание lxc останавливается, вручную или при установке уровня выполнения 0, 1 или 6, оно останавливает эти контейнеры.

Для регистрации автоматического запуска контейнера, создайте символическую ссылку /etc/default/lxc/name.conf, указывающую на конфигурационный файл контейнера. Например, конфигурационный файл для контейнера CN - /var/lib/lxc/CN/config. Чтобы автоматически запускать этот контейнер, используйте команду:

sudo ln -s /var/lib/lxc/CN/config /etc/lxc/auto/CN.conf

Администрирование контейнеров

Создание контейнеров

Самый простой способ создать контейнер - использовать lxc-create. Этот сценарий использует специфические для дистрибутива шаблоны в /usr/lib/lxc/templates/ для установки дружественных контейнеру настроек chroots в /var/lib/lxc/CN/rootfs и инициализации конфигурации в /var/lib/lxc/CN/fstab и /var/lib/lxc/CN/config, где CN - название контейнера.

Команда на создание простейшего контейнера будет выглядеть следующим образом:

sudo lxc-create -t ubuntu -n CN

Она говорит lxc-create использовать шаблон ubuntu (-t ubuntu) и вызывать контейнер CN (-n CN). Поскольку не указан файл настроек (что можно сделать с помощью параметра '-f file'), будет использован файл настроек по умолчанию /etc/lxc/lxc.conf. Это предоставит контейнеру единственный veth сетевой интерфейс, подключенный к мосту lxcbr0.

Шаблоны создания контейнеров также могут воспринимать аргументы. Они могут быть перечислены после --. Например:

sudo lxc-create -t ubuntu -n oneiric1 -- -r oneiric

передает параметры '-r oneiric1' шаблону ubuntu.

Помощь

Помощь по команде lxc-create можно увидеть используя lxc-create -h. Однако шаблоны также забирают свои собственные параметры. Если вы выполните

sudo lxc-create -t ubuntu -h

то после общего экрана помощи lxc-create будет следовать вывод помощи, касающийся шаблона ubuntu. Если не указывать шаблон, то будет показана помощь только по lxc-create.

Шаблон Ubuntu

Шаблон ubuntu может использоваться для создания контейнеров системы Ubuntu любой редакции, по крайней мере начиная с 10.04 LTS. Он использует debootstrap для создания кешированной файловой системы контейнера, с которой будет создаваться копия каждый раз как стартует контейнер. Кешированный образ сохраняется и только пересоздается когда вы создаете контейнер с использованием опции -F (flush), передаваемой шаблону, т.е.:

sudo lxc-create -t ubuntu -n CN -- -F

Редакция Ubuntu, установленная в контейнер, будет той же самой, что и на основной системе, если не указать опцию -r, т.е.:

sudo lxc-create -t ubuntu -n CN -- -r lucid

Если вы хотите создать 32-битный контейнер на 64-битной системе, передайте в контейнер параметр -a i386. Если у вас установлен пакет qemu-user-static, то вы можете создать контейнер, используя любую архитектуру, поддерживаемую qemu-user-static.

В контейнере будет присутствовать пользователь ubuntu с паролем ubuntu, входящий в группу sudo. Если вы хотите хотите добавить открытый ключ ssh для этого пользователя, вы можете это сделать параметром -S sshkey.pub.

Вы можете связать пользователя основной системы jdoe (например) с контейнером, используя опцию -b jdoe. Это позволит скопирует пароль и shadow записи пользователя jdoe в контейнер, удостоверится, что его группа по умолчанию и оболочка доступны, добавит его в группу sudo и смонтирует связыванием (bind-mount) его домашний каталог в контейнер при запуске контейнера.

Когда создается контейнер, архив release-updates добавляется в его sources.list, тем самым его архив пакетов будет обновляться. Если редакция контейнера старше 12.04 LTS, автоматически устанавливается пакет lxcguest. В качестве альтернативы, если указать опцию --trim , пакет lxcguest установлен не будет и множество сервисов будет удалено из контейнера. В результате получится контейнер с более быстрой загрузкой, но менее обновляемый.

Шаблон облака Ubuntu

Шаблон ubuntu-cloud создает контейнеры Ubuntu, загружая и извлекая опубликованные образы для облака Ubuntu. Он воспринимает некоторые из опций шаблона ubuntu, а именно -r release, -S sshkey.pub, -a arch и -F для сброса кешированных образов. Он воспринимает также некоторые дополнительные опции. Опция -C создает облачный контейнер, настроенный на использование с сервисом metedata. Опция -u позволяет инициализирующему облачному файлу с пользовательскими данными настраивать контейнер при старте. Если передается -L, то не будет установлено никаких национальных настроек. Опция -T может быть использована для выбора размещения извлекаемой свертки (tarball) вместо использования опубликованной свертки облачного образа. Наконец, опция -i устанавливает id системы для cloud-init, который по умолчанию приравнивается к случайной строке.

Другие шаблоны

Шаблоны ubuntu и ubuntu-cloud хорошо поддерживаются. Тем не менее поддерживаются и другие шаблоны. Шаблон debian создает контейнер на основе Debian, используя debootstrap почти как для шаблона ubuntu. По умолчанию он устанавливает образ debian squeeze. Другие редакции могут быть выбраны с использованием переменной окружения SUITE:

sudo SUITE=sid lxc-create -t debian -n d1

Поскольку debian не может быть безопасно загружен внутри контейнера, контейнеры debian будут урезаны как при использовании опции --trim для шаблона ubuntu.

Для очистки кеша образа контейнера вызывайте шаблон напрямую и передавайте ему опцию --clean:

sudo SUITE=sid /usr/lib/lxc/templates/lxc-debian --clean

Существующие шаблоны fedora создают контейнеры на базе редакций fedora не выше 14. Редакции fedora, начиная с 15 основаны на systemd, который шаблоны пока не могут преобразовать в установку, загружаемую в контейнере. Перед тем как запускать шаблон fedora вам следует убедиться, что установлены yum и curl. Контейнер с fedora 12 может быть установлен следующим образом:

sudo lxc-create -t fedora -n fedora12 -- -R 12

Существуют шаблоны OpenSuSE, но они требуют программу zypper, которая пока не имеет пакета. Таким образом шаблоны OpenSuSE не поддерживаются.

Еще два шаблона созданы в основном для экспериментальных целей. Шаблон busybox создает очень маленький системный контейнер, основанный целиком на busybox. Шаблон sshd создает контейнер приложений, запускающий sshd в области имен частной сети. Каталоги библиотек и двоичных файлов монтируются связыванием внутрь контейнера, хотя не /home или /root. Для создания, запуска и соединения по ssh с контейнером, вы можете использовать следующее:

sudo lxc-create -t sshd -n ssh1
ssh-keygen -f id
sudo mkdir /var/lib/lxc/ssh1/rootfs/root/.ssh
sudo cp id.pub /var/lib/lxc/ssh1/rootfs/root/.ssh/authorized_keys
sudo lxc-start -n ssh1 -d
ssh -i id root@ssh1.
Резервные хранилища

По умолчанию lxc-create помещает корневую файловую систему контейнера в каталог /var/lib/lxc/CN/rootfs. Другим вариантом является использование логических томов LVM. Если существует группа томов lxc, вы можете создать контейнер на основе lvm с названием CN, используя команду:

sudo lxc-create -t ubuntu -n CN -B lvm

Если вы хотите использовать группу томов с именем schroots с файловой системой xfs на 5 Гб, вы можете использовать:

sudo lxc-create -t ubuntu -n CN -B lvm --vgname schroots --fssize 5G --fstype xfs

Клонирование

Для быстрой подготовки к работе вы можете решить изменить canonical контейнер в соответствии с вашими требованиями и затем сделать множество его копий. Это можно осуществить с помощью программы lxc-clone. Дан существующий контейнер с именем C1, новый контейнер C2 может быть создан:

sudo lxc-clone -o C1 -n C2

Если файловой системой /var/lib/lxc является btrfs, то lxc-clone создаст файловую систему C2 как снимок от C1. Если корневая файловая система контейнера поддерживается lvm, то вы можете указать опцию -s для создания новой rootfs как снимок lvm оригинала следующим образом:

sudo lxc-clone -s -o C1 -n C2

И lvm и btrfs снимки обеспечивают быстрое клонирование с очень небольшим изначально использованием дискового пространства.

Запуск и остановка

Чтобы запустить контейнер используйте lxc-start -n CN. По умолчанию lxc-start выполнит /sbin/init внутри контейнера. Вы можете предоставить другую программу для выполнения плюс аргументы, как дополнительные аргументы для lxc-start:

sudo lxc-start -n container /sbin/init loglevel=debug

Если вы не укажете опцию -d (daemon - сервис), то вы увидите консоль (по поводу контейнерной /dev/console смотрите секцию Консоли) в терминале. Если вы укажете опцию -d, то эту консоль вы не увидите, а lxc-start завершится без ошибок немедленно, даже если дальнейшая часть запуска контейнера завершится неудачей. Вы можете использовать lxc-wait или lxc-monitor (смотрите Отслеживание статуса контейнеров) для проверки удачно или нет запустился контейнер.

Для получения отладочной информации LXC, используйте -o filename -l debuglevel, например:

sudo lxc-start -o lxc.debug -l DEBUG -n container

Наконец, вы можете указать параметры настройки, используя опцию -s. Однако в общем случае рекомендуется вместо этого указывать их в конфигурационном файле контейнера. Аналогичным образом можно указать целиком иной файл настроек с помощью опции -f, но это тоже в общем случае не рекомендуется.

В то время как lxc-start запускает в контейнерный /sbin/init, lxc-execute использует программу минимальной инициализации lxc-init, которая пытается монтировать /proc, /dev/mqueue и /dev/shm, выполняет программы, указанные в командной строке, и ждет их завершения. lxc-start предназначена для использования системными контейнерами, в то время как lxc-execute для использования контейнерами приложений (смотрите эту статью для дополнительной информации).

Остановить контейнер вы можете разными способами. Вы можете использовать shutdown, poweroff и reboot когда подсоединились к контейнеру. Для чистого завершения работы контейнера извне (т.е. из основной системы), вы можете выполнить команду sudo lxc-shutdown -n CN. Она воспринимает необязательный параметр задержки. Если он не указан, команда посылает сигнал SIGPWR в контейнер и немедленно завершается. Если опция указана, как, например, sudo lxc-shutdown -n CN -t 10, то команда ожидает указанное количество секунд завершения работы контейнера. Затем, если контейнер все еще работает, она убивает (kill) его, а также все работающие в нем приложения. Вы можете также немедленно убить контейнер (не оставляя шансов для приложений завершиться аккуратно), используя команду sudo lxc-stop -n CN. Наконец, lxc-kill может быть использована в общем случае для отправки любого сигнала процедуре инициализации контейнера.

В процессе завершения работы контейнера вы можете получить несколько (безопасных) сообщений об ошибке, как например:

$ sudo poweroff
[sudo] password for ubuntu: =

$ =

Broadcast message from ubuntu@cn1
        (/dev/lxc/console) at 18:17 ...

The system is going down for power off NOW!
 * Asking all remaining processes to terminate...
   ...done.
 * All processes ended within 1 seconds....
   ...done.
 * Deconfiguring network interfaces...
   ...done.
 * Deactivating swap...
   ...fail!
umount: /run/lock: not mounted
umount: /dev/shm: not mounted
mount: / is busy
 * Will now halt

Кроме того, контейнер может быть "заморожен" командой sudo lxc-freeze -n CN. Это заблокирует все его процессы до тех пор, пока он не будет "разморожен" командой sudo lxc-unfreeze -n CN.

Отслеживание статуса контейнеров

Две команды доступны для отслеживания изменения статуса контейнера. lxc-monitor отслеживает один или более контейнеров на любые изменения статусов. Она как правило получает имя контейнера с помощью опции -n, однако в этом случае имя контейнера может быть регулярным выражением posix, чтобы позволять отслеживать желаемые наборы контейнеров. lxc-monitor продолжает выполнение пока выводит статусы контейнеров. Вторая команда lxc-wait ожидает специфического изменения статуса контейнера и затем завершается. Например,

sudo lxc-monitor -n cont[0-5]*

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

sudo lxc-wait -n cont1 -s 'STOPPED|FROZEN'

будет ожидать пока контейнер cont1 не войдет в состояния STOPPED или FROZEN и затем завершится.

Консоли

Контейнеры имеют настраиваемое количество консолей. Одна всегда существует в контейнерном /dev/console. Она видна в терминале из которого вы запустили lxc-start, если не указана опция -d. Вывод в /dev/console может быть перенаправлен в файл при использовании опции -c console-file с lxc-start. Количество дополнительных консолей указывается переменной lxc.tty и обычно равно 4. Эти консоли видны как /dev/ttyN (где 1 ⇐ N ⇐ 4). Для соединения с консолью 3 из основной системы используйте

sudo lxc-console -n container -t 3

в противном случае, если -t N не указано, будет автоматически выбрана неиспользуемая консоль. Для входа из консоли используйте последовательность Ctrl-a q. Обратите внимание, что последовательность не сработает в консоли при использовании lxc-start без опции -d.

Каждая консоль контейнера фактически является Unix98 pty, смонтированной в pty основной (не гостевой) системы через связанное монтирование гостевых /dev/ttyN и /dev/console. Следовательно, если гостевая система размонтирует их или с другой стороны попытается получить доступ к символьному устройству 4:N, она не будет обслужена getty на LXC консолях. (При настройках по умолчанию контейнер не сможет получить доступ к этому символьному устройству и getty, соответственно, завершится с ошибкой). Это может легко случиться, когда загрузочный сценарий вслепую монтирует новые устройства в /dev.

Исследование контейнеров

Некоторые команды способны собирать информацию по созданным контейнерам. lxc-ls выведет все существующие контейнеры в своей первой строке вывода и все запущенные контейнеры во второй. lxc-list предоставит ту же информацию в более развернутом формате, перечислив запущенные контейнеры сначала и остановленные в конце. lxc-ps предоставит список процессов в контейнере. Для передачи ps аргументов в lxc-ps, предварите их --. Например, для получения списка всех процессов в контейнере plain:

sudo lxc-ps -n plain -- -ef

lxc-info возвращает статус контейнера и pid его инициирующего процесса. lxc-cgroup может быть использована для получения и установки переменных ограничений и информации управляющих групп контейнера. Это может оказаться более удобным чем взаимодействовать с файловой системой cgroup. Например для получения списка устройств к которым контейнер имеет доступ, вы можете использовать:

sudo lxc-cgroup -n CN devices.list

или для добавления прав доступа mknod, read и write к /dev/sda:

sudo lxc-cgroup -n CN devices.allow "b 8:* rwm"

и для ограничения памяти до 300M:

lxc-cgroup -n CN memory.limit_in_bytes 300000000

lxc-netstat выполняет netstat в запущенном контейнере, давая вам представление о статусе его сети.

lxc-backup создаст резервные копии корневой файловой системы всех существующих контейнеров (за исключением основанных на lvm), используя rsync для сохранения содержимого в /var/lib/lxc/CN/rootfs.backup.1. Эти резервные копии могут использоваться для восстановления с помощью lxc-restore. Однако lxc-backup и lxc-restore неустойчивы благодаря модификациям и, соответственно, не рекомендуются к использованию.

Уничтожение контейнеров

Используйте lxc-destroy для уничтожения контейнера.

sudo lxc-destroy -n CN

Если контейнер запущен, lxc-destroy завершится с сообщением о возможности остановить и уничтожить контейнер с помощью команды

sudo lxc-destroy -n CN -f

Использование расширенного пространства имен

Одной из особенностей ядра Linux, используемой в LXC для создания контейнеров, являются частные пространства имен. Пространства имен позволяют ряду задач иметь частные отображения имен ресурсов для таких вещей как пути и ID процессов. (Смотрите Ссылки для дополнительной информации). В отличие от групп управления и других особенностей монтирования, которые также используются при создании контейнеров, пространствами имен невозможно манипулировать при помощи интерфейса файловой системы. Поэтому LXC поставляется с программой lxc-unshare, которая большей частью используется для тестирования. Она предоставляет возможность создавать новую задачу в частном пространстве имен. Например,

sudo lxc-unshare -s 'MOUNT|PID' /bin/bash

создаст оболочку shell с частными pid и пространством имен монтирования. В этой оболочке вы можете выполнить

root@ubuntu:~# mount -t proc proc /proc
root@ubuntu:~# ps -ef
UID        PID  PPID  C STIME TTY          TIME CMD
root         1     0  6 10:20 pts/9    00:00:00 /bin/bash
root       110     1  0 10:20 pts/9    00:00:00 ps -ef

притом, что ps покажет только задачи в вашем новом пространстве имен.

Недолговечные контейнеры

Недолговечные (эфемерные - ephemeral) контейнеры - это одноразовые контейнеры. Имея созданный контейнер CN, вы можете выполнить команду в недолговечном контейнере, созданном на основе CN, подсоединив пользователя jdoe в контейнер, используя команду:

lxc-start-ephemeral -b jdoe -o CN -- /home/jdoe/run_my_job

После завершения задания, контейнер будет сброшен.

Команды для работы с контейнерами

Далее приведена таблица всех контейнерных команд:

Container commands
Команда Описание
lxc-attach (НЕ ПОДДЕРЖИВАЕТСЯ) Выполняет команду в запущенном контейнере
lxc-backup Создает резервную копию корневой системы для всех контейнеров, основанных на lvm
lxc-cgroup Просмотр и установка параметров групп управления
lxc-checkconfig Проверка основной системы на поддержку контейнеров
lxc-checkpoint (НЕ ПОДДЕРЖИВАЕТСЯ) Контрольная точка для запущенного контейнера
lxc-clone Создание копии существующего контейнера
lxc-console Открытие консоли работающего контейнера
lxc-create Создание нового контейнера
lxc-destroy Уничтожение существующего контейнера
lxc-execute Запуск команды в (незапущенном) контейнере приложений
lxc-freeze Приостановка работающего контейнера
lxc-info Вывод информации о состоянии контейнера
lxc-kill Передача сигнала в инициализирующий процесс контейнера
lxc-list Получение списка всех контейнеров
lxc-ls Получения списка всех контейнеров с меньшим выводом, чем у lxc-list
lxc-monitor Отслеживание изменения состояния одного или нескольких контейнеров
lxc-netstat Выполнение команды netstat в запущенном контейнере
lxc-ps Просмотр информации по процессам в работающем контейнере
lxc-restart (НЕ ПОДДЕРЖИВАЕТСЯ) Сброс контейнера, остановленного в контрольной точке
lxc-restore Восстановление контейнеров из резервных копий, сделанных lxc-backup
lxc-setcap (НЕ РЕКОМЕНДУЕТСЯ) Установка характеристик файлов (file capabilities) на инструменты LXC
lxc-setuid (НЕ РЕКОМЕНДУЕТСЯ) Установка или сброс setuid битов для инструментов LXC
lxc-shutdown Безопасное завершение работы контейнера
lxc-start Запуск остановленного контейнера
lxc-start-ephemeral Запуск недолговечного (одноразового) контейнера
lxc-stop Немедленная остановка работающего контейнера
lxc-unfreeze Восстановление работы приостановленного контейнера
lxc-unshare Инструмент тестирования вручную объединенных пространств имен
lxc-version Вывод версии инструментов LXC
lxc-wait Ожидание перехода контейнера в определенное состояние

Файл настройки

Контейнеры LXC очень гибкие. Пакет Ubuntu lxc устанавливает умолчания таким образом, чтобы создание контейнеров в системе Ubuntu было настолько простым, насколько это возможно. Если вам требуется большая гибкость, то в этой главе будет показано как сделать тонкую настройку ваших контейнеров под ваши нужды.

Детальная информация доступна на странице lxc.conf(5) руководства man. Обратите внимание, что конфигурации по умолчанию, созданные по ubuntu шаблонам, подходят для системных контейнеров и не требуют настройки.

Выбор файлов и опций настройки

Установка контейнера управляется параметрами настроек LXC. Параметры могут быть указаны в нескольких местах:

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

  2. Файл /var/lib/lxc/CN/config, используемый по умолчанию при запуске контейнера.

  3. lxc-start воспринимает альтернативный файл настроек с помощью параметра -f filename.

  4. Отдельные переменные настроек могут быть переопределены в команде lxc-start с использованием параметра -s key=value. В общем случае лучше редактировать конфигурационный файл.

Настройка сети

Настройки сети в LXC контейнерах очень гибкие. Они переключаются lxc.network.type записями в файле настроек. Если таких записей нет, то контейнер будет разделять сетевой стек основной системы. Сервисы и соединения, запущенные в контейнере, будут использовать IP адрес основной системы. Если хотя бы одна запись lxc.network.type присутствует, то контейнер получит частный (уровня 2) сетевой стек. Он будет иметь собственные сетевые интерфейсы и правила firewall. Существует несколько опций для lxc.network.type:

  1. lxc.network.type=empty: Контейнер не будет иметь других сетевых интерфейсов, кроме loopback.

  2. lxc.network.type=veth: Это выбор по умолчанию, когда используются шаблоны ubuntu или ubuntu-cloud, и в этом случае создается veth сетевой туннель. Один конец этого туннеля становится сетевым интерфейсом внутри контейнера, другой подключается к интерфейсу моста основной системы. Любое количество таких туннелей может быть создано добавлением записей lxc.network.type=veth в конфигурационный файл. Мост, к которому будут подсоединяться туннели, определяется с помощью lxc.network.link = lxcbr0.

  3. lxc.network.type=phys: Физический сетевой интерфейс (т.е. eth2) передается в контейнер.

Еще две опции, которые можно использовать, это vlan и macvlan, однако их использование более сложное и не будет здесь рассматриваться. Существует еще несколько других сетевых параметров:

  1. lxc.network.flags может быть установлено только в up и требуется для проверки, что сетевой интерфейс поднят.

  2. lxc.network.hwaddr определяет mac адрес для присвоения сетевой карте внутри контейнера.

  3. lxc.network.ipv4 и lxc.network.ipv6 устанавливают соответствующие IP адреса, если они должны быть статичными.

  4. lxc.network.name определяет имя для присвоения внутри контейнера. Если не определено, выбираются правильные умолчания (т.е. eth0 для первой сетевой карты).

  5. lxc.network.lxcscript.up определяет сценарий, который должен быть вызван после поднятия сети со стороны основной системы. Смотрите страницу lxc.conf(5) руководства man для деталей.

Настройка групп управления

Опции cgroup могут быть указаны с использованием записей lxc.cgroup. lxc.cgroup.subsystem.item = value указывает LXC установить для элемента item подсистемы subsystem значение value. Это возможно проще реализовать, чем просто записать значение в файл группы управления контейнера для подсистемы. Например, чтобы установить ограничение для памяти в 320M вам следует добавить

lxc.cgroup.memory.limit_in_bytes = 320000000

что заставит записать значение 320000000 в файл /sys/fs/cgroup/memory/lxc/CN/limit_in_bytes.

Rootfs, mounts и fstab

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

lxc.rootfs = /var/lib/lxc/CN/rootfs
lxc.mount.entry=proc /var/lib/lxc/CN/rootfs/proc proc nodev,noexec,nosuid 0 0
lxc.mount = /var/lib/lxc/CN/fstab

Первая строка говорит, что корневая файловая система контейнера уже смонтирована в /var/lib/lxc/CN/rootfs. Если файловая система является блоковым устройством (таким как логический том LVM), то вместо этого потребуется указать путь до блокового устройства.

Каждая строка lxc.mount.entry должна содержать элемент для монтирования в правильном формате fstab. Целевой каталог должен предваряться /var/lib/lxc/CN/rootfs, даже если lxc.rootfs указывает на блочное устройство.

Наконец, lxc.mount указывает на файл в формате fstab, содержащий дополнительные элементы монтирования. Обратите внимание, что все эти записи будут смонтированы основной системой до запуска инициализации контейнера. Таким образом существует возможность связанного монтирования различных каталогов из основной системы в контейнер.

Другие опции настройки

  1. lxc.cap.drop может быть использована для предотвращения получения контейнером указанных возможностей. Например, добавление

    lxc.cap.drop = sys_admin

    предотвратит возможность монтирования файловых систем, так же как другие действия, которые требуют cap_sys_admin. Смотрите страницу capabilities(7) руководства man для перечня возможностей и их толкований.

  2. lxc.aa_profile = lxc-CN-profile определяет специальные профили Apparmor в которых запускать контейнер. Смотрите Apparmor для дополнительной информации.

  3. lxc.console=/path/to/consolefile определяет что консольные сообщения должны записываться в указанный файл.

  4. lxc.arch определяет архитектуру контейнера, например, x86 или x86_64.

  5. lxc.tty=5 определяет что 5 консолей (в дополнение к /dev/console) должны быть созданы. Соответственно консоли будут доступны от /dev/tty1 до /dev/tty5. Шаблоны ubuntu устанавливают это значение в 4.

  6. lxc.pts=1024 определяет что контейнер должен иметь смонтированную частную (Unix98) файловую систему devpts. Если не указано, то контейнер будет разделять (иметь в совместном доступе) /dev/pts с основной системой, что редко бывает желательным. Число 1024 означает, что 1024 pty должны быть доступны в контейнере, однако это число в настоящее время игнорируется. Перед запуском инициализации контейнера, LXC (по существу) выполняет

    sudo mount -t devpts -o newinstance devpts /dev/pts

    внутри контейнера. Важно понимать, что контейнер не может монтировать файловые системы devpts сам по себе. Он может безопасно присоединить или переместить точки монтирования своих смонтированных /dev/pts. Но если он выполнит

    sudo mount -t devpts devpts /dev/pts

    он перемонтирует экземпляры devpts основной системы. Если добавлена опция монтирования newinstance, то это смонтирует новые частные (пустые) экземпляры. Ни в одном случае он не может перемонтировать экземпляры, определенные LXC. По этой причине и для предотвращения использования контейнером pty основной системы, политика Apparmor по умолчанию не позволяет контейнерам монтировать файловые системы devpts после запуска инициализации контейнера.

  7. lxc.devttydir определяет каталог внутри /dev, в котором LXC будет создавать свои консольные устройства. Если эта опция не определена, то все pty будут монтироваться связыванием с /dev/console и /dev/ttyN. Однако изредка обновления пакетов могут пытаться слепо выполнять rm -f и затем mknod для этих устройств. Это будет приводить к сбоям (поскольку используется монтирование связыванием), что приведет к сбою установки обновлений. Когда lxc.devttydir установлен на LXC, например, то LXC будет монтировать связыванием консоли pty в /dev/lxc/console и /dev/lxc/ttyN. Это позволит обновлениям пакетов удачно устанавливаться, из-за риска последующего получения сбоя при выполнении gettys на этих консолях до следующей перезагрузки ( FIXME This allows the package updates to succeed, at the risk of making future gettys on those consoles fail until the next reboot). Эта проблема идеально решается с помощью пространств имен устройств.

Обновления в контейнерах Ubuntu

Из-за некоторых ограничений, присутствующих в контейнерах, обновления пакетов временами могут завершаться с ошибкой. Например, установка или обновление пакета может закончиться неудачей, если не позволено создавать или открывать блочные устройства. Это часто блокирует все дальнейшие обновления, пока не разрешится данная проблема. В некоторых случаях вы можете обойти проблему, используя chroot внутри контейнера, чтобы обойти ограничения и завершить обновления внутри chroot.

Некоторые известные специфические вещи, которые могут время от времени препятствовать обновлению пакетов, включают:

  1. Изменения в контейнере, выполненные при создании контейнера с помощью опции --trim.

  2. Действия, выполненные lxcguest. Например, если /lib/init/fstab смонтирован связыванием с другим файлом, обновления mountall, которые настаивают на замене этого файла, могут завершиться неудачей.

  3. Перемонтированные консольные устройства с pty из основной системы могут иметь проблемы с обновлениями udev.

  4. Политики Apparmor и ограничения cgroup для устройств могут мешать обновлениям при выполнении определенных действий.

  5. Разрешения, сброшенные с помощью lxc.cap.drop, могут аналогично остановить обновления пакетов при выполнении определенных действий.

Libvirt LXC

Libvirt является мощным решением по управлению гипервизорами (программами управления операционными системами), с помощью которой можно администрировать виртуальные машины под Qemu, Xen и LXC, как локально так и удаленно. Драйвер libvirt LXC - это отдельная реализация того, что мы обычно называем LXC. Некоторые отличия включают:

  1. Конфигурация сохраняется в XML формате

  2. Нет инструментов, облегчающих создание контейнера

  3. По умолчанию отсутствует консоль /dev/console

  4. Не поддерживается (пока) перезагрузка или полная остановка контейнеров

Преобразование контейнера LXC в libvirt-lxc

В разделе Создание контейнеров показано как создавать LXC контейнеры. Если вы создали рабочий LXC контейнер таким способом, то вы можете управлять им при помощи libvirt. Загрузите xml файл в качестве примера:

wget http://people.canonical.com/~serge/o1.xml

Отредактируйте этот файл, заменив название контейнера и расположение корневой файловой системы. Затем вы можете зарегистрировать контейнер командой:

virsh -c lxc:/// define o1.xml

Создание контейнера из облачного образа

Если вы предпочитаете создавать новые оригинальные контейнеры только под LXC, вы можете загрузить образ для облака ubuntu, извлечь его и указать его расположение в xml файле libvirt LXC. Например, найдем адрес образа последней ежедневной корневой сборки Ubuntu 12.04 LTS следующим образом:

url1=`ubuntu-cloudimg-query precise daily $arch --format "%{url}\n"`
url=`echo $url1 | sed -e 's/.tar.gz/-root\0/'`
wget $url
filename=`basename $url`

Распакуем загруженный образ, например, так:

mkdir $HOME/c1
cd $HOME/c1
sudo tar zxf $filename

Загрузим шаблон xml:

wget http://people.canonical.com/~serge/o1.xml

В шаблоне заменим o1 на c1 и каталог расположения /var/lib/lxc/o1/rootfs на $HOME/c1. Затем зарегистрируем контейнер с помощью:

virsh define o1.xml

Взаимодействие с контейнерами libvirt

Как мы видели, вы можете создавать libvirt-lxc контейнеры с помощью:

virsh -c lxc:/// define container.xml

Для запуска контейнера с именем container используйте:

virsh -c lxc:/// start container

Для остановки запущенного контейнера:

virsh -c lxc:/// destroy container

Обратите внимание, что хотя команда lxc-destroy уничтожает контейнер, команда virsh destroy только останавливает работающий контейнер. Для удаления регистрации контейнера используйте:

virsh -c lxc:/// undefine container

Для получения консоли работающего контейнера используйте:

virsh -c lxc:/// console container

Для выхода из консоли нажмите комбинацию Ctrl-].

Пакет lxcguest

В выпусках Ubuntu 11.04 (Natty) и 11.10 (Oneiric) был представлен пакет lxcguest. Немодифицированный коревой образ не может быть безопасно загружен внутри контейнера, однако образ с установленным пакетом lxcguest может быть загружен как контейнер на голом железе или под виртуальной машиной Xen, kvm или VMware.

Что касается сборки 12.04 LTS, функции, выполнявшиеся ранее пакетом lxcguest, были погружены пакеты ядра, а пакет lxcguest был удален. Как результат, оригинальный образ 12.04 LTS без изменений может быть загружен в качестве контейнера как на голом железе, так и под виртуальными машинами Xen, kvm или VMware. Использование более ранних выпусков все еще требует использование пакета lxcguest .

Безопасность

Пространство имен сопоставляет идентификаторы с ресурсами. Чтобы не предоставлять доступ контейнерам к любым идентификаторам (id) , указывающим на ресурсы, ресурсы должны быть защищены. Это является основой некоторой безопасности, предоставляемой пользователям контейнеров. Например, пространство имен IPC (взаимодействия между процессами) полностью изолировано. Однако другие пространства имен имеют различные уязвимости, которые позволяют получать неправильно предоставленные привилегии из одного контейнера в другой или в основную систему.

По умолчанию LXC контейнеры запускаются под управлением политики Apparmor для ограничения некоторых действий. Несмотря на то, что более строгая безопасность является задачей следующих редакций, в 12.04 LTS задачей политики Apparmor является не прекращение злонамеренных действий, а предупреждения случайных повреждений основной системы из гостевой.

Смотрите LxcSecurity wiki страницу для дополнительной актуальной информации.

Используемые системные вызовы

Возможность совместного использования ядра системы контейнерами является базовой особенностью. Поэтому, если ядро содержит некоторые потенциально опасные вызовы, контейнеры также могут их использовать. Как только контейнер сможет контролировать ядро системы, он сможет полностью управлять любыми ресурсами, доступными основной системе.

Ссылки

~~ Статья в DeveloperWorks [[https://www.ibm.com/developerworks/linux/library/l-lxc-containers/|LXC: Linux container tools]] является введением в использование контейнеров.
~~ [[http://www.ibm.com/developerworks/linux/library/l-lxc-security/index.html|The Secure Containers Cookbook]] демонстрирует использование модулей безопасности с целью сделать контейнеры более безопасными.
~~ Страницы руководств могут быть найдены по данным ссылкам:
  ~~ [[http://manpages.ubuntu.com/manpages/en/man7/capabilities.7.html|capabilities]].
  ~~ [[http://manpages.ubuntu.com/manpages/en/man5/lxc.conf.5.html|lxc.conf]].
~~ Основное развитие проекта LXC на [[http://lxc.sf.net/|Sourceforge]].
~~ Проблемы безопасности приведены и обсуждаются на странице [[http://wiki.ubuntu.com/LxcSecurity|LxcSecurity wiki]].
~~ Для дополнительной информации по пространствам имен в Linux смотрите: [[http://dl.acm.org/citation.cfm?id=1400097.1400109&coll=DL&dl=GUIDE&CFID=133158545&CFTOKEN=80778967|S.Bhattiprolu, E.W.Biederman, S.E.Hallyn, and D.Lezcano. Virtual Servers and Checkpoint/Restart in Mainstream Linux. SIGOPS Operating Systems Review, 42(5), 2008]].
~~ Стефан Грабер (Stéphane Graber), в предверии выхода 20 февраля 2014 года релиза LXC 1.0, опубликовал цикл статей о Linux Containers. [[https://www.stgraber.org/2013/12/20/lxc-1-0-blog-post-series/|LXC 1.0: Blog post series]] ([[http://vasilisc.com/lxc-1-0-blog-post-series|перевод]]).