За последний год я стал большим поклонником использования Consul для всего, что связано с обнаружением сервисов. Если вы пользуетесь микросервисами, вы, вероятно, столкнулись с проблемой, заключающейся в том, что с увеличением количества создаваемых вами услуг становится все сложнее управлять связью между всеми этими службами. Консул обеспечивает идеальное решение этой проблемы. Он предоставляет простой в использовании, открытый (основанный на стандартах) подход к обнаружению услуг (и, кроме того, предоставляет большой набор других функций). Недавно я выступил с докладом о том, как выполнить обнаружение служб в архитектуре микросервисов с помощью Consul , и получил несколько запросов, чтобы объяснить немного больше об этом. Итак, в этой статье и нескольких последующих действиях я расскажу немного больше о том, как вы можете использовать Консул. Я не просто остановлюсь только на части обнаружения услуг, предоставляемой консулом, но также покажу вам пару других функций, предоставляемых консулом или одним из инструментов, его окружающих. Обратите внимание, что все примеры, файлы Docker и т. Д. Можно найти в следующем репозитории: https://github.com/josdirksen/next-build-consul . Поэтому вместо «копирования и вставки» из этой статьи просто клонируйте репозиторий.
Начиная
В этой первой статье мы создадим простую архитектуру на основе докера с множеством сервисов, которые будут взаимодействовать друг с другом с помощью простых HTTP-вызовов и обнаруживать друг друга с помощью Consul. Наша начальная целевая архитектура выглядит примерно так:
Чтобы выполнить все это, нам сначала нужно предпринять следующие шаги для настройки среды, в которой мы можем запускать сервисы:
Примечание: я использую docker-machine на моем Mac, чтобы сделать все это. Если вы используете Windows или Linux, команды могут немного отличаться. Будем надеяться, что Docker для Mac (и Windows) быстро выйдет из бета-версии ( https://blog.docker.com/2016/03/docker-for-mac-windows-beta/) , поэтому нам это больше не нужно …
- Создайте четыре док-машины: одну, на которой мы запустим сервер Consul, и три, на которых мы запустим наши отдельные сервисы и агента Consul.
- Запустите главный консул-сервер: мы будем использовать один сервер Consul (и несколько агентов Consul, подробнее об этом позже), чтобы отслеживать работающие службы и некоторые связанные с докером вещи.
- Настройка Docker Swarm : чтобы избежать необходимости развертывания наших служб по отдельности, мы будем использовать Docker Swarm для управления тремя узлами, на которых мы будем запускать наши службы. В оставшейся части этой статьи мы будем использовать docker-compose для запуска и остановки отдельных сервисов.
- Настройка оверлейной сети Docker: если мы хотим, чтобы наши сервисы общались друг с другом простым способом, мы создадим оверлейную сеть. Это позволит компонентам, которые мы развертываем в Docker, легко общаться друг с другом (так как они будут использовать одну подсеть)
- Запустите агентов Консула: у каждого узла будет свой собственный агент консула, который будет следить за состоянием служб на этом узле и связываться с сервером консула.
Создание докер-машин
Итак, первое, что мы сделаем, это создадим несколько докеров. Сначала мы создадим докер-машину, которая будет содержать наш консул-сервер. Причина, по которой мы запускаем этот первый, заключается в том, что мы можем указать другим докер-машинам консультироваться с запущенным внутри этого контейнера и использовать его для управления docker-swarm и оверлейной сетью, которую мы хотим использовать.
1
|
docker-machine create nb-consul --driver virtualbox |
Прежде чем мы запустим сервер Consul, давайте кратко рассмотрим архитектуру, лежащую в основе Consul.
На этом изображении вы можете увидеть два режима, в которых может работать Консул. Он может работать в режиме сервера или в режиме агента. Все Серверы общаются друг с другом и решают, кто является лидером. Агент просто общается с одним из серверов и обычно работает на узле, на котором также запущены службы. Обратите внимание, что состояние между всеми серверами и агентами в кластере является общим. Поэтому, когда служба регистрируется у одного из агентов, эта информация становится доступной для всех серверов и агентов, которые связаны друг с другом.
Для этого набора статей мы не будем устанавливать кластер серверов, а просто будем его использовать. Теперь, когда у нас работает докер, мы можем запустить консул-сервер. Прежде чем мы начнем, позвольте мне сначала показать вам простой скрипт, который облегчает переключение между различными докер-машинами и псевдонимом, который мы используем, чтобы избежать ввода «docker-machine».
1
2
3
4
5
6
7
|
# quickly switch environments e.g: . dm-env nb-consul $ cat ~/bin/dm-env eval `docker-machine env $ 2 $ 1 ` # avoid typing too much $ alias dm dm=docker-machine |
Таким образом, с этими псевдонимами, сначала мы делаем «dm-env nb-consul», чтобы выбрать правильный докер.
Запустить главный консул машины
Затем мы получаем IP-адрес этого сервера, а затем мы можем запустить наш сервер Consul следующим образом.
1
2
3
4
5
6
7
8
|
# get the ip address $ 192.168 . 99.106 # use this ip address in the advertise docker run -d --restart always -p 8300 : 8300 -p 8301 : 8301 -p 8301 : 8301 /udp -p 8302 : 8302 /udp \ -p 8302 : 8302 -p 8400 : 8400 -p 8500 : 8500 -p 53 : 53 /udp -h server1 progrium/consul \ -server -bootstrap -ui-dir /ui -advertise $(dm ip nb-consul) |
На данный момент у нас работает консольный сервер Docker. Теперь давайте создадим три других сервера, на которых мы будем запускать наши сервисы.
Настройка Docker Swarm
Как вы можете видеть в следующих командах, мы также одновременно создаем рой-кластер Docker, и узел «nb1» является мастером роя.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
|
docker-machine create -d virtualbox --swarm --swarm-master \ --swarm-discovery= "consul://$(docker-machine ip nb-consul):8500" \ --engine-opt= "cluster-store=consul://$(docker-machine ip nb-consul):8500" \ --engine-opt= "cluster-advertise=eth1:2376" nb1 docker-machine create -d virtualbox --swarm \ --swarm-discovery= "consul://$(docker-machine ip nb-consul):8500" \ --engine-opt= "cluster-store=consul://$(docker-machine ip nb-consul):8500" \ --engine-opt= "cluster-advertise=eth1:2376" nb2 docker-machine create -d virtualbox --swarm \ --swarm-discovery= "consul://$(docker-machine ip nb-consul):8500" \ --engine-opt= "cluster-store=consul://$(docker-machine ip nb-consul):8500" \ --engine-opt= "cluster-advertise=eth1:2376" nb3 |
На данный момент у нас есть четыре док-машины и работают. Один из них работает мастером-консулом, а другие пока мало что делают.
1
2
3
4
5
6
|
$ dm ls NAME ACTIVE DRIVER STATE URL SWARM nb1 - virtualbox Running tcp: //192.168.99.110:2376 nb1 (master) nb2 - virtualbox Running tcp: //192.168.99.111:2376 nb1 nb3 - virtualbox Running tcp: //192.168.99.112:2376 nb1 nb-consul * virtualbox Running tcp: //192.168.99.106:2376 |
Прежде чем мы продолжим настройку ведомых устройств, есть еще один полезный скрипт, который может пригодиться:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
|
$ cat addToHost #!/usr/bin/env bash $ cat addToHost #!/usr/bin/env bash update-docker-host(){ # clear existing docker.local entry from /etc/hosts sudo sed -i "/" ${ 1 } "\.local$/d" /etc/hosts # get ip of running machine export DOCKER_IP= "$(docker-machine ip $1)" # update /etc/hosts with docker machine ip && sudo /bin/bash -c "echo \"${DOCKER_IP} $1.local\" >> /etc/hosts" } update-docker-host nb1 update-docker-host nb2 update-docker-host nb3 update-docker-host nb-consul |
Этот скрипт добавляет IP-адреса докер-машин в ваш локальный файл «hosts». Это означает, что мы можем просто получить доступ к узлам докера, просто перейдя, например, по адресу « http: //nb-consul.local: 8500 ».
Настройте сеть докеров
В нашем сценарии мы хотим, чтобы все наши сервисы могли общаться друг с другом. У нас есть несколько хостов докеров, поэтому нам нужно найти простой способ, чтобы службы работали в узле «nb1», чтобы иметь возможность общаться с «nb2». Самый простой способ сделать это — создать единую сеть, которая будет использоваться всеми службами, работающими в док-контейнерах. Для этого мы создаем простую «оверлейную» сеть, например:
1
2
3
|
# select the swarm master $dm-env nb1 --swarm # create an overlay network the the name my-net |
И поскольку мы создали это на нашем мастере роя, эта сеть будет доступна всем членам нашего роя. Когда мы создадим наши сервисы позже, мы подключим их к этой сети, чтобы они все находились в одной подсети.
Начать консул агентов
Чтобы запустить консулов, мы собираемся использовать docker-compose. Файл docker-compose очень прост и является простым способом избежать ввода всех команд запуска (особенно когда вы делаете живые демонстрации)
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
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
|
version: '2' services: agent- 1 : image: progrium/consul container_name: consul_agent_1 ports: - 8300 : 8300 - 8301 : 8301 - 8301 : 8301 /udp - 8302 : 8302 - 8302 : 8302 /udp - 8400 : 8400 - 8500 : 8500 - 53 : 53 /udp environment: - "constraint:node==nb1" command: -ui-dir /ui -join 192.168 . 99.106 -advertise 192.168 . 99.110 networks: default : aliases: - agent- 1 agent- 2 : image: progrium/consul container_name: consul_agent_2 ports: - 8300 : 8300 - 8301 : 8301 - 8301 : 8301 /udp - 8302 : 8302 - 8302 : 8302 /udp - 8400 : 8400 - 8500 : 8500 - 53 : 53 /udp environment: - "constraint:node==nb2" command: -ui-dir /ui -join 192.168 . 99.106 -advertise 192.168 . 99.111 networks: default : aliases: - agent- 2 agent- 3 : image: progrium/consul container_name: consul_agent_3 ports: - 8300 : 8300 - 8301 : 8301 - 8301 : 8301 /udp - 8302 : 8302 - 8302 : 8302 /udp - 8400 : 8400 - 8500 : 8500 - 53 : 53 /udp environment: - "constraint:node==nb3" command: -ui-dir /ui -join 192.168 . 99.106 -advertise 192.168 . 99.112 networks: default : aliases: - agent- 3 networks: default : external: name: my-net |
Ничего особенного в этом файле. Единственное, что вы можете заметить, это то, что мы используем явные IP-адреса в командах для запуска агентов Консула. Мы могли бы просто использовать для этого переменную окружения, которая устанавливается простым скриптом bash. Но для этой статьи мы просто указываем IP-адреса соответствующих докер-машин. Убедитесь, что ваш «DOCKER_HOST» указывает на мастера роя докеров и запускайте агентов следующим образом:
01
02
03
04
05
06
07
08
09
10
11
12
|
# start the agents $ docker-compose -f docker-compose-agents.yml up -d Creating consul_agent_3 Creating consul_agent_2 Creating consul_agent_1 # check what is running $ docker ps --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 |
На данный момент у нас есть сервер Consul, работающий в док-машине «nb-consul», и на наших узлах работают три агента. Чтобы проверить нашу настройку, давайте откроем интерфейс сервера Consul: http: //nb-consul.local: 8500
И, как вы видите, у нас работает 1 сервер (наш сервер Consul) и три агента. Итак, с этого момента мы можем начать добавлять наши сервисы, чтобы перейти к этой архитектуре:
Добавление услуг
Сервисы в этом случае — просто простые приложения golang. Я создал простое приложение, которое может работать в веб-интерфейсе или в бэкэнд-режиме. В режиме внешнего интерфейса он предоставляет минимальный пользовательский интерфейс с кнопкой для вызова бэкэнд-службы, а в режиме внутреннего интерфейса он предоставляет простой API, который возвращает некоторую информацию вызывающей стороне, и предоставляет простой интерфейс, показывающий некоторую статистику. Для удобства я перенес это изображение в концентратор докеров ( https://hub.docker.com/r/josdirksen/demo-service/ ), чтобы вы могли легко использовать его без необходимости сборки из исходного репозитория github.
Как вы можете видеть из предыдущего обзора архитектуры, мы хотим запустить интерфейс и бэкэнд-сервис на каждом из узлов. Мы могли бы сделать это вручную, но, поскольку у нас есть docker-swarm, мы можем легко сделать это через один файл docker-compose. Если вы хотите посмотреть, как этот файл выглядит, вы можете проверить источники здесь ( https://github.com/josdirksen/next-build-consul) .
Давайте сначала запустим сервисы, а затем посмотрим, как они регистрируются в Консуле:
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
|
# make sure you select the swarm master $ . dm-env nb1 --swarm # now use docker-compose to run the backend services $ docker-compose -f docker-compose-backend.yml up -d Creating Backend2 Creating Backend3 Creating Backend1 # and use docker-compose to run the frontend services $ docker-compose -f docker-compose-frontend.yml up -d Creating Frontend1 Creating Frontend3 Creating Frontend2 # check in docker if everything is running $ docker ps --format '{{ .ID }}\t{{ .Image }}\t{{ .Command }}\t{{ .Names}}' 65846be2e367 josdirksen/demo-service "/entrypoint.sh --typ" nb2/Frontend2 aedd80ab0889 josdirksen/demo-service "/entrypoint.sh --typ" nb3/Frontend3 d9c3b1d83b5e josdirksen/demo-service "/entrypoint.sh --typ" nb1/Frontend1 7c860403b257 josdirksen/demo-service "/entrypoint.sh --typ" nb1/Backend1 80632e910d33 josdirksen/demo-service "/entrypoint.sh --typ" nb3/Backend3 534da0670e13 josdirksen/demo-service "/entrypoint.sh --typ" nb2/Backend2 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 |
Как вы можете видеть в последнем выводе «docker ps», у нас работают три интерфейса, три сервера и три агента консула. Это в значительной степени та архитектура, к которой мы стремимся. Мы также можем увидеть это, когда откроем Консул:
Как видите, у нас есть три внешних сервиса и три внутренних сервиса, зарегистрированных в Консуле. Если мы откроем один из бэкэндов, мы увидим некоторую общую информацию:
И мы можем использовать интерфейс внешнего интерфейса, чтобы вызвать один из наших бэкэндов:
Однако есть пара вопросов, на которые мы должны ответить:
- Регистрация сервиса : когда мы запускаем бэкэнд или фронтенд сервис, мы видим его в Консуле. как нам это сделать?
- Обнаружение службы : и когда мы нажимаем кнопку на веб-службе, вызывается одна из внутренних служб. Как веб-интерфейс узнает, какую службу позвонить?
В следующих разделах мы рассмотрим эти вопросы поближе.
Служба регистрации
Во-первых, регистрация сервиса. Чтобы зарегистрировать услугу в Консуле, мы должны сделать очень простой вызов REST нашему местному консулу-агенту, который выглядит примерно так:
1
2
3
4
5
6
7
8
9
|
{ "Name" : "service1" , "address" : "10.0.0.12" , "port" : 8080 , "Check" : { "interval" : "5s" } } |
Как видите, мы указываем имя, адрес и порт, где находится служба, и добавляем дополнительную проверку работоспособности. Когда healtcheck возвращает что-то в диапазоне 200, служба помечается как исправная и может быть обнаружена другими службами. Так, как мы делаем это для наших услуг. Если вы посмотрите на источники для этого примера, вы можете найти файл «script / entrypoint.sh», который выглядит следующим образом:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
|
#!/usr/bin/env bash IP=`ip addr | grep -E 'eth0.*state UP' -A2 | tail -n 1 | awk '{print $2}' | cut -f1 -d '/' ` NAME= "$2-service" read -r -d '' MSG << EOM { "Name" : "$NAME" , "address" : "$IP" , "port" : $PORT, "Check" : { "interval" : "5s" } } EOM curl -v -XPUT -d "$MSG" http: //consul_agent_$SERVER_ID:8500/v1/agent/service/register && /app/main "$@" |
Что делает этот скрипт, так это то, что он создает JSON для отправки на консул-агент, и перед запуском основного приложения он использует «curl» для его отправки. Поэтому, когда служба запускается, она автоматически регистрируется в локальном агенте консула (обратите внимание, что вы также можете сделать это более автоматически, например, с помощью Consul Registrator . Это работает, поскольку мы можем просто ссылаться на локального агента по его имени, поскольку он находится в тот же самый контроллер. Если вы посмотрите внимательно, вы можете увидеть, что здесь мы используем пару переменных окружения. Они передаются через файл docker-compose, который мы используем:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
|
... frontend- 1 : image: josdirksen/demo-service container_name: Frontend1 ports: - 8090 : 8090 environment: - "constraint:node==nb1" - SERVER_ID= 1 - SERVERNAME=Server1 - PORT= 8090 command: /entrypoint.sh --type frontend dns: 192.168 . 99.106 dns_search: service.consul ... |
Интересная часть здесь — записи DNS. Как вы помните, 192.168.99.106 — это адрес нашего консул-сервера. Это означает, что мы проводим поиск DNS против Консула (мы могли бы также указать на агента консула).
Обнаружение услуг
С помощью этой настройки мы можем просто ссылаться на сервис по имени и использовать DNS для его разрешения. Ниже показано, как это работает.
01
02
03
04
05
06
07
08
09
10
11
12
|
# check which IPs are registered for the backend-service # called from outside the container $ dig @nb -consul.local backend-service.service.consul + short 10.0 . 9.7 10.0 . 9.8 10.0 . 9.6 # If we do this from a container, we can do just this docker exec -ti nb2/Frontend2 ping backend-service PING backend-service.service.consul ( 10.0 . 9.8 ): 56 data bytes 64 bytes from 10.0 . 9.8 : icmp_seq= 0 ttl= 64 time= 0.809 ms 64 bytes from 10.0 . 9.8 : icmp_seq= 1 ttl= 64 time= 0.636 ms |
Круто верно? Мы можем обнаружить сервис, просто используя DNS. Это также означает, что интегрировать это в наши существующие приложения очень просто, поскольку мы можем просто полагаться на базовое разрешение DNS. Например, в веб-сервисе мы вызываем бэкэнд, используя этот код:
01
02
03
04
05
06
07
08
09
10
|
if err != nil { // handle error fmt.Println(err) } else { defer resp.Body.Close() body, _ := ioutil.ReadAll(resp.Body) w.Header().Set( "Content-Type" ,resp.Header.Get( "Content-Type" )) w.Write(body) } |
Это вызывает одну из внутренних служб, использующих DNS. Теперь у нас также есть простое переключение при сбое, так как временное время DNS Consul установлено в 0. Приложения могут все еще выполнять некоторое кэширование, но это означает, что у нас уже есть некоторое базовое переключение:
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
|
$ curl -s backend-service: 8081 { "result" : { "servername" : "Server1" , "querycount" : 778 } } # shutdown server 1 and do again, curl has a DNS cache of # 1 minute, so you might need to wait a bit $ curl -s backend-service: 8081 { "result" : { "servername" : "Server2" , "querycount" : 770 } } $ curl -s backend-service: 8081 { "result" : { "servername" : "Server2" , "querycount" : 771 } } |
Что, конечно, также работает для нашего приложения frontend / golang:
В продолжении этой статьи мы также покажем более продвинутую отработку отказа, представив HAProxy в качестве промежуточного звена для более продвинутых методов отработки отказа.
Выводы
Это в значительной степени завершает эту первую статью. Итак, в заключение, что мы сделали:
- Мы настроили простую архитектуру, используя 4 док-узла. 1 для сервера консула и три для наших услуг.
- Службы регистрируются в Консуле при запуске службы.
- Нам не нужно явно что-то делать, чтобы включить обнаружение службы. Мы можем использовать стандартный DNS для поиска службы.
- Consul использует TTL 0 для DNS и возвращает доступные сервисы, используя roundrobin. Как вы уже видели, вы уже можете использовать это для базового переключения при сбое, когда поиск DNS не удается.
Следите за новостями в ближайшие недели.
Ссылка: | Обнаружение услуг с Docker и Consul: часть 1 от нашего партнера JCG Йоса Дирксена из блога Smart Java . |