Настройка Docker Swarm в LXC контейнерах.

Предисловие

Docker Swarm — это простой кластер. Для его создания нужно несколько машин. Чтобы развернуть кластер на локалке, нужно сначала запустить виртуальные машины, а в них настроить кластер. Для этой задачи хорошо подойдут LXC контейнеры. LXC контейнеры в отличии от VirtualBox не будут требовать резервирования оперативной памяти. Также LXC выполняется на том же ядре, что и хост, это ускорит работу Docker Swarm.

Инструкция по запуску Docker Swarm на локальной машине

Для запуска Docker swarm на локальной машине необходимо создать два и более LXC контейнера, установить туда Docker и иницировать Swarm. Контейнеры с докер должны работать в привелигированном режиме.

Перед тем как настривать Docker Swarm в LXC установите:

  1. новое ядро 5.3
  2. программу LXC

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

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

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

_x000D_USE_LXC_BRIDGE="true"_x000D_LXC_BRIDGE="lxcbr0"_x000D_LXC_ADDR="172.30.0.1"_x000D_LXC_NETMASK="255.255.255.0"_x000D_LXC_NETWORK="172.30.0.0/24"_x000D_LXC_DHCP_RANGE="172.30.0.2,172.30.0.254"_x000D_LXC_DHCP_MAX="253"_x000D_LXC_DHCP_CONFILE=/etc/lxc/dnsmasq.conf_x000D_#LXC_DOMAIN="lxc"

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

_x000D_port=53_x000D_listen-address=172.30.0.1_x000D_resolv-file=/etc/resolv.conf_x000D_domain-needed

А также в /etc/hosts пропишите IP адреса контейнеров

_x000D_172.30.0.20 docker0_x000D_172.30.0.21 docker1

Установка драйвера br_netfilter

На хост машине сделайте:

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

Установка контейнера docker0

Все команды надо делать по рутом.

1) В файле /etc/lxc/default.conf закоментируйте строчки:

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

2) Установитк два контейнера:

_x000D_lxc-create -t download -n docker0 -- --dist ubuntu --release focal --arch amd64_x000D_lxc-create -t download -n docker1 -- --dist ubuntu --release focal --arch amd64

3) Раскоментируйте обратнр строчки

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

4) Внесите изменения в файл /var/lib/lxc/docker0/config

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

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

Это позволит создать Nested контейнер — возможность запустить контейнер в контейнере (вложенные контейнеры).

Добавьте в файл следующие строки

_x000D_lxc.net.0.ipv4.address = 172.30.0.20/24_x000D_lxc.net.0.ipv4.gateway = 172.30.0.1_x000D_lxc.mount.auto = cgroup-full:rw_x000D_lxc.apparmor.profile = unconfined_x000D_lxc.cgroup.devices.allow = a_x000D_lxc.cap.drop =

В файле /var/lib/lxc/docker0/rootfs/etc/netplan/10-lxc.yaml укажите:

_x000D_network:_x000D_ version: 2_x000D_ ethernets:_x000D_ eth0:_x000D_ dhcp4: no_x000D_ dhcp6: no_x000D_ addresses: [172.30.0.20/24]_x000D_ gateway4: 172.30.0.1_x000D_ nameservers:_x000D_ addresses: [172.30.0.1]

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

_x000D_rm /var/lib/lxc/docker0/rootfs/etc/resolv.conf_x000D_nano /var/lib/lxc/docker0/rootfs/etc/resolv.conf

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

_x000D_nameserver 172.30.0.1

Достаточно указать ДНС 172.30.0.1. На хост машине запускается dnsmasq на этом адресе и является проксирующим ДНС. Также он резолвит все домены из /etc/hosts. Поэтому при настройке сети на хосте нужно было в /etc/hosts прописать IP адреса контейнеров docker0 и docker1.

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

_x000D_mkdir /var/lib/lxc/docker0/rootfs/root/.ssh_x000D_chmod 700 /var/lib/lxc/docker0/rootfs/root/.ssh_x000D_cat /home/user/.ssh/id_rsa.pub >> /var/lib/lxc/docker0/rootfs/root/.ssh/authorized_keys_x000D_chmod 600 /var/lib/lxc/docker0/rootfs/root/.ssh/authorized_keys_x000D__x000D_mkdir /var/lib/lxc/docker0/rootfs/home/ubuntu/.ssh_x000D_chmod 700 /var/lib/lxc/docker0/rootfs/home/ubuntu/.ssh_x000D_cat /home/user/.ssh/id_rsa.pub >> /var/lib/lxc/docker0/rootfs/home/ubuntu/.ssh/authorized_keys_x000D_chmod 600 /var/lib/lxc/docker0/rootfs/home/ubuntu/.ssh/authorized_keys_x000D_chown -R 101000:101000 /var/lib/lxc/docker0/rootfs/home/ubuntu/.ssh

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

_x000D_lxc-start docker0_x000D_lxc-attach docker0

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

_x000D_apt update_x000D_apt install aptitude mc nano htop iftop bwm-ng iperf iperf3 iotop tmux screen openntpd sshfs net-tools dnsutils bind9-utils

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

_x000D_en_US.UTF-8 UTF-8_x000D_ru_RU.UTF-8 UTF-8

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

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

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

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

_x000D_locale-gen

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

_x000D_aptitude install openssh-server

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

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

_x000D_exit

Настройка конфига ssh

Для упрощения подключения к контейнерам, рекомендуется в локальной домашней папке в файле ~/.ssh/config прописать параметры docker0 и docker1

_x000D_Host *_x000D_ Protocol 2_x000D_ KeepAlive yes_x000D_ TCPKeepAlive yes_x000D_ ServerAliveInterval 5_x000D_ ServerAliveCountMax 100_x000D_ Compression no_x000D_ #CompressionLevel 9_x000D_ #ForwardX11 yes_x000D_ UseRoaming no_x000D__x000D_Host docker0_x000D_ Hostname 172.30.0.20_x000D_ User ubuntu_x000D_ Port 22_x000D__x000D_Host docker1_x000D_ Hostname 172.30.0.21_x000D_ User ubuntu_x000D_ Port 22

После внесенных изменений подключаться можно будет по командам:

_x000D_ssh docker0_x000D_ssh docker1

или:

_x000D_ssh root@docker0_x000D_ssh root@docker1

Настройка контейнера docker0

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

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

_x000D_ssh docker0

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

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

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

_x000D_SystemMaxUse=1G

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

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

_x000D_systemctl daemon-reload

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

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

_x000D_groupadd -r wheel_x000D_usermod -a -G wheel ubuntu

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

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

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

4) Добавьте группу www

_x000D_groupadd -g 800 -r www_x000D_useradd -u 800 -g 800 -r www_x000D_mkdir /home/www_x000D_chown www:www /home/www_x000D_usermod -d /home/www www_x000D_usermod -s /bin/bash www

5) Установите Docker

_x000D_curl -sSL https://get.docker.com | sh_x000D_systemctl enable docker_x000D_systemctl start docker_x000D_apt-get install docker-compose

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

_x000D_{_x000D_ "log-driver": "json-file",_x000D_ "log-opts": {_x000D_ "max-size": "10m",_x000D_ "max-file": "1"_x000D_ }_x000D_}

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

_x000D_systemctl stop apparmor_x000D_systemctl disable apparmor

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

_x000D_docker ps

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

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

_x000D_usermod -a -G docker ubuntu_x000D_usermod -a -G docker www

Установка контейнера docker1

Зайдите по рут на хост машине. Остановите контейнер docker0.

_x000D_lxc-stop docker0

Скопируйте docker0 в docker1

_x000D_mkdir /var/lib/lxc/docker1_x000D_cp -rfpH /var/lib/lxc/docker0/* /var/lib/lxc/docker1

Поменяйте rootfs, hostname, IP и MAC адрес в файле /var/lib/lxc/docker1/config

_x000D_lxc.rootfs.path = dir:/var/lib/lxc/docker1/rootfs_x000D_lxc.uts.name = docker1_x000D_lxc.net.0.hwaddr = 00:16:3e:c5:02:e9_x000D_lxc.net.0.ipv4.address = 172.30.0.21/24

Также в файле /var/lib/lxc/docker1/rootfs/etc/netplan/10-lxc.yaml

_x000D_addresses: [172.30.0.21/24]

В файле /var/lib/lxc/docker1/rootfs/etc/hosts

_x000D_127.0.1.1 docker1_x000D_127.0.0.1 localhost_x000D_::1 localhost ip6-localhost ip6-loopback_x000D_ff02::1 ip6-allnodes_x000D_ff02::2 ip6-allrouters

В файле /var/lib/lxc/docker1/rootfs/etc/hostname

_x000D_docker1

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

1) Запустите оба контейнера:

_x000D_lxc-start docker0_x000D_lxc-start docker1

Подключитесь из двух терминалов к контейнерам:

_x000D_ssh docker0_x000D_ssh docker1

2) На docker0 создайте кластер

_x000D_docker swarm init --advertise-addr 172.30.0.20

3) Эта команда выдаст токен. Искользуйте его, чтобы на docker1 подключитесь к кластеру

_x000D_docker swarm join --token TOKEN 172.30.0.20:2377

Кластер создан. Если вы забыли Token, то введите команду docker swarm join-token manager на primary node

Проверка работы кластера

Создайте файл compose.yaml

_x000D_version: "3.3"_x000D__x000D_services:_x000D_    nginx:_x000D_        image: nginx:latest_x000D_        labels:_x000D_            name: nginx_x000D_            version: 1.0_x000D_        deploy:_x000D_            replicas: 1_x000D_ endpoint_mode: dnsrr_x000D_            update_config:_x000D_                parallelism: 1_x000D_                failure_action: rollback_x000D_                delay: 5s_x000D_            restart_policy:_x000D_                condition: "on-failure"_x000D_                delay: 10s_x000D_                window: 120s_x000D_            placement:_x000D_                constraints:_x000D_                    - "node.hostname == docker1"_x000D_        ports:_x000D_ - target: 80_x000D_  published: 80_x000D_  protocol: tcp_x000D_  mode: host_x000D_        networks:_x000D_            - backend_x000D_        logging:_x000D_            driver: journald_x000D_            _x000D_networks:_x000D__x000D_    backend:_x000D_        driver: overlay_x000D_        attachable: true

Запустите сервис

_x000D_docker stack deploy --compose-file compose.yaml dev

Подождите некоторое время и проверьте запустился ли контейнер на хосте docker1 командой docker ps

Если он не запускается, узнайте ошибку

_x000D_docker service ps --no-trunc dev_nginx

Обратите внимание, что указан параметр mode: host в секции проброса 80 порта. Это означает, что нужно по http обращаться к серверу docker1, т.к. на нем должен запуститься контейнер.

Исправление ошибок при запуске Docker Swarm

1) Если Docker swarm в LXC выдает ошибку:

_x000D_WARNING: No swap limit support_x000D_WARNING: bridge-nf-call-iptables is disabled_x000D_WARNING: bridge-nf-call-ip6tables is disabled

или

_x000D_Failed creating ingress network: error creating external connectivity network: cannot restrict inter-container communication: please ensure that br_netfilter kernel module is loaded

То это означает, что у вас установлено старое ядро. Обновите ядро на хост машине согласно инструкции .

И не загружен модуль ядра br_netfilter. Его нужно загрузить командой:

_x000D_modprobe br_netfilter_x000D_modprobe overlay

Или прописать его в автозагрузку (об этом ниже в инструкции).

Более подробно об этой ошибке:
https://github.com/lxc/lxd/issues/5193#issuecomment-431693318
https://bugs.launchpad.net/ubuntu/+source/docker.io/+bug/1618283

2) Может возникнуть эта ошибка;

_x000D_Starting container failed: container: endpoint create on GW Network failed: failed to create endpoint gateway_00555448fe8e on network docker_gwbridge: network does not exist

и syslog будет выдавать следующее

_x000D_$ tail -n 20 /var/log/syslog_x000D__x000D_[ 3419.850480] br0: port 2(veth0) entered blocking state_x000D_[ 3419.850486] br0: port 2(veth0) entered forwarding state_x000D_[ 3419.943752] br0: port 2(veth0) entered disabled state_x000D__x000D_Oct 16 08:38:25 docker0 kernel: [ 3285.847755] veth0: renamed from veth945e509_x000D_Oct 16 08:38:25 docker0 kernel: [ 3285.964009] eth0: renamed from veth6d61c08_x000D_Oct 16 08:38:25 docker0 kernel: [ 3286.135717] br0: port 3(veth1) entered disabled state_x000D_Oct 16 08:38:25 docker0 kernel: [ 3286.136040] br0: port 3(veth1) entered blocking state_x000D_Oct 16 08:38:25 docker0 kernel: [ 3286.136043] br0: port 3(veth1) entered forwarding state_x000D_

Это связано тоже с версией ядра и тем, что модуль br_netfilter не загружен.

3) Не пингуются контейнеры между собой и не пробрасывается порт. Это происходит по одной причине. Почему то виртуальные IP адреса и ingress в докер под LXC не работают. Решается это просто. Нужно включить dnsrr (DNS Round Robin) в секции для каждого сервиса и использовать mode: host при проброске портов.

DNS Round Robin — это способ адресации к сервисам. В докере существуют два способа через виртуальные ip адреса (по умолчанию) и через DNS Round Robin. При использовании виртуальных IP адресов, для каждого сервиса создается IP адрес и все контейнеры его получают при поиске сервиса. При отправке пакета на этот IP адрес, докер сам разруливает к какому контейнеру отправить запрос. Получается виртуальный IP адрес — это промежуточный IP адрес. При использовании DNS Round Robin будут другие контейнеры будут получать прямые IP адреса контейнеров.

mode: host отвечает за проброс портов. Существует два варианта ingress (по умолчанию) и host. При использовании ingress обращение на порт любого серверу в Docker кластере будет переадресовано в контейнер, где указан проброс этого порта. Если поставить mode: host, то проброс потров будет работать только на том сервер, где запустился этот контейнер.