
Привет! На связи Андрей Иванов, старший системный администратор по облачным продуктам. Очень часто к нам обращаются клиенты по вопросам автоматизации работы с инфраструктурой. Лучший подход для подобных задач — IaC (Infrastructure as Code), когда весь комплекс элементов и их взаимодействие описывается с помощью кода, вместо настройки вручную.
Самый известный инструмент для этого — Terraform от компании HashiCorp, который стал отраслевым стандартом для воплощения IaC в жизнь. Сегодня рассмотрим принципы работы с ним, а в качестве примера создадим кластер Managed Kubernetes версии 1.33.5.
Используйте навигацию, если не хотите читать весь текст
→ Что такое Terraform и в чем его сила
→ Что будем делать
→ Шаг 0. Предварительные требования
→ Шаг 1. Установка и настройка
→ Шаг 2. Описание проекта и инфраструктуры
→ Шаг 3. Развертывание кластера Managed Kubernetes
→ Шаг 4. Применение конфигурации
→ Шаг 5. Доступ к кластеру
→ Шаг 6. Управление инфраструктурой как кодом
→ Шаг 7. Удаление созданных ресурсов
→ Заключение
Что такое Terraform и в чем его сила
Terraform от компании HashiCorp — это инструмент управления состоянием инфраструктуры, который позволяет описывать и разворачивать ее компоненты с помощью декларативных конфигурационных файлов.
С его помощью можно поднимать серверы, настраивать сети, балансировщики нагрузки, служебных пользователей и прочие элементы инфраструктуры. Чаще всего Terraform используют в связке с Ansible, который отвечает за автоматизацию программного обеспечения на оборудовании, подготовленном с помощью Terraform. Немаловажно то, что Terraform и Ansible — это инструменты с открытым исходным кодом.
Ключевые преимущества подхода IaC
Автоматизация и скорость. Вся инфраструктура создается и обновляется одной командой, что экономит десятки часов ручного труда.
Воспроизводимость. Описанные в коде компоненты и их взаимосвязи будут воспроизведены одинаково в любом окружении — будь то тестовая среда или продакшен.
Прозрачность и контроль версий. Конфигурационные файлы можно хранить в Git, отслеживать все изменения, проводить ревью кода и легко откатываться к предыдущим версиям.
Предотвращение ошибок. Команда
terraform planпозволяет увидеть все планируемые изменения до их применения, что помогает избежать случайного удаления или неверной настройки критически важных ресурсов.
Что будем делать
Наша цель — развернуть в облаке базовый, но готовый к реальной работе кластер Kubernetes. Мы создадим для него изолированную приватную сеть и настроим облачный роутер для доступа в интернет.
Этот пример коснется фундаментальных и самых важных практик, таких как правильная структура проекта, которую легко поддерживать и расширять.
При этом мы сознательно опускаем некоторые продвинутые темы, чтобы не перегружать материал. За рамками статьи останутся CI/CD-интеграция, создание сложных переиспользуемых модулей и тонкая настройка Kubernetes. Наша задача — получить прочную и правильную основу.

Бесплатная миграция в Selectel
Начислим до 1 000 000 бонусов на два месяца. А наши инженеры подготовят план и поддержат на всех этапах миграции.
Шаг 0. Предварительные требования
При работе с Terraform мы взаимодействуем с несколькими компонентами.
Terraform CLI — инструмент для выполнения команд. Его можно назвать «ядром» системы.
Конфигурационные файлы (
*.tf) описывают инфраструктуру — конечный результат, который должен получиться.State (состояние) — отражение реальной инфраструктуры. Terraform сравнивает желаемое состояние инфраструктуры с действительным и самостоятельно предпринимает действия, чтобы эта разница перестала обнаруживаться.
Модули — это группы ресурсов, которые могут быть использованы повторно. Они позволяют создавать абстракции и упрощают управление.
Провайдер — плагин, который позволяет Terraform взаимодействовать с внешними сервисами и API.
В Terraform используется несколько команд для взаимодействия с инфраструктурой:
terraform init— первоначальная настройка проекта, которая включает загрузку необходимых провайдеров и модулей;terraform plan— отображает план изменений перед решающим шагом;terraform apply— утверждения плана и воплощение его в реальных компонентах инфраструктуры;terraform destroy— удаление всех созданных ресурсов.
Перед началом работы убедимся, что у нас есть все необходимое.
Аккаунт Selectel
Учетную запись можно завести на странице регистрации. Понадобится указать электронную почту и придумать пароль. Вся дальнейшая работа будет происходить в панели управления Selectel.
Также желательно для нашей задачи создать отдельный проект. Название можно задать произвольное, в нашем случае — WorkLab.

Служебный пользователь
Переходим в проект, выбираем вкладку Пользователи и нажимаем на ссылку Сервисные пользователи.

Создадим одного «начального» служебного пользователя. Ему потребуются роли iam_admin и member в области видимости Аккаунт. Его учетные данные будем использовать для первоначальной аутентификации Terraform.

Предстоит взаимодействие с командной строкой. Демонстрационная работа в статье проводилась в операционной системе Linux, но все команды должны выполняться на любой Unix‑системе, в том числе macOS.
Файлы проекта удобно хранить в одном месте — заранее создадим для них каталог на компьютере. Выбрать для него имя, совпадающее с именем проекта, — вполне логично. Пусть это тоже будет WorkLab:
mkdir ~/WorkLab
cd ~/WorkLab
Terraform CLI
Сложного ничего нет — устанавливаем дистрибутив Terraform, подходящий для нашей операционной системы. Самый простой вариант — с сайта HashiCorp. Обратите внимание: для каждого пакетного менеджера команды свои — их можно взять на той же странице HashiCorp. Ниже — вариант для Debian/Ubuntu/SelectOS:
wget -O - https://apt.releases.hashicorp.com/gpg | sudo gpg --dearmor -o /usr/share/keyrings/hashicorp-archive-keyring.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] https://apt.releases.hashicorp.com $(grep -oP '(?<=UBUNTU_CODENAME=).*' /etc/os-release || lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/hashicorp.list
sudo apt update && sudo apt install terraform
Если с доступом к ресурсам HashiCorp наблюдаются проблемы, то Terraform можно взять с нашего зеркала. В таком случае пригодится инструкция.
Установленный kubectl
В официальной документации Kubernetes подробно описаны действия для установки утилиты командной строки. Поддерживаются различные пакетные менеджеры.
В общем случае, выбрав нужную архитектуру (доступна также x86-64), скачиваем нужный образ, контрольную сумму, проверяем соответствие и, наконец, запускаем установку.
curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"
curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl.sha256"
echo "$(cat kubectl.sha256) kubectl" | sha256sum --check
sudo install -o root -g root -m 0755 kubectl /usr/local/bin/kubectl
Если все прошло без неожиданностей, остается удостовериться, что установлена актуальная версия:
kubectl version --client
В рассматриваемом примере:
Client Version: v1.34.0
Kustomize Version: v5.7.1
Важно отметить, что
kubectlи кластер не должны отличаться более чем на одну минорную версию. Представленные выше команды устанавливаютkubectlв самой последней редакции.На момент написания статьи актуальная версия
kubectl— 1.34.0, а кластера — 1.33.5. В данном случае конфликта не возникает, поскольку условия соблюдены. Если бы мы развернули версию кластера, скажем, 1.31.0, то возникли бы проблемы с совместимостью.
Шаг 1. Установка и настройка
Правильная организация файлов — залог порядка в проекте. Разделение конфигурации на логические части делает код более читаемым и простым в обслуживании. Создадим файлы и разместим в созданной директории для нашего проекта — ~/WorkLab.
versions.tf— здесь мы объявляем версии Terraform и необходимых провайдеров. Такая фиксация гарантирует, что конфигурация будет работать стабильно и предсказуемо даже спустя время.main.tf— размещаем основные ресурсы нашей инфраструктуры, такие как сам кластер Kubernetes, а также проект и сервисного пользователя для него.variables.tf— определяем входные переменные. Такой подход делает код гибким и позволяет, не затрагивая основную логику, легко менять параметры — например, количество серверов.outputs.tf— в этом файле описываем выходные данные. После создания инфраструктуры нам понадобится информация о ней — например, kubeconfig для доступа к кластеру.secret.tfvars— в этом файле опишем пароли, чтобы не указывать их в открытом виде. Флаги доступа к файлу можно настроить по своему усмотрению, но разумно забрать права на чтение и запись у всех, кроме владельца файла и, может быть, его группы.
Будьте внимательны при работе с Git! Если файл попадет в индекс и «улетит» в удаленный репозиторий, его содержимое перестанет быть тайной.
Начнем с файла versions.tf. В качестве провайдеров укажем Selectel и OpenStack, поскольку инфраструктура нашего облака построена на его основе. Мы должны вписать актуальные версии, которые можно посмотреть в реестре Terraform на страницах OpenStack и Selectel.
# Версии используемых провайдеров Selectel и OpenStack
terraform {
required_providers {
selectel = {
source = "selectel/selectel"
version = "~> 6.0"
}
openstack = {
source = "terraform-provider-openstack/openstack"
version = "3.0.0"
}
}
}
Использование оператора пессимистичного ограничения ~> — хорошая практика. Она позволяет автоматически получать минорные обновления и исправления ошибок, но защищает от мажорных версий с ломающими изменениями.
Шаг 2. Описание проекта и инфраструктуры
Теперь, когда инициализация настроена, приступим к авторизации внутри провайдера и описанию инфраструктуры. Создадим нового сервисного пользователя и новый проект в файле main.tf. Такой подход предпочтителен при разворачивании инфраструктуры, поскольку следует принципу наименьших привилегий. Указывать значения напрямую в каждом параметре мы не будем, вместо этого сообщим переменные var, которые Terraform берет из variables.tf.Также рассмотрим, как работать с уже существующими проектом и сервисным пользователем.
1. В main.tf первым делом прописываем авторизационные доступы. Здесь у нас будут задействованы учетные данные сервисного пользователя из панели управления:
provider "selectel" {
domain_name = var.account_name
username = var.name
password = var.password
auth_region = var.region
auth_url = var.auth_url
}
2. Далее объявим проект и сервисного пользователя:
resource "selectel_vpc_project_v2" "project_1" {
name = var.project_name
}
resource "selectel_iam_serviceuser_v1" "serviceuser_1" {
name = var.name
password = var.user_password
role {
role_name = "member"
scope = "project"
project_id = selectel_vpc_project_v2.project_1.id
}
role {
role_name = "iam_admin"
scope = "account"
}
}
provider "openstack" {
auth_url = var.auth_url
domain_name = var.account_name
tenant_id = selectel_vpc_project_v2.project_1.id
user_name = selectel_iam_serviceuser_v1.serviceuser_1.name
password = selectel_iam_serviceuser_v1.serviceuser_1.password
region = var.region
}
Список variables.tf на данном этапе будет выглядеть следующим образом:
variable "name" {
default = "Dozer" # Имя сервисного пользователя, созданного через панель управления
}
variable "password" {
type = string
sensitive = true
} # Пароль сервисного пользователя, которого мы создали в панели управления
variable "account_name" {
default = "123456" # Номер аккаунта
}
variable "project_name" {
default = "WorkLab" # Имя проекта
}
variable "user_password" {
type = string
sensitive = true
} # Пароль сервисного пользователя, которого мы создаем через terraform для управления инфраструктурой
variable "auth_url" {
default = "https://cloud.api.selcloud.ru/identity/v3" # API, к которому мы обращаемся для управления инфраструктурой
}
variable "region" {
default = "ru-1" # Пул, в котором мы будем создавать инфраструктуру. В нашем примере будет использоваться ru-1
}
В случае работы с уже созданным сервисным пользователем в рамках существующего проекта нам потребуется предварительно импортировать нужные ресурсы. Для этого понадобятся их идентификаторы. Перейдем в раздел Аккаунт → Проекты и увидим UUID проекта. Посмотреть UUID сервисного пользователя можно в разделе Аккаунт → Сервисные пользователи. Копируем их и указываем при импорте ресурсов:
terraform import selectel_vpc_project_v2.project_1 "7c2c2e1769e849c3b97ae1ff4bafd0cc"
terraform import selectel_iam_serviceuser_v1.serviceuser_1 "1a3e8a0c78864f14ab9f02bdea87d925"
Остается только объявить их в variables.tf:
variable "name" {
default = "Dozer" # Имя сервисного пользователя, которого мы импортировали
}
variable "password" {
type = string
sensitive = true
} # Пароль сервисного пользователя, которого мы импортировали
variable "project_name" {
default = "WorkLab" # Имя импортированного проекта
}
В variables.tf в качестве пароля мы указывали параметр sensitive, который позволяет использовать переменную вместо указания реального пароля в манифесте. Если мы выполним в консоли terraform apply, нас спросят пароль. Чтобы не вводить его постоянно, мы создали отдельный файл secret.tfvars для хранения секретов:
password = "ks;kmvfod[oq" # Пароль пользователя из панели управления
user_password = "|^]dh#UM>5^xp;?vY?>p" # Пароль сервисного пользователя
Чтобы файл с секретами сработал, при выполнении apply используем параметр -var-file="secret.tfvars".
Подробнее о параметрах
sensitiveи защите конфиденциальных данных рекомендуем почитать в официальной документации Hoshicorp.
То есть после указания провайдеров, а также сервисного пользователя, приступаем к инициализации конфигурации Terraform.
terraform init
terraform apply -var-file="secret.tfvars"
При выполнении скачиваются плагины провайдеров, указанные в versions.tf, и настраивается удаленное хранилище состояния (бэкенд), описанное там же. Если возникнет проблема с инициализацией провайдеров, попробуйте использовать VPN. Если все прошло благополучно, в числе прочей информации вы увидите:
Terraform has been successfully initialized!
Кроме того, команда apply создаст сервисного пользователя и новый проект, если мы не импортировали существующие.
Шаг 3. Развертывание кластера Managed Kubernetes
Для развертывания кластера нужно создать сеть, при этом крайне важно учесть несколько особенностей:
подсеть должна быть подключена к облачному роутеру;
также она не должна пересекаться с диапазонами
10.10.0.0/16,10.96.0.0/12,10.250.0.0/16и10.251.0.0/24, которые участвуют во внутренней адресации Managed Kubernetes;в подсети должен быть выключен DHCP.
В таком случае в main.tf нам потребуется добавить ресурсы: приватную сеть и подсеть, а также облачный роутер. В данном примере возьмем подсеть, которую будем использовать для будущего кластера. В сеть пропишем облачный роутер, подключенный к интернету — через него наши тестовые воркер‑ноды будут выходить в мир. Для примера возьмем CIDR 192.168.195.0/24, и DNS-сервера Selectel. Добавляем следующие аннотации:
resource "openstack_networking_network_v2" "network_1" {
name = "network_k8s"
admin_state_up = "true"
}
resource "openstack_networking_subnet_v2" "subnet_1" {
name = "subnet_k8s"
network_id = openstack_networking_network_v2.network_1.id
cidr = "192.168.195.0/24"
dns_nameservers = ["188.93.16.19", "188.93.17.19"]
enable_dhcp = false
}
data "openstack_networking_network_v2" "external_network_1" {
external = true
}
resource "openstack_networking_router_v2" "router_1" {
name = "router"
external_network_id = data.openstack_networking_network_v2.external_network_1.id
}
resource "openstack_networking_router_interface_v2" "router_interface_1" {
router_id = openstack_networking_router_v2.router_1.id
subnet_id = openstack_networking_subnet_v2.subnet_1.id
}
Все готово для создания самого кластера. Добавим в main.tf ресурсы для него и группы нод. Ресурс data "selectel_mks_kubeconfig_v1" "kubeconfig" позволит нам пробросить в директорию /WorkLab наш kubeconfig и работать с кластером сразу после его создания:
data "selectel_mks_kube_versions_v1" "versions" {
project_id = selectel_vpc_project_v2.project_1.id
region = var.region
}
resource "selectel_mks_cluster_v1" "cluster_1" {
name = "Test"
project_id = selectel_vpc_project_v2.project_1.id
region = var.region
kube_version = data.selectel_mks_kube_versions_v1.versions.latest_version
zonal = true
enable_patch_version_auto_upgrade = false
network_id = openstack_networking_network_v2.network_1.id
subnet_id = openstack_networking_subnet_v2.subnet_1.id
maintenance_window_start = "04:00:00"
}
resource "selectel_mks_nodegroup_v1" "nodegroup_1" {
cluster_id = selectel_mks_cluster_v1.cluster_1.id
project_id = selectel_mks_cluster_v1.cluster_1.project_id
region = selectel_mks_cluster_v1.cluster_1.region
availability_zone = var.availability_zone
nodes_count = var.nodes_count
flavor_id = var.flavor_id
volume_gb = var.volume_gb
volume_type = var.volume_type
install_nvidia_device_plugin = false
preemptible = false
}
data "selectel_mks_kubeconfig_v1" "kubeconfig" {
cluster_id = selectel_mks_cluster_v1.cluster_1.id
project_id = selectel_mks_cluster_v1.cluster_1.project_id
region = selectel_mks_cluster_v1.cluster_1.region
}
output "kubeconfig" {
value = data.selectel_mks_kubeconfig_v1.kubeconfig.raw_config
sensitive = true
}
Обратите внимание: согласно техническому требованию API, для базового, не отказоустойчивого кластера (zonal = true), необходимо отключать автоматическое обновление патч-версий:
enable_patch_version_auto_upgrade = false
Наконец, опишем все наши переменные в файле variables.tf.
variable "availability_zone" {
default = "ru-1c" #Укажите пул для создания ресурсов
}
variable "nodes_count" {
default = 2 #Укажите количество необходимых нод
}
variable "flavor_id" {
default = 1011 #Укажите ID флейвора.
}
variable "volume_gb" {
default = 32 #Укажите размер диска
}
variable "volume_type" {
default = "fast.ru-1c" # Укажите тип диска и пул, в котором он находится
}
Список доступных флейворов для нод представлен в документации.
Шаг 4. Применение конфигурации
Проводим генеральную репетицию
terraform plan
Команда выше анализирует наш код и сравнивает его с текущим состоянием инфраструктуры, а затем показывает детальный план будущих изменений.
Следует внимательно изучить вывод этой команды. В конце находится сводка:
Plan: X to add, Y to change, Z to destroy.
Нужно убедиться, что количество ресурсов на создание, изменение и особенно на удаление (destroy) соответствует ожиданиям. Это главный инструмент для предотвращения непреднамеренных разрушений.
Выполняем план, показанный на предыдущем шаге
terraform apply -var-file="secret.tfvars"
Команда apply создает, изменяет или удаляет ресурсы в облаке в соответствии с планом. Участие пользователя тоже потребуется — надо подтвердить действие, напечатав yes.
Шаг 5. Доступ к кластеру
Кластер создан, но как к нему подключиться? Для этого нам понадобится файл kubeconfig. Terraform может сгенерировать его автоматически.
После выполнения terraform apply можно извлечь kubeconfig командой:
terraform output -raw kubeconfig > kubeconfig.yaml
Теперь настроим kubectl для работы с новым кластером. Необходимо указать полный путь до kubeconfig:
export KUBECONFIG=/home/<user>/WorkLab/kubeconfig.yaml
Наконец, проверяем ноды:
kubectl get nodes
Если видно список нод со статусом Ready, поздравляем — кластер готов к работе!
Шаг 6. Управление инфраструктурой как кодом
Главная сила Terraform — в управлении изменениями. Допустим, нам нужно увеличить количество узлов в кластере с двух до трех. Для этого достаточно поправить одно значение в файле variables.tf:
variable "nodes_count" {
default = 3 # Было 2
}
Можно запустить terraform plan, и удостовериться, что Terraform предлагает изменить только один параметр у одного ресурса:
Plan: 0 to add, 1 to change, 0 to destroy.
После ввода следующей команды, исправления отразятся в реальной инфраструктуре:
terraform apply -var-file="secret.tfvars"
Шаг 7. Удаление созданных ресурсов
Если вы хотите удалить только определенные ресурсы, достаточно убрать связанные с ними аннотации из файла main.tf , после чего выполнить:
terraform apply -var-file="secret.tfvars"
Когда инфраструктура больше не нужна, ее можно полностью удалить одной командой. Такой подход убережет от списания средств за неиспользуемые ресурсы:
terraform destroy
Terraform покажет список всех удаляемых ресурсов и попросит подтверждения.
Заключение
Мы рассмотрели полный цикл управления кластером Managed Kubernetes в Selectel с помощью Terraform. Ресурсы можно не просто создавать без усилий, а делать это безопасно:
использовать отдельный файл для секретов,
получать доступ к кластеру правильным способом.
Помните, верные практики — прочная основа для построения надежной и масштабируемой «инфраструктуры как код».