Основы Terraform: Развертывание виртуальных машин

Глубокое погружение в синтаксис HCL и жизненный цикл ресурсов Terraform. Вы научитесь описывать инфраструктуру как код, управлять провайдерами и развертывать вычислительные мощности в Yandex Cloud.

1. Анатомия Terraform: инициализация проекта и конфигурация провайдера Yandex Cloud

Анатомия Terraform: инициализация проекта и конфигурация провайдера Yandex Cloud

Пустая директория на локальном компьютере и масштабная инфраструктура в облаке изначально существуют в параллельных вселенных. В одной есть текстовый файл с кодом, в другой — серверы, сети и балансировщики. Инструмент, который вы скачиваете с официального сайта под именем terraform, сам по себе не умеет создавать виртуальные машины ни в Yandex Cloud, ни в AWS, ни где-либо еще. Это лишь ядро, движок, который умеет читать конфигурационные файлы, строить графы зависимостей и вычислять разницу между текущим и желаемым состоянием. Чтобы этот движок научился управлять конкретным облаком, ему нужен переводчик.

Архитектура: Ядро и Провайдеры

Terraform построен на модульной архитектуре, которая разделяет логику работы с кодом и логику взаимодействия с внешними API. Эта архитектура состоит из двух главных компонентов: Terraform Core (ядро) и Terraform Providers (провайдеры).

Ядро — это статически скомпилированный бинарный файл, написанный на языке Go. Его задача — парсинг файлов конфигурации (HCL), управление State-файлом и построение графа ресурсов. Ядро ничего не знает о том, какие параметры нужны для создания диска в Yandex Cloud.

Провайдер — это отдельный исполняемый плагин, который выступает мостом между ядром Terraform и API конкретного сервиса. Провайдер понимает, как перевести абстрактную команду «создать ресурс типа yandex_compute_instance» в конкретный HTTP-запрос POST к API Yandex Cloud, используя правильные эндпоинты и методы аутентификации.

!Взаимодействие Terraform Core и провайдера

Когда вы запускаете Terraform, ядро и провайдер запускаются как независимые процессы операционной системы. Они общаются между собой через механизм gRPC (Remote Procedure Call). Ядро говорит провайдеру: «Пользователь хочет создать сеть с такими-то параметрами, проверь, корректны ли они». Провайдер валидирует параметры, отправляет запрос в облако, получает ответ и возвращает ядру идентификатор созданной сети.

Такое разделение позволяет разработчикам облачных платформ (например, инженерам Yandex) самостоятельно писать и обновлять провайдеры для своих сервисов, не дожидаясь релизов самого Terraform.

Блок terraform: фиксация зависимостей

Любой проект начинается с явного указания того, какие именно плагины-переводчики потребуются ядру для работы. Это делается в специальном блоке terraform, который обычно размещают в файле main.tf или выносят в отдельный файл versions.tf.

Этот блок выполняет роль манифеста зависимостей. Если вы работали с Node.js, это аналог package.json, а в Python — requirements.txt.

Разберем каждую строку этого манифеста:

  • required_providers — словарь, в котором перечисляются все необходимые плагины. Локальное имя yandex мы придумываем сами (оно будет использоваться далее в коде), но принято называть его так же, как и сам провайдер.
  • source — глобальный адрес провайдера в Terraform Registry (официальном хранилище плагинов). Адрес строится по формату hostname/namespace/type. Если hostname опущен, по умолчанию используется registry.terraform.io. Таким образом, строка "yandex-cloud/yandex" означает, что мы просим скачать провайдер yandex от разработчика yandex-cloud из глобального реестра.
  • version — версия плагина. Развитие облака идет непрерывно: появляются новые сервисы, меняются параметры старых. Провайдер обновляется вслед за API.
  • Особое внимание стоит уделить оператору ~> (пессимистичный оператор ограничения версии). Запись ~> 0.95.0 означает: «разрешено использовать любую версию, начиная с 0.95.0, но строго до 0.96.0». То есть версия 0.95.1 или 0.95.99 подойдет, а 0.96.0 — уже нет.

    Почему нельзя написать просто >= 0.95.0? Провайдеры следуют правилам семантического версионирования, где изменение второй цифры (минорной версии) часто означает добавление нового функционала, а в мире Terraform до версии 1.0.0 это может включать и ломающие изменения (breaking changes) в синтаксисе ресурсов. Жесткая фиксация версии спасает от ситуации, когда код, написанный полгода назад, внезапно отказывается работать из-за того, что Terraform скачал самую свежую версию плагина, где изменились названия обязательных аргументов.

    Параметр required_version в конце блока указывает требования к версии самого бинарного файла Terraform Core. Это защищает команду от конфигурационного дрейфа: если один инженер применит изменения версией 1.5.0, а другой попытается сделать это устаревшей версией 1.2.0, Terraform выдаст ошибку, предотвращая повреждение State-файла.

    Блок provider: настройка контекста Yandex Cloud

    После того как мы указали, какой плагин нам нужен, необходимо объяснить ему, где и с какими правами он будет работать. Для этого используется блок provider.

    Вспомним иерархию ресурсов из предыдущих этапов: чтобы создать виртуальную машину, нам нужно знать идентификатор облака (Cloud ID), идентификатор каталога (Folder ID) и зону доступности по умолчанию. Также провайдеру нужен способ аутентификации — авторизованный ключ сервисного аккаунта в формате JSON.

    Этот блок инициализирует плагин. Когда Terraform начнет создавать ресурсы, провайдер yandex прочитает файл key.json, подпишет им свои запросы к API и направит их в указанный каталог folder_id. Параметр zone устанавливает зону доступности по умолчанию: если позже при описании конкретной виртуальной машины вы забудете указать зону, провайдер автоматически подставит ru-central1-a.

    Проблема захардкоженных секретов

    Приведенный выше код технически абсолютно рабочий, но концептуально содержит критическую уязвимость. IaC-код (Infrastructure as Code) предназначен для хранения в системах контроля версий (Git). Если вы закоммитите файл main.tf с реальными путями к ключам и идентификаторами, а затем запушите его в корпоративный или публичный репозиторий, вы скомпрометируете доступ к вашей облачной инфраструктуре.

    Профессиональный подход заключается в использовании переменных окружения. Провайдер Yandex Cloud спроектирован так, что если он не находит нужных аргументов внутри блока provider, он автоматически ищет их в переменных операционной системы.

    Если в терминале выполнить экспорт: export YC_TOKEN="ваш_oauth_токен" (или export YC_SERVICE_ACCOUNT_KEY_FILE="/path/to/key.json") export YC_CLOUD_ID="ваш_cloud_id" export YC_FOLDER_ID="ваш_folder_id"

    То блок конфигурации в коде можно оставить абсолютно пустым:

    Такой код становится переносимым. Вы можете передать его коллеге, он экспортирует свои переменные окружения, и тот же самый код развернет инфраструктуру уже в его тестовом каталоге, не требуя ни единой правки в main.tf.

    Инициализация рабочего окружения: terraform init

    Написав блоки terraform и provider, мы создали лишь текстовое описание намерений. Если сейчас попытаться запустить команду планирования или применения изменений, Terraform выдаст ошибку. Ядро прочитает манифест, увидит требование загрузить провайдер yandex, но не найдет его на жестком диске.

    Первая команда, которая выполняется в любом новом проекте Terraform — это terraform init.

    !Процесс инициализации Terraform

    Команда init не обращается к облаку и не проверяет, правильный ли у вас folder_id или валиден ли ваш сервисный ключ. Ее задача — подготовить локальное рабочее окружение. Процесс состоит из нескольких этапов:

  • Анализ конфигурации. Ядро сканирует все файлы с расширением .tf в текущей директории и собирает список всех упомянутых провайдеров.
  • Обращение к реестру. Terraform отправляет запрос к registry.terraform.io, запрашивая метаданные о провайдере yandex-cloud/yandex.
  • Разрешение версий. Ядро сопоставляет доступные версии с ограничением ~> 0.95.0 и выбирает максимально свежую версию, удовлетворяющую условию (например, 0.95.3).
  • Загрузка. Бинарный файл плагина для вашей операционной системы (Windows, macOS или Linux) скачивается и помещается в скрытую директорию .terraform/providers/.
  • Фиксация хэшей. Создается файл .terraform.lock.hcl.
  • Анатомия файла .terraform.lock.hcl

    Файл блокировки зависимостей (lock-файл) — важнейший элемент безопасности и стабильности инфраструктуры. Заглянув внутрь него, вы увидите нечто подобное:

    В отличие от директории .terraform, которая добавляется в .gitignore и никогда не попадает в репозиторий, файл .terraform.lock.hcl обязательно должен быть закоммичен в Git.

    Он решает две задачи. Во-первых, он жестко фиксирует точную версию скачанного провайдера. Даже если завтра выйдет версия 0.95.4 (которая подходит под условие ~> 0.95.0), ваш коллега, склонировав репозиторий и запустив terraform init, скачает именно 0.95.3. Это гарантирует, что вся команда работает с идентичным инструментарием.

    Во-вторых, массив hashes содержит криптографические контрольные суммы бинарных файлов провайдера для разных операционных систем. При скачивании плагина Terraform вычисляет его хэш и сверяет с тем, что записан в lock-файле. Если злоумышленник скомпрометирует реестр плагинов и подменит бинарный файл провайдера на вредоносный, его хэш изменится. Terraform заметит несовпадение и экстренно прервет работу, защитив вашу систему от атаки на цепочку поставок (supply chain attack).

    Поведение при ошибках инициализации

    Понимание того, как работает init, помогает быстро диагностировать проблемы.

    Если в процессе инициализации вы получаете ошибку Failed to query available provider packages, это означает проблему сетевого уровня. Ядро не может достучаться до глобального реестра. Такое часто случается в корпоративных сетях с жесткими правилами фаервола или при блокировках. В таких случаях инфраструктурные команды настраивают локальные зеркала (Provider Network Mirror) и через специальный конфигурационный файл ~/.terraformrc указывают ядру скачивать плагины не из интернета, а из внутреннего хранилища компании.

    Если вы изменили версию в блоке required_providers (например, решили обновиться до ~> 0.100.0), обычный запуск terraform init завершится ошибкой. Terraform увидит конфликт: в коде запрошена версия 0.100, а в lock.hcl жестко зафиксирована 0.95.3. Чтобы легитимно обновить плагин и перезаписать lock-файл, команду нужно запускать с флагом обновления: terraform init -upgrade.

    Важно помнить границу ответственности этапа инициализации. Успешное завершение terraform init с зеленым сообщением "Terraform has been successfully initialized!" означает лишь то, что плагины скачаны и готовы к работе. Terraform на этом этапе не делает тестовых запросов в Yandex Cloud. Если вы ошиблись в folder_id или ваш key.json просрочен, команда init этого не заметит — эти ошибки проявятся только на следующем шаге, когда провайдер попытается построить план инфраструктуры.

    Связка блоков terraform и provider, подкрепленная успешной инициализацией, формирует надежный фундамент. Ядро получило нужный плагин, плагин получил инструкции по аутентификации и целевому каталогу облака. Рабочая директория трансформировалась из набора текста в полноценный инструмент управления, готовый к описанию реальных вычислительных ресурсов.

    2. Синтаксис HCL: типы данных, интерполяция и структура блоков конфигурации

    Синтаксис HCL: типы данных, интерполяция и структура блоков конфигурации

    Языки описания конфигураций часто заставляют инженера выбирать между строгой машинной логикой и удобством чтения. JSON идеален для парсеров, но не поддерживает комментарии и перегружен кавычками. YAML читается как обычный текст, но его чувствительность к отступам регулярно становится причиной скрытых ошибок и упавших production-сред. HashiCorp Configuration Language (HCL) был создан как компромисс: он обладает строгой структурой, не зависит от пробелов, поддерживает комментарии и изначально спроектирован так, чтобы конфигурацию инфраструктуры было одинаково легко писать человеку и валидировать машине.

    Понимание синтаксиса HCL — это фундамент, без которого невозможно написать ни один манифест Terraform. HCL является декларативным языком, но внутри него скрыт мощный движок вычислений, типизации и встроенных функций.

    Анатомия конфигурационного блока

    Весь код Terraform состоит из блоков. Блок — это базовый контейнер для хранения конфигурации определенного объекта. Независимо от того, описываете ли вы провайдер, сеть или базу данных, структура блока остается неизменной.

    !Анатомия блока HCL

    Любой блок в HCL состоит из четырех основных элементов:

  • Тип блока (Block Type) — ключевое слово, определяющее назначение блока. Например, resource (для создания инфраструктуры), data (для чтения существующих данных) или provider (для настройки плагина).
  • Метки (Labels) — строковые значения, следующие за типом блока. Количество меток жестко задано типом блока. Например, блок provider требует одну метку (имя провайдера), а блок resource — две (тип ресурса и его локальное имя).
  • Тело блока (Body) — содержимое, заключенное в фигурные скобки { }. Оно определяет конкретные параметры объекта.
  • Аргументы (Arguments) — пары «ключ-значение» внутри тела блока. Именно они задают конфигурацию. Имя аргумента всегда пишется без кавычек, а после знака равенства указывается значение соответствующего типа.
  • Рассмотрим абстрактный пример описания облачной сети:

    В этом примере resource — тип блока. За ним следуют две метки: "yandex_vpc_network" (тип ресурса в терминологии провайдера Yandex Cloud) и "main_network" (локальное имя).

    Локальное имя имеет критическое значение. Идентификатор "main_network" существует только внутри вашего кода Terraform. Облако Yandex Cloud ничего о нем не знает — для облака сеть будет называться "production-vpc" (согласно аргументу name). Локальное имя нужно исключительно для того, чтобы вы могли ссылаться на этот блок из других частей вашего кода.

    Примитивные типы данных

    HCL строго типизирован. Каждое значение, присваиваемое аргументу, имеет определенный тип. Попытка передать строку туда, где ожидается число, вызовет ошибку на этапе валидации кода.

    Строки (String)

    Строки всегда заключаются в двойные кавычки. Одинарные кавычки в HCL не используются для строк и вызовут синтаксическую ошибку.

    Если необходимо передать многострочный текст (например, bash-скрипт для инициализации сервера или содержимое конфигурационного файла), использование обычных кавычек с символами переноса строки \n делает код нечитаемым. Для таких случаев в HCL предусмотрен синтаксис Heredoc.

    Heredoc открывается последовательностью << за которой следует маркер (любое слово, традиционно используют EOF), и закрывается тем же маркером на отдельной строке:

    Существует важный нюанс: стандартный Heredoc (<<EOF) сохраняет все отступы слева. Если ваш блок находится глубоко в иерархии кода, текст внутри Heredoc сломает визуальное форматирование, так как его придется прижимать к левому краю экрана. Чтобы решить эту проблему, используется Indented Heredoc с дефисом <<-EOF:

    Числа (Number)

    Числа в HCL пишутся без кавычек. Язык поддерживает как целые числа (integer), так и числа с плавающей точкой (float).

    Иногда возникает соблазн написать memory_cores = "4". Terraform попытается выполнить неявное преобразование типов (implicit type conversion) и превратить строку в число. Однако полагаться на это — плохая практика. Явное указание правильного типа делает код предсказуемым и защищает от неожиданного поведения при обновлениях провайдера.

    Логический тип (Bool)

    Принимает только два значения: true или false. Пишется строго без кавычек.

    Структурные типы данных (Коллекции)

    Инфраструктура редко описывается одиночными значениями. Часто требуется передать список IP-адресов, набор меток или сложную вложенную конфигурацию. Для этого HCL использует коллекции.

    Списки (List) и Кортежи (Tuple)

    Список — это упорядоченная последовательность значений, заключенная в квадратные скобки [ ]. Элементы разделяются запятыми. Важное правило типизации HCL: в классическом списке (list) все элементы должны быть одного типа (например, только строки).

    Доступ к элементам списка осуществляется по индексу, начиная с нуля. Синтаксис обращения выглядит так: subnets[0].

    Если последовательность содержит элементы разных типов (например, строку, число и булево значение), в терминологии HCL это называется кортежем (Tuple). Внешне кортеж выглядит так же, как список, но внутренний движок типизации обрабатывает его иначе, сохраняя информацию о типе каждого элемента на своей позиции.

    Словари (Map) и Объекты (Object)

    Словарь — это неупорядоченная коллекция пар «ключ-значение», заключенная в фигурные скобки { }. Ключи всегда являются строками.

    Как и в случае со списками, классический словарь (map) требует, чтобы все его значения имели одинаковый тип. Если вы попытаетесь создать словарь, где одно значение — строка, а другое — число, Terraform автоматически преобразует их к единому типу (обычно к строкам), либо выдаст ошибку, если преобразование невозможно.

    Когда требуется структура с ключами, значения которых имеют принципиально разные типы, используется Объект (Object). Объекты часто встречаются при описании сложных вложенных блоков внутри ресурсов.

    Отдельного внимания заслуживает синтаксис присваивания внутри словарей. В HCL допускается использовать как знак равенства =, так и двоеточие :.

    Современный стандарт форматирования Terraform предпочитает использование знака равенства для аргументов и словарей, оставляя двоеточие для специфических случаев определения типов.

    Интерполяция и выражения

    Интерполяция — это механизм динамического вычисления значений внутри строк. Она позволяет ссылаться на атрибуты других ресурсов, вызывать функции или выполнять математические операции прямо в конфигурации.

    Синтаксис интерполяции представляет собой знак доллара и фигурные скобки: {yandex_vpc_network.main.name}" } hcl

    Устаревший синтаксис (HCL1) - антипаттерн в современных реалиях

    network_id = "{ } не просто избыточно, но и считается плохим стилем. Современный HCL позволяет ссылаться на объекты напрямую:

    Интерполяцию ${ ... } следует использовать исключительно тогда, когда динамическое значение нужно встроить внутрь более длинной статической строки.

    Встроенные функции

    HCL не является языком программирования общего назначения. В нем нельзя написать собственный цикл while или объявить пользовательскую функцию. Однако язык предоставляет обширную стандартную библиотеку встроенных функций для трансформации данных.

    Функции вызываются по их имени, за которым следуют круглые скобки с аргументами: function_name(arg1, arg2).

    Некоторые часто используемые категории функций:

  • Строковые: upper("hello") вернет "HELLO", split(",", "a,b,c") разобьет строку в список ["a", "b", "c"].
  • Коллекции: length(["a", "b"]) вернет 2, merge({a=1}, {b=2}) объединит два словаря в один {a=1, b=2}.
  • Кодирование: base64encode("text") часто используется для передачи пользовательских скриптов (user-data) внутрь виртуальных машин, так как облачные API требуют данные в формате Base64.
  • Проверить работу любой функции можно без создания файлов конфигурации. Команда terraform console открывает интерактивную оболочку (REPL), в которой можно вводить выражения HCL и мгновенно видеть результат их вычисления.

    Особые значения: null

    В HCL существует специальное значение null, которое означает отсутствие значения. Присвоение null аргументу эквивалентно тому, что этот аргумент вообще не был написан в конфигурации.

    На первый взгляд это кажется бесполезным, но null незаменим при создании переиспользуемых модулей. Если вы хотите, чтобы пользователь вашего кода мог опционально задать параметр, вы устанавливаете значение по умолчанию в null. Если пользователь ничего не передаст, Terraform просто проигнорирует этот аргумент, и облачный провайдер применит свои внутренние настройки по умолчанию.

    Форматирование и комментарии

    HCL поддерживает три стиля комментариев:

  • # — однострочный комментарий (рекомендуемый стандарт).
  • // — альтернативный однострочный комментарий (наследие C-подобных языков).
  • / ... / — многострочный комментарий, удобный для временного отключения больших блоков кода.
  • Одной из фундаментальных особенностей экосистемы Terraform является встроенная утилита форматирования. Команда terraform fmt автоматически сканирует все .tf файлы в директории и переписывает их согласно единому стандарту HashiCorp.

    Она выравнивает знаки равенства в блоках, исправляет отступы и приводит списки к единообразному виду. В HCL разрешены (и даже приветствуются) висячие запятые в многострочных списках и словарях:

    Наличие запятой после последнего элемента означает, что при добавлении новой зоны в будущем, система контроля версий (Git) покажет изменение только одной новой строки, а не модификацию предыдущей. terraform fmt автоматически расставляет такие запятые там, где это необходимо для чистоты коммитов.

    Понимание структуры блоков, строгой типизации и правил интерполяции дает полный контроль над конфигурацией. Эти механизмы позволяют перейти от написания плоских текстовых файлов к созданию динамической, ссылающейся на саму себя инфраструктуры, где изменение одного параметра каскадно обновляет все зависимые ресурсы.

    3. Ресурсная модель: описание и создание виртуальных машин Compute Cloud

    Ресурсная модель: описание и создание виртуальных машин Compute Cloud

    Создание виртуальной машины через веб-консоль облачного провайдера занимает около пяти минут: нужно пройти через десяток экранов, выбрать параметры процессора, тип диска, сеть, вставить SSH-ключ и нажать кнопку. Если сервер нужен один — это приемлемо. Если их нужно сто, или если конфигурацию сервера нужно в точности повторить через полгода в другом контуре, ручной подход неизбежно приводит к ошибкам. Декларативное описание ресурса в Terraform сводит эти пять минут к одному текстовому блоку, который становится единственным источником истины о том, как должен выглядеть сервер.

    Анатомия ресурса yandex_compute_instance

    В Terraform управление любой инфраструктурной единицей происходит через блок resource. Для создания виртуальной машины в Yandex Cloud используется тип ресурса yandex_compute_instance. Этот блок объединяет в себе конфигурацию вычислительных мощностей, дисковой подсистемы, сетевых интерфейсов и операционной системы.

    !Структура блока виртуальной машины

    Синтаксис объявления начинается с ключевого слова resource, за которым следуют две метки: тип ресурса, предоставляемый провайдером, и локальное имя, которое выбирает инженер.

    Аргумент name задает имя виртуальной машины внутри самого Yandex Cloud — именно оно будет отображаться в веб-консоли и биллинге. Локальное имя "web_server" существует только в памяти Terraform и используется для того, чтобы другие ресурсы в коде могли ссылаться на эту машину.

    Аргумент zone жестко привязывает виртуальную машину к конкретному физическому дата-центру. Если зона не указана явно, провайдер попытается использовать зону по умолчанию, заданную при инициализации провайдера, однако в production-коде хорошим тоном считается явное указание зоны для каждого вычислительного ресурса.

    Аргумент platform_id определяет физическую архитектуру процессоров, на которых будет запущена виртуальная машина. Yandex Cloud предлагает несколько платформ, например:

  • standard-v1 — процессоры Intel Broadwell.
  • standard-v2 — процессоры Intel Cascade Lake.
  • standard-v3 — процессоры Intel Ice Lake.
  • Выбор платформы напрямую влияет на доступные комбинации ядер и памяти, а также на стоимость аренды.

    Вычислительные мощности: CPU и RAM

    Спецификация процессора и оперативной памяти задается во вложенном блоке resources. В отличие от физического сервера, где вы ограничены купленным железом, облачная ВМ позволяет гибко настраивать эти параметры.

    Аргументы cores (количество vCPU) и memory (объем RAM в гигабайтах) интуитивно понятны. Однако параметр core_fraction является специфичной и критически важной концепцией платформы.

    core_fraction (базовая производительность ядра) определяет, какую долю вычислительного времени физического ядра гипервизор гарантированно выделяет вашему vCPU. Допустимые значения зависят от выбранной платформы, но чаще всего это , , или .

    !Влияние core_fraction на распределение процессорного времени

    Если установить core_fraction = 20, виртуальная машина будет получать процессорного времени физического ядра постоянно. При этом, если физический хост не нагружен соседями, машина может использовать до мощности ядра (burst), но провайдер этого не гарантирует. Как только соседям по физическому серверу потребуются ресурсы, производительность вашего vCPU будет жестко ограничена (throttled) до гарантированных .

    Эта механика позволяет радикально экономить на серверах, которые не требуют постоянной высокой нагрузки (например, тестовые стенды, балансировщики с низким трафиком, пет-проекты). Для высоконагруженных баз данных или production-серверов всегда устанавливается core_fraction = 100.

    Дисковая подсистема: загрузочный том

    Каждой виртуальной машине требуется как минимум один диск, с которого будет загружаться операционная система. В Terraform это описывается блоком boot_disk.

    Существует два сценария работы с дисками: подключение уже существующего независимого диска или инициализация нового диска непосредственно при создании ВМ. Второй сценарий используется в абсолютном большинстве случаев для базовой инфраструктуры.

    Блок initialize_params дает Terraform команду: «создай новый диск на основе указанного образа и сразу сделай его загрузочным для этой машины».

    Аргумент image_id принимает уникальный идентификатор образа ОС из реестра Yandex Cloud. В примере выше используется ID публичного образа Ubuntu 22.04. Жесткое кодирование ID образа (хардкод) допустимо для первых тестов, но в реальных проектах идентификаторы меняются по мере выхода обновлений безопасности.

    Аргумент type определяет физический носитель. Доступны:

  • network-hdd — стандартные жесткие диски, подходят для холодных данных и бэкапов.
  • network-ssd — твердотельные накопители, стандарт де-факто для ОС и приложений.
  • network-nvme — высокопроизводительные диски для баз данных с высокими требованиями к IOPS.
  • Аргумент size задает размер диска в гигабайтах. Если указать размер меньше, чем минимально допустимый для выбранного образа ОС, облачный API вернет ошибку на этапе применения конфигурации.

    Сетевой интерфейс и доступ в интернет

    Чтобы виртуальная машина могла взаимодействовать с другими ресурсами или интернетом, ей необходим виртуальный сетевой интерфейс (vNIC). За это отвечает блок network_interface.

    Аргумент subnet_id обязателен. Он указывает, к какой именно подсети облака будет подключен интерфейс. Виртуальная машина получит внутренний IP-адрес из диапазона этой подсети. Важно понимать, что зона доступности ВМ (например, ru-central1-a) должна строго совпадать с зоной доступности подсети, ID которой вы передаете.

    Аргумент nat = true дает команду облачному провайдеру выделить машине публичный (белый) IP-адрес по технологии 1:1 NAT. Этот адрес будет динамическим: если вы остановите (stop) и затем запустите (start) виртуальную машину, публичный IP-адрес изменится. Если для сервера требуется статический IP-адрес, он создается отдельным ресурсом в Terraform и передается в блок network_interface через аргумент nat_ip_address.

    Метаданные: ключи SSH и cloud-init

    Когда Terraform отправляет команду на создание ВМ, облако разворачивает образ диска и запускает сервер. Но как получить доступ к операционной системе? По умолчанию в облачных образах отключен вход по паролю из соображений безопасности. Доступ осуществляется только по SSH-ключам.

    Terraform не подключается к серверу по SSH, чтобы создать пользователя или положить ключ. Вместо этого используется механизм инъекции метаданных и служба cloud-init, которая предустановлена во всех публичных образах Yandex Cloud.

    !Процесс чтения метаданных через cloud-init при загрузке ВМ

    Блок metadata позволяет передать данные внутрь гостевой ОС на этапе ее первой загрузки.

    Ключ словаря ssh-keys имеет строгий формат: <имя_пользователя>:<содержимое_публичного_ключа>. Когда виртуальная машина загружается первый раз, демон cloud-init обращается к специальному локальному IP-адресу провайдера (обычно ), скачивает переданные метаданные, создает пользователя ubuntu (если его нет) и записывает публичный ключ в файл ~/.ssh/authorized_keys.

    Вставлять длинную строку ключа прямо в код неудобно и небезопасно. В HCL есть встроенная функция file(), которая позволяет прочитать содержимое файла с локального диска инженера:

    Этот манифест описывает желаемое состояние. Если применить этот код в пустом облаке, Terraform вызовет API Yandex Cloud и создаст машину с нуля. Если машина уже существует, но кто-то вручную через веб-консоль изменил размер диска с 20 на 30 ГБ, при следующем запуске Terraform обнаружит расхождение (Configuration Drift) и попытается вернуть систему к состоянию, описанному в коде.

    Конфигурация ВМ — это фундамент, на котором строится логика масштабирования. Понимание каждого аргумента в блоке yandex_compute_instance необходимо для того, чтобы в дальнейшем не просто создавать одиночные серверы, а управлять их зависимостями, объединять в кластеры и динамически балансировать нагрузку между зонами доступности.

    4. Жизненный цикл ресурсов: от планирования изменений до уничтожения инфраструктуры

    Жизненный цикл ресурсов: от планирования изменений до уничтожения инфраструктуры

    Написанный на HCL код виртуальной машины — это лишь статичное описание желаемого результата. Если вы измените в этом коде одну строку, например, добавите новый тег или измените зону доступности, система должна принять решение: обновить существующий сервер на лету или безвозвратно удалить его и создать новый. Ошибка в понимании этого механизма регулярно приводит к случайному уничтожению боевых баз данных и падению production-сред. Управление инфраструктурой как кодом требует точного понимания того, как абстрактный текст превращается в реальные API-вызовы, как система запоминает созданное и как безопасно выводит ресурсы из эксплуатации.

    Фундамент жизненного цикла: State-файл

    Главное отличие Terraform от обычных bash-скриптов или инструментов вроде curl заключается в наличии памяти. Когда скрипт обращается к Yandex Cloud с командой «создать виртуальную машину», он выполняет действие и завершает работу. Если запустить его повторно, он создаст вторую машину, затем третью.

    Terraform работает иначе. Ему необходимо точно знать, какие именно ресурсы в облаке соответствуют конкретным блокам кода в ваших .tf файлах. Эту задачу решает механизм состояния — State.

    При первом успешном развертывании ресурсов ядро Terraform создает локальный файл terraform.tfstate. Это JSON-документ, который выполняет роль строгой карты соответствия (маппинга).

    !Схема взаимодействия HCL-кода, State-файла и реальных ресурсов в Yandex Cloud

    В коде вы оперируете логическими именами. Например, блок yandex_compute_instance.web_server. Для облака Yandex Cloud этого имени не существует, оно оперирует уникальными идентификаторами, такими как epd45g7.... State-файл связывает их вместе. Он записывает, что логическому ресурсу web_server в текущем рабочем каталоге соответствует физическая машина с конкретным ID, конкретным IP-адресом и набором характеристик на момент последнего запуска.

    Наличие State-файла меняет саму парадигму работы. При каждом следующем запуске Terraform не просто читает ваш код. Он выполняет трехстороннее сравнение:

  • Что написано в конфигурационных файлах .tf (Желаемое состояние).
  • Что записано в terraform.tfstate (Последнее известное состояние).
  • Что реально существует в Yandex Cloud прямо сейчас (Фактическое состояние).
  • Именно на основе этого трехстороннего анализа строится весь жизненный цикл управления ресурсами.

    Фаза планирования: terraform plan и Refresh

    Любое изменение инфраструктуры начинается с команды terraform plan. Это безопасный режим (dry run), который ничего не меняет в облаке, но показывает точный прогноз будущих действий.

    Процесс планирования начинается скрытой, но критически важной фазой — Refresh (обновление состояния). До того как анализировать ваш код, Terraform берет все ID ресурсов из terraform.tfstate и отправляет запросы к API Yandex Cloud: «Существует ли еще машина с ID epd45g7...? Какой у нее сейчас IP? Не изменились ли теги?».

    Получив ответы, система обновляет State-файл в оперативной памяти и только после этого сравнивает его с вашим HCL-кодом. Результатом этого сравнения становится план выполнения (Execution Plan).

    В выводе команды terraform plan каждое планируемое действие помечается специальным символом. Понимание этих символов — базовый навык инженера.

    Создание нового ресурса (+)

    Символ + означает, что ресурс описан в коде, но отсутствует в State-файле (или был удален из облака).

    Обратите внимание на значение (known after apply). Terraform честно сообщает, что некоторые параметры (например, внутренний ID машины или динамически выдаваемый публичный IP-адрес) невозможно вычислить на этапе планирования. Они будут сгенерированы на стороне Yandex Cloud только в момент фактического создания ресурса. После создания эти значения будут записаны в State-файл.

    Обновление на месте (~)

    Символ ~ (In-place update) — самый безопасный тип изменений. Он означает, что вы изменили параметр, который API облачного провайдера позволяет модифицировать «на горячую», без остановки или пересоздания ресурса.

    Типичный пример для виртуальной машины — изменение описания, добавление метаданных (если это не требует перезагрузки) или изменение списка тегов (labels).

    В этом случае Terraform отправит PATCH-запрос к API Yandex Cloud, который просто обновит метаинформацию. Сама виртуальная машина продолжит работать без прерывания обслуживания.

    Деструктивное обновление: удаление и создание (-/+)

    Символ -/+ (Force Replacement) — сигнал максимальной опасности. Он означает, что запрошенное вами изменение невозможно выполнить на существующем ресурсе. Архитектура облака не позволяет изменить этот параметр на лету.

    Единственный способ привести систему к желаемому состоянию — полностью удалить существующую виртуальную машину и создать на ее месте новую с нужными параметрами.

    Для yandex_compute_instance деструктивное обновление триггерится, например, при изменении зоны доступности (zone). Вы не можете физически переместить работающий сервер из дата-центра во Владимире (ru-central1-a) в дата-центр в Рязани (ru-central1-b).

    Комментарий # forces replacement указывает точную причину, по которой Terraform принял решение об уничтожении ресурса. При выполнении такого плана произойдет кратковременный (или длительный) простой сервиса (downtime), так как старая машина будет удалена до того, как новая будет полностью готова к работе.

    Удаление ресурса (-)

    Символ - означает, что ресурс присутствует в State-файле и в облаке, но вы удалили его блок из .tf файла (или закомментировали). Terraform расценивает это как команду на демонтаж.

    Фаза применения: terraform apply

    После того как план изучен и утвержден, выполняется команда terraform apply. По умолчанию она сначала заново генерирует план, выводит его на экран и запрашивает ручное подтверждение (ввод слова yes).

    В момент начала применения Terraform блокирует State-файл. Если вы используете локальный файл, рядом с ним на доли секунды появляется файл блокировки. Это сделано для того, чтобы два инженера, одновременно запустившие apply, не отправили конфликтующие команды в API и не повредили структуру состояния.

    Во время выполнения apply Terraform транслирует HCL-код в REST API вызовы к Yandex Cloud. Процесс строго синхронен для каждого отдельного ресурса:

  • Отправляется HTTP-запрос на создание/изменение.
  • Terraform переходит в режим ожидания (polling), периодически опрашивая API Yandex Cloud о статусе операции.
  • Только когда облако возвращает статус DONE (или RUNNING для ВМ), Terraform считает операцию успешной.
  • Новые данные (ID, IP-адреса) немедленно записываются в State-файл.
  • Если на шаге 2 облако вернет ошибку (например, закончились квоты на процессоры в выбранном каталоге), выполнение apply прервется. При этом те ресурсы, которые успели создаться до ошибки, останутся работать, и их данные будут корректно сохранены в State. Terraform не имеет встроенного механизма автоматического отката (rollback) всей транзакции при частичном сбое. Исправление ситуации ложится на плечи инженера: нужно исправить лимиты и запустить apply повторно. Благодаря идемпотентности, успешно созданные ресурсы не будут затронуты, а упавшие — будут досозданы.

    Обнаружение и устранение Configuration Drift

    Жизненный цикл ресурсов не всегда протекает в стерильных условиях. Часто возникает ситуация, называемая дрейфом конфигурации (Configuration Drift) — расхождение между тем, что описано в коде, и тем, что реально существует в облаке.

    Дрейф возникает, когда кто-то вносит изменения в инфраструктуру в обход Terraform. Например, во время ночного инцидента дежурный администратор заходит в веб-консоль Yandex Cloud и вручную увеличивает объем оперативной памяти виртуальной машины с 4 ГБ до 8 ГБ, чтобы спасти сервис от падения.

    На утро код в репозитории по-прежнему утверждает, что памяти должно быть 4 ГБ. Возникает конфликт источников истины.

    !Процесс возникновения и устранения дрейфа конфигурации

    Как Terraform реагирует на дрейф? Механика отрабатывает на фазе Refresh при следующем запуске terraform plan.

  • Terraform читает код: требуется 4 ГБ.
  • Terraform читает State: там записано 4 ГБ (с прошлого успешного запуска).
  • Terraform опрашивает API Yandex Cloud: реальная машина имеет 8 ГБ.
  • Terraform принимает жесткое решение: код является единственным доверенным источником истины (Single Source of Truth). Если реальность не соответствует коду, реальность должна быть изменена.

    В плане выполнения появится действие In-place update (~), в котором Terraform предложит принудительно вернуть объем памяти к 4 ГБ.

    > Если ручное изменение было правильным и его нужно сохранить, инженер обязан перенести это изменение в код (исправить 4 на 8 в .tf файле) до запуска apply. Если запустить apply без изменения кода, спасительное ночное расширение памяти будет безжалостно отменено, и сервер может снова упасть.

    Эта бескомпромиссность — главная ценность IaC. Она гарантирует, что инфраструктура всегда находится в предсказуемом, задокументированном состоянии, а любые «ручные хаки» быстро выявляются и либо легализуются через код, либо уничтожаются.

    Фаза вывода из эксплуатации: terraform destroy

    Когда инфраструктура больше не нужна (например, завершилось тестирование проекта), ее необходимо корректно удалить. Удаление ресурсов через веб-консоль Yandex Cloud — плохая практика, так как State-файл Terraform ничего об этом не узнает и при следующем запуске попытается восстановить удаленное.

    Правильный путь — команда terraform destroy. Технически это не отдельный процесс, а просто алиас для команды terraform apply -destroy.

    При запуске этой команды Terraform выполняет инверсию своего обычного поведения:

  • Читает State-файл, чтобы составить полный список существующих ресурсов.
  • Строит граф зависимостей в обратном порядке (например, сначала удаляет виртуальную машину, и только потом — подсеть, к которой она была подключена, иначе облако выдаст ошибку блокировки сети).
  • Генерирует план, в котором абсолютно все ресурсы помечены символом -.
  • После подтверждения отправляет API-запросы на удаление.
  • Проблема потерянного состояния (Orphaned Resources)

    Критически важно понимать зависимость команды destroy от State-файла. Terraform знает, что именно нужно удалять, только благодаря файлу terraform.tfstate.

    Если вы удалите папку с проектом на своем ноутбуке или случайно сотрете файл terraform.tfstate, связь между вашим кодом и реальным облаком будет разорвана навсегда.

    В такой ситуации запуск terraform destroy (даже если вы восстановите .tf файлы) ничего не даст. Terraform создаст новый, пустой State-файл, посмотрит в него, увидит, что ресурсов нет, и радостно сообщит: No changes. Infrastructure is up-to-date.

    При этом в Yandex Cloud продолжат работать виртуальные машины, потребляя бюджет. Такие ресурсы называются «осиротевшими» (orphaned). Вернуть их под управление Terraform можно только через сложную процедуру импорта (terraform import), которая вручную связывает существующие облачные ID с локальным кодом. Именно поэтому в промышленных средах State-файл никогда не хранится на локальном диске инженера — он выносится в защищенные удаленные хранилища (S3 бакеты), но эту архитектуру мы подробно разберем на этапе рефакторинга.

    Точечное удаление (-target)

    Иногда возникает потребность удалить не всю инфраструктуру, а только конкретный ресурс. Использование terraform destroy уничтожит всё. Удаление блока ресурса из кода и запуск apply — правильный путь, но иногда код нужно сохранить для будущего использования.

    В таких редких случаях применяется флаг -target. Команда terraform destroy -target=yandex_compute_instance.web прикажет ядру сфокусироваться исключительно на указанном ресурсе. Terraform вычислит, можно ли безопасно удалить только эту машину, не разрушая остальную сеть, сгенерирует план удаления одного объекта и выполнит его. Код при этом остается нетронутым, но при следующем запуске обычного apply Terraform, руководствуясь кодом, снова создаст эту машину.

    Понимание того, как планируются изменения, почему происходят деструктивные обновления и как строго система защищает код от ручных вмешательств, позволяет предсказывать поведение Terraform до того, как будет нажата клавиша Enter. Это переводит работу инженера из режима «напишем и посмотрим, что получится» в режим осознанного управления облачной архитектурой.