Ansible: развёртывание Django-стека на 3 VM

Это четвертая и завершающая статья из цикла: настройка и развертывание Django-стека на серверах. До этого мы освоили основы Terraform, научились управлять облачным ЦОДом через него и подробно рассмотрели работу Django-стека на одном сервере.

В этой статье — освежим знания по Ansible, познакомимся с ролями и развернем на нашей подготовленной заранее инфраструктуре Django-стек. Код проекта есть на github, там же краткий мануал в виде удобного readme-файла.

Важное из предыдущих шагов

Важное из предыдущих шагов

Обозначим буквально несколько моментов из предыдущих шагов (статей), которые важны для нас сейчас:

  1. Мы создали ВЦОД и получили IP адрес виртуального роутера — 89.22.173.56. Его мы будем указывать для подключения Ansible в инвентаризационном файле.
  2. Перед созданием ВМ мы развернули маршрутизируемую сеть — 10.10.0.1/24, чтобы ВМ могли общаться между собой и, чтобы у Ansible был доступ к ним по SSH.
  3. На виртуальном роутере мы настроили S-NAT(исходящий трафик) и D-NAT(входящий трафик) правила. Получились следующие соотношение портов:
    • 89.22.173.56:22 → 10.10.0.3:22 — Nginx;
    • 89.22.173.56:80 → 10.10.0.3:80 — Nginx;
    • 89.22.173.56:23 → 10.10.0.4:22 — Django;
    • 89.22.173.56:24 → 10.10.0.5:22 — PostgreSQL.
    Каждой VM был назначен свой внешний SSH-порт.

На локальной машине (с той, которой запускается Ansible) мы установили Go, Terraform, провайдер VDC и Ansible. Установили программу sshpass (sudo apt install sshpass). Теперь мы готовы к развертыванию Django-стека на серверах. Делать мы это будем с помощью Ansible-ролей.

Что такое Ansible роли?

Что такое Ansible роли?

Роль в Ansible — это независимая сущность, решающая какой-то свой набор задач. Говоря проще, роль — это директория с поддиректориями и файлами, где расположены задачи.

Обычно роли независимы от проектов и хранятся удаленно, если их зависимость не продиктована логикой проекта. Роли имеют строго заданную структуру и создаются командой ansible-galaxy init [название роли].

Структура Ansible-ролей:

├── defaults — директория содержит файлы с переменными по умолчанию
│ └── main.yml
├── files — директория с файлами, которые будут скопированы на удалённый сервер(а)
├── handlers — директория содержит хендлеры*
│ └── main.yml
├── meta — директория содержит вспомогательную информацию
│ └── main.yml
├── README.md
├── tasks — директория содержит набор задач, которые должна выполнять роль
│ └── main.yml
├── templates — директория содержит шаблоны Jinja2
├── tests — директория содержит тесты**
│ ├── inventory
│ └── test.yml
└── vars — директория содержит пользовательские переменные
└── main.yml

* хендлеры — это специальные задачи, которые выполняются после выполнения основных задач (тасков).
** Тесты запускаются после выполнения плейбука.

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

Для нашего проекта мы создадим 3 роли: frontend, backend и postgresql. Задачи для каждой роли помещаются в tasks/main.yaml. Шаблоны файлов, которые будут скопированы на сервер с подменой данных размещаются в templates, а вот переменные у всех 3 ролей общие, поэтому мы вынесли их в отдельный файл, находящийся в верхней директории уровня проекта — group_vars/all.yaml.

Из group_vars/all.yaml данные также подтягиваются в инвентаризационный файл и мастер-playbook — side.yaml. Рассмотрим ближе структуру проекта и логику его работы.

Структура проекта и логика работы

Структура проекта и логика работы

Начнем со структуры проекта. Для Ansible очень важна строгая структура директорий, поэтому роли должны располагаться в отдельной поддиректории проекта. Название поддиректории должно быть лаконичным и понятным.

Вот структура проекта, которая получилась у нас:

.
├── Ansible_roles #Директория Ansible ролей
│ ├── backend #Директория backend Ansible роли
│ │ ├── defaults
│ │ ├── handlers
│ │ ├── meta
│ │ ├── README.md
│ │ ├── tasks
│ │ ├── templates
│ │ ├── tests
│ │ └── vars
│ ├── frontend #Директория frontend Ansible роли
│ │ ├── defaults
│ │ ├── files
│ │ ├── handlers
│ │ ├── meta
│ │ ├── README.md
│ │ ├── tasks
│ │ ├── templates
│ │ ├── tests
│ │ └── vars
│ └── postresql #Директория postresql Ansible роли
│ ├── defaults
│ ├── handlers
│ ├── meta
│ ├── README.md
│ ├── tasks
│ ├── tests
│ └── vars
├── group_vars #Общие переменные
│ └── all.yaml
├── hosts.ini #Инвентаризационный файл
├── main.tf
├── pars_state.py
├── README.md
├── settings.tf
├── side.retry
├── side.yaml #Мастер-playbook
├── terraform-provider-vcd
├── terraform.tfstate
└── terraform.tfstate.backup

Роли содержат задачи, которые Ansible будет выполнять на удаленных серверах, а hosts.ini и side.yaml содержат данные для подключения.

hosts.ini — инвентаризационный файл, в нём указываются IP-адреса серверов и данные для подключения. Наш hosts.ini внутри выглядит так:

[all]
frontend_server ansible_port=22 ansible_host={{vdc_ip}} ansible_username={{user}} ansible_password={{passwd}} ansible_python_interpreter={{interp}}
backend_server ansible_port=23 ansible_host={{vdc_ip}} ansible_username={{user}} ansible_password={{passwd}} ansible_python_interpreter={{interp}}
postgresql ansible_port=24 ansible_host={{vdc_ip}} ansible_username={{user}} ansible_password={{passwd}} ansible_python_interpreter={{interp}}

В квадратных скобках указывается название секции с серверами — [all]. Далее следует название сервера — frontend_server, затем идёт набор параметров для подключения в формате «ключ=значение». Параметры отделяются друг от друга пробелом, а «ключ=значение» — нет.

Конструкция {{какое-то название}} — это ссылка на переменную. В нашем случае переменные находятся в group_vars/all.yaml. Название директории и файла не случайны, они фиксированы, если бы у нас группа в инвентаризационном файле называлась — [back], то название файла в group_vars соответствовало бы названию группы.

side.yaml — Мастер-playbook, который соотносит роли и целевые серверы, на которых будут выполняться задачи ролей. Вот как выглядит наш мастер-playbook:

- name: Установка Nginx
hosts: frontend_server
roles:
- frontend

- name: Установка PostgreSQL
hosts: postgresql
roles:
- postresql

- name: Установка Django и Gunicorn
hosts: backend_server
roles:
- backend

Тут всё просто! Name — название задачи, hosts — указывает на сервер, на котором будет исполняться Ansible-роль, roles — роли, которые будут выполняться, на указанных в hosts серверах.

Ролей и серверов в одной задаче может быть огромное количество, так же как и задач. Важно! Задачи выполняются последовательно, поэтому если вы пишете роли, которые зависят от последовательности — располагайте их в правильной последовательности.

Например, второй задачей у нас идёт установка PostgreSQL, потому что Django в завершении установки будет делать миграцию БД и если PSQL не будет установлен и настроен — миграция не пройдёт.

Со структурой, ролями, инвентаризационным файлом и мастер-playbook разобрались, осталось основное — задачи в ролях. Вот они:

Тут мы тоже использовали ссылки на переменные — {{ название переменной }}. Так же как и в шаблонах файлов, расположенных в директории templates каждой роли.

Используемые в проекте переменные выглядят так:

#Параметры подключения
vdc_ip: 89.22.173.56
user: "root"
passwd: "Pseudokatkov1"
interp: "/usr/bin/python3"

#Параметры Nginx
gunicorn_ip: 10.10.0.4:8001
domain: my_test_site.ru

#Параметры Django, Gunicorn и PSQL
project_name_path: /var/www/django_test
project_name: test_project
web_app: first
bd_name: dtb_db
bd_user: dc
bd_passwd: Django_Connecter
bd_host: 10.10.0.5
db_host_port: 5432

Они удобно сгруппированы по условным блокам и позволяют создать единую точку входа в проект. Единственное, что вам нужно поменять для воспроизведения ролей в виртуальном ЦОДе — параметры подключения.

Итог

Итог

В итоге мы имеем проект, который подключается к Cloud Director по API с помощью провайдера VCD, разворачивает 3 ВМ, связывает их маршрутизируемой сетью, настраивает S-NAT и D-NAT правила для трафика и устанавливает на серверы Django-стек с помощью Ansible.

Графически схема работы проекта выглядит так:

Графически схема работы проекта

Кликнете на схему — она откроется в новой вкладке, и вы сможете детально её рассмотреть.

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

Достаточно скачать проект с github и развернуть его, на каком-нибудь удаленном сервере, например, VPS/VDS от 1cloud. В readme проекта есть краткая инструкция.

Если вы интересуетесь темой IaC и, в частности, Terraform или Ansible — вам могут быть полезны и интересны наши статьи: