В этой статье я описал создание в AWS состоящего из трех нод кластера Docker Swarm и подключение к нему общего для всех нод реплицируемого тома GlusterFS.
Введение
Режим Docker Swarm используется для создания кластера Docker-хостов. При этом, если контейнер A с подключенным к нему именованным томом voldata запущен на node1, все изменения voldata будут сохранены локально на node1. Если контейнер A будет выключен и случится так, что он снова запустится, скажем, на node3, при подключении тома voldata это хранилище окажется пустым и не будет содержать изменений, сделанных на node1.
Как это обойти?
Один из путей решения проблемы — использовать GlusterFS для репликации томов, что позволит сделать данные доступными для всех нод в любое время. При этом для каждого Docker-хоста именованные тома останутся локальными.
Для выполнения этого упражнения я использовал три AWS EC2-инстанса, к каждому из которых было подключено по одному EBS-тому.
Подготовка серверов
В качестве ОС будем использовать Ubuntu 16.04.
Сначала пропишем имена нод в /etc/hosts:
XX.XX.XX.XX node1
XX.XX.XX.XX node2
XX.XX.XX.XX node3
Затем обновим систему:
$ sudo apt update
$ sudo apt upgrade
Перезагрузимся и запустим установку необходимых пакетов на всех нодах:
$ sudo apt install -y docker.io
$ sudo apt install -y glusterfs-server
Запустим сервисы:
$ sudo systemctl start glusterfs-server
$ sudo systemctl start docker
Создадим хранилище GlusterFS:
$ sudo mkdir -p /gluster/data /swarm/volumes
Настройка GlusterFS
На всех нодах подготовим файловую систему для хранилища Gluster:
$ sudo mkfs.xfs /dev/xvdb
$ sudo mount /dev/xvdb /gluster/data/
На node1:
$ sudo gluster peer probe node2
peer probe: success.
$ sudo gluster peer probe node3
peer probe: success.
Создадим реплицируемый том:
$ sudo gluster volume create swarm-vols replica 3 node1:/gluster/data node2:/gluster/data node3:/gluster/data force
volume create: swarm-vols: success: please start the volume to access data
Разрешим монтирование только с localhost:
$ sudo gluster volume set swarm-vols auth.allow 127.0.0.1
volume set: success
Запустим том:
$ sudo gluster volume start swarm-vols
volume start: swarm-vols: success
Затем подмонтируем его на каждой ноде Gluster:
$ sudo mount.glusterfs localhost:/swarm-vols /swarm/volumes
Настройка Docker swarm
Наша цель: создать 1 управляющий и 2 рабочих узла.
$ sudo docker swarm init
Swarm initialized: current node (82f5ud4z97q7q74bz9ycwclnd) is now a manager.
To add a worker to this swarm, run the following command:
docker swarm join --token SWMTKN-1-697xeeiei6wsnsr29ult7num899o5febad143ellqx7mt8avwn-1m7wlh59vunohq45x3g075r2h 172.31.24.234:2377
To add a manager to this swarm, run 'docker swarm join-token manager' and follow the instructions.
Получим токен для рабочих узлов:
$ sudo docker swarm join-token worker
To add a worker to this swarm, run the following command:
```docker
docker swarm join --token SWMTKN-1-697xeeiei6wsnsr29ult7num899o5febad143ellqx7mt8avwn-1m7wlh59vunohq45x3g075r2h 172.31.24.234:2377
На обоих рабочих узлах выполним:
$ sudo docker swarm join --token SWMTKN-1-697xeeiei6wsnsr29ult7num899o5febad143ellqx7mt8avwn-1m7wlh59vunohq45x3g075r2h 172.31.24.234:2377
This node joined a swarm as a worker.
Проверим swarm-кластер:
$ sudo docker node ls
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS
6he3dgbanee20h7lul705q196 ip-172-31-27-191 Ready Active
82f5ud4z97q7q74bz9ycwclnd * ip-172-31-24-234 Ready Active Leader
c7daeowfoyfua2hy0ueiznbjo ip-172-31-26-52 Ready Active
Тестирование
Будем действовать следующим образом: создадим метки для node1 и node3, создадим контейнер на node1, выключим его, создадим снова на node3, подмонтировав такие же тома, и посмотрим, остались ли в нашем хранилище файлы, созданные во время работы контейнера на node1.
Поставим метки на узлы swarm:
$ sudo docker node update --label-add nodename=node1 ip-172-31-24-234
ip-172-31-24-234
$ sudo docker node update --label-add nodename=node3 ip-172-31-26-52
ip-172-31-26-52
Проверим метки:
$ sudo docker node inspect --pretty ip-172-31-26-52
ID: c7daeowfoyfua2hy0ueiznbjo
Labels:
- nodename = node3
Hostname: ip-172-31-26-52
Joined at: 2017-01-06 22:44:17.323236832 +0000 utc
Status:
State: Ready
Availability: Active
Platform:
Operating System: linux
Architecture: x86_64
Resources:
CPUs: 1
Memory: 1.952 GiB
Plugins:
Network: bridge, host, null, overlay
Volume: local
Engine Version: 1.12.1
Создадим на node1 сервис Docker, который будет использоваться для тестирования работы с файлами в общем хранилище:
$ sudo docker service create --name testcon --constraint 'node.labels.nodename == node1' --mount type=bind,source=/swarm/volumes/testvol,target=/mnt/testvol /bin/touch /mnt/testvol/testfile1.txt
duvqo3btdrrlwf61g3bu5uaom
Проверим сервис:
$ sudo docker service ls
ID NAME REPLICAS IMAGE COMMAND
duvqo3btdrrl testcon 0/1 busybox /bin/bash
Убедимся, что он запущен на node1:
$ sudo docker service ps testcon
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR
6nw6sm8sak512x24bty7fwxwz testcon.1 ubuntu:latest ip-172-31-24-234 Ready Ready 1 seconds ago
6ctzew4b3rmpkf4barkp1idhx \_ testcon.1 ubuntu:latest ip-172-31-24-234 Shutdown Complete 1 seconds ago
Также проверим подмонтированные тома:
$ sudo docker inspect testcon
[
{
"ID": "8lnpmwcv56xwmwavu3gc2aay8",
"Version": {
"Index": 26
},
"CreatedAt": "2017-01-06T23:03:01.93363267Z",
"UpdatedAt": "2017-01-06T23:03:01.935557744Z",
"Spec": {
"ContainerSpec": {
"Image": "busybox",
"Args": [
"/bin/bash"
],
"Mounts": [
{
"Type": "bind",
"Source": "/swarm/volumes/testvol",
"Target": "/mnt/testvol"
}
]
},
"Resources": {
"Limits": {},
"Reservations": {}
},
"RestartPolicy": {
"Condition": "any",
"MaxAttempts": 0
},
"Placement": {
"Constraints": [
"nodename == node1"
]
}
},
"ServiceID": "duvqo3btdrrlwf61g3bu5uaom",
"Slot": 1,
"Status": {
"Timestamp": "2017-01-06T23:03:01.935553276Z",
"State": "allocated",
"Message": "allocated",
"ContainerStatus": {}
},
"DesiredState": "running"
}
]
Выключим сервис и создадим его заново на node3:
$ sudo docker service create --name testcon --constraint 'node.labels.nodename == node3' --mount type=bind,source=/swarm/volumes/testvol,target=/mnt/testvol ubuntu:latest /bin/touch /mnt/testvol/testfile3.txt
5y99c0bfmc2fywor3lcsvmm9q
Убедимся, что теперь он запущен на node3:
$ sudo docker service ps testcon
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR
5p57xyottput3w34r7fclamd9 testcon.1 ubuntu:latest ip-172-31-26-52 Ready Ready 1 seconds ago
aniesakdmrdyuq8m2ddn3ga9b \_ testcon.1 ubuntu:latest ip-172-31-26-52 Shutdown Complete 2 seconds ago
В итоге мы должны увидеть, что созданные в обоих контейнерах файлы находятся вместе в одном хранилище:
$ ls -l /swarm/volumes/testvol/
total 0
-rw-r--r-- 1 root root 0 Jan 6 23:59 testfile3.txt
-rw-r--r-- 1 root root 0 Jan 6 23:58 testfile1.txt
Комментарии (16)
mcleod095
07.02.2017 19:16и интересно было бы еще увидеть тесты на производительность
Использую glusterfs для синхронизации файлов конфигурации, решил попробовать для синхронизации файлов веб интерфейса zabbix. Так вот, если просто с файловой системы читать тот же ab выдает 40 запросов в секунду, если файлы на glusterfs то ab уже выдает 4 запроса в секунду, в итоге имеем ситуацию когда интерфейс заббикса открывается секунд 20.SchmeL
08.02.2017 16:46производительность fuse драйвера для glusterfs оставляет желать лучшего. Для улучшения производительности лучше использовать встроенный в нее же NFS сервер и монтировать через mount.nfs.
o_serega
08.02.2017 17:38У распределенных кластерных фс на dht, будут проблемы с большим колличеством мелких файлов. Улучшить положение можно настройками кеширования для файлов.
Как дял примера, откуда гуглить:
gluster volume set vol performance.cache-max-file-size 10MB
gluster volume set vol performance.cache-refresh-timeout 5
gluster volume set vol performance.write-behind-window-size 10MB
gluster volume set vol performance.io-thread-count 64
gluster volume set vol performance.cache-size 5G
da45nik
07.02.2017 21:55Так, на заметку, если использовать AWS, то можно просто подключить ко всем нодам, один EBS, где и будут сохранятся данные.
gekk0
08.02.2017 08:34Получаем таким образом привязку к AWS, и при желании включить в swarm docker-сервер, развёрнутый не в амазоне, получим дополнительные проблемы.
SchmeL
08.02.2017 16:47Про настройку glusterfs уже много где написано, но вот очень мало статей, по восстановлению отвалившегося «кирпича».
Как-то упала одна нода, и проще было заново пересоздать кластер из двух кирпичей, чем добавить новый.stychos
10.02.2017 17:48как я понял, у глюстры такая парадигма — отвалился кирпич? — добавляем новый и делаем ребалансировку
stychos
10.02.2017 17:46репликация без тома-арбитра чревата редкостным потенциальным гембелем со split-brain'ами
stychos
10.02.2017 17:50и забыл добавить, что глюстер очень требователен к поступающим данным — никогда не режьте ему входящие порты даже на тех интерфейсах, к которым он не имеет никакого отношения — в логах будет ливень ошибок о том, что он даже к локалхосту не может подключиться, и хилинг будет сходить с ума
nazarpc
А что с безопасностью? Должны же быть какие-то настройки шифрования (как в примере с Docker Swarm).
ertaquo
В Gluster для безопасности используется SSL с сертификатами.