Docker в LXC без Swarm

Иногда требуется запустить docker в lxc, например, для установки Docker Swarm, или для установки Kubernetos. LXC позволяет запускать Docker внутри контейнера. Рекомендуется использовать хост машину Ubuntu 18.04.

Перейдите по ссылке, если вас интересует обычная настройка Docker Swarm.

Перейдите по ссылке, если вас интересует настройка Docker Swarm в LXC.

Прежде чем начать:

  1. выполните базовую настройку Ubuntu
  2. обновите ядро системы до версии 5.3
  3. установите LXC на Ubuntu по инструкции

На хост машине добавьте драйвера br_netfilter в автозагрузку:

echo overlay >> /etc/modules-load.d/docker.conf
echo br_netfilter >> /etc/modules-load.d/docker.conf

Для того, чтобы запустить Docker в LXC нужно выполнить следующие действия:

  1. запустить контейнер в privileged mode
  2. включить мод lxc nested containers

Запуск контейнера в privileged mode

Privileged mode - это привелигированный режим, запуск контейнера от рута. 

Перед установкой контейнера, нужно закоментировать строки в файле /etc/lxc/default.conf. Комментировать строки, нужно только при установке контейнера, который вы хотите, чтобы он работал в привелигированном режиме.

#lxc.idmap = u 0 100000 65536
#lxc.idmap = g 0 100000 65536

Это позволит установить контейнер под рутом.

Выполните команду установки контейнера для Centos 7:

lxc-create -t download -n docker0 -- --dist centos --release 7 --arch amd64

Для Ubuntu 20.04

lxc-create -t download -n docker0 -- --dist ubuntu --release focal --arch amd64

Затем нужно обязательно раскоментировать эти строки обратно в файле /etc/lxc/default.conf

lxc.idmap = u 0 100000 65536
lxc.idmap = g 0 100000 65536

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

Рекомендуется для LXC использовать сеть 172.30.0.1/24. Более подробный список указан в списке сетей.

В файле /etc/default/lxc-net пропишите

USE_LXC_BRIDGE="true"
LXC_BRIDGE="lxcbr0"
LXC_ADDR="172.30.0.1"
LXC_NETMASK="255.255.255.0"
LXC_NETWORK="172.30.0.0/24"
LXC_DHCP_RANGE="172.30.0.2,172.30.0.254"
LXC_DHCP_MAX="253"
LXC_DHCP_CONFILE=/etc/lxc/dnsmasq.conf
#LXC_DOMAIN="lxc"

Создайте файл /etc/lxc/dnsmasq.conf и пропишите в нем:

port=53
listen-address=172.30.0.1
resolv-file=/etc/resolv.conf
domain-needed

LXC nested containers

Nested контейнер — это возможность запустить контейнер в контейнере (вложенные контейнеры). Чтобы включить данную опцию, нужно внести изменения в конфиг LXC контейнера.

Опция включается в файле конфига контейнера /var/lib/lxc/<название контейнера>/config

Раскоментируйте строчку:

#lxc.include = /usr/share/lxc/config/nesting.conf

Также вам понадобится прописать параметры для монтирования файловой системы cgroup и отключения AppArmor в контейнере

lxc.mount.auto = cgroup-full:rw
lxc.apparmor.profile = unconfined
lxc.cgroup.devices.allow = a
lxc.cap.drop =

Пропишите параметры сети:

lxc.net.0.ipv4.address = 172.30.0.20/24
lxc.net.0.ipv4.gateway = 172.30.0.1

Для Centos7 в файле /var/lib/lxc/docker0/rootfs/etc/sysconfig/network-scripts/ifcfg-eth0 укажите:

DEVICE=eth0
ONBOOT=yes

IPADDR=172.30.0.20
NETMASK=255.255.255.0
GATEWAY=172.30.0.1
DNS1=172.30.0.1

#BOOTPROTO=dhcp
HOSTNAME=test-centos
NM_CONTROLLED=no
TYPE=Ethernet
MTU=
DHCP_HOSTNAME=`hostname`

Для Ubuntu 20.04 в файле /var/lib/lxc/docker0/rootfs/etc/netplan/10-lxc.yaml укажите:

network:
  version: 2
  ethernets:
    eth0:
      dhcp4: no
      dhcp6: no
      addresses: [172.30.0.20/24]
      gateway4: 172.30.0.1
      nameservers:
        addresses: [172.30.0.1]

Пересоздайте resolv.conf

rm /var/lib/lxc/docker0/rootfs/etc/resolv.conf
nano /var/lib/lxc/docker0/rootfs/etc/resolv.conf

Укажите в нем новый адреса DNS серверов:

nameserver 10.0.3.1

Скопируйте ssh ключ. Вместо /home/user укажите вашу домашнюю папку.

mkdir /var/lib/lxc/docker0/rootfs/root/.ssh
chmod 700 /var/lib/lxc/docker0/rootfs/root/.ssh
cat /home/alfa/.ssh/id_rsa.pub >> /var/lib/lxc/docker0/rootfs/root/.ssh/authorized_keys
chmod 600 /var/lib/lxc/docker0/rootfs/root/.ssh/authorized_keys

mkdir /var/lib/lxc/docker0/rootfs/home/ubuntu/.ssh
chmod 700 /var/lib/lxc/docker0/rootfs/home/ubuntu/.ssh
cat /home/alfa/.ssh/id_rsa.pub >> /var/lib/lxc/docker0/rootfs/home/ubuntu/.ssh/authorized_keys
chmod 600 /var/lib/lxc/docker0/rootfs/home/ubuntu/.ssh/authorized_keys
chown -R 1000:1000 /var/lib/lxc/docker0/rootfs/home/ubuntu/.ssh

Настройка контейнера Ubuntu 20.04

1) Запустите контейнер и подключитесь к нему:

lxc-start docker0
lxc-attach docker0

2) Установите программы:

apt update
apt install aptitude mc nano htop iftop bwm-ng iperf iperf3 iotop tmux screen openntpd sshfs net-tools

3) Отключите apparmor в LXC контейнере

systemctl stop apparmor
systemctl disable apparmor

4) Установите локаль. Раскоментируйте строки в файле /etc/locale.gen

en_US.UTF-8 UTF-8
ru_RU.UTF-8 UTF-8

Пропишите локаль на уровне системы:

nano /etc/profile.d/0.locale.sh

export PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin:/sbin:/bin"
export LANG="en_US.UTF-8"
export LANGUAGE="en_US:en"
export LC_CTYPE="en_US.UTF-8"
export LC_NUMERIC="en_US.UTF-8"
export LC_TIME="en_US.UTF-8"
export LC_COLLATE="en_US.UTF-8"
export LC_MONETARY="en_US.UTF-8"
export LC_MESSAGES="en_US.UTF-8"
export LC_PAPER="en_US.UTF-8"
export LC_NAME="en_US.UTF-8"
export LC_ADDRESS="en_US.UTF-8"
export LC_TELEPHONE="en_US.UTF-8"
export LC_MEASUREMENT="en_US.UTF-8"
export LC_IDENTIFICATION="en_US.UTF-8"
export EDITOR=nano

Пересоздайте локаль:

locale-gen

5) Установите ssh сервер:

aptitude install openssh-server

6) Ограничьте размер логов systemd

Пропишите в /etc/systemd/journald.conf строчку:

SystemMaxUse=1G

Это строчка ограничивает максимальный размер логов в 1 гигабайт. 

Перезагрузите конфигурацию systemd:

systemctl daemon-reload

7) Сделайте sudo su -l без ввода пароля

Добавьте группу wheel

groupadd -r wheel
usermod -a -G wheel ubuntu

в /etc/sudoers добавьте строчку

%wheel ALL=(ALL:ALL) NOPASSWD: ALL

Все пользователи, которые находятся в группе wheel будут иметь возможность выполнять команды рут без пароля

8) Далее нужно переподключиться к контейнеру через ssh.

Выйдите из контейнера:

exit

9) Подключитесь к контейнеру через ssh

Откройте терминал под текущим пользователем (не рут) и подключитесь к системе:

ssh root@172.30.0.20

Если сертификат установлен верно, то должно подключиться без пароля.

Установка Docker на Ubuntu 20.04

Установите Docker

curl -sSL https://get.docker.com | sh
systemctl enable docker
systemctl start docker
apt-get install docker-compose

Для хранения логов рекомендуется journald. В файле /etc/docker/daemon.json пропишите:

{
  "log-driver": "journald",
  "log-opts": {
    "labels":"com.docker.swarm.service.name"
  }
}

Проверьте запущен ли докер:

docker ps

Если выдает ошибку, значит проблема в файле /etc/docker/daemon.json. Пересоздайте его. Возможно в нем скопировались невидимые символы.

Добавьте в группу docker пользователя ubuntu чтобы он мог управлять docker

usermod -a -G docker ubuntu

Установка Docker на Centos 7

Запустите контейнер и войдите в него:

lxc-start docker0
lxc-attach docker0

Установите docker и включите автозапуск:

yum install -y yum-utils
yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
yum install -y docker-ce docker-ce-cli containerd.io

systemctl enable docker
systemctl start docker

После установки, скачайте тестовый контейнер и убедитесь что он работает:

docker pull alpine
docker run -it -d --name test alpine /bin/sh

Выполните команду docker ps, должно вам выдать:

root@docker0:/# docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
93f0d96197e0        alpine              "/bin/sh"           28 seconds ago      Up 25 seconds                           test

Проверьте работу контейнера:

root@docker0:/# docker exec -it test ping google.com
PING google.com (64.233.164.101): 56 data bytes
64 bytes from 64.233.164.101: seq=0 ttl=42 time=222.140 ms
64 bytes from 64.233.164.101: seq=1 ttl=42 time=140.911 ms
^C
--- google.com ping statistics ---
2 packets transmitted, 2 packets received, 0% packet loss
round-trip min/avg/max = 140.911/181.525/222.140 ms



root@docker0:/# docker exec -it test ifconfig
eth0      Link encap:Ethernet  HWaddr 02:42:AC:11:00:02
          inet addr:172.17.0.2  Bcast:172.17.255.255  Mask:255.255.0.0
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:34 errors:0 dropped:0 overruns:0 frame:0
          TX packets:33 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0
          RX bytes:3728 (3.6 KiB)  TX bytes:3293 (3.2 KiB)

lo        Link encap:Local Loopback
          inet addr:127.0.0.1  Mask:255.0.0.0
          UP LOOPBACK RUNNING  MTU:65536  Metric:1
          RX packets:0 errors:0 dropped:0 overruns:0 frame:0
          TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000
          RX bytes:0 (0.0 B)  TX bytes:0 (0.0 B)

Возникаемые ошибки

Если возникают ошибки:

docker: Error response from daemon: Could not check if 
docker-default AppArmor profile was loaded: open 
/sys/kernel/security/apparmor/profiles: permission denied.
failed to register layer: Error processing tar file(exit status 1): 
remount /, flags: 0x84000: permission denied
docker: Error response from daemon: OCI runtime create failed: 
container_linux.go:345: starting container process caused 
"process_linux.go:430: container init caused \"process_linux.go:396: 
setting cgroup config for procHooks process caused \\\"
failed to write c 10:200 rwm to devices.allow: write /sys/fs/cgroup/devices/docker/27822e65d0ccd42267cd420309f1de3ea2ddfc353d18d9ab9d362b0549b43ed0/devices.allow: 
operation not permitted\\\"\"": unknown.

То это скорее всего связано с AppArmor. Сделайте tail -n 20 /var/log/syslog. Если в логе присутсвуют следующие строки:

Jul 16 23:49:04 alfabook kernel: 
[45843.968279] audit: type=1400 audit(1563299344.593:66): 
apparmor="DENIED" operation="mount" info="failed flags match" 
error=-13 profile="lxc-container-default-cgns" name="/" 
pid=5274 comm="exe" flags="rw, rslave"
Jul 17 00:00:05 alfabook kernel:
[46504.633713] audit: type=1400 audit(1563300005.284:68): 
apparmor="DENIED" operation="mount" info="failed flags match" 
error=-13 profile="lxc-container-default-with-nesting" 
name="/run/docker/runtime-runc/moby/0f36b9a62ec3234ad83b341a67f1a2f16c5db3dc55a6827720b85e30f0ecc547/runc.DcCoEi" 
pid=7011 comm="exe" flags="ro, remount, bind"

Jul 17 00:00:05 alfabook kernel:
[46504.676276] audit: type=1400 audit(1563300005.328:69):
apparmor="DENIED" operation="mount" info="failed flags match"
error=-13 profile="lxc-container-default-with-nesting"
name="/var/lib/docker/overlay2/f40a7cc6f04db2b5cd48ddf8ec82303e07d9a744241250852c4462c12aa58df1/merged/"
pid=7028 comm="runc:[2:INIT]" srcname="/var/lib/docker/overlay2/f40a7cc6f04db2b5cd48ddf8ec82303e07d9a744241250852c4462c12aa58df1/merged/"
flags="rw, rbind"

То, да причина в AppArmor. Обычно в конфиг LXC lxc.apparmor.profile, lxc.mount.auto, lxc.cgroup.devices.allow помогает. Но если не помогло, то выполните следующие команды:

Включите обучение AppArmor

aa-complain /etc/apparmor.d/*

Запустите LXC контейнеры, и попробуйте заново запустить docker. Затем, после выдачи ошибки, перейдите на хост машину и выполните команду aa-logprof. Данная комманда анализирует лог /var/log/syslog и предлагает вам варианты патчинга правил AppArmor. 

Пропатчите конфиг AppArmor и затем выключите обучение:

aa-enforce /etc/apparmor.d/*

Дополнительные ссылки

  1. Установка LXC в Ubuntu 18.04
  2. Установка Docker в Ubuntu 18.04
  3. Установка Docker Swarm на локальной машине
  4. Docker Swarm в LXC
  5. Официальная инструкция установки Docker в Centos 7
  6. Безопасность в LXC
  7. Справка LXC
  8. Описание файла config