Docker: использование configs и secrets в Swarm

Автор: | 15/11/2017

Начиная с версии 17.06 – у Docker Swarm появилась поддержка новой концепции хранения и подключения файлов настроек для сервисов – configs.

А ещё ранее, с версии 1.13 – secrets, для хранения и передачи зашифрованных данных между менеджерами и нодами.

Ниже – описание и применение secrets и configs в Docker Swarm.

Подготовка

VirtualBox

Создаём машинку в VirtualBox – это будет наш Swarm manager и он же – worker:

[simterm]

$ VBoxManage createvm --name dc_swarm --register
Virtual machine 'dc_swarm' is created and registered.
UUID: f2552be5-6e06-4594-acb5-e565444df11a
Settings file: '/home/setevoy/VirtualBox VMs/dc_swarm/dc_swarm.vbox'

[/simterm]

Создаём сетевой интерфейс:

[simterm]

$ VBoxManage modifyvm dc_swarm --nic1 bridged --bridgeadapter1 enp0s25 --nictype1 82540EM --cableconnected1 on

[/simterm]

Устанавливаем тип ОС:

[simterm]

$ VBoxManage modifyvm dc_swarm --ostype Debian_64

[/simterm]

Создаём жёсткий диск:

[simterm]

$ cd /home/setevoy/VirtualBox\ VMs/dc_swarm/ && VBoxManage createhd --filename dc_swarm.vdi --size 10000
0%...10%...20%...30%...40%...50%...60%...70%...80%...90%...100%
Medium created. UUID: 14f607dc-4513-4f01-9eef-8d99f4a2f2fb

[/simterm]

К машине добавляем IDE контроллер:

[simterm]

$ VBoxManage storagectl dc_swarm --name "IDE Controller" --add ide

[/simterm]

Подключаем диск:

[simterm]

$ VBoxManage storageattach dc_swarm --storagectl "IDE Controller"  --port 0 --device 0 --type hdd --medium dc_swarm.vdi

[/simterm]

Подключаем ISO с Debian:

[simterm]

$ VBoxManage storageattach dc_swarm --storagectl "IDE Controller" --port 1 --device 0 --type dvddrive --medium /home/setevoy/OS/debian-9.1.0-amd64-netinst.iso

[/simterm]

Увеличиваем память на машине до 2-х гигабайт:

[simterm]

$ VBoxManage modifyvm dc_swarm --memory 2048

[/simterm]

И наконец – запускаем машину:

[simterm]

$ VBoxManage startvm dc_swarm
Waiting for VM "dc_swarm" to power on...
VM "dc_swarm" has been successfully started.

[/simterm]

Выполняем установку ОС:

Обновляем пакеты:

[simterm]

# apt update && apt upgrade

[/simterm]

Устанавливаем Docker:

[simterm]

# curl https://get.docker.com/ | bash

[/simterm]

(вы ведь в курсе, что под рутом через пайп не стоит запускать скрипты из интернета? только из доверенных источников)

Добавляем пользователя setevoy в группу docker, что бы не пользоваться sudo:

[simterm]

# usermod -aG docker setevoy

[/simterm]

Проверяем docker:

[simterm]

setevoy@dcswarm:~$ docker version 
Client:
 Version:      17.10.0-ce
 API version:  1.33
 Go version:   go1.8.3
 Git commit:   f4ffd25
 Built:        Tue Oct 17 19:02:43 2017
 OS/Arch:      linux/amd64

Server:
 Version:      17.10.0-ce
 API version:  1.33 (minimum version 1.12)
 Go version:   go1.8.3
 Git commit:   f4ffd25
 Built:        Tue Oct 17 19:01:22 2017
 OS/Arch:      linux/amd64
 Experimental: false

[/simterm]

Docker

Создание Docker Swarm

Создаём Swarm:

[simterm]

setevoy@dcswarm:~$ docker swarm init
Swarm initialized: current node (dfl55pzcsugb73eojmcfy58h4) is now a manager.

To add a worker to this swarm, run the following command:

    docker swarm join --token SWMTKN-1-1fo611jv34gf9ib6zkh9t9lmova6jvqarw5bj3d2rydaqbe5op-bkzz114rc7v2cs1v16k9b9q6k 10.11.100.163:2377

To add a manager to this swarm, run 'docker swarm join-token manager' and follow the instructions.

[/simterm]

Проверяем:

[simterm]

setevoy@dcswarm:~$ docker node ls
ID                            HOSTNAME            STATUS              AVAILABILITY        MANAGER STATUS
dfl55pzcsugb73eojmcfy58h4 *   dcswarm             Ready               Active              Leader

[/simterm]

На одной машине сейчас работает и Swarm manager и worker.

Для проверки – создадим сервис и запустим простой контейнер с NGINX:

[simterm]

root@dcswarm:/home/setevoy# docker service create --name nginx -p 80:80 nginx
ioid8welvkfy77ad1brampqdm                                                                                                                                                                                                                     
overall progress: 1 out of 1 tasks 
1/1: running   [==================================================>] 
verify: Service converged

[/simterm]

Проверяем:

[simterm]

root@dcswarm:/home/setevoy# docker service ls   
ID                  NAME                MODE                REPLICAS            IMAGE               PORTS
ioid8welvkfy        nginx               replicated          1/1                 nginx:latest        *:80->80/tcp

[/simterm]

Docker configs

Swarm service configs позволяет хранить некритичные данные, такие как файлы конфигурации, вне docker-образа или контейнера: вместо того, что бы подключать файл настроек через volume (-v) или передавать переменные с параметрами – вы можете создать объект конфига и подключить его напрямую к файловой системе в создаваемом сервисе/контейнере.

Когда вы создаёте такой configDocker отправляет его на manager-ноду кластера по TLS, где он хранится в зашифрованных Raft логах (/var/lib/docker/swarm/raft), которые реплицируются по всем менеджерам swarm-а.

Хороший обзор по Raft в Docker Swarm есть тут>>>.

Когда вы даёте доступ к конфигу для нового или уже запущенного сервиса в Swarm – он монтируется в файловую систему контейнера, по умолчанию для Linux это будет /<config_name>.

Вы можете так же отозвать доступ к конфигу или добавить дополнительный в любой момент.

Создание configs

Создать конфиг очень просто, например – создадим файл настроек виртуалхоста для NGINX:

server {

        listen 80;

        root /usr/share/nginx/html/;

        index index.html index.htm index.nginx-debian.html;

        server_name dc-swarm.local;

        location / {
                try_files $uri $uri/ =404;
        }
}

Создаём объект Docker config:

[simterm]

root@dcswarm:/home/setevoy# docker config create vhost_nginx nginx_vhost.conf 
i7ckjge352lvght06qr8dzonc

[/simterm]

Проверяем:

[simterm]

root@dcswarm:/home/setevoy# docker config ls
ID                          NAME                CREATED             UPDATED
i7ckjge352lvght06qr8dzonc   vhost_nginx         15 seconds ago      15 seconds ago

[/simterm]

Просмотреть его содержимое:

[simterm]

root@dcswarm:/home/setevoy# docker config inspect vhost_nginx --pretty
ID:                     i7ckjge352lvght06qr8dzonc
Name:                   vhost_nginx
Created at:             2017-11-15 12:09:50.035131215 +0000 utc
Updated at:             2017-11-15 12:09:50.035131215 +0000 utc
Data:
server {

        listen 80;

        root /usr/share/nginx/html/;

        index index.html index.htm index.nginx-debian.html;

        server_name dc-swarm.local;

        location / {
                try_files $uri $uri/ =404;
        }
}

[/simterm]

Теперь можно удалить исходный файл, и использовать созданный конфиг.

Применение configs

Обновляем сервис, подключаем конфиг:

[simterm]

root@dcswarm:/home/setevoy# docker service update --config-add vhost_nginx nginx
nginx
overall progress: 1 out of 1 tasks 
1/1: running   [==================================================>] 
verify: Service converged

[/simterm]

Проверяем:

[simterm]

root@dcswarm:/home/setevoy# docker exec -ti 2ca2986076ef cat /vhost_nginx
server {

        listen 80;

        root /usr/share/nginx/html/;

        index index.html index.htm index.nginx-debian.html;

        server_name dc-swarm.local;

        location / {
                try_files $uri $uri/ =404;
        }
}

[/simterm]

Что бы подключить конфиг в определённый каталог – используем src и target:

[simterm]

root@dcswarm:/home/setevoy# docker service update --config-add src=vhost_nginx,target="/etc/nginx/conf.d/dc-swarm.local.conf" nginx
nginx
overall progress: 1 out of 1 tasks 
1/1: running   [==================================================>] 
verify: Service converged

[/simterm]

Проверяем:

[simterm]

root@dcswarm:/home/setevoy# docker exec -ti $(docker ps | tail -n 1 | awk '{print $1}') ls -l /etc/nginx/conf.d
total 8
-r--r--r-- 1 root root  242 Nov 15 12:25 dc-swarm.local.conf
-rw-r--r-- 1 root root 1093 Sep 14 16:35 default.conf

[/simterm]

Пробуем получить доступ к  dc-swarm.local с хоста:

[simterm]

$ sudo sh -c "echo '10.11.100.163 dc-swarm.local' >> /etc/hosts"

[/simterm]

Проверяем:

[simterm]

$ curl -I dc-swarm.local
HTTP/1.1 200 OK
Server: nginx/1.13.6
Date: Wed, 15 Nov 2017 12:27:34 GMT
Content-Type: text/html
Content-Length: 612
Last-Modified: Thu, 14 Sep 2017 16:35:09 GMT
Connection: keep-alive
ETag: "59baafbd-264"
Accept-Ranges: bytes

[/simterm]

Аналогично можно добавить любые данные.

Например – повторим для создания индексного файла:

[simterm]

root@dcswarm:/home/setevoy# echo "This is dc-swarm.local" > index_local.html
root@dcswarm:/home/setevoy# docker config create dc_index index_local.html 
4ych3x1inktdkuqfz6q9hklby

[/simterm]

Подключаем конфиг к сервису:

[simterm]

root@dcswarm:/home/setevoy# docker service update --config-add src=dc_index,target="/usr/share/nginx/html/dc_index.html" nginx
nginx
overall progress: 1 out of 1 tasks 
1/1: running   [==================================================>] 
verify: Service converged

[/simterm]

Обновление configs

Обновляем настройки виртуалхоста – поменяем индексный файл:

server {

        listen 80;

        root /usr/share/nginx/html/;

        index dc_index.html;

        server_name dc-swarm.local;

        location / {
                try_files $uri $uri/ =404;
        }
}

Создаём конфиг с именем vhost_nginx_v2:

[simterm]

root@dcswarm:/home/setevoy# docker config create vhost_nginx_v2 nginx_vhost.conf 
h13yug0d1jfcmdznosttyvqbp

[/simterm]

Отключаем конфиг от сервиса:

[simterm]

root@dcswarm:/home/setevoy# docker service update --config-rm vhost_nginx nginx
nginx
overall progress: 1 out of 1 tasks 
1/1: running   [==================================================>] 
verify: Service converged

[/simterm]

Удаляем старый конфиг из Swarm:

[simterm]

root@dcswarm:/home/setevoy# docker config rm vhost_nginx
vhost_nginx

[/simterm]

Проверяем:

[simterm]

root@dcswarm:/home/setevoy# docker config ls
ID                          NAME                CREATED              UPDATED
4ych3x1inktdkuqfz6q9hklby   dc_index            5 minutes ago        5 minutes ago
h13yug0d1jfcmdznosttyvqbp   vhost_nginx_v2      About a minute ago   About a minute ago

[/simterm]

Подключаем новый, v2:

[simterm]

root@dcswarm:/home/setevoy# docker service update --config-add src=vhost_nginx_v2,target="/etc/nginx/conf.d/dc-swarm.local.conf" nginx
nginx
overall progress: 1 out of 1 tasks 
1/1: running   [==================================================>] 
verify: Service converged

[/simterm]

Проверяем сервис:

[simterm]

$ curl dc-swarm.local
This is dc-swarm.local

[/simterm]

Всё работает – новый конфиг подключился к NGINX.

Docker secrets

secrets – это данные, например пароль или SSH ключ, которые не должны храниться в открытом виде в Dockerfile и не должны передаваться в таком виде  по сети.

Главное отличие secrets от configs это то, что configs подключаются к файловой системе контейнера, тогда как secrets – монтируются в память (/run/).

Когда вы явно указываете доступ к секрету для сервиса или контейнера – по умолчанию он будет помещён в /run/secrets/<secret_name>.

Создание secrets

Создаём новый “секрет”:

[simterm]

root@dcswarm:/home/setevoy# echo "password" > pwd.txt
root@dcswarm:/home/setevoy# docker secret create pwd pwd.txt 
urgibv1kmkz3o3850n8msc0ej

[/simterm]

Проверяем:

[simterm]

root@dcswarm:/home/setevoy# docker secret ls
ID                          NAME                DRIVER              CREATED              UPDATED
urgibv1kmkz3o3850n8msc0ej   pwd                                     About a minute ago   About a minute ago

[/simterm]

Даём доступ к этому секрету для сервиса nginx:

[simterm]

root@dcswarm:/home/setevoy# docker service update --secret-add pwd nginx
ID                          NAME                DRIVER              CREATED              UPDATED
urgibv1kmkz3o3850n8msc0ej   pwd                                     About a minute ago   About a minute ago

[/simterm]

Проверяем /var/run/secrets в контейнере:

[simterm]

root@dcswarm:/home/setevoy# docker exec -ti $(docker ps | tail -n 1 | awk '{print $1}') ls -l /var/run/secrets
total 4
-r--r--r-- 1 root root 9 Nov 15 14:37 pwd

[/simterm]

И содержимое файла:

[simterm]

root@dcswarm:/home/setevoy# docker exec -ti $(docker ps | tail -n 1 | awk '{print $1}') cat /var/run/secrets/pwd
password

[/simterm]

Аналогично с configs – можно указать точку монтирования.

Отключаем секрет от контейнера:

[simterm]

root@dcswarm:/home/setevoy# docker service update --secret-rm pwd nginx
nginx
overall progress: 1 out of 1 tasks 
1/1: running   [==================================================>] 
verify: Service converged

[/simterm]

И подключаем его в /etc/pwd:

[simterm]

root@dcswarm:/home/setevoy# docker service update --secret-add src=pwd,target="/etc/pwd" nginx
nginx
overall progress: 1 out of 1 tasks 
1/1: running   [==================================================>] 
verify: Service converged

[/simterm]

Проверяем:

[simterm]

root@dcswarm:/home/setevoy# docker exec -ti $(docker ps | tail -n 1 | awk '{print $1}') cat /etc/pwd
password

[/simterm]

Docker secrets vs configs

Как говорилось, основная разница в том, что configs при подключении к контейнеру становится частью файловой системы, тогда как secret – помещается в памяти.

Проверить это можно сделав новый образ запущенного контейнера.

Сейчас у нас есть сервис nginx к которому подключен config dc_index и vhost_nginx_v2, и secretpwd:

[simterm]

root@dcswarm:/home/setevoy# docker service inspect nginx | jq '.[0].Spec.TaskTemplate.ContainerSpec.Secrets , .[0].Spec.TaskTemplate.ContainerSpec.Configs'
[
  {
    "File": {
      "Name": "/etc/pwd",
      "UID": "0",
      "GID": "0",
      "Mode": 292
    },
    "SecretID": "urgibv1kmkz3o3850n8msc0ej",
    "SecretName": "pwd"
  }
]
[
  {
    "File": {
      "Name": "/usr/share/nginx/html/dc_index.html",
      "UID": "0",
      "GID": "0",
      "Mode": 292
    },
    "ConfigID": "4ych3x1inktdkuqfz6q9hklby",
    "ConfigName": "dc_index"
  },
  {
    "File": {
      "Name": "/etc/nginc/conf.d/dc-swarm.local.conf",
      "UID": "0",
      "GID": "0",
      "Mode": 292
    },
    "ConfigID": "h13yug0d1jfcmdznosttyvqbp",
    "ConfigName": "vhost_nginx_v2"
  },
  {
    "File": {
      "Name": "/etc/nginx/conf.d/dc-swarm.local.conf",
      "UID": "0",
      "GID": "0",
      "Mode": 292
    },
    "ConfigID": "h13yug0d1jfcmdznosttyvqbp",
    "ConfigName": "vhost_nginx_v2"
  }
]

[/simterm]

Создаём новый образ из этого запущенного контейнера:

[simterm]

root@dcswarm:/home/setevoy# docker commit $(docker ps | tail -n 1 | awk '{print $1}') nginx_copy
sha256:4987a44e070804ff20f2146f0060e79b94b8b98fc31a734f720a76b3e907d002

[/simterm]

Проверяем образы:

[simterm]

root@dcswarm:/home/setevoy# docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
nginx_copy          latest              4987a44e0708        16 seconds ago      108MB
nginx               <none>              40960efd7b8f        10 days ago         108MB

[/simterm]

Запускаем новый контейнер из созданного образа:

[simterm]

root@dcswarm:/home/setevoy# docker run -tid --name=nginx_copy nginx_copy 
c62fce9e32fd728e3cebd6da2257828818b9ed4f4420fc6addeeaf0810684a9c

[/simterm]

Проверяем файлы configs:

[simterm]

root@dcswarm:/home/setevoy# docker exec -ti nginx_copy ls -l /etc/nginx/conf.d
total 4
-rwxr-xr-x 1 root root    0 Nov 15 14:40 dc-swarm.local.conf
-rw-r--r-- 1 root root 1093 Sep 14 16:35 default.conf

[/simterm]

ОК – на месте.

И secret:

[simterm]

root@dcswarm:/home/setevoy# docker exec -ti nginx_copy ls -l /run/
total 12
drwxrwxrwt 2 root root 4096 Oct  9 00:00 lock
drwxr-xr-x 2 root root 4096 Oct  9 00:00 mount
-rw-r--r-- 1 root root    2 Nov 15 15:11 nginx.pid
-rw-rw-r-- 1 root utmp    0 Oct  9 00:00 utmp

[/simterm]

Пусто.

Docker secrets и configs в Compose

Устанавливаем Docker Compose:

[simterm]

# curl -o /usr/local/bin/docker-compose -L "https://github.com/docker/compose/releases/download/1.15.0/docker-compose-$(uname -s)-$(uname -m)"
# curl -o /usr/local/bin/docker-compose -L "https://github.com/docker/compose/releases/download/1.15.0/docker-compose-$(uname -s)-$(uname -m)"

[/simterm]

Что бы использовать secrets и configs в Compose файле – используем configs и secrets:

version: "3.3"

configs:
  vhost_nginx:
    file: ./nginx_vhost.conf
  dc_index:
    file: ./index_local.html 

secrets:
  pwd:
    file: ./pwd.txt

services:
  nginx:
    image: nginx
    secrets: 
    - pwd
    configs:
    - source: vhost_nginx
      target: /etc/nginc/conf.d/dc-swarm.local.conf
    - source: dc_index
      target: /usr/share/nginx/html/dc_index.html

Создаём стек:

[simterm]

root@dcswarm:/home/setevoy# docker stack deploy -c compose_secrets_configs.yml nginx
Creating service nginx_nginx

[/simterm]

Проверяем:

[simterm]

root@dcswarm:/home/setevoy# docker service inspect nginx_nginx | jq '.[0].Spec.TaskTemplate.ContainerSpec.Secrets , .[0].Spec.TaskTemplate.ContainerSpec.Configs'
[
  {
    "File": {
      "Name": "pwd",
      "UID": "0",
      "GID": "0",
      "Mode": 292
    },
    "SecretID": "uxr0m6wib0otfqglnwwwis19s",
    "SecretName": "nginx_pwd"
  }
]
[
  {
    "File": {
      "Name": "/usr/share/nginx/html/dc_index.html",
      "UID": "0",
      "GID": "0",
      "Mode": 292
    },
    "ConfigID": "twcghzoyzxb7j3l4yg15inwil",
    "ConfigName": "nginx_dc_index"
  },
  {
    "File": {
      "Name": "/etc/nginc/conf.d/dc-swarm.local.conf",
      "UID": "0",
      "GID": "0",
      "Mode": 292
    },
    "ConfigID": "pulxlolcovj6ew4qniruvy3r5",
    "ConfigName": "nginx_vhost_nginx"
  }
]

[/simterm]

Готово.

Ссылки по теме

Manage sensitive data with Docker secrets

Store configuration data using Docker Configs

Raft logs on Swarm mode

Docker Security: Using Docker Secrets with Swarm