Перейти до вмісту

Інфраструктура як код і доставка

Ви створили в консолі мережу, кластер і три бакети. За місяць колега додав правило файрвола «тимчасово», ви змінили розмір диска, а хтось третій вимкнув логування, бо воно коштувало грошей. Тепер потрібне те саме середовище для тестів, і ніхто не може сказати, з чого воно складається.

Інфраструктура як код (infrastructure as code, IaC) розв’язує цю ситуацію так само, як система контролю версій розв’язала її для програм: опис середовища лежить у файлах, змінюється через pull request, перевіряється перед застосуванням і відтворюється командою. Друга половина модуля про те, як ці зміни доходять до хмари: хто має право їх застосовувати і як випустити нову версію застосунку, не зупинивши сервіс.

Передумови. Акаунти й ієрархія ресурсів (модуль 2), ролі й федерація (модуль 3), мережа й обчислення (модуль 4, модуль 5), контейнери (модуль 8). Про контейнери й образи в основі є модуль 17 курсу ОС.

Скрипт з викликами aws ec2 run-instances описує дії: створити, потім приєднати, потім увімкнути. Запустіть його вдруге, і він створить дублікат або впаде на помилці «вже існує».

Декларативний опис фіксує результат: «має існувати мережа з такими параметрами». Інструмент сам обчислює, що треба створити, змінити чи видалити, щоб реальність збіглася з описом. Повторний запуск нічого не змінює, якщо збігається вже й так. Ця властивість називається ідемпотентністю (idempotency), і вона потрібна, щоб застосовувати опис автоматично, без людини.

Головний інструмент курсу, Terraform, працює саме так. Мова конфігурації називається HCL. Термін «провайдер» тут має два значення, тож розрізняйте їх за контекстом: хмарний провайдер (AWS, Azure, Google Cloud) і Terraform-провайдер, плагін, що вміє викликати API конкретної платформи. Ресурси описані в коді, плагіни завантажуються командою terraform init, а версії фіксуються у файлі .terraform.lock.hcl, який слід зберігати в репозиторії.

terraform {
required_version = ">= 1.11"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 6.0"
}
azurerm = {
source = "hashicorp/azurerm"
version = "~> 5.0"
}
google = {
source = "hashicorp/google"
version = "~> 8.0"
}
}
}

Один корінь може використовувати всі три плагіни одночасно, але в реальних проєктах так роблять рідко: кожен провайдер зазвичай отримує окремий корінь зі своїм станом. Зверніть увагу на мажорні версії: azurerm 5.x і google 8.x вийшли вже після більшості публікацій в інтернеті, тож приклади з чужих статей часто містять атрибути, яких у поточній версії немає. Наприклад, у поточній документації azurerm_storage_container описано лише з storage_account_id, а не з storage_account_name. terraform validate ловить такі помилки до того, як вони коштують грошей.

Стан: що Terraform знає про світ

Section titled “Стан: що Terraform знає про світ”

Файл конфігурації описує бажане. Але щоб знайти різницю з реальністю, Terraform має пам’ятати, які саме об’єкти хмари відповідають якому ресурсу з коду. Ця пам’ять називається станом (state). Стан зіставляє адресу ресурсу в коді (aws_s3_bucket.reports) з ідентифікатором у хмарі та зберігає останні відомі значення його атрибутів.

Цикл Terraform: конфігурація і стан порівнюються для побудови плану, apply змінює хмару й оновлює стан; стан лежить у віддаленому сховищі з блокуваннямapply: виклики API хмари, потім запис у станконфігурація .tfбажаний станстан (state)що Terraform вважає створенимхмаращо є насправдіdiff → планrefreshвіддалене сховище стануS3 · Blob · GCS + блокуваннядрейф: зміна поза Terraform
Три джерела істини. План будується як різниця між кодом і станом, оновленим із хмари. Apply змінює хмару й записує результат у стан. Зміна, зроблена в консолі, стає дрейфом, який Terraform виявить лише під час наступного refresh.

З цього випливає кілька практичних наслідків.

Стан містить секрети. Пароль бази, який ви передали ресурсу, лежить у стані відкритим текстом. Тому стан зберігають у сховищі з шифруванням і жорстким доступом, а не в git. Нові версії Terraform дають ефемерні значення й атрибути лише для запису (write-only), які в стан не потрапляють; корисно користуватися ними для секретів, де провайдер це підтримує.

Втрата стану означає втрату зв’язку з реальністю. Ресурси в хмарі лишаються, але Terraform про них «забув» і при наступному запуску спробує створити дублікати. Тому бакет зі станом вмикає версіонування, щоб можна було повернути попередню версію файлу.

Два одночасні apply псують стан. Якщо двоє запускають зміни, кожен читає однакову початкову версію, а записує свою, і одна зміна затирає іншу. Захищає від цього блокування (locking): перед змінами Terraform отримує ексклюзивне право на стан, а другий запуск чекає або завершується помилкою.

Віддалене зберігання стану

Section titled “Віддалене зберігання стану”

За замовчуванням стан лежить у локальному файлі terraform.tfstate. Для команди це не працює: файл є лише в одного. Тому стан переносять у віддалене сховище (backend). Кожна хмара має природне місце для нього: об’єктне сховище, яке і так є в акаунті. Спершу сховище треба створити, і це єдина частина, яку зазвичай створюють вручну або окремим невеликим коренем.

variable "state_bucket_name" {
type = string
}
resource "aws_s3_bucket" "state" {
bucket = var.state_bucket_name
}
resource "aws_s3_bucket_versioning" "state" {
bucket = aws_s3_bucket.state.id
versioning_configuration {
status = "Enabled"
}
}
resource "aws_s3_bucket_public_access_block" "state" {
bucket = aws_s3_bucket.state.id
block_public_acls = true
block_public_policy = true
ignore_public_acls = true
restrict_public_buckets = true
}

Після цього в основному корені додається блок backend, і terraform init пропонує перенести наявний локальний стан у нове сховище.

terraform {
backend "s3" {
bucket = "acme-tfstate-prod"
key = "network/terraform.tfstate"
region = "eu-central-1"
encrypt = true
use_lockfile = true
}
}

Блокування влаштоване в кожного сховища по-своєму:

  • S3. Раніше для блокування потрібна була ще й таблиця DynamoDB. Тепер use_lockfile = true змушує Terraform створювати поряд зі станом файл .tflock умовним записом: S3 приймає запис лише якщо об’єкта ще немає, тож другий запуск отримує відмову. Підтримка з’явилася в Terraform 1.10 як експериментальна і стала стабільною в 1.11; аргумент dynamodb_table позначено застарілим. Обліковим даним потрібні права на читання, запис і видалення об’єкта .tflock, а не лише самого стану. При міграції спершу вмикають обидва механізми, потім прибирають DynamoDB.
  • Azure Blob Storage. Terraform бере оренду (lease) на blob зі станом: поки оренда в одного запуску, другий не запишеться. Окремої таблиці чи файла не потрібно. Автентифікація use_azuread_auth означає доступ за ролями Entra ID, без ключа облікового запису сховища.
  • Google Cloud Storage. Блокування вбудоване, окремих аргументів немає. Так само вмикають версіонування об’єктів і за потреби ключ Cloud KMS (kms_encryption_key).

Робочий цикл складається з невеликого набору команд:

Terminal window
terraform init # завантажити плагіни, підключити сховище стану
terraform fmt -check # єдиний стиль коду
terraform validate # синтаксис і схема ресурсів, без звернення до хмари
terraform plan -out=tfplan
terraform show tfplan # переглянути збережений план
terraform apply tfplan # застосувати саме той план, який ви бачили

plan оновлює стан із хмари, порівнює його з кодом і показує кожну дію зі знаком: + створити, ~ змінити, - видалити, -/+ замінити (видалити й створити наново). Останній символ найважливіший для читання: для бази даних чи диска «замінити» означає втрату даних, і Terraform це показує, але не зупиняє. Збережений через -out план гарантує, що apply виконає саме те, що затвердила людина, а не те, що змінилося за годину між рев’ю й запуском.

Для CI корисні два прапорці: -detailed-exitcode повертає код 2, якщо зміни є, а -lock-timeout=5m змушує чекати на блокування замість негайної помилки. Якщо запуск обірвався й лишив блокування, його знімає terraform force-unlock з ідентифікатором блокування, але лише після того, як ви переконалися, що жоден запуск справді не працює.

До цього ж циклу належать перевірки припущень: блоки precondition і postcondition у ресурсах та окремі блоки check спрацьовують під час plan і apply, а команда terraform test запускає тести модулів.

Дрейф (drift) виникає, коли реальна інфраструктура розійшлася з тим, що знає Terraform: правило файрвола змінили в консолі, вимкнули шифрування вручну, автомасштабування змінило кількість інстансів. Виявляє його той самий plan, бо він оновлює стан із хмари. Щоб побачити лише розбіжності, без пропозицій щодо коду, є режим:

Terminal window
terraform plan -refresh-only # що змінилося поза Terraform
terraform apply -refresh-only # прийняти це у стан, не змінюючи хмару

Далі рішення людини: або привести хмару до коду (звичайний apply скасує ручну зміну), або підправити код під нову реальність. Друге буває виправданим, наприклад коли автомасштабування змінює desired_count, і тоді ці атрибути виносять у lifecycle { ignore_changes = [...] }.

Зворотна ситуація: ресурс вже існує, а в коді його немає. Його підключають до стану блоком import, який працює в межах звичайного plan і apply:

resource "aws_s3_bucket" "legacy" {
bucket = "acme-legacy-reports"
}
import {
to = aws_s3_bucket.legacy
id = "acme-legacy-reports"
}

Формат id різний: назва в S3 і GCS, повний шлях ARM у Azure. Його завжди беруть зі сторінки ресурсу в документації Terraform-провайдера, розділ Import. Для перейменування ресурсу в коді без перестворення служить блок moved:

moved {
from = aws_s3_bucket.reports
to = module.reports.aws_s3_bucket.this
}

Модуль є каталогом з .tf-файлами, який викликають з іншого місця з параметрами. Потрібен він з тієї самої причини, що й функція: у вас десять бакетів з однаковими правилами приватності й версіонування, і правило треба змінити в одному місці. Вхідні змінні (variable) є аргументами модуля, вихідні (output) поверненим значенням. Змінна може мати validation, яка відхилить хибний вхід ще до звернення до API хмари.

variable "name" {
type = string
validation {
condition = can(regex("^[a-z0-9][a-z0-9.-]{2,62}$", var.name))
error_message = "Назва бакета: 3-63 символи, малі літери, цифри, крапки й дефіси."
}
}
resource "aws_s3_bucket" "this" {
bucket = var.name
}
resource "aws_s3_bucket_versioning" "this" {
bucket = aws_s3_bucket.this.id
versioning_configuration {
status = "Enabled"
}
}
output "arn" {
value = aws_s3_bucket.this.arn
}

Викликається модуль однаково в усіх трьох випадках; відрізняються лише аргументи, які він оголосив:

module "reports" {
source = "./modules/private-store"
name = "acme-reports-prod"
}

Модуль може лежати в сусідньому каталозі, у git-репозиторії з тегом версії або в реєстрі. Готових модулів у Terraform Registry багато, але кожен чужий модуль ви успадковуєте разом з його рішеннями: перш ніж брати, прочитайте, які ресурси він створює і що станеться, коли вийде нова версія. Зручна межа: модуль інкапсулює одне рішення (приватний бакет, мережа з підмережами), а не «весь проєкт» зі сотнею змінних.

Ревʼю коду ловить помилки, які бачить людина. Помилку «бакет відкритий у світ» вона легко пропускає, а перевірка може виявити її щоразу. Політики як код працюють на двох рівнях.

Перед застосуванням політику запускають проти плану. Terraform вміє вивести його в JSON (terraform show -json tfplan), а інструмент політик перевіряє цей файл у CI. Поширені інструменти: Open Policy Agent з мовою Rego (і його обгортка conftest), HashiCorp Sentinel у комерційних продуктах, Checkov для статичного аналізу. Політика на Rego може, наприклад, вимагати ручного погодження для кожного видалення бакета:

guard.rego
package terraform.guard
import rego.v1
deny contains msg if {
some rc in input.resource_changes
rc.type == "aws_s3_bucket"
"delete" in rc.change.actions
msg := sprintf("видалення бакета %s потребує ручного погодження", [rc.address])
}
Terminal window
terraform show -json tfplan > plan.json
conftest test plan.json # код виходу не нуль, якщо є порушення

Після застосування, на боці хмари. Це останній рубіж: навіть якщо код обійшов перевірку, платформа відмовить у порушенні. Кожен провайдер має власний механізм, і його теж можна описати в Terraform:

variable "target_id" {
type = string
description = "ID організаційної одиниці або акаунта"
}
resource "aws_organizations_policy" "guardrails" {
name = "guardrails-baseline"
type = "SERVICE_CONTROL_POLICY"
content = jsonencode({
Version = "2012-10-17"
Statement = [{
Sid = "DenyLeavingAndPublicAccessChanges"
Effect = "Deny"
Action = ["organizations:LeaveOrganization", "s3:PutAccountPublicAccessBlock"]
Resource = "*"
}]
})
}
resource "aws_organizations_policy_attachment" "guardrails" {
policy_id = aws_organizations_policy.guardrails.id
target_id = var.target_id
}

Політика керування службами (service control policy, SCP) обмежує максимум прав у всіх акаунтах одиниці, навіть для адміністратора акаунта.

Різниця між двома рівнями практична: перевірка плану дає розробнику швидку й зрозумілу відповідь у pull request, а політика платформи діє на всі шляхи до хмари, включно з консоллю й ручними командами.

У серпні 2023 року HashiCorp змінила ліцензію Terraform з MPL 2.0 на Business Source License (BSL). Вона обмежує використання коду для продуктів, які конкурують з HashiCorp; звичайне користування Terraform для власної інфраструктури не заборонене. У відповідь спільнота створила форк OpenTofu під егідою Linux Foundation, який лишається на відкритій ліцензії MPL. Мова, провайдери й формат стану в них спільні, тож весь код цього курсу працює в обох інструментах без змін: замість terraform ви запускаєте tofu. Надалі ми пишемо «Terraform», маючи на увазі обидва.

Кожен провайдер має власну систему опису інфраструктури. Вони глибше інтегровані з платформою, зате прив’язують до неї:

AWS Azure Google Cloud
Декларативний шаблон CloudFormation (JSON/YAML), ресурси групуються в стеки Bicep (компілюється в ARM-шаблони) Infrastructure Manager, розгортає Terraform-конфігурації
Код замість шаблону AWS CDK: TypeScript, Python та інші, генерує CloudFormation Bicep є мовою, окремого CDK немає Terraform як основа
Де зберігається стан Сервіс CloudFormation, окремого файла немає Azure Resource Manager Стан Terraform веде сам Infrastructure Manager

Нативні інструменти мають дві переваги: стан веде сама платформа, а нові функції сервісів зазвичай з’являються в них першими, тоді як Terraform-провайдери іноді відстають. Мінус той самий, що й завжди: шаблон Bicep не працює в AWS. Для команди, яка працює з двома хмарами, один Terraform дає єдину мову й єдиний робочий цикл, хоча самі ресурси різні.

Google Cloud тут відрізняється: його власний Deployment Manager вийшов із підтримки, і рекомендованою заміною став Infrastructure Manager, який керований сервіс над Terraform.

Доставка: від коміту до хмари

Section titled “Доставка: від коміту до хмари”

Опис інфраструктури лежить у git. Наступне питання: хто його застосовує. Відповідь «розробник з ноутбука» має три вади: ключі облікових даних лежать на ноутбуці, немає журналу, хто що застосував, а результат залежить від локальної версії плагінів. Тому plan і apply виконує конвеєр CI/CD.

Федерація CI з хмарою через OIDC

Section titled “Федерація CI з хмарою через OIDC”

Конвеєру потрібні права на хмару. Найпростіше вписати ключ доступу в секрети репозиторію, і саме цей шлях призводить до витоків: ключ не має терміну дії, лежить у багатьох місцях і його важко відкликати. Чому ролі й тимчасові облікові дані безпечніші, розказано в модулі 3, тут лише механіка для CI.

GitHub Actions видає кожному завданню підписаний токен OpenID Connect (OIDC): JWT, у якому сказано, з якого репозиторію, гілки чи середовища запущене завдання. Хмара налаштована довіряти підписам GitHub (така довіра називається федерацією, federation) і обмінює токен на тимчасові облікові дані. Довгоживучого секрету в GitHub немає.

Схему обміну токена показано в модулі 3.

Найважливіше твердження (claim) у токені називається sub, суб’єкт. Його формат залежить від того, з чого запущено завдання:

Запуск Значення sub
Гілка repo:ORG/REPO:ref:refs/heads/main
Pull request repo:ORG/REPO:pull_request
Середовище repo:ORG/REPO:environment:production
Тег repo:ORG/REPO:ref:refs/tags/v1.2.0

Довірчі умови в хмарі перевіряють саме sub. Тому можна видати окремі ролі: plan у pull request отримує роль лише для читання, а apply у середовищі production, яке вимагає ручного затвердження, отримує роль із правом запису. Завдання мусить мати дозвіл id-token: write, інакше токена не буде. GitHub описує також незмінний формат sub із числовими ідентифікаторами власника й репозиторію; перевірте в документації, який діє у вашій організації.

on:
push:
branches: [main]
permissions:
id-token: write
contents: read
jobs:
apply:
runs-on: ubuntu-latest
environment: production
steps:
- uses: actions/checkout@v7
- uses: aws-actions/configure-aws-credentials@v6
with:
role-to-assume: arn:aws:iam::111122223333:role/gh-terraform-apply
aws-region: eu-central-1
- uses: hashicorp/setup-terraform@v4
- run: terraform init
- run: terraform apply -auto-approve

Довірча політика ролі містить умову на token.actions.githubusercontent.com:sub зі значенням repo:ORG/REPO:environment:production; аудиторія (aud) дорівнює sts.amazonaws.com. OIDC-провайдер у IAM створюється один раз на акаунт.

Числа після @ є мажорними тегами дій. У виробничих конвеєрах дії краще фіксувати за хешем коміту: сторонній код виконується з правами вашого завдання.

Нова версія застосунку має замінити стару. Способів три, і вони різняться тим, скільки користувачів ризикують і що коштує тримати стару версію поруч.

Стратегія Як працює Ціна Коли обирати
Rolling Інстанси оновлюються партіями, поки не замінені всі Зайвих потужностей немає; відкат означає нову викатку Типовий вибір; зміни, сумісні зі старою версією
Blue-green Поруч із поточним («синім») середовищем піднімають нове («зелене»), потім перемикають трафік Подвійні потужності на час викатки; відкат миттєвий Зміни, які не можна змішувати зі старою версією
Canary Нова версія отримує малу частку трафіку (наприклад 5%), потім її збільшують за метриками Потрібні метрики й автоматична оцінка Великі зміни, ризик яких видно лише в реальному трафіку

Вибір визначає схема даних. При rolling і canary дві версії працюють з однією базою одночасно, тому міграції роблять у два кроки: спершу зміна, сумісна з обома версіями, і лише після повної викатки прибирання старого.

Кожна платформа розподіляє трафік між версіями по-своєму. Ось поділ 90 до 10 для канарки:

Terminal window
aws lambda update-alias \
--function-name my-function \
--name live \
--routing-config 'AdditionalVersionWeights={2=0.1}'

Псевдонім live вказує на основну версію функції, а 10% викликів йдуть на версію 2. Для контейнерів у ECS цю роль виконує AWS CodeDeploy із конфігураціями на кшталт CodeDeployDefault.ECSCanary10Percent5Minutes.

У Kubernetes ті самі стратегії реалізують контролери на кшталт Argo Rollouts і Flagger, а канарку можна вести за метриками з модуля 11: якщо частка помилок нової версії перевищила поріг, відкат виконується автоматично.

Feature flag відокремлює розгортання коду від його вмикання. Код нової функції потрапляє в продакшн вимкненим, а вмикається змінною конфігурації для 1% користувачів або для співробітників, без нової викатки. Якщо функція шкодить, її вимикають за секунди.

Прапорці накопичуються, і кожен є гілкою в коді, яку колись треба видалити, тому в нього є власник і дата видалення. Нейтральний інтерфейс для коду дає стандарт OpenFeature: код питає про прапорець, а сховище за ним можна замінити. Керовані сховища є в AWS (AppConfig, розділ feature flags) і Azure (App Configuration з керуванням функціями). Окремого сервісу такого класу в Google Cloud немає, тож команди беруть сторонній продукт.

Для Kubernetes існує інша модель доставки. Конвеєр не «штовхає» зміни в кластер: усередині кластера працює контролер (Argo CD або Flux, обидва є випускниками CNCF), який стежить за git-репозиторієм і сам приводить кластер до описаного там стану.

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: shop
namespace: argocd
spec:
project: default
source:
repoURL: https://github.com/acme/deploy
targetRevision: main
path: apps/shop/prod
destination:
server: https://kubernetes.default.svc
namespace: shop
syncPolicy:
automated:
prune: true
selfHeal: true

Приклад однаковий для EKS, AKS і GKE, бо описує Kubernetes, а не хмару. prune видаляє з кластера те, чого вже немає в git, а selfHeal повертає ручні зміни назад: це той самий дрейф, виправлений автоматично. Відкат зводиться до git revert, а конвеєру не потрібні права на кластер. Ціна цього: репозиторій із конфігурацією стає найпривілейованішим місцем, і право запису в нього захищають як адміністративне.

Середовища як окремі акаунти

Section titled “Середовища як окремі акаунти”

Розділити dev і prod можна каталогами, гілками, просторами імен або воркспейсами Terraform. Найміцніша межа проходить по акаунтах: AWS-акаунт, підписка Azure, проєкт Google Cloud (ієрархію розглянуто в модулі 2). Тоді помилка в dev не зачепить prod просто тому, що в неї немає прав; бюджети, ліміти й журнали теж розділені. Групуються акаунти в організаційні одиниці (AWS), групи керування (Azure) і папки (Google Cloud), а політики на групу задають SCP, Azure Policy і політики організації.

Воркспейси Terraform ділять код і налаштування сховища, а облікові дані лишаються однакові, тож межею безпеки вони не є. Надійніша схема: окремий корінь для кожного середовища зі своїм станом і власною роллю конвеєра, а prod додатково вимагає ручного затвердження.

Задача AWS Azure Google Cloud
Сховище стану Terraform S3 + use_lockfile Blob Storage + оренда blob Cloud Storage
Нативний шаблон CloudFormation, CDK Bicep Infrastructure Manager
Вхід CI без ключів IAM OIDC-провайдер + роль Федеративні облікові дані Entra ID Workload Identity Federation
Політика на рівні платформи SCP, AWS Config Azure Policy Політика організації
Розподіл трафіку між версіями Псевдоніми Lambda, CodeDeploy Ревізії Container Apps, слоти App Service Ревізії Cloud Run
Керовані feature flags AppConfig App Configuration немає окремого сервісу

Чим відрізняється і що з цього випливає. Terraform прибирає різницю в мові, але не в моделі. Один і той самий «приватний бакет» у AWS складається з трьох ресурсів (сам бакет, версіонування, блокування публічного доступу), в Azure це обліковий запис сховища з вкладеними блоками, а в Google Cloud один ресурс. Модуль, який інкапсулює таку різницю, для кожної хмари виглядає по-своєму; переносити його між хмарами без переписування не вийде. Друга відмінність у розподілі трафіку між версіями: у AWS він розкиданий по кількох сервісах (псевдоніми Lambda, CodeDeploy, зважені цільові групи ALB), а в Azure Container Apps і Cloud Run це властивість самої платформи.

Розбір: довіра OIDC-ролі, яка надто довірлива

Section titled “Розбір: довіра OIDC-ролі, яка надто довірлива”

Це типова конфігураційна помилка, а не один конкретний інцидент, тому подаємо її як сценарій. Команда налаштувала OIDC-довіру AWS для репозиторію й, щоб «все працювало», записала умову як repo:acme/*:*. Це означає: роль може отримати кожен репозиторій організації, у будь-якій гілці, навіть pull request.

Механізм такий. Токен підписаний GitHub і справжній, тож хмара його приймає. Але хмара перевіряє лише те, що вказано в умові. Розробник, який відкрив pull request у будь-якому репозиторії організації, змінює workflow: замість terraform plan додає крок, який виводить облікові дані. Завдання запускається з токеном, sub якого підпадає під шаблон, і отримує роль із правами запису в prod. Довгоживучого ключа не було, а доступ усе одно отримано, бо довіра описана надто широко.

Виправлення складається з трьох частин. Умова в довірчій політиці має бути точною: repo:acme/infra:environment:production, без символів підстановки по репозиторію й гілці. Роль для pull request має лише читати. Середовище production у GitHub вимагає ручного затвердження, тож навіть змінений workflow не запустить apply без людини. Ці правила ви перевіряєте так само, як політики Terraform: як код, у репозиторії, з ревʼю.

Типові помилки розуміння

Section titled “Типові помилки розуміння”

«Стан — це кеш, його можна видалити й відновити». Стан є єдиним зв’язком між ресурсами в коді й об’єктами в хмарі. Без нього Terraform про наявні ресурси не знає й створить дублікати.

«Воркспейси Terraform ізолюють dev і prod». Вони ділять сховище й облікові дані. Реальну межу дає окремий акаунт чи підписка з власною ролею.

«plan показав усе, apply не може здивувати». Між plan і apply хмара змінюється. Тому застосовують збережений через -out план, а для небезпечних змін (-/+ для баз і дисків) додають політику й ручне затвердження.

«OIDC безпечний сам по собі». Безпека залежить від умови на sub. Надто широка умова дає доступ будь-кому, хто може запустити завдання у вашій організації.

«Дрейф — це помилка інструмента». Дрейф є наслідком змін поза кодом. Лікується він процесом: заборона ручних змін у prod, selfHeal у GitOps або регулярний plan -refresh-only за розкладом.

«Canary і blue-green — те саме». Blue-green перемикає весь трафік одразу й дає швидкий відкат за подвійну ціну. Canary поступово збільшує частку і виявляє проблеми на невеликій групі користувачів, але потребує метрик.

Перевір себе

1. Двоє колег одночасно запустили `terraform apply` в один і той самий корінь зі сховищем S3 без блокування. Що ймовірно станеться?
2. У Terraform 1.11 ви налаштовуєте нове сховище стану в S3. Як правильно робити блокування?
3. Хтось у консолі вимкнув шифрування диска, який створив Terraform. Як побачити зміну, нічого не змінюючи?
4. Довірча політика OIDC-ролі дозволяє `repo:acme/shop:*`. Що це означає на практиці?
5. Нова версія змінює формат повідомлень у черзі, і стара версія його не розуміє. Яка стратегія розгортання небезпечна?
6. Вам потрібні dev, staging і prod з незалежними правами й бюджетами. Що обрати основною межею?
7. Чим GitOps відрізняється від конвеєра, що виконує `kubectl apply` після збірки?