Proxmox. Часть 7. Разворачивание кластера Docker Swarm

Продолжим 5 и 6 часть заметок по Proxmox и развернем еще 3 примера разных сервисов. Но в данной части использовать будет не просто Docker, а платформа оркестровки контейнеров Docker Swarm, которая доступна из «коробки», если у вас установлен сам Docker.

Что такое Docker Swarm?

Docker Swarm — это стандартный, легкий и простой оркестратор для docker контейнеров с ограниченными возможностями.

Для дальнейшей работы следует иметь представление о нескольких сущностях Docker Swarm:

  • Node — это виртуальные машины, на которых установлен docker. Есть manager и workers ноды. Manager нода управляет workers нодами. Она получает запросы и распределяют задачи на workers ноды. Workers ноды используются только для выполнения поставленных задач и не могут управлять кластером.
  • Stack — это набор сервисов, которые логически связаны между собой. По сути это набор сервисов, которые мы описываем в обычном docker-compose файле.
  • Service — это сущность из которых состоит stack. Service — описание процесса создания docker контейнеров. Такие же сервисы мы описываем в docker-compose.yml.
  • Task — это контейнер, который docker создал на основе той информации, которую мы указали при описании service. Docker Swarm будет следить за состоянием контейнера и при необходимости его перезапускать или перемещать на другую ноду.

Разворачивание кластера Docker Swarm

Создадим кластер Docker Swarm состоящий из 2-х worker node и 1 manager node. Для этого создадим 3-и виртуальные машины на основе шаблона виртуальной машины 9000:

Manager node
Worker node 1
Worker node 2

Запускаем виртуальные машины. После запуска виртуальных машин проверим их ip:

ip a

Сетевые интерфейсы manager1
Сетевые интерфейсы worker2

Заметим, что каждой виртуальной машине достается один и тот же ip адрес. Помогает сбрасывание сетевой конфигурации на каждой виртуальной машине и последующая перезагрузка:

sudo rm -f /etc/machine-id
sudo systemd-machine-id-setup
sudo rm -f /var/lib/dhcp/dhclient.leases

После выполнения команд и перезагрузки всех трех виртуальных машин проверим получаемые ip адреса, они должны быть уникальные!

На каждой из 3-х машин установим Docker в соответствии с инструкцией из 5-ой части.

На виртуальной машине manager1 (192.168.31.199) инициализируем кластер Docker Swarm:

sudo docker swarm init --advertise-addr 192.168.31.199

Manager node 192.168.31.199

В ответ получаем команду, которую надо выполнить на worker нодах — второй и третьей виртуальной машине, чтобы объединить все три машины в кластер:

docker swarm join --token SWMTKN-1-2jg6olmfwi3yv34f23s60x7918zt53tbvqm7zjz6wf6w38he91-3f7kha8oh34vsafynrew9ntts 192.168.31.199:2377

Эту команду с токеном можно получить командой:

Для worker нод:

docker swarm join-token worker

Для manager нод:

docker swarm join-token manager

Выполняем команду для двух worker нод:

Добавление worker1
Добавление worker2

На первой виртуальной машине (manager1) убедимся, что две другие виртуальные машины подключились к кластеру как worker node:

docker node ls

Ноды кластера

Установка Portainer

Portainer — это инструмент управления контейнерами.

Для управления нашим кластером установим Portainer. Инструкцию находим на https://docs.portainer.io/start/install-ce/server/swarm/linux. Копируем команды и выполним их на manager ноде (manager1). Создаем папку port, в которую скачиваем конфигурацию сервисов portainer-agent-stack.yml:

mkdir ~/port
cd ~/port
curl -L https://downloads.portainer.io/ce2-21/portainer-agent-stack.yml -o portainer-agent-stack.yml

Содержимое portainer-agent-stack.yml на момент написания заметки:

version: '3.2'

services:
  agent:
    image: portainer/agent:2.21.5
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - /var/lib/docker/volumes:/var/lib/docker/volumes
    networks:
      - agent_network
    deploy:
      mode: global
      placement:
        constraints: [node.platform.os == linux]

  portainer:
    image: portainer/portainer-ce:2.21.5
    command: -H tcp://tasks.agent:9001 --tlsskipverify
    ports:
      - "1102:9443"
      - "9000:9000"
      - "8000:8000"
    volumes:
      - portainer_data:/data
    networks:
      - agent_network
    deploy:
      mode: replicated
      replicas: 1
      placement:
        constraints: [node.role == manager]

networks:
  agent_network:
    driver: overlay
    attachable: true

volumes:
  portainer_data:
  1. сервисы находятся в общей сети agent_network типа overlay;
  2. сервис portainer/agent:
  • режим развертывания: Глобальный;
  • имеет доступ к файлам /var/run/docker.sock и /var/lib/docker/volumes;

3. сервис portainer/portainer:

  • работает только на ноде manager1;
  • веб интерфейс доступен по порту 1102;
  • данные сервиса хранятся в томе portainer_data;
  • автоматически подключается ко всем агентам развернутых нодах в кластере;

Запускаем стек:

docker stack deploy -c portainer-agent-stack.yml portainer

Проверим работоспособность сервисов:

docker service ls

Надо обратить внимание, что главная нода manager1 должна в своем имени иметь слово manager. Иначе необходимо будет поправить конфиг portainer-agent-stack.yml:

Откроем web-интерфейс Portainer по адресу главной manager ноды:
https://192.168.31.199:1102

Возможно вы получите следующую ошибку:

Your Portainer instance timed out for security purposes. To re-enable your Portainer instance, you will need to restart Portainer.

Для решения данной проблемы перезапустим stack Portainer:

docker stack rm portainer
docker stack deploy -c portainer-agent-stack.yml portainer

Попробуем снова открыть Portainer:

https://192.168.31.199:1102

Придумываем пароль, в моем случае пароль – nickcodenickcode.

Portainer запущен:

Визуализация нашего кластера:

Разворачивание сервиса dockersamples/visualizer

Развернем сервис dockersamples/visualizer с помощью docker stack deploy со следующими параметрами:

  1. Сервис доступен по порту 1101;
  2. Сервису обеспечен доступ к файлу /var/run/docker.sock;
  3. Работает только на ноде manager1.

Создадим папку для проекта visualizer,

mkdir ~/visualizer
cd ~/visualizer/
nano docker-compose.yml

а в ней файл docker-compose.yml с содержимым:

version: "3.8"
services:
  visualizer:
    image: dockersamples/visualizer:latest
    ports:
      - "1101:8080"
    volumes:
      - "/var/run/docker.sock:/var/run/docker.sock"
    deploy:
      placement:
        constraints:
          - node.hostname == manager1

Развернем стек:

docker stack deploy -c docker-compose.yml visualizer_stack

Проверим, что сервис работает корректно:

docker stack services visualizer_stack

Перейдем по адресу ноды manager1 и порту 1101, указанному в docker-compose.yml http://192.168.31.199:1101/:

Разворачивание сервиса nginxdemos/nginx-hello

Развернем третий сервис — образы nginxdemos/nginx-hello и nginx со следующими параметрами:

1. сервисы находятся в общей сети web_network типа overlay;

2. сервис web:

  • режим развертывания: Глобальный;

3.сервис nginx:

  • работает только на ноде manager1;
  • веб интерфейс доступен по порту 1103;
  • конфигурация Nginx: load balancing. При обращении по адресу http://<ip>:1103 сервис nginx перенаправляет случайно на один из контейнеров сервиса Web вне зависимости от того на какой ноде он функционирует.

Создадим папку для проекта web, а в ней файл docker-compose.yml и файл nginx.conf:

mkdir ~/web
cd ~/web
nano docker-compose.yml
nano nginx.conf

Содержимое конфигурационного файла docker-compose.yml:

version: "3.8"

services:
  web:
    image: nginxdemos/nginx-hello:latest
    networks:
      - web_network
    deploy:
      mode: global

  proxy:
    image: nginx:latest
    ports:
      - "1103:8080"
    networks:
- web_network
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf
    deploy:
      mode: replicated
      replicas: 1
      placement:
        constraints: [node.role == manager]

networks:
  web_network:
    driver: overlay

Содержимое конфигурационного файла nginx.conf:

worker_processes 1;

events {
  worker_connections 1024;
}

http {
  server {
    listen 8080;
    location / {
      proxy_pass http://web:8080;
    }
  }
}

Развернем стек:

docker stack deploy -c docker-compose.yml web_stack

Проверим состояние развернутых сервисов:

docker stack services web_stack

Убедимся, что сеть web_network была создана:

docker network ls

Проверяем работоспособность сервиса по адресу ноды manager1 и порту 1103, указанному в docker-compose.yml http://192.168.31.199:1103/:

При обновлении страницы видим, что Server address меняется в зависимости от того, какая нода отдает нам ответ на наш запрос:

Объединение всех сервисов

В завершении давайте попробуем объединим все наши стеки в один общий стек.

Создадим папку all, а в ней файл docker-compose.yml и nginx.conf:

mkdir ~/all
cd ~/all
nano docker-compose.yml
cp ~/web/nginx.conf ~/all

Содержимое конфигурационного файла docker-compose.yml:

version: "3.8"
services:
  visualizer:
    image: dockersamples/visualizer:latest
    ports:
      - "1101:8080"
    volumes:
      - "/var/run/docker.sock:/var/run/docker.sock"
    deploy:
      placement:
        constraints:
          - node.hostname == manager1

  agent:
    image: portainer/agent:2.21.5
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - /var/lib/docker/volumes:/var/lib/docker/volumes
    networks:
      - agent_network
    deploy:
      mode: global
      placement:
        constraints: [node.platform.os == linux]

  portainer:
    image: portainer/portainer-ce:2.21.5
    command: -H tcp://tasks.agent:9001 --tlsskipverify
    ports:
      - "1102:9443"
      - "9000:9000"
      - "8000:8000"
    volumes:
      - portainer_data:/data
    networks:
      - agent_network
    deploy:
      mode: replicated
      replicas: 1
      placement:
        constraints: [node.role == manager]

  web:
    image: nginxdemos/nginx-hello:latest
    networks:
      - web_network
    deploy:
      mode: global

  proxy:
    image: nginx:latest
    ports:
      - "1103:8080"
    networks:
      - web_network
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf
    deploy:
      mode: replicated
      replicas: 1
      placement:
        constraints: [node.role == manager]


networks:
  agent_network:
    driver: overlay
    attachable: true
  web_network:
    driver: overlay


volumes:
  portainer_data:

Содержимое конфигурационного файла nginx.conf:

worker_processes 1;

events {
worker_connections 1024;
}

http {
server {
listen 8080;
location / {
proxy_pass http://web:8080;
}
}
}

Развернем стек:

docker stack deploy -c docker-compose.yml all_stack

Проверим состояние развернутых сервисов:

docker stack services all_stack

Проверим наличие сетей:

docker network ls

И проверим работоспособность сервисов по адресам http://192.168.31.199:1101/, https://192.168.31.199:1102/, http://192.168.31.199:1103/.

Все сервисы работают в одном стеке так же, как и по отдельности.