Пятая часть цикла, посвященного контейнеризации MEAN-stack приложений, в которой мы развернем MongoDB Replica Set из трех узлов в Docker Swarm.
Содержание цикла
- Часть 1 - Dockerfile, образы, контейнеры
 - Часть 2 - Docker Compose
 - Часть 3 - Traefik reverse proxy
 - Часть 4 - Docker Swarm
 - Часть 5 - Mongo Replica Set в Docker Swarm
 
Введение
В предыдущей части мы задеплоили приложение в Docker Swarm. Однако сервис базы данных у нас не был реплицирован. В данной статье исправим это досадное недоразумение и развернем 3 реплики базы данных на нодах нашего Docker-роя. Но с репликацией mongo есть небольшая сложность - нельзя просто указать в compose-файле replicas: 3. В случае падения одной из рабочих нод докер перенесет сервис mongo на ноду, где такой сервис уже есть. И эти сервисы начнут работать с общей data/db. Но это недопустимо. Поэтому предстоит провести небольшую настройку.
Маркируем ноды
Каждой ноде укажем label с требуемым id реплики mongo. Для этого на менеджере выполним следующие команды:
docker node update --label-add mongo.replica=0 vm0
docker node update --label-add mongo.replica=1 vm1
docker node update --label-add mongo.replica=2 vm2
Настроим сервисы
В compose-файле вместо сервиса db создадим 3 других:
services:
  # ...
  mongo0:
    image: mongo
    command:
      - "--smallfiles"
      - "--replSet"
      - "rs0"
    deploy:
      mode: global
      restart_policy:
        condition: on-failure
      placement:
        constraints:
          - node.labels.mongo.replica == 0
      labels:
        - traefik.enable=false
    volumes:
      - mongodata0:/data/db
    networks:
      - db-net
  mongo1:
    image: mongo
    command:
      - "--smallfiles"
      - "--replSet"
      - "rs0"
    deploy:
      mode: global
      restart_policy:
        condition: on-failure
      placement:
        constraints:
          - node.labels.mongo.replica == 1
      labels:
        - traefik.enable=false
    volumes:
      - mongodata1:/data/db
    networks:
      - db-net
  mongo2:
    image: mongo
    command:
      - "--smallfiles"
      - "--replSet"
      - "rs0"
    deploy:
      mode: global
      restart_policy:
        condition: on-failure
      placement:
        constraints:
          - node.labels.mongo.replica == 2
      labels:
        - traefik.enable=false
    volumes:
      - mongodata2:/data/db
    networks:
      - db-net
В командах указываем запускать mongo как replica set с именем rs0. В deploy указываем глобальный режим mode: global и задаем constraints-правило node.labels.mongo.replica == 0, т.е. запускать mongo0 только на ноде с меткой mongo.replica=0.
У сервера изменим путь для подключения mongoose к базе данных:
  server:
    ...
    environment:
      - "db:uri=mongodb://mongo0,mongo1,mongo2/docker?replicaSet=rs0"
В конфиге других сервисов ничего не меняется (см. предыдущую часть цикла).
Volumes
Для хранения баз данных создадим следующие volumes:
volumes:
  mongodata0:
  mongodata1:
  mongodata2:
и используем их в сервисах mongo:
  mongo0:
    ...
    volumes:
      - mongodata0:/data/db
Deploy
docker stack deploy -c docker-stack.mongo-rs.yml meanstack
Инициируем Replica Set в самой mongo
docker exec -it <mongo_container_id> sh
> mongo
> rs.initiate( {
   _id : "rs0",
   members: [
      { _id: 0, host: "mongo0:27017" },
      { _id: 1, host: "mongo1:27017" },
      { _id: 2, host: "mongo2:27017" }
   ]
})
Результат
В браузере по адресу http://visualizer.docker-example.local/ мы должны увидеть что-то похожее:
