- Пример файла terraform/main.tf
- Пример файла terraform/.gitignore
- Пример файла ansible/setup_servers.yml
- Пример файла ansible/setupconsulserver.yml
- Пример файла ansible/setupconsulclient.yml
- Пример файла ansible/requirements.yml
- Пример файла ansible/registerconsulservice.yml
- Пример файла ansible/hosts.ini
- Пример файла ansible/group_vars/all.yml
- Пример файла ansible/common/registerconsulserver_ip.yml
- Пример файла ansible/files/devops.json
Ниже приведён пример инфраструктуры из нескольких серверов, на которые устанавливается агент Consul
Пример файла terraform/main.tf
// Для удобства всё находится в одном файле
// При необходимости разделяется на отдельные .tf файлы
// https://registry.terraform.io/providers/digitalocean/digitalocean/latest/docs
terraform {
required_providers {
digitalocean = {
source = "digitalocean/digitalocean"
version = "1.22.2"
}
}
}
provider "digitalocean" {
// Использование переменной (токен доступа к DO)
// https://www.terraform.io/docs/language/values/variables.html
token = var.do_token
}
variable "do_token" {}
variable "pvt_key" {
default = "~/.ssh/id_rsa"
}
output "webserver_ips" {
value = digitalocean_droplet.servers.*.ipv4_address
}
// Создаём дроплеты
// https://registry.terraform.io/providers/digitalocean/digitalocean/latest/docs/resources/droplet
resource "digitalocean_droplet" "servers" {
count = 2
image = "docker-20-04"
name = "service-discovery-${count.index + 1}"
region = "ams3"
size = "s-1vcpu-1gb"
private_networking = true
// Добавление приватного ключа на создаваемый сервер
// Обращение к datasource выполняется через data.
ssh_keys = [
data.digitalocean_ssh_key.example.id
]
}
// Ключ можно либо получить созданный, либо создать новый
// resource "digitalocean_ssh_key" "default" {
// name = "Terraform Homework"
// public_key = file("~/.ssh/id_rsa.pub")
// }
// Используется data source - ресурс не создаётся. Terraform запрашивает информацию о ресурсе
// https://registry.terraform.io/providers/digitalocean/digitalocean/latest/docs/data-sources/droplet
data "digitalocean_ssh_key" "example" {
name = "key"
}
Пример файла terraform/.gitignore
.terraform
terraform.tfstate
*.backup
Пример файла ansible/setup_servers.yml
- name: Setup servers
hosts: all
tasks:
- name: Update cache
apt:
update_cache: yes
- name: install pip
include_role:
name: geerlingguy.pip
- name: Disable UFW
community.general.ufw:
state: disabled
Пример файла ansible/setup_consul_server.yml
- name: Run consul server
hosts: consul_servers
tasks:
- name: Run Consul server
community.docker.docker_container:
name: consul-server
recreate: yes
image: consul:latest
network_mode: host
container_default_behavior: no_defaults
command: "agent -server -ui -node=server-1 -bootstrap-expect=1 -client=0.0.0.0 -advertise={{ advertise_ip }}"
Пример файла ansible/setup_consul_client.yml
- name: Run Consul client container
hosts: consul_clients
tasks:
- include_tasks: common/register_consul_server_ip.yml
- name: Run Consul container
community.docker.docker_container:
name: consul-client
image: consul
recreate: yes
network_mode: host
container_default_behavior: no_defaults
command: "agent -node=client-1 -join={{ consul_server_ip }} -advertise={{ advertise_ip }}"
Пример файла ansible/requirements.yml
collections:
- community.general
- ansible.posix
roles:
- name: geerlingguy.docker
- name: geerlingguy.pip
Пример файла ansible/register_consul_service.yml
- name: Run Consul client container with service
hosts: consul_clients
tasks:
- include_tasks: common/register_consul_server_ip.yml
- name: Copy file with owner and permissions
ansible.builtin.copy:
src: files/devops.json
dest: /tmp/devops.json
- name: Run Consul container
community.docker.docker_container:
name: consul-client
image: consul
recreate: yes
network_mode: host
volumes:
- /tmp/devops.json:/consul/config/devops.json
container_default_behavior: no_defaults
command: "agent -node=client-1 -join={{ consul_server_ip }} -advertise={{ advertise_ip }}"
- name: Run app container
community.docker.docker_container:
name: hexlet-app
image: hexlet/hexlet-app
published_ports: 5000:5000
restart_policy: always
restart: yes
pull: yes
container_default_behavior: no_defaults
env:
SERVER_MESSAGE: "Hello!"
Пример файла ansible/hosts.ini
[consul_servers]
server1 ansible_host=142.93.137.128 ansible_user=root
[consul_clients]
client1 ansible_host=134.122.51.58 ansible_user=root
Пример файла ansible/group_vars/all.yml
pip_package: python3-pip
pip_install_packages:
- name: docker
# Описываем переменную в которой будет храниться IP адрес из предоставляемой VPC
advertise_ip: "..."
Пример файла ansible/common/register_consul_server_ip.yml
- name: Gather facts from consul_server
ansible.builtin.setup:
delegate_to: "server1"
delegate_facts: true
register: consul_server_facts
- set_fact:
# Регистрируем в переменную IP адрес в приватной подсети
consul_server_ip: "..."
- debug:
var: consul_server_ip
Пример файла ansible/files/devops.json
{
"service": {
"name": "devops",
"tags": ["hexlet"],
"port": 5000
}
}
Самостоятельная работа
Вы на опыте познакомитесь с Consul. В данной практике мы описали небольшую инфраструктуру из нескольких серверов. Представим, что эти сервера периодически добавляются в инфраструктуру и убираются из нее (например, для обновления), а перед нами стоит задача понять, какие из серверов доступны. Допустим, у нас есть приложение, которое делает некие вычисления на этих серверах. Для того чтобы сделать эти вычисления, нужно знать ip адрес сервера и убедиться, что он готов к нужной нам работе (например, на нем запущен docker или любая другая программа необходимая для вычислений). Эта задача может быть решена следующими способами:
-
После добавления очередного сервера, копируем его ip адрес в конфиг нашего приложения и перезапускаем его. При удалении сервера делаем все то же самое. Минусы - ручные операции, перезагрузка приложения и тд.
-
Устанавливаем consul и настраиваем service discovery. На каждом сервере устанавливается consul agent, который подключается к другим агентам в известной ему сети. После этого, он начинает передавать всю известную ему информацию остальным агентам в сети. Таким образом происходит быстрый обмен информацией, а при остановке сервера или появлении нового, другие агенты об этом узнают практически мгновенно. Когда мы хотим узнать ip адреса доступных серверов в нашей сети, мы делаем http запрос в Consul агент (О котором мы знаем. Например, в локальный агент.) и получаем список живых серверов вместе с ip адресами. Минусы такого подхода тоже есть. Самый значимый для нас на этом этапе - сложность настройки и обилие новых концепций.
В этой практике сервера и консул агенты на них практически полностью настроены за вас. Ваша задача заключается в том, чтобы установить инфраструктуру и увидеть, как это все работает. В практике нет сложного сервиса, которому требуется знать ip адреса других серверов в сети. Вместо этого вы являетесь этим сервисом, а тестирование происходит через утилиту curl. Поэтому успехом будет считаться получение списка серверов после выполнения всех действий, а так же обзор инфраструктуры и понимание роли consul в ней.
Подготовка
-
Форкните или склонируйте репозиторий с примером инфраструктуры
-
Выполните инициализацию Terraform в директории terraform и создайте инфраструктуру.
-
Скопируйте IP адреса серверов в ansible/hosts из вывода Terraform. В каждой группе должно быть по одному хосту. В группе серверов с алиас хоста —
server1
, в группе клиентов —client1
. -
Выполните подготовку серверов командой make setup-servers. Команда выполнит подготовку серверов: установит необходимые модули для Docker и отключит фаервол (Для простоты тестирования данной практики - в реальном проекте не отключайте, а настраивайте фаервол. Подробнее это разбиралось в уроке Безопасность).
Подготовка окружения завершена, теперь необходимо связать клиент Consul с сервером. Агенты Consul будут запускаться внутри Docker контейнеров. Для того чтобы они могли между собой общаться, им необходимо указать IP адреса из приватной подсети. В качестве такой сети будем использовать VPC которая предоставляется Digital Ocean.
Настройка Service Discovery
-
В файле ansible/group_vars/all.yml опишите переменную
advertise_ip
, в которой будет храниться IP адрес из предоставляемой VPC. Чтобы это сделать, вам необходимо собрать факты о сервере и отфильтровать IP адреса по верной подсети. Этот IP адрес будет выставлять агент Consul, чтобы его могли найти другие агенты. -
В файле ansible/common/register_consul_server_ip.yml опишите задачу, которая зарегистрирует в переменную
consul_server_ip
IP адрес в приватной подсети. Принцип такой же, как и на предыдущем шаге, только данные нужно взять изconsul_server_facts
-
Выполните команду
make setup-consul-server
. Откройте в браузере http://<server_ip>:8500. По этому адресу открывается страница с дашбордом Consul. Убедитесь, что отображается запущенный сервер Consul и других нод нет -
Зайдите по ssh на сервер с сервером Consul. Выполните
docker exec consul-server consul members
и убедитесь, что запущена одна нода -
Выполните запрос с помощью curl по адресу http://<server_ip>:8500/v1/catalog/nodes Убедитесь, что в списке только нода сервера
-
Выполните команду
make setup-consul-client
. Команда запустит на втором сервере Consul, который будет клиентом. Выполните предыдущие действия: посмотрите изменения в дашборде Consul, выполнитеconsul members
(выполните эту команду в контейнере на сервере и с клиентом), выполните запрос к API сервера Consul. -
Выполните команду
make register-consul-service
. Команда запустит контейнер с приложением, скопирует конфиг и перезапустит контейнер с клиентом Consul. Теперь наше приложение будет доступно в дашборде и по api сервера Consul по адресу http://<server_ip>:8500/v1/catalog/service/devops
Дополнительные материалы
Остались вопросы? Задайте их в разделе «Обсуждение»
Вам ответят команда поддержки Хекслета или другие студенты