Управление и доступ
Мы управляем контейнерами с помощью команды Docker. Теперь, допустим, появилась необходимость управлять удалённо. Использовать VNC, SSH или другое для управления командой Docker, наверное, будет слишком трудоёмким если задача усложнится. Правильно, сперва будет разобраться, что из себя представляет Docker, ведь команда Docker и программа Docker не одно и тоже, а точнее команда Docker является консольным клиентом для управления клиент серверным приложение Docker Engine. Команда взаимодействует с сервером Docker Machine через Docker REST API, который и предназначен для удалённого взаимодействия с сервером. Но, в таком случае необходимо позаботиться об авторизации и SSL-шифровании трафика. Обеспечивается это созданием ключей, но в общем случае, если стоит задача централизованного управления, разграничения прав и обеспечения безопасности лучше посмотреть в сторону продуктов, изначально обеспечивающих это и использующих Docker в качестве именно запуска контейнеров, а не как систему.
По умолчанию, для безопасности, клиент общается с сервером через Unix сокет (специальный файл /var/run/socket.sock), а не через сетевой сокет. Для работы через Unix сокет можно использовать указать программе отправки запросов curl его использовать curl unix-socket /var/run/docker.sock http:/v1.24/containers/json, но это возможность поддерживается с версии curl 1.40, которая пока не поддерживается в CentOS. Для решения этой проблемы и взаимодействия между удалёнными серверами используем сетевой сокет. Для активации его останавливаем сервер systemctl stop docker и запускаем его с настройками dockerd -H tcp://0.0.0.0:9002 & (слушать всех на порту 9002, что не допустимо для продакшна). После запуска команда docker ps не будет работать, а будет docker -H 5.23.52.111:9002 ps или docker -H tcp://geocode1.essch.ru:9202 ps. По умолчанию Docker использует для http порт 2375, а для https 2376. Чтобы не менять везде код и каждый раз не указывать сокет, пропишем его в переменных окружения:
export DOCKER_HOST=5.23.52.111:9002
Docker ps
Docker info
unser DOCKER_HOST
Важно прописать export для обеспечения доступности переменной всем программам: дочерним процессам. Также не должно стоять пробелов вокруг =. Теперь мы так можем обращаться из любого места находящимся в одной сети сервером Dockerd. Для управления удалёнными и локальными Docker Engine (Docker на хостах) разработана программа Docker Machine. Взаимодействие с ней осуществляется с помощью команды Docker-machine. Для начала установим:
base=https://github.com/docker/machine/releases/download/v0.14.0 &&
curl -L $base/docker-machine-$(uname -s)-$(uname -m) >/usr/local/bin/docker-machine &&
chmod +x /usr/local/bin/docker-machine
Группа взаимосвязанных приложений
У нас уже есть несколько разных приложений, допустим, таких как NGINX, MySQL и наше приложение. Мы их изолировали в разных контейнерах, теперь она не конфликтуют между собой и NGINX и MySQL мы не стали тратить время и усилия на изготовление собственной конфигурации, а просто скачали: Docker run mysql, docker run Nginx, а для приложения docker build .; docker run myapp -p 80:80 bash. Как видно, казалось бы, всё очень просто, но есть два момента: управление и взаимосвязь.
Для демонстрации управления возьмём контейнер нашего приложения, и реализуем две возможности старт и создание (переборку). Для ручного старта, когда мы знаем, что контейнер уже создан, но просто остановлен, достаточно выполнить docker start myapp, но для автоматического режима этого недостаточно, а нам нужно написать скрипт, который бы учитывал, существует ли уже контейнер, существует ли для него образ:
if $(docker ps | grep myapp)
then
docker start myapp
else
if ! $(docker images | grep myimage)
docker build .
fi
docker run -d name myapp -p 80:80 myimage bash
fi
. А для создания нужно удалить контейнер, если он существует:
if $(docker ps | grep myapp)
docker rm -f myapp
fi
if ! $(docker images | grep myimage)
docker build .
fi
docker run -d name myapp -p 80:80 myimage bash
. Понятно, что нужно общими параметры, название образа, контейнера вывести в переменные, проверить, что Dockerfile есть, он валидный и только после этого удалять контейнер и много другого. Для понимания реального масштаба, на вдаваясь во взаимодействие контейнеров, о клонировании (масштабирование) этих групп и тому подобного, а только упомяну, то, что команда Docker run может превышать один два десятка строк. Например, десяток проброшенных портов, монтируемых папок, ограничения на память и процессор, связи с другими контейнерами и ещё немного специфичных параметров. Да, это нехорошо, но делить в таком варианте на множество контейнеров сложно, из-за отсутствия карты взаимодействия контейнеров. Но возникает вопрос: Не много ли нужно делать, чтобы просто предоставить пользователю возможность стартовать контейнер или пересобрать? Зачастую ответ системного администратора сводится к тому, что давать доступы можно только избранным. Но и тут есть решение: Docker-compose инструмент для работы с группой контейнеров:
#docker-compose
version: v1
services:
myapp:
container-name: myapp
images: myimages
ports:
80:80
build: .
. Для старта docker-compose up -d, а для переборки docker down; docker up -d. Причём, при изменении конфигурации, когда не нужна полная переборка, произойдёт просто её обновление.
Теперь, когда мы упрости процесс управления одиночным контейнером, давайте поработаем с группой. Но тут, для нас изменится только сам конфиг:
#docker-compose
version: v1
services:
mysql:
images: mysql
Nginx:
images: Nginx
ports:
80:80
myapp:
container-name: myapp
build: .
depence-on: mysql
images: myimages
link:
db:mysql
Nginx:Nginx
. Здесь мы видим всю картину в целом, контейнера связаны одной сетью, где приложение может обращаться к mysql и NGINX по хостам db и NGINX, соответственно, контейнер myapp будет создан, только когда после поднятия базы данных mysql, даже если это займёт некоторое время.
Service Discovery
С ростом кластера вероятность падения нод вырастает и ручное обнаружение произошедшего усложняется, для автоматизации обнаружение вновь появившихся сервисов и их исчезновение предназначены системы Service Discovery. Но, чтобы кластер мог обнаруживать состояние, учитывая, что система децентрализована ноды должны уметь обмениваться сообщениями с друг другом и выбирать лидера, примерами могут быть Consul, ETCD и ZooKeeper. Мы рассмотрим Consul исходя их следующих его особенностей: вся программа одни файл, крайне прост в работе и настройке, имеет высокоуровневый интерфейс (ZooKeeper его не имеет, полагается, что со временем должны появиться сторонние приложения, его реализующие), написан на не требовательном языке к ресурсам вычислительной машины (Consul Go, ZooKeeper Java) и пренебрегли его поддержкой в других системах, такай как, например, ClickHouse (поддерживает по умолчанию ZooKeeper).
Проверим распределение информации между нодами с помощью распределенного key-value хранилища, то есть, если мы в одну ноду добавили записи, то они должны распространиться и на другие ноды, при этом жёстко заданного координирующего (Master) ноды у неё не должно быть. Поскольку Consul состоит из одного исполняемого файла скачиваем его с официального сайта по ссылке https://www.consul.io/downloads. html на каждой ноде:
wget https://releases.hashicorp.com/consul/1.3.0/consul_1.3.0_linux_amd64.zip -O consul.zip
unzip consul.zip
rm -f consul.zip
Теперь необходимо запустить одну ноду, пока, как master consul -server -ui, а другие как slave consul -server -ui и consul -server -ui . После чего остановим Consul, находящийся в режиме master, и запускаем его как равного, в результате Consul переизберут временного лидера, а в случае иго падения, переизберут снова. Проверим работу нашего кластера consul members:
consul members;
И так проверим распределение информации нашего хранилища:
curl -X PUT -d 'value1' .....:8500/v1/kv/group1/key1
curl -s .....:8500/v1/kv/group1/key1
curl -s .....:8500/v1/kv/group1/key1
curl -s .....:8500/v1/kv/group1/key1
Настроим мониторинг сервисов, подробнее в документации https://www.consul.io/docs/agent/options. html #telemetry, для этого .... https://medium.com/southbridge/monitoring-consul-with-statsd-exporter-and-prometheus-bad8bee3961b
Чтобы не настраивать, воспользуемся контейнером и режимом для разработки с уже настроенным IP-адресом на 172.17.0.2:
essh@kubernetes-master:~$ mkdir consul && cd $_
essh@kubernetes-master:~/consul$ docker run -d name=dev-consul -e CONSUL_BIND_INTERFACE=eth0 consul
Unable to find image 'consul:latest' locally
latest: Pulling from library/consul
e7c96db7181b: Pull complete
3404d2df15cb: Pull complete
1b2797650ac6: Pull complete
42eaf145982e: Pull complete
cef844389e8c: Pull complete
bc7449359c58: Pull complete
Digest: sha256:94cdbd83f24ec406da2b5d300a112c14cf1091bed8d6abd49609e6fe3c23f181
Status: Downloaded newer image for consul:latest
c6079f82500a41f878d2c513cf37d45ecadd3fc40998cd35020c604eb5f934a1
essh@kubernetes-master:~/consul$ docker inspect dev-consul | jq ' .[] | .NetworkSettings.Networks.bridge.IPAddress'
"172.17.0.4"
essh@kubernetes-master:~/consul$ docker run -d name=consul_follower_1 -e CONSUL_BIND_INTERFACE=eth0 consul agent -dev -join=172.17.0.4
8ec88680bc632bef93eb9607612ed7f7f539de9f305c22a7d5a23b9ddf8c4b3e
essh@kubernetes-master:~/consul$ docker run -d name=consul_follower_2 -e CONSUL_BIND_INTERFACE=eth0 consul agent -dev -join=172.17.0.4
babd31d7c5640845003a221d725ce0a1ff83f9827f839781372b1fcc629009cb
essh@kubernetes-master:~/consul$ docker exec -t dev-consul consul members
Node Address Status Type Build Protocol DC Segment
53cd8748f031 172.17.0.5:8301 left server 1.6.1 2 dc1 < all>
8ec88680bc63 172.17.0.5:8301 alive server 1.6.1 2 dc1 < all>
babd31d7c564 172.17.0.6:8301 alive server 1.6.1 2 dc1 < all>
essh@kubernetes-master:~/consul$ curl -X PUT -d 'value1' 172.17.0.4:8500/v1/kv/group1/key1
true
essh@kubernetes-master:~/consul$ curl $(docker inspect dev-consul | jq -r ' .[] | .NetworkSettings.Networks.bridge.IPAddress'):8500/v1/kv/group1/key1
[
{
"LockIndex": 0,
"Key": "group1/key1",
"Flags": 0,
"Value": "dmFsdWUx",
"CreateIndex": 277,
"ModifyIndex": 277
}
]
essh@kubernetes-master:~/consul$ firefox $(docker inspect dev-consul | jq -r ' .[] | .NetworkSettings.Networks.bridge.IPAddress'):8500/ui
С определением местоположения контейнеров необходимо обеспечить авторизацию, для этого используются хранилища ключей.