
Привет, друзья!
В этой серии статей я делюсь с вами своим опытом решения различных задач из области веб-разработки и не только.
Другие статьи серии:
Предыдущая статья была посвящена деплою Angular+Java веб-приложения на виртуальном сервере Ubuntu Linux с помощью Ansible. В этой статье мы научимся настраивать для этого деплоя сеть и создавать виртуальный сервер в облаке с помощью Terraform.
Интересно? Тогда прошу под кат.
Итак, наша задача — подготовить облачную инфраструктуру к деплою приложения, а именно:
-
создать виртуальный сервер Ubuntu Linux с пользователем
ansible -
получить внешний IP-адрес сервера для подключения CLI
ansible
В качестве облака мы будем использовать Yandex Cloud. Обратите внимание, что конфигурация Terraform сильно зависит от конкретного облака, поэтому в другом облаке она будет выглядеть иначе.
Предварительные условия:
-
установка terraform (я буду использовать версию 1.5.7)
-
OAuth-токен Yandex Cloud (
yc config set token <OAuth-токен>)
Пара слов о Terraform
Terraform — это инфраструктура как код (IaC) — инструмент с открытым исходным кодом, разработанный компанией HashiCorp, который позволяет управлять и автоматизировать инфраструктуру в облаках и дата-центрах с помощью конфигурационных файлов.
Основные особенности Terraform:
-
Язык конфигурации
Terraform использует HCL (HashiCorp Configuration Language) — простой и читаемый язык, похожий на JSON. Примеры конфигурации описывают ресурсы, такие как виртуальные машины, базы данных, сети и т.д.
-
Поддержка множества провайдеров
Terraform может работать с разными облачными провайдерами и сервисами:
-
AWS
-
Google Cloud Platform
-
Microsoft Azure
-
Yandex Cloud
-
Kubernetes
-
и др.
-
Управление жизненным циклом инфраструктуры
Terraform позволяет:
-
создавать инфраструктуру (
terraform apply) -
изменять существующую конфигурацию
-
удалять ненужные ресурсы (
terraform destroy) -
Планирование изменений
Команда terraform plan показывает, какие изменения будут внесены в инфраструктуру, прежде чем они будут применены — это снижает риск ошибок.
-
Идемпотентность
При повторном применении конфигурации Terraform гарантирует, что результат останется тем же — никакого дублирования ресурсов.
Дополнительные материалы:
-
Terraform: от незнания к best practices (близко к тому, чем мы будем заниматься в этой статье)
Конфигурация Terraform
В домашней директории (/home/<user>) создаем файл .terraformrc следующего содержания:
provider_installation { network_mirror { url = "https://terraform-mirror.yandexcloud.net/" include = ["registry.terraform.io/*/*"] } direct { exclude = ["registry.terraform.io/*/*"] } }
Создаем директорию terraform со следующей структурой:
- main.tf - provider.tf - user-data.txt - variables.tf - versions.tf
Определяем провайдера в файлах versions.tf и provider.tf.
versions.tf:
terraform { required_providers { yandex = { source = "yandex-cloud/yandex" version = ">= 0.87.0" } } # Хранение состояния Terraform (terraform.tfstate) в бакете (bucket) Yandex Cloud # backend "s3" { # endpoint = "https://storage.yandexcloud.net" # bucket = "terraform-state" # region = "ru-central1" # key = "terraform.tfstate" # skip_region_validation = true # skip_credentials_validation = true # } }
provider.tf:
provider "yandex" { cloud_id = var.cloud_id folder_id = var.folder_id zone = var.zone }
Определяем переменные в файле variables.tf:
variable "cloud_id" { type = string # Заменить default = "b1g..." } variable "folder_id" { type = string # Заменить default = "b1g..." } variable "zone" { type = string default = "ru-central1-a" }
Определяем данные пользователей в файле user-data.txt (он потребуется нам позднее):
#cloud-config users: - name: ansible shell: /bin/bash sudo: "ALL=(ALL) NOPASSWD:ALL" ssh_authorized_keys: # Заменить - ssh-rsa AAA...
Обратите внимание:
-
комментарий
#cloud-configявляется обязательным -
содержание этого файла зависит от версии Terraform
Определяем основную конфигурацию Terraform в файле main.tf:
module "instance" { source = "./modules/tf-yc-instance" subnet_id = module.network.subnet_ids[var.zone] } module "network" { source = "./modules/tf-yc-network" } output "external_ip" { value = module.instance.external_ip } output "internal_ip" { value = module.instance.internal_ip } output "network_id" { value = module.network.network_id } output "subnet_ids" { value = module.network.subnet_ids }
Здесь мы видим подключение 2 модулей: instance для виртуальной машины и network для сети. Обратите внимание, что мы передаем ID подсети, соответствующий зоне ru-central1-a (subnet_id), из модуля network в модуль instance. Также мы указываем Terraform после завершения работы вывести в терминал внешний и внутренний IP-адреса сервера, ID сети и подсетей. Из этих значений нам нужен только внешний IP, остальное выводится ради забавы.
Не забудьте добавить файлы состояния Terraform в файл .gitignore:
*.tfstate *.tfstate.*
Конфигурация модуля сети
Создаем директорию modules/tf-yc-network со следующей структурой:
- main.tf - outputs.tf - README.md - variables.tf - versions.tf
Файл versions.tf идентичен одноименному файлу в директории terraform (без блока s3).
Определяем ресурсы сети и подсети в файле main.tf:
data "yandex_vpc_network" "default" { name = "default" } data "yandex_vpc_subnet" "default" { for_each = var.network_zones name = "default-${each.value}" }
Определяем переменную в файле variables.tf:
variable "network_zones" { description = "Список зон для создания подсетей" type = set(string) default = ["ru-central1-a", "ru-central1-b", "ru-central1-d"] }
Определяем возвращаемые значения в файле outputs.tf:
output "network_id" { description = "ID созданной сети" value = data.yandex_vpc_network.default.id } output "subnet_ids" { description = "ID созданных подсетей" value = { for zone, subnet in data.yandex_vpc_subnet.default : zone => subnet.id } }
Разберем, что такое yandex_vpc_subnet и как будет выглядеть subnet_ids на выходе.
-
data "yandex_vpc_subnet" "default"
Этот блок получает существующие подсети по имени default-<zone> в разных зонах.
Мы используем for_each по списку зон, значит, получаем несколько подсетей, по одной на каждую зону из переменной network_zones.
Terraform создаст следующие ресурсы:
data.yandex_vpc_subnet.default["ru-central1-a"] data.yandex_vpc_subnet.default["ru-central1-b"] data.yandex_vpc_subnet.default["ru-central1-d"]
Каждый из них будет искать подсеть с именем:
-
default-ru-central1-a -
default-ru-central1-b -
default-ru-central1-d -
output "subnet_ids"
Этот блок создает словарь (map), где:
-
ключ — это имя зоны (
ru-central1-aи т.д.) -
значение — это
subnet.id(идентификатор найденной подсети)
Примерный вывод:
subnet_ids = { "ru-central1-a" = "e2lu123abcde12345678" "ru-central1-b" = "e2lu456fghij45678901" "ru-central1-d" = "e2lu789klmno78901234" }
Этот вывод используется для передачи идентификатора подсети ru-central1-a в модуль instance.
Обратите внимание на следующее:
-
все указанные подсети должны уже существовать в Yandex Cloud с именами
default-ru-central1-aи т.д. Если хотя бы одной не существует — Terraform выдаст ошибку -
мы работаем с
data, а не сresource, то есть ничего не создается, только читается
Конфигурация модуля сервера
Создаем директорию modules/tf-yc-instance со следующей структурой:
- main.tf - outputs.tf - README.md - variables.tf - versions.tf
Файл versions.tf идентичен одноименному файлу в директории terraform (без блока s3).
Определяем создание виртуального сервера vm-1 в файле main.tf:
resource "yandex_compute_instance" "vm-1" { name = var.resource_name platform_id = var.platform_id resources { cores = var.cpu_cores memory = var.memory_size } boot_disk { initialize_params { image_id = var.image_id size = var.disk_size } } network_interface { # Получаем из модуля `network` subnet_id = var.subnet_id nat = var.enable_nat } scheduling_policy { preemptible = var.preemptible } metadata = { # Данные пользователей user-data = "${file(var.user_data_file)}" # Альтернатива - ssh-keys = "ansible:${file("~/.ssh/id_rsa.pub")}" } }
Определяем переменные в файле variables.tf:
variables.tf
variable "resource_name" { description = "Имя виртуальной машины" type = string default = "my-vm" } variable "platform_id" { description = "Платформа виртуальной машины" type = string default = "standard-v3" } variable "cpu_cores" { description = "Количество ядер процессора" type = number default = 2 } variable "memory_size" { description = "Объем оперативной памяти (в ГБ)" type = number default = 2 } variable "image_id" { description = "ID образа диска" type = string default = "fd80qm01ah03dkqb14lc" } variable "disk_size" { description = "Размер диска (в ГБ)" type = number default = 50 } variable "enable_nat" { description = "Включить NAT для ВМ" type = bool default = true } variable "preemptible" { description = "Использовать прерываемую ВМ" type = bool default = true } variable "user_data_file" { description = "Путь к файлу с данными пользователей" type = string default = "./user-data.txt" } variable "subnet_id" { type = string }
Обратите внимание на следующее:
-
image_id— образ диска Ubuntu 20.04 -
user_data_file— путь к файлу с данными пользователей (относительно директорииterraform) -
subnet_id— идентификатор подсети, который передается из модуляnetwork
Определяем возвращаемые значения в outputs.tf:
output "external_ip" { description = "Внешний IP-адрес виртуальной машины" value = yandex_compute_instance.vm-1.network_interface.0.nat_ip_address } output "internal_ip" { description = "Внутренний IP-адрес виртуальной машины" value = yandex_compute_instance.vm-1.network_interface.0.ip_address }
Обратите внимание, что названия outputs в модулях и terraform/main.tf должны совпадать.
Итого
Финальная структура проекта:
terraform | main.tf | provider.tf | user-data.txt | variables.tf | versions.tf | modules | tf-yc-instance | main.tf | outputs.tf | README.md | variables.tf | versions.tf | tf-yc-network | main.tf | outputs.tf | README.md | variables.tf | versions.tf
Команды для запуска Terraform:
# Выполняется в корневой директории (`terraform`). # Установка провайдера terraform init # Опционально, валидация конфигурации terraform validate # Генерация плана создания и настройки инфрастуктуры terraform plan # Применение конфигурации terraform apply
Мы рассмотрели далеко не все возможности, предоставляемые Terraform, но думаю вы получили неплохое представление о том, что и как позволяет делать этот замечательный инструмент. Наряду с другими популярными решениями для автоматизации ИТ-процессов (Ansible, Docker, Kubernetes и т.д.), Terraform на сегодняшний день является важной частью арсенала DevOps-инженера.
Happy devopsing!
ссылка на оригинал статьи https://habr.com/ru/articles/937376/
Добавить комментарий