IaC в действии: как работают Terraform, Ansible и Helm, и зачем они нужны

Кто не слышал про Terraform, Ansible и Helm? Таких действительно почти нет, ведь это - самые популярные инструменты оптимизации инфраструктуры. Но у них есть свои особенности и недостатки, с которыми приходится учиться работать. Как и подбирать подходящий инструмент для конкретного случая.

Приветствую! Меня зовут Михаил Коробка, и сегодня я поделюсь, как мы выбираем и используем Terraform, Ansible и Helm, и что нужно учитывать при их внедрении.

Про Terraform, Ansible и Helm в общих чертах

Terraform

Это программная платформа, которая позволяет описывать всю инфраструктуру как код, а также визуализировать ее. Администратору или DevOps-специалисту после этого нужно просто решить - использовать или нет.

Главные преимущества Terraform - предсказуемость структуры, а также простая интеграция с облачными платформами, за что его особенно любят. Например, с Amazon Web Services (AWS), Google Cloud Platform (GCP), Билайн.Облако (Beeline Cloud) и т.д.

Для наглядности - вот поэтапное создание БД PostgreSQL в AWS и сохранение доступа к ней в Github secrets:

  1. Определяем переменные, которыми будем пользоваться: для примера взяты db_name, availability_zone, github_repo;

  2. Генерируем случай пароль длиной 24 символа;

  3. Создаём саму БД PostgreSQL;

  4. Записываем данные о БД в секреты и переменные Github Actions.

// локальные переменные
locals {
 db_name           = "myproject"
 availability_zone = "eu-west-2c"
 github_repo       = "my_github_repo"
}


// генерируем случайный пароль для БД
resource "random_password" "root" {
 length      = 24
 special     = false
 min_lower   = 1
 min_numeric = 1
 min_upper   = 1
}


// создаём RDS PostgreSQL
resource "aws_db_instance" "my_db" {
 identifier             = "${local.db_name}-db"
 allocated_storage      = 10
 db_name                = local.db_name
 engine                 = "postgres"
 engine_version         = "15"
 instance_class         = "db.t3.micro"
 username               = "root"
 password               = random_password.root.result
 skip_final_snapshot    = true
 availability_zone      = local.availability_zone
 db_subnet_group_name   = "your_subnet_name"
 vpc_security_group_ids = ["your_security_group_id"]
}


// Сохраняем данные о БД в секреты github
resource "github_actions_variable" "postgres_host" {
 repository    = local.github_repo
 variable_name = "POSTGRES_HOST"
 value         = aws_db_instance.my_db.address
}
resource "github_actions_secret" "postgres_user" {
 repository      =  local.github_repo
 secret_name     = "POSTGRES_USER"
 plaintext_value = aws_db_instance.my_db.username
}
resource "github_actions_secret" "postgres_password" {
 repository      =  local.github_repo
 secret_name     = "POSTGRES_PASSWORD"
 plaintext_value = aws_db_instance.my_db.password

Ansible

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

Самый главный плюс Ansible - простота конфигурации. Инфраструктуру в нем легко и записать, и прочитать. Тут даже самые hard-задачи будут выглядеть просто. 

Что еще приятно - инструмент без особых проблем интегрируется в уже существующую инфраструктуру на любом ее этапе. 

Ниже пример установки и настройки Web-сервера Caddy:

  1. Установка зависимостей;

  2. Добавление репозитория в apt;

  3. Установка самого Caddy;

  4. Копирование конфигурации из шаблона.

*Важно убедиться, что в реальном Playbook есть handlers: для notify: restart caddy.

- name: install dependencies
  apt:
   name:
     - apt-transport-https
     - debian-archive-keyring
     - debian-keyring
   update_cache: yes
   cache_valid_time: 3600
- name: gpg keyring
  apt_key:
   url: "https://dl.cloudsmith.io/public/caddy/stable/gpg.key"
- name: Add Caddy repository to sources list
  apt_repository:
   repo:
     "deb https://dl.cloudsmith.io/public/caddy/stable/deb/debian any-version main"
   state: present
   filename: caddy-stable
- name: install caddy
  apt:
   name: caddy
   update_cache: yes
- name: caddy config
  template:
   src: caddy.j2
   dest: /etc/caddy/Caddyfile
  notify: restart caddy

Helm и Kubernetes

Kubernetes - важное лицо в IT-мире. Именно ему мы обязаны эффективным управлением контейнерами, а впоследствии и ускоренной автоматизацией и масштабированием.

Helm же - пакетный менеджер Kubernetes, за счет которого упрощается работа с ним и его микросервисами. Еще он управляет различными версиями приложений. Например, в случае выката некорректной версии на продакшн, через Helm можно без проблем вернуться к другой версии.

Чтобы было понятнее, давайте разберем YAML-манифест и покажем реальную схему деплоя. Приведенный ниже пример helm-чарта устанавливает matrix-сервис с подключением в Keycloak.

# once: helm repo add ananace-charts https://ananace.gitlab.io/charts
# helm upgrade --install matrix-synapse ananace-charts/matrix-synapse --create-namespace --namespace matrix --values values-matrix.yaml
serverName: matrix.my-project.ru
publicServerName: matrix.my-project.ru
wellknown.enabled: true
config:
 enableRegistration: false
 turnUris: ["turn:sip.my-project.ru?transport=udp", "turn:sip.my-project.ru?transport=tcp"]
extraConfig:
 turn_shared_secret: "oXFeWO4gzXG0BjqL"
 # enable_registration_without_verification: "true"
 sso:
   update_profile_information: true
 oidc_providers:
   - idp_id: keycloak
     idp_name: "Центральный сервер авторизации CAS"
     issuer: "https://auth.my-project.ru/realms/my-project"
     client_id: "matrix"
     client_secret: "VerySecurePassword"
     scopes: ["openid", "profile"]
     user_mapping_provider:
       config:
         localpart_template: "{{ user.preferred_username }}"
         display_name_template: "{{ user.name }}"
         email_template: "{{ user.email }}"
     backchannel_logout_enabled: true # Optional
ingress:
 enabled: true
 hostname: matrix.my-project.ru
 ingressClassName: nginx
 tls:
   - secretName: chart-my-project-tls
     hosts:
       - matrix.my-project.ru
 annotations:
   cert-manager.io/cluster-issuer: "http01-clusterissuer"
   kubernetes.io/ingress.class: nginx
   nginx.ingress.kubernetes.io/proxy-body-size: 10m
   kubernetes.io/tls-acme: "true"

«Подводные камни» Terraform, Ansible и Helm

Есть некоторые особенности инструментов, которые необходимо учитывать.
Какие бы мощные ни были инструменты, у каждого из них есть свои особенности, о которых важно знать заранее. Это не «минусы» в классическом смысле — скорее ограничения, с которыми нужно уметь работать, особенно в командной разработке и масштабной инфраструктуре.

Terraform

  1. Привязка к конкретной версии.
    Terraform довольно строго относится к версиям CLI. Даже небольшое различие между ними может вызвать неожиданное поведение — от ошибок планирования до несовместимости провайдеров. Поэтому при работе в команде важно синхронизировать версии через .terraform-version, tfenv или аналогичные механизмы.

  2. Зависимость от state-файла.
    Вся суть Terraform — в его state, где хранится текущее состояние инфраструктуры. И вот тут есть несколько моментов:

    • state — это единый источник истины. Он не версионируется в git и не поддерживает параллельную работу по умолчанию.

    • Один файл — один пользователь. Если кто-то работает с инфраструктурой, а в это же время другой запускает terraform apply, возникает риск конфликтов.

Ansible

  • Ограничения при наличии большого количества серверов.
    Главный плюс Ansible — отсутствие агентов и простота, но с ростом количества серверов это превращается в ограничение. Инфраструктура на 1000+ хостов означает, что playbook будет поочерёдно подключаться ко всем через SSH. Даже при использовании параллельных потоков (forks) это может занять значительное время.

Helm

  • Ограничен сферой применения.
    Это мощный пакетный менеджер, но с одним жёстким условием: он работает только с Kubernetes. Если у тебя нет кластера - Helm не нужен. Сам по себе он инфраструктуру не создаёт, только управляет её содержимым внутри уже существующего Kubernetes.

Как выбрать инструмент под конкретную задачу?

Terraform работает через API (где есть provider), и в первую очередь - с облаками. Но им же можно настроить всё, что поддерживает API, например, Hashicorp vault, Keycloak, Grafana, Gitlab/Github и т.п.

Ansible же работает через SSH. Всё, что можно сделать на сервере через SSH, он предлагает сделать идемпотентно. То есть так, чтобы повторное выполнение приводило к тому же результату.

пример внедрения

Возьмём типичный проект вывода продукта на рынок. Разработчики создают продукт и пишут его по частям. В какой-то момент требуется поднять тестовый контур. Надо запустить 5 приложений, две базы данных, сторадж и сервис очередей.

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

  • Получение сертификатов SSL;

  • Мониторинг метрик;

  • Сбор и анализ логов;

  • Репликация и отказоустойчивость БД и очередей;

  • Пайплайны и автотесты;

  • Управление секретами;

  • Фаерволы и сети;

  • Доступы разработчиков к данным;

  • Дашборды с Grafana/Kibana, AKHQ, Argo CD и пр.

Команда научилась это делать и все прекрасно. Проект сдается и забывается. Но приходит новый project-менеджер, который неожиданно просит поднять стейджинг. 100% что-то окажется потеряно.

Подход IaC позволит развернуть новую инфраструктуру за минуты или часы со всем вышеперечисленным, вместо того, чтобы тратить на это недели.

Конечно, любая оптимизация инфраструктуры будет иметь свои особенности и тонкости. И Terraform, и Ansible, и Helm внедряются в нее поэтапно, чтобы минимизировать возможные риски. Поэтому IaC-подход и рекомендуют использовать с самого начала, чтобы избежать проблем. 

Плюс рекомендуется начать с небольшого пилотного проекта, предварительно тестируя все гипотезы на практике и только потом включая их полноценно! 

А с какими инструментами больше всего привыкли работать вы и какой из них - Terraform, Ansible или Helm, считаете удобным? Приглашаю поделиться своими мнениями в комментариях, так как знакомиться с чужим опытом всегда интересно.