Автоматизация сетевой инфраструктуры с помощью IaC: Terraform и Ansible | AdminWiki

Автоматизация сетевой инфраструктуры с помощью IaC: Terraform и Ansible

20 июля 2026 10 мин. чтения

Введение: зачем автоматизировать сеть с помощью кода

Ручное управление сетью создает три проблемы. Первая - ошибки. Опечатка в маске подсети или неправильный тег в консоли AWS ломает связность между серверами. Вторая - время. Развернуть VPC с десятком подсетей, таблицами маршрутизации и NAT-шлюзами через веб-интерфейс можно за час, а повторить это для dev, staging и production окружений - за три. Третья - невоспроизводимость. Конфигурация, собранная кликами, не задокументирована и не версионирована. Через полгода никто не помнит, почему в этой подсети другой шлюз.

Infrastructure as Code решает эти проблемы. Вы описываете сеть декларативно - в файлах конфигурации. Terraform создает ресурсы в облаке по этому описанию. Ansible доводит их до нужного состояния: настраивает маршруты, файрволы, политики на серверах и оборудовании. Результат - идентичные окружения, воспроизводимые за минуты, с историей изменений в Git.

В этой статье вы получите готовые примеры кода для AWS и GCP. Разберем создание VPC, подсетей, NAT-шлюзов и балансировщиков нагрузки на Terraform. Затем подключим Ansible для тонкой настройки маршрутизации на Linux и сетевом оборудовании. В финале - проверенные практики модульной структуры, версионирования и безопасного отката изменений. Если вы уже работали с Terraform и Ansible по отдельности, материал по их связке с Dynamic Inventory и remote state дополнит эту статью конкретными скриптами.

Декларативное описание сети в Terraform: от VPC до балансировщика

Terraform оперирует провайдерами - плагинами для конкретных облаков. Для AWS это hashicorp/aws, для GCP - hashicorp/google. Вы описываете желаемое состояние ресурсов в файлах .tf, запускаете terraform plan для предпросмотра изменений и terraform apply для применения. Никаких мастеров настройки, только код.

Базовая структура проекта Terraform

Рабочий проект начинается с четкой организации файлов. Типовая структура для сетевого слоя:

network/
├── main.tf          # вызов модулей
├── variables.tf     # входные параметры
├── outputs.tf       # экспортируемые значения
├── terraform.tfvars # значения переменных для окружения
└── modules/
    ├── vpc/
    │   ├── main.tf
    │   ├── variables.tf
    │   └── outputs.tf
    └── loadbalancer/
        ├── main.tf
        ├── variables.tf
        └── outputs.tf

Такое разделение позволяет переиспользовать модуль VPC в разных проектах, меняя только входные переменные. Переменные определяют CIDR-блоки, имена, теги. Outputs возвращают ID созданных ресурсов - их получат другие модули, например compute, через удаленное состояние.

Создание VPC и подсетей в AWS

Начнем с базового блока - изолированной сети. Код ниже создает VPC с двумя публичными и двумя приватными подсетями в разных зонах доступности:

resource "aws_vpc" "main" {
  cidr_block           = var.vpc_cidr
  enable_dns_support   = true
  enable_dns_hostnames = true

  tags = {
    Name        = "${var.environment}-vpc"
    Environment = var.environment
  }
}

resource "aws_subnet" "public" {
  count                   = length(var.public_subnet_cidrs)
  vpc_id                  = aws_vpc.main.id
  cidr_block              = var.public_subnet_cidrs[count.index]
  availability_zone       = var.availability_zones[count.index]
  map_public_ip_on_launch = true

  tags = {
    Name = "${var.environment}-public-${var.availability_zones[count.index]}"
  }
}

resource "aws_subnet" "private" {
  count             = length(var.private_subnet_cidrs)
  vpc_id            = aws_vpc.main.id
  cidr_block        = var.private_subnet_cidrs[count.index]
  availability_zone = var.availability_zones[count.index]

  tags = {
    Name = "${var.environment}-private-${var.availability_zones[count.index]}"
  }
}

Параметр map_public_ip_on_launch = true критичен для публичных подсетей - без него инстансы не получат публичный IP автоматически. Приватные подсети этого флага не имеют: доступ в интернет они получат через NAT-шлюз на следующем шаге.

Настройка маршрутизации и NAT-шлюза

Публичные подсети должны иметь выход в интернет напрямую, приватные - через NAT. Для этого создаем Internet Gateway, NAT Gateway и таблицы маршрутизации:

resource "aws_internet_gateway" "main" {
  vpc_id = aws_vpc.main.id

  tags = {
    Name = "${var.environment}-igw"
  }
}

resource "aws_eip" "nat" {
  domain = "vpc"
}

resource "aws_nat_gateway" "main" {
  allocation_id = aws_eip.nat.id
  subnet_id     = aws_subnet.public[0].id

  tags = {
    Name = "${var.environment}-nat"
  }
}

resource "aws_route_table" "public" {
  vpc_id = aws_vpc.main.id

  route {
    cidr_block = "0.0.0.0/0"
    gateway_id = aws_internet_gateway.main.id
  }

  tags = {
    Name = "${var.environment}-public-rt"
  }
}

resource "aws_route_table" "private" {
  vpc_id = aws_vpc.main.id

  route {
    cidr_block     = "0.0.0.0/0"
    nat_gateway_id = aws_nat_gateway.main.id
  }

  tags = {
    Name = "${var.environment}-private-rt"
  }
}

resource "aws_route_table_association" "public" {
  count          = length(var.public_subnet_cidrs)
  subnet_id      = aws_subnet.public[count.index].id
  route_table_id = aws_route_table.public.id
}

resource "aws_route_table_association" "private" {
  count          = length(var.private_subnet_cidrs)
  subnet_id      = aws_subnet.private[count.index].id
  route_table_id = aws_route_table.private.id
}

NAT Gateway размещается в публичной подсети и требует Elastic IP. Маршрут по умолчанию для приватных подсетей указывает на него, для публичных - на Internet Gateway. Ассоциации привязывают таблицы к подсетям. После terraform apply инстансы в приватных подсетях смогут скачивать пакеты, но останутся недоступны извне.

Развертывание балансировщика нагрузки

Application Load Balancer распределяет входящий трафик между инстансами в нескольких зонах. Минимальная конфигурация:

resource "aws_security_group" "alb" {
  name        = "${var.environment}-alb-sg"
  vpc_id      = aws_vpc.main.id

  ingress {
    from_port   = 443
    to_port     = 443
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }
}

resource "aws_lb" "main" {
  name               = "${var.environment}-alb"
  internal           = false
  load_balancer_type = "application"
  security_groups    = [aws_security_group.alb.id]
  subnets            = aws_subnet.public[*].id
}

resource "aws_lb_target_group" "app" {
  name     = "${var.environment}-app-tg"
  port     = 80
  protocol = "HTTP"
  vpc_id   = aws_vpc.main.id

  health_check {
    path                = "/health"
    interval            = 30
    healthy_threshold   = 3
    unhealthy_threshold = 3
  }
}

resource "aws_lb_listener" "https" {
  load_balancer_arn = aws_lb.main.arn
  port              = "443"
  protocol          = "HTTPS"
  ssl_policy        = "ELBSecurityPolicy-2016-08"
  certificate_arn   = var.certificate_arn

  default_action {
    type             = "forward"
    target_group_arn = aws_lb_target_group.app.arn
  }
}

Балансировщик размещается в публичных подсетях, принимает трафик на 443 порт и направляет его в target group с инстансами приложения. Health check проверяет endpoint /health каждые 30 секунд. Три успешных проверки - инстанс считается здоровым, три неуспешных - выводится из балансировки.

Аналогичная топология в GCP

Принципы те же, ресурсы называются иначе. Вместо VPC - google_compute_network, вместо Internet Gateway - автоматическая маршрутизация через google_compute_route, NAT реализуется через Cloud NAT, а балансировщик - через google_compute_url_map и google_compute_target_http_proxy. Пример создания сети с подсетями:

resource "google_compute_network" "main" {
  name                    = "${var.environment}-vpc"
  auto_create_subnetworks = false
}

resource "google_compute_subnetwork" "public" {
  count         = length(var.public_subnet_cidrs)
  name          = "${var.environment}-public-${count.index}"
  ip_cidr_range = var.public_subnet_cidrs[count.index]
  region        = var.region
  network       = google_compute_network.main.id
}

resource "google_compute_router" "main" {
  name    = "${var.environment}-router"
  region  = var.region
  network = google_compute_network.main.id
}

resource "google_compute_router_nat" "main" {
  name                               = "${var.environment}-nat"
  router                             = google_compute_router.main.name
  region                             = var.region
  nat_ip_allocate_option             = "AUTO_ONLY"
  source_subnetwork_ip_ranges_to_nat = "ALL_SUBNETWORKS_ALL_IP_RANGES"
}

Ключевое отличие от AWS: GCP использует Cloud Router как промежуточный слой для NAT. Подсети глобальны в рамках региона, а не привязаны к зонам доступности жестко. Балансировщик HTTP(S) в GCP - это глобальный ресурс, не требующий указания подсетей. Выбор провайдера влияет на синтаксис, но не на архитектурный подход. Если сравниваете инструменты IaC для разных задач, обратитесь к детальному сравнению Terraform, Ansible и Pulumi.

Гибкая конфигурация с Ansible: политики маршрутизации и не только

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

Динамический инвентарь из Terraform state

Ручная передача IP-адресов из Terraform в Ansible - источник ошибок. Правильный путь - динамический инвентарь, который читает state-файл Terraform и строит список хостов автоматически. Плагин terraform.py для Ansible делает именно это. Альтернатива - генерировать статический inventory через шаблон Terraform:

resource "local_file" "ansible_inventory" {
  content = templatefile("${path.module}/templates/inventory.tpl", {
    web_servers = aws_instance.web[*].public_ip
    db_servers  = aws_instance.db[*].private_ip
  })
  filename = "../ansible/inventory/hosts.ini"
}

Этот подход проще для небольших проектов. Для крупных лучше использовать плагин - он всегда отражает актуальное состояние инфраструктуры. Подробная схема такой интеграции описана в руководстве по связке Terraform и Ansible с готовым скриптом Dynamic Inventory.

Настройка маршрутизации на Linux-серверах

Облачная маршрутизация работает на уровне VPC. Но бывают сценарии, где нужны статические маршруты на самом хосте: несколько сетевых интерфейсов, VPN-туннели, source-based routing для разделения трафика. Ansible-роль для такой задачи:

- name: Add static route via eth1
  ansible.builtin.lineinfile:
    path: /etc/network/interfaces.d/eth1-routes
    line: "up ip route add 10.100.0.0/16 via 192.168.10.1 dev eth1"
    create: yes
  notify: restart networking

- name: Configure source-based routing table
  ansible.builtin.lineinfile:
    path: /etc/iproute2/rt_tables
    line: "200 custom_table"
  notify: apply routing rules

- name: Add routing rule for source IP
  ansible.builtin.command:
    cmd: "ip rule add from 10.0.1.0/24 table custom_table"
  when: ansible_check_mode == false

Первый блок добавляет статический маршрут через конкретный интерфейс. Второй создает отдельную таблицу маршрутизации для source-based routing - это нужно, когда трафик от определенных подсетей должен уходить через другой шлюз. Третий блок добавляет правило, направляющее пакеты от сети 10.0.1.0/24 в эту таблицу.

Управление сетевым оборудованием

Ansible работает с коммутаторами и маршрутизаторами через специализированные модули. Для Cisco IOS:

- name: Configure VLAN on Cisco switch
  cisco.ios.ios_vlans:
    config:
      - vlan_id: 100
        name: web_servers
        state: active

- name: Configure OSPF on Cisco router
  cisco.ios.ios_ospfv2:
    config:
      processes:
        - process_id: 1
          network:
            - address: 10.0.0.0
              wildcard_bits: 0.0.0.255
              area: 0

Для Juniper используется модуль junipernetworks.junos.junos_vlans, для Arista - arista.eos.eos_vlans. Принцип везде одинаков: описываете желаемое состояние, модуль приводит оборудование к нему. Ansible сам определяет, какие команды нужно отправить, и проверяет результат. Это критично для крупных сетей, где ручная правка конфигурации на десятках устройств гарантированно приводит к расхождениям.

Проверенные практики: модульность, версионирование и безопасные изменения

IaC без инженерных практик превращается в набор скриптов, которые страшно запускать. Четыре правила меняют ситуацию.

Модульная структура Terraform-проекта

Модуль - это изолированный набор ресурсов с четким интерфейсом: переменные на вход, outputs на выход. Модуль network создает VPC и возвращает ID подсетей. Модуль compute получает эти ID и размещает в них инстансы. Модуль loadbalancer принимает список инстансов и вешает на них балансировщик. Связываются они через удаленное состояние:

data "terraform_remote_state" "network" {
  backend = "s3"
  config = {
    bucket = "terraform-state"
    key    = "network/terraform.tfstate"
    region = "us-east-1"
  }
}

module "compute" {
  source            = "./modules/compute"
  public_subnet_ids = data.terraform_remote_state.network.outputs.public_subnet_ids
}

Такая архитектура позволяет менять сетевой слой, не трогая вычислительный, и наоборот. Каждый модуль можно тестировать отдельно. Подход детально разобран в практическом руководстве по IaC с примерами для AWS и Kubernetes.

Версионирование конфигураций в Git

Инфраструктурный код живет в репозитории наравне с прикладным. Стратегия ветвления - trunk-based: основная ветка main, короткоживущие feature-ветки для изменений. Каждый pull request запускает terraform plan в CI/CD и показывает diff ревьюеру. Тегирование релизов привязывает версию инфраструктуры к версии приложения:

git tag -a v1.3.0 -m "Добавлен NAT-шлюз для staging"
git push origin v1.3.0

Это дает полную историю: кто, когда и зачем изменил конфигурацию сети. Откат - это просто checkout предыдущего тега и terraform apply.

Планирование изменений и минимизация рисков

terraform plan - главный инструмент безопасности. Он показывает, какие ресурсы будут созданы, изменены или уничтожены, до применения. Вывод нужно читать внимательно: символ -/+ означает уничтожение и пересоздание ресурса - это прервет трафик. Флаг -target позволяет применить изменения только к конкретному ресурсу:

terraform plan -target=aws_nat_gateway.main

Для критичных окружений подключаются политики безопасности. HashiCorp Sentinel или Open Policy Agent проверяют план на соответствие правилам: нельзя создать публичный S3-бакет, нельзя открыть SSH на 0.0.0.0/0, нельзя превысить лимит ресурсов. Нарушение блокирует apply.

Стратегии быстрого отката

State-файл Terraform - это источник истины о текущем состоянии инфраструктуры. Его потеря означает ручное восстановление или повторное создание ресурсов. State хранят в S3 или GCS с включенным версионированием и блокировками через DynamoDB:

terraform {
  backend "s3" {
    bucket         = "terraform-state"
    key            = "network/terraform.tfstate"
    region         = "us-east-1"
    dynamodb_table = "terraform-locks"
    encrypt        = true
  }
}

Блокировка через DynamoDB предотвращает одновременный запуск apply двумя инженерами - второй получит ошибку и не повредит состояние. Для отката достаточно восстановить предыдущую версию state-файла из S3 и запустить terraform apply с кодом нужной версии из Git. Время восстановления - минуты, а не часы ручной правки консоли.

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

Заключение: ваш путь к полностью автоматизированной сети

Автоматизация сети через IaC - это три конкретных шага. Первый: опишите топологию в Terraform - VPC, подсети, шлюзы, балансировщик. Код из этой статьи дает готовую основу для AWS и GCP. Второй: подключите Ansible для настройки маршрутов, файрволов и оборудования. Начните с динамического инвентаря и ролей для статических маршрутов. Третий: внедрите инженерные практики - модули, Git, terraform plan перед каждым изменением и версионируемый state в S3.

Проверьте подход на тестовом проекте. Создайте репозиторий, скопируйте примеры, запустите terraform plan. Увидите список ресурсов, которые появятся в облаке - и ни одного клика в консоли. Для продакшена добавьте CI/CD пайплайн, который автоматически применяет изменения после review. Полный стек с Auto Scaling и Nginx описан в руководстве по отказоустойчивому веб-приложению.

Для практической отработки навыков потребуется облачная инфраструктура. Timeweb Cloud предоставляет VDS, базы данных и Kubernetes с поминутной тарификацией - удобно для тестовых окружений. Если в процессе настройки понадобится консультация по сложным конфигурациям, AiTunnel дает доступ к GPT и Claude через единый API без VPN - можно быстро проверить синтаксис или сгенерировать шаблон модуля.

Поделиться:
Сохранить гайд? В закладки браузера