Настройка Terraform для управления сервисом CLO
Утилита Terraform позволяет управлять проектами CLO с помощью удобного языка описания инфраструктуры. Такой подход упрощает создание и администрирование сложных проектов, предоставляет понятное и полное описание доступных ресурсов проекта и связей между ними, а также снижает вероятность человеческих ошибок при управлении сетевыми ресурсами.
Возможности Terraform
На данный момент Terraform позволяет управлять развёртыванием и работой следующих сущностей проекта CLO:
- Серверы
- Кластеры СУБД
- Диски
- Ресурсы объектного хранилища S3
- Балансировщики нагрузки
- Виртуальный роутер
- Снапшоты сервера
- Ключи SSH
Установка Terraform
Используйте официальную документацию для установки приложения Terraform.
Провайдер CLO доступен на Github и в официальном репозитории HashiCorp.
Подключение Terraform к сервису
Создайте каталог манифестов, и в нём файл манифеста main.tf для данных о конфигурации Terraform.
В файле манифеста перечисляются провайдеры Terraform, необходимые для создания инфраструктуры. При запуске манифеста провайдер будет автоматически загружен из репозитория HashiCorp.
Примечание. Для ручной установки провайдера обратитесь к соответствующему разделу официальной документации.
Добавьте в файл следующий блок:
terraform {
required_providers {
clo = {
version = "2.8.0"
source = "clo-ru/clo"
}
}
}
provider "clo" {
auth_url = "https://api.clo.ru"
token = <mysecrettoken>
}
Примечание. Актуальные версии провайдера можно найти в официальной документации, нажав на кнопку USE PROVIDER.
Провайдер использует API-токен для запросов, отправляемых провайдеру. Токен можно создать в Личном кабинете → меню-гамбургер слева сверху → Аккаунт → Токены API.
Описание плана инфраструктуры
Опишите в файле main.tf план инфраструктуры. Добавьте описание ресурсов, используя документацию к провайдеру.
Совет. Вы можете использовать примеры из GitHub-репозитория.
Перейдите в каталог, где находится файл манифеста main.tf, и выполните следующую команду:
terraform init
Проверьте, что план инфраструктуры не содержит ошибок:
terraform plan
Программа выведет список ресурсов, готовых к созданию. Если план инфраструктуры содержит ошибки, устраните их и повторите предыдущую операцию.
Далее разверните инфраструктуру и создайте необходимые ресурсы командой:
terraform apply
В панели управления будут автоматически отображены все созданные ресурсы.
Изменение инфраструктуры
Для изменения инфраструктуры отредактируйте файл манифеста и выполните команду:
terraform apply
Примечание. Изменения в инфраструктуре, сделанные через панель управления, в файле манифеста не записываются и не отображаются.
Удаление инфраструктуры
Чтобы удалить ресурсы, выполните в каталоге с файлом манифеста следующую команду:
terraform destroy
Примечание. Ресурсы инфраструктуры, удалённые с помощью панели управления, не удаляются автоматически из файла манифеста.
Пример плана инфраструктуры
Применение этого плана в файле манифеста создаст инфраструктуру, которая будет включать в себя следующие ресурсы:
- два облачных сервера с загрузочным диском из образа Ubuntu 26.04 LTS 64-bit, с произвольной конфигурацией с 2 vCPU и 4 ГБ RAM, диск 40 ГБ, внешний IP-адрес
- кластер СУБД с базой данных
- балансировщик нагрузки
- четыре внешних IP-адреса
- привязку новых IP-адресов к созданным сущностям на проекте
План описан в виде файла main.tf. В этом файле хранится описание создаваемых ресурсов и объявлены переменные, которые могут затем использоваться в других файлах манифестов.
Файл main.tf
variable "os_name" {
default = "Ubuntu 26"
}
variable "clo_api_token" {
description = "API-токен CLO.ru из личного кабинета"
type = string
sensitive = true
}
variable "ssh_public_key" {
description = "Публичный SSH-ключ для доступа на серверы"
type = string
}
variable "db_admin_username" {
description = "Имя администратора базы данных"
type = string
}
variable "db_admin_password" {
description = "Пароль администратора базы данных"
type = string
sensitive = true
}
variable "db_name" {
description = "Имя базы данных внутри кластера"
type = string
default = "app_database"
}
# Инициализация Terraform и конфигурации провайдеров
terraform {
required_providers {
clo = {
version = "2.8.0"
source = "clo-ru/clo"
}
}
}
# Указываем данные для аутентификации
provider "clo" {
auth_url = "https://api.clo.ru"
token = var.clo_api_token
}
# Получаем список проектов
data "clo_projects" "all_projects" {
}
# Получаем список доступных СУБД в проекте
data "clo_dbaas_datastores" "postgres" {
project_id = local.pr_id
}
locals {
pr_id = data.clo_projects.all_projects.result[0].id
postgres_datastore_id = data.clo_dbaas_datastores.postgres.result[0].id
}
# Получаем ID образа операционной системы. Имя образа берём из переменных.
data "clo_project_image" "ubuntu" {
project_id = local.pr_id
name = var.os_name
}
# Создаём SSH-ключ для доступа на серверы
resource "clo_compute_keypair" "ssh_key" {
project_id = local.pr_id
name = "main-ssh-key"
public_key = var.ssh_public_key
}
# Создаем 4 внешних IP-адреса
resource "clo_network_ip" "external_ips" {
count = 4
project_id = local.pr_id
# Опционально можно включить DDoS-защиту для критичных IP, раскомментировав строку ниже:
# ddos_protection = true
}
# Создаем два сервера с нужными параметрами
resource "clo_compute_instance" "web_servers" {
count = 2
project_id = local.pr_id
name = "web-server-${count.index + 1}"
# Конфигурация 1 vCPU и 2 GB RAM задается напрямую
flavor_vcpus = 1
flavor_ram = 2
image_id = data.clo_project_image.ubuntu.image_id
keypairs = [clo_compute_keypair.ssh_key.id]
block_device {
bootable = true
size = 20 # Размер загрузочного диска в ГБ
storage_type = "volume" # Тип хранилища: "volume" (сетевой) или "local"
}
# Привязываем первые два внешних IP к серверам
addresses {
address_id = clo_network_ip.external_ips[count.index].id
external = true
version = 4
ddos_protection = false
}
}
# Создаём кластер СУБД
resource "clo_dbaas_cluster" "main_db" {
project_id = local.pr_id
name = "main-db-cluster"
datastore_id = local.postgres_datastore_id # ID СУБД из data source
storage_size = 10 # Размер хранилища в GiB
flavor {
vcpus = 1
ram = 2
}
# Привязываем третий внешний IP к кластеру БД
address {
id = clo_network_ip.external_ips[2].id
}
}
# Создаём базу данных
resource "clo_dbaas_database" "app_db" {
cluster_id = clo_dbaas_cluster.main_db.id
name = var.db_name
admin_username = var.db_admin_username
admin_password = var.db_admin_password
}
# Создаём балансировщик нагрузки
resource "clo_network_loadbalancer" "web_lb" {
project_id = local.pr_id
name = "web-load-balancer"
algorithm = "ROUND_ROBIN"
# Привязываем четвёртый внешний IP к балансировщику
address {
id = clo_network_ip.external_ips[3].id
}
# Задаём параметры монитора
healthmonitor {
type = "HTTP"
delay = 80 # Минимально допустимое значение интервала
timeout = 15 # Минимально допустимое значение таймаута
max_retries = 3
url_path = "/"
http_method = "GET"
expected_codes = "200"
}
}
# Правило балансировки должно указывать на внутренний (приватный, fixed) адрес
# конкретного backend-сервера, а не на публичный floating IP и не на адрес самого
# балансировщика — иначе к правилу не привяжется backend-сервер (останется server = null,
# и CLO API будет валить 500 при любом чтении списка правил). Ресурс clo_compute_instance
# не отдаёт этот скрытый внутренний адрес, поэтому берём его через data source.
data "clo_compute_instance" "web_servers" {
count = 2
id = clo_compute_instance.web_servers[count.index].id
}
locals {
web_server_internal_address_id = [
for i in range(2) : [
for addr_id in data.clo_compute_instance.web_servers[i].addresses : addr_id
if addr_id != clo_network_ip.external_ips[i].id
][0]
]
}
# Задаём правила балансировки — одно на каждый backend-сервер, оба слушают один и тот же
# внешний порт: LB распределяет трафик между ними по алгоритму ROUND_ROBIN.
resource "clo_network_loadbalancer_rule" "http_rules" {
count = 2
loadbalancer_id = clo_network_loadbalancer.web_lb.id
address_id = local.web_server_internal_address_id[count.index]
external_protocol_port = 80
internal_protocol_port = 80
}
# Выводим информацию о внешних IP-адресах созданных сущностей
output "load_balancer_ip" {
description = "Внешний IP-адрес балансировщика нагрузки"
value = clo_network_ip.external_ips[3].address
}
output "web_server_ips" {
description = "Внешние IP-адреса веб-серверов"
value = [clo_network_ip.external_ips[0].address, clo_network_ip.external_ips[1].address]
}
output "database_ip" {
description = "Внешний IP-адрес кластера баз данных"
value = clo_network_ip.external_ips[2].address
}