Это может показаться довольно странным, на первый взгляд, однако, в определенных случаях может быть весьма полезным. Например, в случае если необходимо на тестовом сервере выделить площадку для разработки с возможностью запускать привилегированные контейнеры Docker. В случае, если вся работа происходит на одном сервере, то пользователь получает доступ сразу ко всем контейнерам/образам на сервере, что неправильно с точки зрения безопасности. С другой стороны, такие системы виртуализации, как QEMU/KVM или Xen обладают определенным оверхедом по производительности и сложнее в настройке. LXC, в данном случае, позволяет обойти оба этих недостатка.
Теория
Как известно, контейнеры LXC и Docker основаны на идентичных технологиях — cgroups, которые могут быть неограниченно вложенными. Отсюда следует, что уровень вложенности контейнеров также неограничен, все что необходимо — возможность создавать новые cgroups.
Как и многое другое в UNIX, cgroups представлены в файловой семантике, то есть каждая группа — это папка в псевдофайловой системе в /sys/fs/cgroup/<controller_name>/.
Для виртуализации системных каталогов, общих для хост-машины (/proc/ и /sys/) и контейнеров LXC использует технологию LXCFS, которая монтирует статические файлы поверх данных директорий в контейнерах. Так как основное дерево cgroups расположено в /sys/, необходимо отключить LXCFS.
Однако, разграничение ресурсов между контейнерами LXC основано еще на двух технологиях: AppArmor и Capabilities, доступ к которым также присутствовать у контейнера для создания дочерних контейнеров. Первая используется для того, чтобы контейнер не мог изменить ресурсы, разделяемые с хост-машиной и другими контейнерами. Вторая — позволяет внести дополнительные ограничения контейнер (по умолчанию на привилегированные контейнеры отсутствуют какие-либо ограничения со стороны capabilities), например запретить доступ к привилегированным портам TCP/UDP, возможность менять владельца файлов и др.
Таким образом, для того, чтобы создавать субконтейнеры, необходимо дать доступ контейнеру с возможностью записи с этим трем подсистемам хост-машины — cgroups, AppArmor и capabilities. Это подразумевает под собой определенные риски, т. к. получается, что, теоретически, каждый контейнер может изменить параметры другого контейнера. Это необходимо принять во внимание перед внедрением данного решения. С другой стороны, дочерние контейнеры не обязаны наследовать эти параметры от родителя (а в случае с Docker, они не наследуются, если не задать обратное явно в файле конфигурации), и приложения внутри них будут также изолированы от окружающей среды, как если бы эти контейнеры были запущены на хост-машине.
Практика
В данном примере используется Debian 8 и привилегированные контейнеры LXC. Совет — для того чтобы сделать доступными все возможности LXC и Docker (overlayfs, docker swarm), установите из бекпортов свежее ядро и LXC (здесь и далее вывод команд пропущен):
root@sol:~# echo "deb http://mirror.yandex.ru/debian/ jessie-backports main" >> /etc/apt/sources.list.d/backports.list root@sol:~# apt-get update root@sol:~# apt-get install -t jessie-backports linux-image-amd64 lxc
Отключите LXCFS:
root@sol:~# systemctl stop lxcfs root@sol:~# systemctl disable lxcfs
Создайте контейнер LXC:
root@sol:~# lxc-create -t debian -n dock0
В зависимости от вашей конфигурации, вам могут потребоваться дополнительные действия с контейнером (например, настройка сети). В моем случае получился следующий конфиг в /var/lib/lxc/dock0/config:
# Template used to create this container: /usr/share/lxc/templates/lxc-debian # Parameters passed to the template: # Template script checksum (SHA-1): 2ad4d9cfe8988ae453172bd4fe3b06cf91756168 # For additional config options, please look at lxc.container.conf(5) # Uncomment the following line to support nesting containers: #lxc.include = /usr/share/lxc/config/nesting.conf # (Be aware this has security implications) lxc.network.type = veth lxc.network.hwaddr = 00:16:3e:af:12:18 lxc.network.link = br0 lxc.network.name = eth0 lxc.network.flags = up lxc.start.auto = 1 lxc.start.delay = 2 lxc.rootfs = /var/lib/lxc/dock0/rootfs lxc.rootfs.backend = dir # Common configuration lxc.include = /usr/share/lxc/config/debian.common.conf # Container specific configuration lxc.tty = 4 lxc.utsname = test lxc.arch = amd64
Внесите следующие изменения в конфигурационный файл:
Раскомментируйте данную строку, она убирает ограничения AppArmor и capabilities:
lxc.include = /usr/share/lxc/config/nesting.conf
Добавьте следующие строки:
lxc.network.ipv4 = 172.16.1.253/24 172.16.1.255 lxc.network.ipv4.gateway = 172.16.1.1 lxc.mount.auto = cgroup-full:rw
Первые две строки необязательны — это настройка сети. Третья монтирует с хост-машины директорию с cgroups
Запустите контейнер:
root@sol:~# lxc-start -n dock0
Установите и протестируйте работу Docker:
Установка:
root@dock0:~# apt-get update root@dock0:~# apt-get install apt-transport-https curl gnupg root@dock0:~# echo deb https://download.docker.com/linux/debian jessie stable >> /etc/apt/sources.list.d/docker.list root@dock0:~# curl -fsSL https://download.docker.com/linux/debian/gpg | apt-key add - root@dock0:~# apt-get update root@dock0:~# apt-get install docker-ce
Проверка работоспособности:
root@dock0:~# docker run -d --rm postgres:9.6 Unable to find image 'postgres:9.6' locally 9.6: Pulling from library/postgres ad74af05f5a2: Pull complete 8996b4a29b2b: Pull complete c03b4ca2b603: Pull complete 3243337e6ae1: Pull complete ce0a50b46ced: Pull complete 97177a7c64ab: Pull complete b5fb28a05da6: Pull complete ae499890cb44: Pull complete d2c07cb2c5c1: Pull complete 92eecd72c92a: Pull complete 56de99a4d80f: Pull complete 607901f31580: Pull complete Digest: sha256:b8bf3cb0e7d660964e92b57fda1e8571eff9c852426c948f7a0871ad8e8faa62 Status: Downloaded newer image for postgres:9.6 eba9357d95fb675b74580324124a375c130fcb855098a42c6d92884e1238ee58 root@dock0:~# docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES eba9357d95fb postgres:9.6 "docker-entrypoint..." 5 seconds ago Up 3 seconds 5432/tcp eloquent_volhard root@test:~#
Готово! LXC никак не влияет на функционал контейнеров Docker они полностью аналогичны установленным на bare metal.