- Пример файла ansible/playbook.yml
- Пример файла hosts
- Пример файла ansible/roles/infrastructure/defaults/main.yml
- Пример файла ansible/roles/infrastructure/tasks/main.yml
- Пример файла ansible/roles/infrastructure/templates/hosts.j2
- Пример файла ansible/group_vars/webservers.yml
- Пример файла ansible/.gitignore
- Пример файла terraform/main.tf
- Пример файла terraform/.terraform.lock.hcl
- Пример файла ansible/.gitignore
- Примеры команд
Ниже приведён пример создания инфраструктуры и конфигурирования серверов с помощью Ansible, который использует модуль community.general.terraform
Пример файла ansible/playbook.yml
- name: Setup Infrastructure
hosts: localhost
connection: local
roles:
# Пример использования своей роли
# Структура директорий роли описана здесь https://docs.ansible.com/ansible/latest/user_guide/playbooks_reuse_roles.html
- name: infrastructure
vars:
terraform_dir: "{{ playbook_dir }}/../terraform"
Пример файла hosts
[webservers]
web-1 ansible_host=255.248.82.204 ansible_user=root
web-2 ansible_host=255.248.92.7 ansible_user=root
Пример файла ansible/roles/infrastructure/defaults/main.yml
# стандартные значения переменных, если для роли не задали переменные
pvt_key: ~/.ssh/id_rsa
do_token:
Пример файла ansible/roles/infrastructure/tasks/main.yml
- name: Create terraform infrastructure (webservers, loadbalancer, domain)
community.general.terraform:
project_path: "{{ terraform_dir }}"
variables:
do_token: "{{ do_token }}"
pvt_key: "{{ pvt_key }}"
force_init: yes
state: present
# Используются outputs из terraform для получения данных о серверах (ip-адреса)
register: infra
- name: Add hosts
template:
src: templates/hosts.j2
dest: hosts
Пример файла ansible/roles/infrastructure/templates/hosts.j2
{# Формируется инвентори файл с перечисленными серверами #}
[webservers]
{% for droplet in infra.outputs.droplets.value %}
{{droplet.name}} ansible_host={{droplet.ipv4_address}} ansible_user=root
{% endfor %}
Пример файла ansible/group_vars/webservers.yml
do_token: !vault |
$ANSIBLE_VAULT;1.1;AES256
30613562646532633834346535353961333262646639633331663261633164643763643137313861
3834373939663737393830383364393332333963643664310a656266303431656666326339376230
30303638393664353736323931373036343065396330643263316565316539376664623539393564
3736653339643433300a303833623035353132633333363030376436303639643436633537396431
33303538333961313562363261653932303762626433623932396637393131663764316431636330
37616564616337396433333239306137633839343234356136383834633761653334343539386234
38336564373632373037393534646533393731646165343937343264653030386231656461393536
62386537306263613432
Пример файла ansible/.gitignore
vault-password
Пример файла terraform/main.tf
// Для удобства всё находится в одном файле
// При необходимости разделяется на отдельные .tf файлы
// https://registry.terraform.io/providers/digitalocean/digitalocean/latest/docs
terraform {
required_providers {
digitalocean = {
source = "digitalocean/digitalocean"
version = "1.22.2"
}
}
}
// Токен DO и путь к приватному ключу, будут передаваться через CLI
variable "do_token" {}
variable "pvt_key" {}
provider "digitalocean" {
// Использование переменной (токен доступа к DO)
// https://www.terraform.io/docs/language/values/variables.html
token = var.do_token
}
// Ключ можно либо получить созданный, либо создать новый
// 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" "terraform" {
// Имя под которым ключ сохранён в DO
// https://cloud.digitalocean.com/account/security
name = "key"
}
// Outputs похожи на возвращаемые значения. Они позволяют сгруппировать информацию или распечатать то, что нам необходимо
// https://www.terraform.io/docs/language/values/outputs.html
output "droplets" {
// Обращение к ресурсу. Каждый ресурс имеет атрибуты, к которым можно получить доступ
value = [
digitalocean_droplet.web-1,
digitalocean_droplet.web-2
]
}
// Создание балансировщика нагрузки
// https://registry.terraform.io/providers/digitalocean/digitalocean/latest/docs/resources/loadbalancer
resource "digitalocean_loadbalancer" "lb" {
name = "lb"
region = "ams3"
forwarding_rule {
// Порт по которому балансировщик принимает запросы
entry_port = 80
entry_protocol = "http"
// Порт по которому балансировщик передает запросы (на другие сервера)
target_port = 8080
target_protocol = "http"
}
// Порт, протокол, путь по которому балансировщик проверяет, что дроплет жив и принимает запросы
healthcheck {
port = 8080
protocol = "http"
path = "/"
}
droplet_ids = [
digitalocean_droplet.web-1.id,
digitalocean_droplet.web-2.id
]
}
// Создание домена
// https://registry.terraform.io/providers/digitalocean/digitalocean/latest/docs/resources/domain
resource "digitalocean_domain" "example" {
name = "hexlet.devops-baby.club"
ip_address = digitalocean_loadbalancer.example.ip
}
// Создаём дроплет
// https://registry.terraform.io/providers/digitalocean/digitalocean/latest/docs/resources/droplet
resource "digitalocean_droplet" "web-1" {
image = "ubuntu-20-10-x64"
name = "web-1"
region = "ams3"
size = "s-1vcpu-1gb"
private_networking = true
// Добавление приватного ключа на создаваемый сервер
// Обращение к datasource выполняется через data.
ssh_keys = [
data.digitalocean_ssh_key.terraform.id
]
connection {
host = self.ipv4_address
user = "root"
type = "ssh"
private_key = file(var.pvt_key)
timeout = "2m"
}
}
resource "digitalocean_droplet" "web-2" {
image = "ubuntu-20-10-x64"
name = "web-2"
region = "ams3"
size = "s-1vcpu-1gb"
private_networking = true
// Добавление приватного ключа на создаваемый сервер
ssh_keys = [
data.digitalocean_ssh_key.terraform.id
]
connection {
host = self.ipv4_address
user = "root"
type = "ssh"
private_key = file(var.pvt_key)
timeout = "2m"
}
}
Пример файла terraform/.terraform.lock.hcl
# This file is maintained automatically by "terraform init".
# Manual edits may be lost in future updates.
provider "registry.terraform.io/digitalocean/digitalocean" {
version = "1.22.2"
constraints = "1.22.2"
hashes = [
"h1:VNz0Gte3Rmd/yhUh96FuStEXJWRYx8x2LMolm54U7Rw=",
"zh:406d8f4704f7ff17a5b60c394674e4e85910c86a0090c9f4bb4b33196e132d2b",
"zh:476d9600a13e1d425540c3750a6966d5867ea5d6d5dbfa2d8c8dc6a6ea07b1ac",
"zh:56f56d312ec707e9f33db833876a5086af4f07043eb0c471e002b42339a31bf5",
"zh:669cf9c9dd7e9c812e8896fae34a22cdc938854f6d91778ad74a12083744f4ae",
"zh:714ed181254a95f774de372fac3afdf6ae9658f18b5fe13324df31cb12892f76",
"zh:7762077125a6df36b28cdd4966356d2a7fddb187d010d06aca05a8ecf7ce1612",
"zh:877cb27bf81c56c2e863d9574e34716e1eb49bcb0de0a48578c828bef1863d87",
"zh:92049f21072b76fe5bb9a49842d820eda3ca86098e01bd936ef031b44f0d0207",
"zh:b4fec336c0fa22fd2a053cf3c6a9a4b8622557c18aba622127c86d0f14a1bf52",
"zh:d4df235797896ba7efb16ace5a8f5e27e5751b7bddc102a4f2617d80f71c0cb2",
"zh:ea5ca95527064e3740183f997aecd6e39313bd63d155447d338e2aa7818209b6",
]
}
Пример файла ansible/.gitignore
terraform/.gitignore
Примеры команд
# Инициализация проекта терраформ
# может добавляться флаг -chdir=<dirname> если нужно команда выполняется не в директории с tf файлами
terraform init
# В примерах ниже используется переменная окружения для токена Digital Ocean
export DO_PAT=<token>
# Посмотреть изменения без их выполнения
# https://www.terraform.io/docs/cli/commands/plan.html
terraform plan -var "do_token=$DO_PAT"
# Применить изменения
# https://www.terraform.io/docs/cli/commands/apply.html
terraform apply -var "do_token=$DO_PAT"
# Удалить созданную инфраструктуру
# https://www.terraform.io/docs/cli/commands/destroy.html
terraform destroy -var "do_token=$DO_PAT"
# После применения изменений сохраняется состояние инфраструктуры. Её можно посмотреть
terraform show
# Форматирование
# https://www.terraform.io/docs/cli/commands/fmt.html
terraform fmt
# Проверка стиля (формата) с выводом диффа
terraform fmt -check -diff
# Проверка конфигурации
# https://www.terraform.io/docs/cli/commands/validate.html
terraform validate
# Таким образом можно зашифровать значение hexlet в переменную the_secret
# с помощью пароля в файле vault-password
ansible-vault encrypt_string --vault-password-file vault-password 'hexlet' --name 'the_secret'
# Чтобы расшифровать зашифрованные с помощью Ansible Vault переменные
# если пароль хранится в файле, используется флаг --vault-password-file
ansible-playbook -v --vault-password-file vault-password playbook.yml
# Устанавливаем коллекции определённые в файле requirements.yml
ansible-galaxy collection install -r requirements.yml
# Устанавливаем роли определённые в файле requirements.yml
ansible-galaxy role install -r requirements.yml
# Деплоим приложение
ansible-playbook -i hosts playbook.yml -v --vault-password-file vault-password
# Подключаемся по ssh к хосту с IP 192.168.0.2
ssh username@192.168.0.2
Самостоятельная работа
Представим, что перед вами стоит задача создать инфраструктуру для приложения. Для него необходимо создать как минимум 1 сервер и подключить мониторинг. У приложения могут быть разные среды выполнения: продакшен, стейджинг, стенд для разработчиков.
Процесс создания инфраструктуры и настройки окружения может выглядеть следующим образом:
-
Этап подготовки. Создание файла переменных для Terraform, установка необходимых зависимостей
-
Создание инфраструктуры
-
Установка необходимых пакетов на серверах
-
Деплой проекта
Опишите инфраструктуру и задеплойте приложение. При этом должно соблюдаться требование: все чувствительные данные (токены, пароли) должны быть зашифрованы, а все необходимые данные должны находиться в одном источнике данных и готовиться для Terraform с помощью Ansible. Например такой подход используется в проекте hexlet-basics
Остались вопросы? Задайте их в разделе «Обсуждение»
Вам ответят команда поддержки Хекслета или другие студенты