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.

Если вы хотите использовать для /var иную файловую систему, вы можете смонтировать в этот каталог другую файловую систему большего объема. Если у вас есть диск, предназначенный для этих целей, вы можете просто смонтировать его в /var/lib/lxc. Если вы предпочитаете использовать другое расположение, такое как /srv, вы можете примонтировать его к этому каталогу или создать символическую ссылку. Например, если /srv является большой смонтированной файловой системой, создайте два каталога и символьные ссылки на них:

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

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

sudo mkdir /srv/lxclib /srv/lxccache
sudo sed -i '$a \
/srv/lxclib /var/lib/lxc    none defaults,bind 0 0 \
/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.

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, содержащий дополнительные элементы монтирования. Обратите внимание, что все эти записи будут смонтированы основной системой до запуска инициализации контейнера. Таким образом существует возможность связанного монтирования различных каталогов из основной системы в контейнер.

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

  lxc.cap.drop can be used to prevent the container from having or ever obtaining the listed capabilities. For instance, including
  lxc.cap.drop = sys_admin
  will prevent the container from mounting filesystems, as well as all other actions which require cap_sys_admin. See the capabilities(7) manual page for a list of capabilities and their meanings.
  lxc.aa_profile = lxc-CN-profile specifies a custom Apparmor profile in which to start the container. See Apparmor for more information.
  lxc.console=/path/to/consolefile will cause console messages to be written to the specified file.
  lxc.arch specifies the architecture for the container, for instance x86, or x86_64.
  lxc.tty=5 specifies that 5 consoles (in addition to /dev/console) should be created. That is, consoles will be available on /dev/tty1 through /dev/tty5. The ubuntu templates set this value to 4.
  lxc.pts=1024 specifies that the container should have a private (Unix98) devpts filesystem mount. If this is not specified, then the container will share /dev/pts with the host, which is rarely desired. The number 1024 means that 1024 ptys should be allowed in the container, however this number is currently ignored. Before starting the container init, LXC will do (essentially) a
  sudo mount -t devpts -o newinstance devpts /dev/pts
  inside the container. It is important to realize that the container should not mount devpts filesystems of its own. It may safely do bind or move mounts of its mounted /dev/pts. But if it does
  sudo mount -t devpts devpts /dev/pts
  it will remount the host's devpts instance. If it adds the newinstance mount option, then it will mount a new private (empty) instance. In neither case will it remount the instance which was set up by LXC. For this reason, and to prevent the container from using the host's ptys, the default Apparmor policy will not allow containers to mount devpts filesystems after the container's init has been started.
  lxc.devttydir specifies a directory under /dev in which LXC will create its console devices. If this option is not specified, then the ptys will be bind-mounted over /dev/console and /dev/ttyN. However, rare package updates may try to blindly rm -f and then mknod those devices. They will fail (because the file has been bind-mounted), causing the package update to fail. When lxc.devttydir is set to LXC, for instance, then LXC will bind-mount the console ptys onto /dev/lxc/console and /dev/lxc/ttyN, and subsequently symbolically link them to /dev/console and /dev/ttyN. This allows the package updates to succeed, at the risk of making future gettys on those consoles fail until the next reboot. This problem will be ideally solved with device namespaces.

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

Because of some limitations which are placed on containers, package upgrades at times can fail. For instance, a package install or upgrade might fail if it is not allowed to create or open a block device. This often blocks all future upgrades until the issue is resolved. In some cases, you can work around this by chrooting into the container, to avoid the container restrictions, and completing the upgrade in the chroot.

Some of the specific things known to occasionally impede package upgrades include:

  The container modifications performed when creating containers with the --trim option.
  Actions performed by lxcguest. For instance, because /lib/init/fstab is bind-mounted from another file, mountall upgrades which insist on replacing that file can fail.
  The over-mounting of console devices with ptys from the host can cause trouble with udev upgrades.
  Apparmor policy and devices cgroup restrictions can prevent package upgrades from performing certain actions.
  Capabilities dropped by use of lxc.cap.drop can likewise stop package upgrades from performing certain actions.

Libvirt LXC

Libvirt is a powerful hypervisor management solution with which you can administer Qemu, Xen and LXC virtual machines, both locally and remote. The libvirt LXC driver is a separate implementation from what we normally call LXC. A few differences include:

  Configuration is stored in xml format
  There no tools to facilitate container creation
  By default there is no console on /dev/console
  There is no support (yet) for container reboot or full shutdown

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

Creating Containers showed how to create LXC containers. If you've created a valid LXC container in this way, you can manage it with libvirt. Fetch a sample xml file from

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

Edit this file to replace the container name and root filesystem locations. Then you can define the container with:

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

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

If you prefer to create a pristine new container just for LXC, you can download an ubuntu cloud image, extract it, and point a libvirt LXC xml file to it. For instance, find the url for a root tarball for the latest daily Ubuntu 12.04 LTS cloud image using

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`

Extract the downloaded tarball, for instance

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

Download the xml template

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

In the xml template, replace the name o1 with c1 and the source directory /var/lib/lxc/o1/rootfs with $HOME/c1. Then define the container using

virsh define o1.xml

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

As we've seen, you can create a libvirt-lxc container using

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

To start a container called container, use

virsh -c lxc:/// start container

To stop a running container, use

virsh -c lxc:/// destroy container

Note that whereas the lxc-destroy command deletes the container, the virsh destroy command stops a running container. To delete the container definition, use

virsh -c lxc:/// undefine container

To get a console to a running container, use

virsh -c lxc:/// console container

Exit the console by simultaneously pressing control and ].

Пакет lxcguest

In the 11.04 (Natty) and 11.10 (Oneiric) releases of Ubuntu, a package was introduced called lxcguest. An unmodified root image could not be safely booted inside a container, but an image with the lxcguest package installed could be booted as a container, on bare hardware, or in a Xen, kvm, or VMware virtual machine.

As of the 12.04 LTS release, the work previously done by the lxcguest package was pushed into the core packages, and the lxcguest package was removed. As a result, an unmodified 12.04 LTS image can be booted as a container, on bare hardware, or in a Xen, kvm, or VMware virtual machine. To use an older release, the lxcguest package should still be used.

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

A namespace maps ids to resources. By not providing a container any id with which to reference a resource, the resource can be protected. This is the basis of some of the security afforded to container users. For instance, IPC namespaces are completely isolated. Other namespaces, however, have various leaks which allow privilege to be inappropriately exerted from a container into another container or to the host.

By default, LXC containers are started under a Apparmor policy to restrict some actions. However, while stronger security is a goal for future releases, in 12.04 LTS the goal of the Apparmor policy is not to stop malicious actions but rather to stop accidental harm of the host by the guest.

See the LXC security wiki page for more, uptodate information.

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

It is a core container feature that containers share a kernel with the host. Therefore, if the kernel contains any exploitable system calls, the container can exploit these as well. Once the container controls the kernel it can fully control any resource known to the host.

Ссылки

  The DeveloperWorks article LXC: Linux container tools was an early introduction to the use of containers.
  The Secure Containers Cookbook demonstrated the use of security modules to make containers more secure.
  Manual pages referenced above can be found at:
  capabilities
  lxc.conf
  The upstream LXC project is hosted at Sourceforge.
  LXC security issues are listed and discussed at the LXC Security wiki page
  For more on namespaces in Linux, see: S. Bhattiprolu, E. W. Biederman, S. E. Hallyn, and D. Lezcano. Virtual Servers and Check- point/Restart in Mainstream Linux. SIGOPS Op- erating Systems Review, 42(5), 2008.