Привет! На связи Андрей Иванов, старший системный администратор по облачным продуктам. Очень часто к нам обращаются клиенты по вопросам автоматизации работы с инфраструктурой. Лучший подход для подобных задач — IaC (Infrastructure as Code), когда весь комплекс элементов и их взаимодействие описывается с помощью кода, вместо настройки вручную. 

Самый известный инструмент для этого — Terraform от компании HashiCorp, который стал отраслевым стандартом для воплощения IaC в жизнь. Сегодня рассмотрим принципы работы с ним, а в качестве примера создадим кластер Managed Kubernetes версии 1.33.5.

Используйте навигацию, если не хотите читать весь текст

Что такое 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. Ресурсы можно не просто создавать без усилий, а делать это безопасно:

  • использовать отдельный файл для секретов,

  • получать доступ к кластеру правильным способом.

Помните, верные практики — прочная основа для построения надежной и масштабируемой «инфраструктуры как код».

Комментарии (0)