ZIM-НИЙ SAAALEЗимние скидки: до −50% на старт и −20% на продление
до 31.01.2026 Подробнее
Выберите продукт

GitOps на практике: Terraform + Ansible + CI/CD для VDS и инфраструктуры

Разбираем практический GitOps-подход для админов и DevOps: как связать Terraform, Ansible и CI/CD, разложить код по репозиториям, организовать dev/stage/prod, план и apply, работу с секретами и откаты инфраструктуры на серверах и VDS.
GitOps на практике: Terraform + Ansible + CI/CD для VDS и инфраструктуры

GitOps уже давно перестал быть модным словом и превратился в рабочий стандарт: инфраструктура и конфигурации описываются в Git, любые изменения проходят через pull request, а развёртывание делают роботы, а не люди по SSH. Даже если у вас не Kubernetes, а обычный стек на VDS и часть на виртуальном хостинге, связка Git + Terraform + Ansible + CI/CD отлично работает и сильно уменьшает количество неожиданностей на проде.

В этой статье разберём, как собрать практически применимый GitOps-конвейер для инфраструктуры на VDS:

  • какую роль играют Terraform, Ansible и CI/CD и где границы ответственности;
  • как разложить всё по репозиториям и каталогам;
  • как реализовать промоут окружений (dev → stage → prod);
  • как хранить и прокатывать секреты;
  • как организовать откаты и контроль изменений.

Что такое GitOps в контексте Terraform + Ansible + CI/CD

Классический GitOps — это когда текущее состояние инфраструктуры и сервисов полностью описано в Git. Любое изменение делается через коммит и проходит одинаковый путь:

  1. Разработчик или админ делает изменение в репозитории.
  2. Создаётся pull/merge request, запускаются проверки, план изменений и тесты.
  3. После ревью мёрж в основную ветку триггерит автоматическое применение изменений.

Ключевая идея GitOps: «истина об инфраструктуре» живёт в Git, а не в голове админа и не только в облаке. Ручные правки в консолях и по SSH — враги воспроизводимости.

В реальном мире для GitOps по инфраструктуре обычно используют три основных компонента:

  • Terraform — управляет ресурсами: VDS, сети, балансировщики, базы, DNS, объектное хранилище и т.д.
  • Ansible — настраивает систему и ПО внутри созданных серверов: пакеты, конфиги, юниты systemd, деплой приложений.
  • CI/CD — связывает всё вместе: запускает terraform plan/apply, ansible-playbook, тесты, проверки форматирования, lint.

В итоге мы получаем цепочку:

Git → CI (plan, тесты) → review → CI (apply) → обновлённая инфраструктура

Границы ответственности: Terraform против Ansible

Самая частая проблема начинающих с GitOps — попытка сделать «всё одним инструментом». Важно провести чёткую границу и договориться об ответственности заранее.

Terraform — про ресурсы и внешний контур

Terraform хорош там, где есть явный API и состояние, которое можно описать как набор ресурсов:

  • виртуальные серверы (VDS) и их параметры;
  • сети, подсети, firewall-правила, балансировщики;
  • облачные базы данных и кластеры;
  • объектное хранилище, bucket'ы, права доступа;
  • DNS-записи, сертификаты, прочие окружные сервисы.

Terraform должен отвечать на вопросы:

  • Сколько у нас серверов и с какими характеристиками?
  • Какие у них IP, какие firewall-правила и маршруты?
  • Какие DNS-записи указывают на наши сервисы?

Ansible — про конфигурацию и софт внутри VDS

Ansible отвечает за то, что происходит внутри машины после её появления:

  • установка пакетов и зависимостей (nginx, php-fpm, PostgreSQL, Redis и т.п.);
  • раскладка конфигураций по шаблонам Jinja2;
  • создание пользователей, прав, настроек SSH;
  • деплой приложений, миграции БД, перезапуски сервисов.

Хороший критерий: если это можно выполнить по SSH и это меняет систему изнутри — почти всегда это задача Ansible, а не Terraform.

CI/CD — клей между ними

CI/CD-платформа (GitHub Actions, GitLab CI, Jenkins, Gitea Actions и т.п.):

  • запускает terraform fmt/validate/plan при каждом PR;
  • запускает terraform apply только после мёржа и с контролем окружения;
  • запускает ansible-lint, тесты ролей, dry-run (--check);
  • по тэгу или мёржу в ветку окружения разворачивает Ansible на нужных VDS.

Такой конвейер снижает соблазн «быстренько накатить по SSH» и делает изменения предсказуемыми.

Схема GitOps-конвейера с Terraform, Ansible и CI/CD для инфраструктуры на VDS

Структура репозиториев: mono-repo или несколько?

Есть два основных подхода к организации Git-репозиториев для GitOps с Terraform и Ansible. Выбор сильно влияет на скорость работы команды и сложность сопровождения.

Подход 1: mono-repo для инфраструктуры

Всё, что относится к инфраструктуре и конфигурации, лежит в одном репозитории. Возможная структура:

infra/
  terraform/
    envs/
      dev/
        main.tf
        variables.tf
      stage/
      prod/
    modules/
      vds_web/
      vds_db/
  ansible/
    inventories/
      dev/
        hosts.ini
        group_vars/
      stage/
      prod/
    roles/
      base/
      nginx/
      app/
    playbooks/
      site.yml
      app-deploy.yml
  ci/
    github-actions/
    gitlab-ci/

Плюсы:

  • всё в одном месте, проще искать и согласовывать изменения;
  • жёсткая связь между ресурсами (Terraform) и конфигурацией (Ansible) через один PR;
  • единые практики, линтеры и шаблоны пайплайнов.

Минусы:

  • репозиторий растёт; нужен порядок в директориях и правила по веткам;
  • командам приложений иногда неудобно, если они не работают с инфраструктурой напрямую.

Подход 2: отдельный repo для Terraform и отдельный для Ansible

Второй вариант — разделить репозитории:

  • infra-terraform — только Terraform-код для ресурсов;
  • infra-ansible — роли, плейбуки, инвентори.

Плюсы:

  • чистое разделение обязанностей между командами;
  • разные циклы изменений: Terraform трогают реже, Ansible — чаще;
  • меньше конфликтов в Git при параллельной работе.

Минусы:

  • сложнее увязать версии инфраструктуры и конфигураций;
  • нужно продумывать, как передавать адреса/хосты из Terraform в Ansible (artefact, inventory generator, объектное хранилище и т.п.).

Для небольших и средних проектов на VDS часто удобнее mono-repo: проще стартануть и навести порядок. Для больших команд можно начинать с mono-repo, а потом, при росте, выделять отдельные части.

Организация окружений: dev, stage, prod

GitOps без чёткого разделения окружений быстро превращается в хаос. Важно, чтобы было понятно, какой код на каком окружении крутится и как изменения «едут» по цепочке dev → stage → prod.

Окружения по каталогам

Один из самых понятных вариантов — каталоги по окружениям. В Terraform:

terraform/envs/dev/
terraform/envs/stage/
terraform/envs/prod/

Внутри у каждого окружения свой набор *.tf-файлов и свой backend для состояния. Аналогично в Ansible — отдельные инвентори и переменные:

ansible/inventories/dev/
ansible/inventories/stage/
ansible/inventories/prod/

CI/CD читает путь или параметр окружения и применяет только нужную часть. Можно завязать запуск на теги, label'ы в PR или manual job для прод-окружения.

Окружения по веткам

Другой вариант — каждая ветка Git отражает окружение:

  • main — prod;
  • stage — stage;
  • dev — dev.

Тогда:

  • изменения сначала попадают в dev, там тестируются;
  • после проверки делается merge dev → stage, затем stage → main;
  • каждая ветка имеет свой backend для terraform.tfstate и свои переменные для Ansible.

Так проще мыслить «промоутом» изменений, но сложнее с PR между ветками, и merge-конфликты возникают чаще. На практике часто комбинируют подходы: каталоги для окружений внутри репозитория и правило, что в main живёт только prod-конфиг, а для экспериментальных фич создаются временные ветки.

CI/CD-пайплайн для Terraform: plan и apply с контролем

CI/CD-пайплайн для Terraform обычно делят на два отдельных этапа: план и применение. Это даёт прозрачность для ревью и уменьшает риск «неожиданного» apply на проде.

Этап plan при каждом PR

Типичная логика:

  • при открытии или обновлении PR запускается job Terraform;
  • выполняется terraform fmt -check и terraform validate;
  • для целевого окружения (dev/stage/prod) делается terraform plan;
  • результат (plan) публикуется в комментарии к PR или в артефактах.

Важно: состояние Terraform (terraform.tfstate) хранить не в Git, а во внешнем backend (объектное хранилище, специализированный сервис состояния). Это основа для корректной работы GitOps и одновременного доступа из нескольких runner'ов.

Этап apply после мёржа

После мёржа PR в ветку, привязанную к окружению, запускается terraform apply. Здесь есть два важных момента:

  • ручное подтверждение для prod — полезно вставить «manual job» или «environment protection», чтобы apply на проде запускался только после явного approve в CI;
  • одновременный доступ к состоянию — убедитесь, что backend блокирует параллельные операции (remote backend с lock); не запускайте два apply сразу.

Неплохая практика — сохранять план как артефакт и использовать именно его на этапе apply. Так вы избежите ситуации, когда между план и применением кто-то успел влить ещё один PR и итоговый diff уже не тот, что смотрел ревьюер.

CI/CD-пайплайн для Ansible: lint, check, deploy

С Ansible подход похож, но есть свои особенности: он не хранит состояние, его задача — привести сервер к описанному виду. Поэтому особенно важно качество плейбуков и ролей.

Проверки при PR

Базовый набор проверок:

  • ansible-lint для ролей и плейбуков;
  • синтаксическая проверка плейбуков: ansible-playbook playbooks/site.yml --syntax-check;
  • опционально — ansible-playbook ... --check --diff против тестового инвентори или локального окружения.

Это помогает отловить типовые ошибки (опечатки в переменных, неправильные модули, кривые шаблоны) раньше, чем они попадут на прод.

Деплой на окружения

После мёржа в ветку окружения CI делает отдельный job для Ansible:

  • подтягивает инвентори для окружения (например, ansible/inventories/prod/hosts.ini);
  • запускает нужные плейбуки (site.yml, app-deploy.yml и т.п.);
  • логирует результат и, при ошибках, даёт быстрый доступ к логам.

Здесь важно следить, чтобы никто не правил конфигурации руками на серверах. Любые внеплановые правки надо оформлять отдельным PR, иначе GitOps-развёртывание их просто затрёт при следующем запуске.

Интеграция Terraform и Ansible: инвентори и переменные

Задача GitOps-пайплайна — сделать так, чтобы Ansible знал о том, какие VDS и ресурсы созданы Terraform, и мог по ним отрабатывать. Обычно это решают через динамический или генерируемый статический инвентори.

Вариант 1: dynamic inventory на основе Terraform state

Можно использовать динамический инвентори, который читает Terraform state (через CLI или API backend'а) и формирует список хостов и групп. Общая схема:

  1. Terraform создаёт VDS и записывает в state их IP/hostname.
  2. Динамический инвентори-скрипт или плагин вытаскивает эти данные и строит структуру групп.
  3. Ansible подключается к этим хостам и применяет роли.

Плюс: меньше ручной синхронизации. Минус: больше «магии» вокруг state, нужно аккуратно работать с правами доступа и версионированием схемы инвентори.

Вариант 2: генерация статического инвентори

Более простой и прозрачный подход:

  • Terraform после apply генерирует файл инвентори в понятном для Ansible формате (INI или YAML);
  • этот файл складывается как артефакт CI или в отдельное хранилище (например, в выделенную ветку git с артефактами или в объектное хранилище);
  • Ansible-пайплайн использует этот инвентори для запуска плейбуков.

С точки зрения управления изменениями удобно, если генерируемый инвентори не коммитится вручную, а создаётся только в CI: так вы точно знаете, что все хосты в нём соответствуют текущему состоянию Terraform.

Если вы хотите чуть более продвинутую схему шифрования инвентори и конфигов, можно посмотреть в сторону подходов с sops и age, подробнее разбирали это в материале о GitOps и шифровании секретов sops + age.

Схема интеграции Terraform и Ansible через динамический или статический инвентори

Секреты в GitOps: как не сжечь всё к чертям

GitOps подразумевает, что конфиги живут в Git, но это не значит, что в репозиторий нужно класть открытые пароли и ключи. Есть несколько действительно рабочих подходов, которые сочетаются с Terraform и Ansible.

Переменные окружения CI/CD

Самый простой уровень — хранить чувствительные данные в зашифрованном хранилище переменных CI/CD:

  • доступ к переменным строго ограничен окружением (dev/stage/prod);
  • Terraform и Ansible подхватывают их как TF_VAR_* или через extra-vars;
  • секреты не попадают в логи (важно следить за маскированием в настройках CI).

Минус — сложнее отслеживать изменения секретов и делать ревью: всё живёт «внутри CI», а не в Git-истории.

Зашифрованные файлы в репозитории

Более GitOps-ориентированный подход — хранить секреты в Git, но в зашифрованном виде:

  • Ansible Vault для group_vars и host_vars;
  • утилиты вроде sops для шифрования YAML/JSON с ключами;
  • файлы с секретами шифруются, а ключ доступа лежит вне Git (в CI/CD, в хранилище ключей и т.п.).

Тогда изменения в секретах также проходят через PR, ревью и историю Git (без раскрытия значений). Удобный паттерн — хранить шифровальные ключи на уровне окружений и давать к ним доступ только тем пайплайнам, которые разворачивают соответствующее окружение.

Откаты в GitOps: Terraform и Ansible

Одно из важных преимуществ GitOps — понятный откат к предыдущему состоянию. Никаких ручных восстановлений «как было неделю назад» — всё через revert и повторный запуск пайплайна.

Откат Terraform

С Terraform откат — это в первую очередь «отмотать конфиг к предыдущему коммиту»:

  • находите коммит, где инфраструктура была в рабочем состоянии;
  • делаете revert или отдельный PR, который приводит код к тому состоянию;
  • CI снова делает terraform plan/apply и приводит ресурсы к этому описанию.

Важно понимать, что не все изменения обратимы без потерь (например, удалённый диск с данными Terraform не вернёт). Поэтому для критичных вещей нужен отдельный план бэкапов, снапшотов и восстановления данных.

Откат Ansible

С Ansible сложнее: он не хранит состояние, а просто приводит систему к описанному в плейбуках виду. Стратегия отката:

  • описать «старое» состояние в виде кода (ролей, шаблонов) и иметь его в истории Git;
  • сделать revert коммита, который сломал конфигурацию;
  • запустить плейбук снова — Ansible приведёт систему к предыдущему стабильному состоянию.

Для приложений дополнительно используют стратегии blue/green и canary-деплои, но это уже тема отдельного разговора. С точки зрения инфраструктуры на VDS гораздо важнее, чтобы все изменения конфигурации действительно проходили через Git и Ansible, а не через ручные правки.

Практические советы по внедрению GitOps на Terraform + Ansible

Чтобы GitOps-подход не остался на бумаге, полезно соблюдать несколько простых, но жёстких правил. С ними связка Terraform + Ansible начинает реально спасать от ночных приключений на проде.

1. Никаких ручных правок мимо Git

Любое изменение инфраструктуры или конфигурации должно жить в Git. Если нужно «быстро пофиксить конфиг на проде», делаем это:

  1. правим код в репозитории;
  2. открываем PR, запускаем проверки;
  3. мержим и даём CI всё развернуть.

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

2. Один источник истины на ресурс

То, что описано в Terraform, не должно параллельно конфигурироваться Ansible. Например, не стоит одновременно управлять пользователями или сетевыми правилами и там, и там. Чётко делите зоны ответственности, иначе получите состояние, которое невозможно воспроизвести.

3. Маленькие, частые изменения вместо гигантских PR

Чем меньше объём изменений, тем проще их ревьюить и откатывать. Terraform и Ansible отлично поддерживают инкрементальную эволюцию: добавили один VDS — один PR, внедрили новую роль — отдельный PR, обновили версию PostgreSQL — отдельный PR плюс миграции.

4. Автоматические проверки как «ворота»

Не пускайте изменения в основную ветку, если не прошли:

  • terraform fmt/validate и terraform plan без ошибок;
  • ansible-lint и --syntax-check по ключевым плейбукам;
  • базовые unit-/интеграционные тесты приложений, если они связаны с конфигурацией.

CI/CD должен быть не формальностью, а реальным фильтром проблем. В долгую это экономит часы отладки и значительно снижает риск поломать прод разовым неудачным коммитом.

Итоги

GitOps — это не только про Kubernetes. Для классической инфраструктуры на VDS сочетание Terraform + Ansible + CI/CD даёт те же ключевые преимущества:

  • прозрачная история изменений инфраструктуры и конфигураций в Git;
  • повторяемые деплои и минимизация ручных действий на серверах;
  • быстрый откат за счёт revert'ов и повторного запуска пайплайнов;
  • чёткое разделение зон ответственности между Terraform (ресурсы) и Ansible (конфигурации внутри).

Начать можно с малого: вынести инфраструктуру VDS в Terraform, обернуть текущие плейбуки Ansible в простой CI-пайплайн и договориться в команде, что всё теперь проходит через PR. Дальше добавятся динамический инвентори, автоматизированное управление секретами, промоут окружений и другие «приятные сложности», но базовый GitOps-подход уже начнёт работать на вас и делать инфраструктуру предсказуемой.

Дополнительно инфраструктурные DNS-записи и записи A/CNAME для сервисов имеет смысл тоже втащить в Terraform — это упрощает жизнь при работе с доменами и SSL-сертификаты. Подробно про такой сценарий есть в статье о GitOps и управлении DNS через Terraform.

Поделиться статьей

Вам будет интересно

Pet‑проект на VDS: как не превратить домашнего зверька в боевой продакшн OpenAI Статья написана AI (GPT 5)

Pet‑проект на VDS: как не превратить домашнего зверька в боевой продакшн

Pet‑проекты на VDS помогают прокачаться без давления продакшна: можно свободно экспериментировать со стеком, деплоем, бэкапами и м ...
PHP‑фреймворки Laravel, Symfony и Yii: что выбрать для проекта OpenAI Статья написана AI (GPT 5)

PHP‑фреймворки Laravel, Symfony и Yii: что выбрать для проекта

Разбираем Laravel, Symfony и Yii глазами админа и разработчика: архитектура, производительность, требования к хостингу, типовые ке ...
Self‑hosted Git на VDS: Gitea, GitLab CE и Forgejo для команд и pet‑проектов OpenAI Статья написана AI (GPT 5)

Self‑hosted Git на VDS: Gitea, GitLab CE и Forgejo для команд и pet‑проектов

Разбираемся, как выбрать и развернуть self-hosted Git на VDS с Gitea, GitLab Community Edition и Forgejo. Обсудим, чем платформы о ...