Статьи

Обнаружение службы с помощью Docker и Consul: часть 2

Итак, добро пожаловать во вторую часть серии, посвященную использованию Consul для обнаружения сервисов вместе с докером. Во второй статье мы рассмотрим, как вы можете использовать ряд инструментов, связанных с Консулом, так что упростите обнаружение сервисов и несколько других связанных функций.

Для других статей в этой серии вы можете посмотреть здесь:

  1. Презентация по открытию Сервиса с консулом
  2. Служба обнаружения с докером и консулом: часть 1

Обзор архитектуры и что мы будем делать

В этой статье мы расширим настройки, которые мы создали в предыдущей статье . В этой статье мы создали следующую архитектуру микросервисов с использованием Docker и Consul:

demo1_0

Итак, если вы этого не сделали, пройдитесь по шагам в этой статье и убедитесь, что у вас есть настройка, которая может использовать Docker Swarm, Docker Compose и имеет правильную настройку сети Docker. Чтобы проверить, все ли настроено правильно, проверьте вывод следующих команд:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# See the previous article for the definitions of these commands
#
# First, check Consul. If consul isn't up, the swarm won't come up or work.
  
$ . dm-env nb-consul
$ docker ps --format '{{ .ID }}\t{{ .Image }}\t{{ .Command }}\t{{ .Names}}'
  
b5d55e6df248       progrium/consul "/bin/start -server -"  clever_panini
  
# if consul isn't up, make sure to start it, before trying any of the swarm
# commands.
$ . dm-env nb1 --swarm
$ docker ps -a --format '{{ .ID }}\t{{ .Image }}\t{{ .Command }}\t{{ .Names}}'
  
bf2000882dcc    progrium/consul "/bin/start -ui-dir /"  nb1/consul_agent_1
a1bc26eef516    progrium/consul "/bin/start -ui-dir /"  nb2/consul_agent_2
eb0d1c0cc075    progrium/consul "/bin/start -ui-dir /"  nb3/consul_agent_3
d27050901dc1    swarm:latest    "/swarm join --advert"  nb3/swarm-agent
f66738e086b8    swarm:latest    "/swarm join --advert"  nb2/swarm-agent
0ac59ef54207    swarm:latest    "/swarm join --advert"  nb1/swarm-agent
17fc5563d018    swarm:latest    "/swarm manage --tlsv"  nb1/swarm-agent-master
  
# this should at least list the swarm master, swarm agents, and the consul agents.
# if not, see previous article on how to setup your environment.
  
# the last thing we need to check is our network. When the swarm master is selected
# run the following command:
$ docker network ls | grep -i my-net
8ecec72e7b68        my-net                overlay
  
# if it shows an overlay network with my-net we're all set.

Если вы видите какие-либо внешние или внутренние службы, возможно, лучше их остановить и удалить. Таким образом, вы можете следовать командам в остальной части этой статьи.

Итак, что мы будем делать в этой статье. Что ж, мы покажем вам, как использовать следующие два инструмента из вселенной Consul:

  • Consultemplate : с Consultemplate вы можете прослушивать события от Consul (например, когда добавляется служба), и на основе этих обновлений переписать и перезагрузить файл конфигурации. Мы будем использовать это для автоматического обновления конфигурации обратного прокси-сервера на основе HAProxy с последним набором исправных служб.
  • EnvConsul : С Envconsul вы можете легко читать переменные окружения напрямую из Consul, вместо того, чтобы передавать их вручную при запуске контейнера Docker. Мы покажем, как вы могли бы использовать это с услугами, которые мы используем в этих статьях.

Консультация, докер и HAProxy

В предыдущей настройке мы создали архитектуру, как показано на рисунке выше. Хотя это уже отлично работало благодаря использованию DNS-функций Consul для обнаружения служб и некоторого базового переключения при сбое, нам пришлось полагаться на DNS и время ожидания сокетов, чтобы определить, когда служба вышла из строя. Хотя это работало, оно не было таким надежным и предлагало лишь некоторые простые функции восстановления после сбоев и балансировки нагрузки. В этом сценарии мы собираемся создать следующее:

demo2

Таким образом, у нас будет следующий сценарий:

  1. Пользователь получит доступ к нашему веб-сервису через HAProxy.
  2. HAProxy перенаправит запрос в одну из исправных служб.
  3. Фронт-сервис, хочет получить доступ к бэкэнд-сервису. Это также делается через компонент HAProxy.

Консул удостоверится, что всякий раз, когда служба регистрируется в Консуле, она обновляет конфигурацию HAProxy.

Как это работает

Если вы извлекли хранилище для этих статей ( https://github.com/josdirksen/next-build-consul ), вы также можете найти каталог с именем extra / consul-template . В этом каталоге находится HAProxy, который мы будем использовать для этого примера. Я также добавил его в dockerhub ( https://hub.docker.com/r/josdirksen/demo-haproxy/) , что немного упрощает его использование. Прежде чем мы рассмотрим, как нам нужно определить наш шаблон, давайте посмотрим, что делает этот образ докера. Проще всего взглянуть на скрипт запуска:

01
02
03
04
05
06
07
08
09
10
11
#!/bin/bash
  
HAPROXY="/etc/haproxy"
PIDFILE="/var/run/haproxy.pid"
CONFIG_FILE=${HAPROXY}/haproxy.cfg
  
cd "$HAPROXY"
  
haproxy -f "$CONFIG_FILE" -p "$PIDFILE" -D -st $(cat $PIDFILE)
  
/usr/local/bin/consul-template -consul=${CONSUL_ADDRESS} -config=/consul.hcl

Здесь нет ничего особенного, что происходит, когда мы запускаем этот контейнер, consul-template будет работать с указанным файлом конфигурации. Обратите внимание, что нам нужно предоставить переменную окружения CONSUL_ADDRESS, чтобы указать шаблон-консула одному из наших агентов или серверу консула. Интересный материал находится в файле consul.hcl :

01
02
03
04
05
06
07
08
09
10
max_stale = "10m"
retry     = "10s"
wait      = "5s:20s"
  
template {
  source = "/etc/haproxy/haproxy.template"
  destination = "/etc/haproxy/haproxy.cfg"
  command = "/hap.sh"
  perms = 0600
}

этот файл довольно очевиден. В основном происходит то, что всякий раз, когда что-то меняется в Consul, шаблон haproxy.template будет запускаться против информации в Consul, и в результате будет заменен файл haproxy.cfg . После этого запускается команда hap.sh для перезагрузки конфигурации. Для полноты картины файл hap.sh выглядит так:

1
2
3
#!/bin/bash
  
haproxy -f /etc/haproxy/haproxy.cfg -p /var/run/haproxy.pid -D -st $(cat /var/run/haproxy.pid)

Что это делает, это перезагрузить файл конфигурации в чистом виде. Итак, что в шаблоне? Давайте посмотрим на haproxy.template :

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
global
  log 127.0.0.1 local0
  log 127.0.0.1 local1 notice
  chroot /var/lib/haproxy
  user haproxy
  group haproxy
  
defaults
  log global
  mode http
  option httplog
  option dontlognull
  balance roundrobin
  timeout connect 5000
  timeout client 50000
  timeout server 50000
  errorfile 400 /etc/haproxy/errors/400.http
  errorfile 403 /etc/haproxy/errors/403.http
  errorfile 408 /etc/haproxy/errors/408.http
  errorfile 500 /etc/haproxy/errors/500.http
  errorfile 502 /etc/haproxy/errors/502.http
  errorfile 503 /etc/haproxy/errors/503.http
  errorfile 504 /etc/haproxy/errors/504.http
  
listen stats
  bind *:8001
  stats enable
  stats uri /
  stats auth admin:123123q
  stats realm HAProxy\ Statistics
  
frontend nb-front
  bind *:1080
  mode http
  default_backend nb-frontend
  
frontend nb-back
  bind *:1081
  mode http
  default_backend nb-backend
  
backend nb-frontend
    balance roundrobin{{range service "frontend-service"}}
    server {{.Node}} {{.Address}}:{{.Port}} check{{end}}
  
backend nb-backend
   balance roundrobin{{range service "backend-service"}}
   server {{.Node}} {{.Address}}:{{.Port}} check{{end}}

Первые пару строк не интересны. Становится интересным, где мы определяем внешний интерфейс и элементы внутреннего интерфейса . Здесь мы говорим о том, что мы указываем, что haproxy будет прослушивать запрос внешнего интерфейса на порту 1080 и перенаправлять эти запросы службам, определенным в внутреннем интерфейсе nb-frontend . В этом элементе мы настраиваем шаблон. В этом примере мы берем все сервисы с именем frontend-service от Consul, и для каждого сервиса мы записываем запись. Поэтому, когда мы вызываем haproxy через порт 1080, он перенаправляет запрос любой из служб с именем frontend-service, зарегистрированным в Consul. Мы делаем то же самое для бэкэнд-сервиса .

Запустить его!

Итак, теперь, когда мы знаем, как это работает, пришло время запустить этот haproxy. Для этого мы определили файл docker-compose, который будет запускать HAProxy на узле nb1 :

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
version: '2'
  
services:
  nb-proxy:
    image: josdirksen/demo-haproxy
    container_name: nb-haproxy
    ports:
      - 1080:1080
      - 1081:1081
    environment:
      - CONSUL_ADDRESS=192.168.99.106:8500
      - "constraint:node==nb1"
  
networks:
  default:
    external:
      name: my-net

Для этого выполните следующие команды:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
# make sure we're at the swarm master
$ . dm-env nb1 --swarm
  
# in the root of the nextbuild-consul project
$ docker-compose -f ./docker-compose-haproxy.yml up -d
Creating nb-haproxy
  
$ docker ps -a --format '{{ .ID }}\t{{ .Image }}\t{{ .Command }}\t{{ .Names}}'
  
dc28caa4c420    josdirksen/demo-haproxy "/startup.sh"   nb1/nb-haproxy
bf2000882dcc    progrium/consul "/bin/start -ui-dir /"  nb1/consul_agent_1
a1bc26eef516    progrium/consul "/bin/start -ui-dir /"  nb2/consul_agent_2
eb0d1c0cc075    progrium/consul "/bin/start -ui-dir /"  nb3/consul_agent_3
d27050901dc1    swarm:latest    "/swarm join --advert"  nb3/swarm-agent
f66738e086b8    swarm:latest    "/swarm join --advert"  nb2/swarm-agent
0ac59ef54207    swarm:latest    "/swarm join --advert"  nb1/swarm-agent
17fc5563d018    swarm:latest    "/swarm manage --tlsv"  nb1/swarm-agent-master

В моей настройке я вижу, что HAProxy запущен. Но поскольку у нас не запущены какие-либо серверные или внешние службы, конфигурация haproxy должна отражать это:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
$ docker exec -ti nb1/nb-haproxy cat /etc/haproxy/haproxy.cfg | tail -n 15
frontend nb-front
  bind *:1080
  mode http
  default_backend nb-frontend
  
frontend nb-back
  bind *:1081
  mode http
  default_backend nb-backend
  
backend nb-frontend
    balance roundrobin
  
backend nb-backend
   balance roundrobin

И если мы откроем порт 1080 (для внешнего интерфейса) или 1081 (для внутреннего API), мы увидим ошибку от HAProxy.

haproxy_1

Этого и следовало ожидать, так как у нас не запущен ни один внешний или внутренний сервис. Итак, давайте предоставим HAProxy ряд сервисов для работы с:

1
2
3
4
$ docker-compose -f ./docker-compose-backend.yml up -d
Creating Backend2
Creating Backend3
Creating Backend1

Теперь у нас должно быть несколько бэкэндов, работающих за HAProxy. Сначала давайте проверим, обновил ли Консул наш экземпляр HAProxy:

1
2
3
4
5
6
7
8
9
$ docker exec -ti nb1/nb-haproxy cat /etc/haproxy/haproxy.cfg | tail -n 8
backend nb-frontend
    balance roundrobin
  
backend nb-backend
   balance roundrobin
   server a1bc26eef516 10.0.9.7:8081 check
   server bf2000882dcc 10.0.9.9:8081 check
   server eb0d1c0cc075 10.0.9.8:8081 check

Круто, верно! В Haproxy теперь определены три сервиса. Это должно позволить нам вызвать http: //nb1.local: 1081 и вернуть API одного из внутренних сервисов:

haproxy_2

Если вы обновляете пару раз, вы должны увидеть, как он переключается между различными службами.

И если мы убьём одного, мы увидим, что он автоматически пропустит убитого:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
$ docker stop nb1/Backend1
nb1/Backend1
  
$ curl nb1.local:1081
  
        {"result" : {
          "servername" : "Server2",
          "querycount" : 80
          }
        }
$ curl nb1.local:1081
  
        {"result" : {
          "servername" : "Server3",
          "querycount" : 86
          }
        }

Теперь давайте посмотрим, могут ли наши веб-службы использовать это таким же образом. Для этого мы запускаем компоненты внешнего интерфейса, как это:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
$ docker-compose up -f ./docker-compose-frontend-proxy
  
# remember we killed one of the backend services, so we only see two backend ones
$ docker exec -ti nb1/nb-haproxy cat /etc/haproxy/haproxy.cfg | tail -n 10
backend nb-frontend
    balance roundrobin
    server a1bc26eef516 10.0.9.11:8090 check
    server bf2000882dcc 10.0.9.9:8090 check
    server eb0d1c0cc075 10.0.9.10:8090 check
  
backend nb-backend
   balance roundrobin
   server a1bc26eef516 10.0.9.7:8081 check
   server eb0d1c0cc075 10.0.9.8:8081 check

И похоже, что HAProxy корректно обновляется с помощью новых сервисов. Теперь мы должны иметь возможность вызывать порт 1080 на haproxy, чтобы получить один из сервисов внешнего интерфейса, и использовать кнопку для вызова одного из доступных сервисов внутреннего интерфейса (еще раз через HAproxy).

haproxy_3

И все работает как положено! Если вы обновите эту страницу, вы увидите, что она циклически переключается между внешними службами, а когда вы несколько раз нажмете кнопку, она будет вызывать только доступные службы. И, как и следовало ожидать, как только мы снова запустим бэкэнд-сервис, он будет отображаться в списке при нажатии кнопки:

1
2
$ docker start nb1/Backend1
nb1/Backend1

Результаты в:

haproxy_4

HAProxy и консул шаблон Выводы

Это было очень быстрое введение в использование Docker вместе с Consultemplate и HAProxy. Как вы видели, заставить все это работать довольно легко. После того, как вы настроили докер и консул, связать дополнительные компоненты стало намного проще. В реальном сценарии мы также должны зарегистрировать HAProxy в Consul, чтобы другие службы могли легко найти экземпляр HAProxy.

В последней части этой статьи мы кратко рассмотрим некоторые функции, предоставляемые EnvConsul.

EnvConsul, легко предоставить свойства среды при запуске приложения.

Обычный способ передачи данных конфигурации в приложение (особенно в архитектуре докера / микросервисов) — использование переменных среды. Это простой, ненавязчивый, независимый от языка способ настройки ваших служб. Это даже одна из тем приложения 12 фактор .

«Приложение с двенадцатью факторами сохраняет конфигурацию в переменных среды (часто сокращается до env vars или env). Env vars легко переключать между развертываниями без изменения кода; в отличие от конфигурационных файлов, существует небольшая вероятность того, что они случайно попадут в репозиторий; и в отличие от пользовательских файлов конфигурации или других механизмов конфигурации, таких как Java System Properties, они являются независимым от языка и ОС стандартом ».

Однако, это становится очень громоздким, когда вы имеете дело с большими приложениями или большими конфигурациями, особенно когда вам приходится иметь дело с экранированием кавычек / переносов строк и т. Д.

К счастью, мы можем решить эту проблему с Консулом. Как вы, наверное, уже знаете, Consul также является распределенным хранилищем Key-Value:

envconsul1

С EnvConsul мы можем использовать информацию из хранилища KV в Консуле в качестве параметров среды перед запуском нашего приложения. Так как это будет работать? Мы можем проверить это очень легко, поскольку EnvConsul — это просто исполняемый файл golang, который мы можем запустить. Я добавил версию для Mac в хранилище (в каталоге extras ), но вы можете скачать сборки для вашей ОС здесь: https://releases.hashicorp.com/envconsul/0.6.1/

Мы просто запустим его и посмотрим, что произойдет:

1
2
3
4
5
6
./envconsul -consul=nb-consul.local:8500 -prefix docker -once env
...
network/v1.0/endpoint/815dd44b77f391bd9a63f4e107aa1a7d3f371c91427abcf4be34493aa7ec25cd/=
nodes/192.168.99.112:2376=192.168.99.112:2376
swarm/nodes/192.168.99.110:2376=192.168.99.110:2376
...

Я пропустил большую часть результатов, но в основном то, что мы здесь делаем, это использование env-consul, чтобы получить все ключи, которые он сохранил в дереве докеров , и добавить их в качестве переменных среды. После того, как они установлены, мы запускаем команду env , которая просто выводит все переменные окружения, которые у нас есть. Если вы запустите это самостоятельно, вы увидите множество информации, связанной с docker-network и docker-swarm, установленной в качестве переменных среды.

Конечно, мы также можем сами установить несколько пар KV:

envconsul2

И когда мы получаем их, мы видим, что значения передаются непосредственно в нашу команду как переменные среды:

1
2
3
$ ./envconsul -consul=nb-consul.local:8500 -prefix smartjava -once env | tail -n 2
key2=The value of key 2
key1=The Value of Key 1

Это, однако, не все, что вы можете сделать с envconsul. Он также предоставляет простой способ реагировать на изменения в парах ключ-значение. Представьте, что у вас запущена служба, настроенная через свойство env. И если это свойство env изменяется, вам следует перезапустить службы. Это то, что EnvConsul может сделать для вас:

01
02
03
04
05
06
07
08
09
10
11
$ cat env.sh
#!/bin/bash
env
tail -f /dev/null
  
$ ./envconsul -consul=nb-consul.local:8500 -pristine -prefix=smartjava ./env.sh
PWD=/Users/jos/dev/git/nextbuild-consul/extra/envconsul
SHLVL=1
key2=The value of key 2
key1=The Value of Key 1
_=/usr/bin/env

В этот момент процесс продолжит работу, и envconsul будет ожидать обновления хранилища ключей Консула. Поэтому, когда мы что-то там обновляем, это результат:

envconsul3

Как вы можете видеть на этом изображении, всякий раз, когда мы меняем значение в хранилище KV, наш скрипт перезапускается с новой конфигурацией, передаваемой как переменные env.

Выводы

В этой статье мы показали, что использовать HAProxy очень просто. Вы можете включить его в свою архитектуру, используя те же концепции и идеи, которые мы видели в предыдущей статье. Объедините это с ConsulTemplate, и получить легко настраиваемую программную настройку балансировки нагрузки — это просто.

Еще одна интересная технология — EnvConsul, которая прекрасно интегрируется с распределенным хранилищем KV, предоставляемым Consul. Он даже предоставляет функциональные возможности для перезапуска вашего приложения при изменениях конфигурации, что является действительно мощной функцией.

И это завершает вторую статью в этой серии. В следующей части этой серии мы рассмотрим, как вы можете упростить регистрацию услуг в Консуле. Поэтому замените пользовательский скрипт, который мы показали в предыдущей статье, более продвинутым решением.

Ссылка: Обнаружение услуг с Docker и Consul: часть 2 от нашего партнера JCG Йоса Дирксена из блога Smart Java .