open atlas
↑ К треку
AWS на практике AWS · 06 · 01

Инфраструктура как код в AWS: CloudFormation и CDK

CloudFormation — нативный AWS-IaC: template описывает ресурсы, stack — развёрнутый экземпляр, change sets показывают add/modify/replacement. CDK пишет инфраструктуру на настоящем языке и синтезирует её в CloudFormation — те же стеки, change sets и drift под капотом.

AWS Senior ◷ 20 min
Уровень
ОсновыJuniorMiddleSenior

В 2 часа ночи дежурному инженеру нужно больше места на продакшен-инстансе RDS, и он увеличивает размер прямо в консоли — быстро, готово, спать. Через две недели запускается рутинный деплой, и CloudFormation, который всё ещё считает, что у базы старый размер и другая версия движка, вычисляет diff относительно template, а не относительно реальности. Одно из свойств, которое он хочет «исправить», требует пересоздания ресурса: change set тихо помечает базу как Replacement. Деплой выполняется, CloudFormation создаёт новый пустой инстанс, переключает соединение и удаляет старый. Разбор инцидента отследил потерю данных не к деплою, а к той правке в консоли в 2 ночи — к drift, который никто не обнаружил, — столкнувшейся со свойством, у которого update behavior равен Replacement. Инфраструктура как код должна предотвращать ровно это, и она предотвращает — но только если код является источником истины и никто не правит в обход него.

CloudFormation: декларативное ядро

CloudFormation — нативный для AWS сервис «инфраструктура как код». Ты пишешь template — документ в YAML или JSON, — который описывает желаемое конечное состояние инфраструктуры. У template есть несколько верхнеуровневых секций: Resources (единственная обязательная — собственно ресурсы AWS, которые надо создать), Parameters (типизированные входы, которые ты передаёшь при деплое), Mappings (статические таблицы соответствий, например регион к AMI), Conditions (булевы значения, которые включают или нет ресурс или свойство) и Outputs (значения, которые стек экспортирует, например ID VPC, который импортирует другой стек). Ты описываешь, что хочешь; CloudFormation сам разбирается с порядком, зависимостями и вызовами API.

Stack — это развёрнутый экземпляр template, работающая совокупность ресурсов, которые CloudFormation создал и теперь управляет ими как единым целым. Отношение точь-в-точь как у класса и объекта: один template может стоять за многими стеками (dev, staging, prod). И важнейшее: CloudFormation управляет состоянием за тебя. Нет файла состояния, который надо хранить, блокировать или повредить, — AWS отслеживает, какие реальные ресурсы принадлежат какому стеку, на своей стороне. Это и есть самое большое операционное отличие от сторонних IaC-инструментов.

AWSTemplateFormatVersion: "2010-09-09"
Description: A bucket and a table, parameterized by environment.

Parameters:
  Environment:
    Type: String
    AllowedValues: [dev, prod]
    Default: dev

Conditions:
  IsProd: !Equals [!Ref Environment, prod]

Resources:
  AssetsBucket:
    Type: AWS::S3::Bucket
    Properties:
      BucketName: !Sub "myapp-assets-${Environment}"
      VersioningConfiguration:
        Status: !If [IsProd, Enabled, Suspended]

  SessionsTable:
    Type: AWS::DynamoDB::Table
    Properties:
      # Переименование DynamoDB-таблицы требует REPLACEMENT — новая таблица, данные исчезают.
      TableName: !Sub "sessions-${Environment}"
      BillingMode: PAY_PER_REQUEST
      AttributeDefinitions:
        - AttributeName: sessionId
          AttributeType: S
      KeySchema:
        - AttributeName: sessionId
          KeyType: HASH

Outputs:
  BucketName:
    Value: !Ref AssetsBucket
    Export:
      Name: !Sub "${Environment}-assets-bucket"

Когда ты обновляешь стек, CloudFormation сравнивает новый template с текущим стеком и формирует change set — превью того, что именно произойдёт, до того как что-либо изменится. Change set классифицирует каждый ресурс как add, modify или remove, а для изменений сообщает update behavior: No interruption (ресурс обновляется на месте без простоя), Some interruption (обновляется на месте, но с кратким перебоем) или — опасный вариант — Replacement (CloudFormation создаёт новый ресурс, затем удаляет старый). Replacement зависит от свойства: некоторые свойства просто нельзя изменить на живом ресурсе, поэтому их изменение пересоздаёт его. Для базы данных или stateful-хранилища Replacement может быть разрушительным — новый ресурс пуст, а старый удаляется. Весь смысл change set в том, чтобы показать это слово Replacement до выполнения, а не после.

CloudFormation также откатывается при сбое: если любой ресурс в обновлении падает, он пытается вернуть каждый ресурс в прежнее состояние. У этой страховки есть собственный печально известный режим отказа — UPDATE_ROLLBACK_FAILED, когда сам откат не может завершиться (часто из-за того, что ресурс изменили в обход), и стек заклинивает, пока ты вручную не починишь или не пропустишь проблемный ресурс.

Drift: когда реальность перестаёт совпадать с template

У той правки в консоли в 2 ночи из вступления есть имя: drift. CloudFormation определяет drift как расхождение фактической конфигурации стека с ожидаемой, записанной в template. Любое изменение вне CloudFormation — через консоль, CLI или другой инструмент, что часто называют ClickOps, — может его внести. Drift detection сравнивает живые значения свойств каждого поддерживаемого ресурса с template и сообщает статус: IN_SYNC, когда они совпадают, MODIFIED, когда значение свойства отличается, DELETED, когда ресурс удалили в обход, и NOT_CHECKED для типов ресурсов, не поддерживающих drift detection. Стек в статусе DRIFTED, если дрейфнул хотя бы один ресурс.

Drift не просто косметика. Как показало вступление, следующий деплой делает diff относительно картины мира template, поэтому изменение в обход может быть молча откачено — или, хуже, может столкнуться со свойством Replacement и уничтожить данные. Зрелые команды гоняют drift detection по расписанию и считают любой DRIFTED-стек инцидентом, потому что это значит, что код больше не источник истины. Дисциплина, делающая IaC ценным, — это не править в обход него; drift detection — сигнализация, которая сообщает, что кто-то это сделал.

Для крупных систем template разбивают: nested stacks (родительский стек, чьи ресурсы сами являются дочерними стеками) и cross-stack references (Outputs одного стека экспортируются и импортируются другим через Fn::ImportValue) позволяют собирать инфраструктуру без одного неуправляемого файла на 3000 строк. Учти один острый угол: drift detection на родительском стеке не рекурсирует во вложенные — drift на каждый nested stack ты определяешь напрямую.

CDK: настоящий язык, компилирующийся в CloudFormation

Писать YAML руками быстро становится больно: нет циклов, нет настоящих условий, нет типов, и интент из пяти ресурсов раздувается в сотни строк. AWS Cloud Development Kit (CDK) — это ответ: ты описываешь инфраструктуру на языке общего назначения (TypeScript, Python, Java, C#, Go), используя constructs — переиспользуемые строительные блоки. Конструкты бывают трёх уровней: L1 (классы Cfn*) — голые соответствия один-к-одному ресурсам CloudFormation; L2 оборачивают их разумными, безопасными умолчаниями и хелперами (s3.Bucket, который сам настраивает шифрование и removal policy); L3 — это patterns, которые собирают много ресурсов в один мнениевый блок (ApplicationLoadBalancedFargateService, который поднимает VPC, кластер, балансировщик и сервис разом).

Ключевое озарение — в механизме: cdk synth синтезирует твой код в template CloudFormation, а cdk deploy передаёт этот template в CloudFormation для провижининга. CDK — это генератор более высокого уровня поверх CloudFormation, а не его замена. Ты по-прежнему получаешь стеки, change sets, откат и drift detection — потому что фактически запускается всё тот же стек CloudFormation. Несколько десятков строк CDK рутинно выдают template на 500+ строк и 50+ ресурсов.

import { Stack, StackProps, RemovalPolicy } from "aws-cdk-lib";
import { Construct } from "constructs";
import * as s3 from "aws-cdk-lib/aws-s3";
import * as dynamodb from "aws-cdk-lib/aws-dynamodb";

export class AppStack extends Stack {
  constructor(scope: Construct, id: string, props?: StackProps) {
    super(scope, id, props);

    const isProd = id.includes("prod");

    // L2 конструкт: разумные, безопасные умолчания (шифрование по умолчанию включено).
    new s3.Bucket(this, "AssetsBucket", {
      versioned: isProd,                      // настоящий boolean, а не CFN Condition
      removalPolicy: isProd
        ? RemovalPolicy.RETAIN                // защищаем данные prod при удалении стека
        : RemovalPolicy.DESTROY,
    });

    new dynamodb.Table(this, "SessionsTable", {
      partitionKey: { name: "sessionId", type: dynamodb.AttributeType.STRING },
      billingMode: dynamodb.BillingMode.PAY_PER_REQUEST,
    });
  }
}
// `cdk synth` превращает это в template CloudFormation выше (и больше);
// `cdk deploy` прогоняет его через CloudFormation как стек.
Почему это работает

Почему «читай вывод synth» всплывает снова и снова? Потому что удобство CDK — это и его риск: конструкт L2 или L3 может тихо создать больше, чем ты просил, — лишние IAM-политики, NAT gateway с почасовой оплатой, log group с бесконечным хранением. Код CDK читается как пять строк интента, но развёрнутая реальность — это то, что говорит сгенерированный template, и именно его выполняет CloudFormation. cdk synth и cdk diff — это то, как ты держишь код и реальность честными: ты ревьюишь выданный CloudFormation, а не только TypeScript, ровно так же, как читал бы вывод компилятора, когда на кону пересоздание продакшен-ресурса.

Трейдофф реален. CDK даёт тебе циклы, условия, проверку типов, юнит-тесты и переиспользуемые абстракции — весь инструментарий языка программирования, применённый к инфраструктуре. Стоит это шага сборки, сгенерированных template, которые бывают большими и плохо читаемыми, и обязанности понимать выдаваемый CloudFormation. Голый CloudFormation — противоположность: более многословный и менее переиспользуемый, зато абсолютно прозрачный — что написал, то и развернётся, без слоя синтеза, о котором надо рассуждать.

ИзмерениеCloudFormation (YAML/JSON)CDK (TypeScript/Python…)
Модель написанияДекларативный template, что написал — то и развернётсяИмперативный код, синтезирующий template
Абстракция / переиспользованиеНизкое — копипаст, nested stacks, без настоящих цикловВысокое — циклы, условия, типы, конструкты L2/L3
Шаг сборкиНет — деплоишь файл напрямуюНужен — cdk synth компилирует в CFN
ПрозрачностьПолная — template и есть артефактКосвенная — надо читать вывод synth, чтобы знать, что развернётся
Движок под капотомCloudFormation: стеки, change sets, drift, откатТот же — CDK деплоит через CloudFormation
Выбери лучший вариант

Продуктовая команда из app-разработчиков (сильны в TypeScript) должна поднять воспроизводимую инфраструктуру по средам — VPC, ECS Fargate-сервис, RDS, балансировщик — для dev/staging/prod, с ревью в pull request, одной и той же формой, продублированной трижды. Выбери подход.

Викторина

Коллега запустил cdk deploy и спрашивает, где хранится файл состояния в стиле Terraform, чтобы его залочить. Какой ответ верный?

Викторина

Change set для обновления стека помечает твою продакшен-базу как 'Replacement: True'. Ты всё равно его выполняешь. Что, скорее всего, произойдёт?

Вспомните перед уходом
  1. 01
    Объясни связь между template CloudFormation, стеком, change set и drift — и где сюда вписывается CDK.
  2. 02
    Что такое конструкты L1/L2/L3 и чем ты жертвуешь, выбирая CDK вместо CloudFormation руками?
Итог

У инфраструктуры как кода в AWS есть нативный путь с двумя лицами. CloudFormation — декларативное ядро: template — Resources, Parameters, Mappings, Conditions, Outputs — описывает желаемое конечное состояние, stack — развёрнутый экземпляр этого template, и CloudFormation управляет состоянием за тебя без файла состояния, который надо лочить или портить. При обновлении он вычисляет change set (набор предварительно рассчитанных изменений), показывающий каждое добавление, изменение и удаление, помечая изменения как No interruption, Some interruption или Replacement; Replacement пересоздаёт ресурс и удаляет старый, что для базы данных или stateful-хранилища уничтожает данные, — поэтому change set и существует, чтобы показать это слово до выполнения. Drift detection ловит изменения в обход, через ClickOps, которые рассинхронизируют template с реальностью — сообщая IN_SYNC, MODIFIED, DELETED или NOT_CHECKED, — потому что дрейфнувший стек значит, что код больше не источник истины, а следующий деплой может молча откатить или уничтожить. CDK — генератор поверх: ты описываешь инфраструктуру на настоящем языке через конструкты L1 (голые), L2 (разумные умолчания) и L3 (patterns), затем cdk synth выдаёт template CloudFormation, а cdk deploy его запускает — так что стеки, change sets, откат и drift остаются под капотом, а ты получаешь циклы, типы и переиспользование ценой шага сборки и обязанности читать вывод synth. Выбирай CloudFormation для прозрачной нативной для AWS декларативной инфраструктуры; выбирай CDK для программной абстракции, когда команда уже пишет прикладной код, — и в обоих случаях никогда не правь в обход кода. Теперь, когда увидишь слово Replacement в change set или услышишь «я только чуть подправлю в консоли», ты будешь знать, к какому именно инциденту это ведёт.

Практика

Начни сверху. Задачи идут от простого к сложному: вспомнить факт, применить к случаю, затем senior-уровень. Открой, попробуй, потом открой ответ.

вспомнитьприменитьуглубить0 из 5 завершено

Что-то непонятно?

Задай вопрос по этому уроку. Вопросы анонимны и попадают напрямую автору — урок станет лучше.

Примени это

Примени этот урок в реальном проекте.

хоткеи развернуть
поиск
K
пред. пьеса
k
след. пьеса
j
тиры
t
это меню
?
sources3
expand
  1. 01
  2. 02
  3. 03

Trademarks belong to their respective owners. Editorial reference only.