ІаС: тестування Terraform-коду — чому це потрібно та як це зробити

💡 Усі статті, обговорення, новини про DevOps — в одному місці. Приєднуйтесь до DevOps спільноти!

Привіт, спільното! Мене звати Антон Мужайло, я директор з технологій у GlobalLogic Ukraine. У компанії я вже більше 13 років, пройшов весь шлях від початківця до директора. Сьогодні я є одним з лідерів Офісу Технологій — організації, яка підтримує розвиток бізнесу GlobalLogic: ми займаємось пресейлами, цифровими трансформаціями, технічним консалтингом і запуском нових акаунтів. Моя спеціалізація — це ентерпрайз-рішення, генеративний інтелект, автоматизація тестування та кібербезпека. Маю доменну експертизу в галузі медіа та профільну освіту, а основний стек — Python і C++.

У цій статті хочу розповісти про тестування сучасної ІаС («Інфраструктури як коду»). Ми вже давно звикли до розумних рішень, які спрощують розробку інфраструктури: від простих на власних серверах до складних хмарних в Azure, AWS, GCP або навіть приватних хмар на базі OpenStack/OpenShift. Важко повірити, що раніше все це робили звичайними скриптами на Bash/PowerShell/Perl.

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

А ви вже чули, що 21 червня DOU Mobile Day?

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

Цей матеріал буде цікавий інженерам, DevOps-спеціалістам, тестувальникам, архітекторам і всім, хто хоч раз писав terraform apply зі смикаючимся оком і Корвалолом в кишені. Якщо ви хочете зробити свій інфраструктурний код більш стабільним, безпечним і таким, що легко підтримується — ця стаття для вас. Розберемося, як уникати граблів ще до мерджу, автоматизувати перевірки та впевнено запускати складні середовища без страху зламати прод. Тож поговоримо про тестування коду Terraform і як інтегрувати такі перевірки в єдиний пайплайн валідації пулреквестів.

Чому ми маємо тестувати Terraform-код

Щоб розпочати таке тестування, нам потрібно продати цю ідею стейкхолдерам. Чому це може бути складно? Бо тестування коду Terraform — це лише попередження дефектів, фактичного розгортання в цей момент не відбувається. Такий підхід називається shift-left testing — він знижує ризики невдалих розгортань. Це, своєю чергою, зменшує загальні витрати на пошук, перевірку та усунення дефектів.

Тож як описати, навіщо нам це тестування?

  1. Правильність. Вкрай важливо переконатися, що ваш Terraform-код точно відображає бажану інфраструктуру. Похибки, друкарські помилки або неправильні конфігурації у вашому коді можуть призвести до надання неправильних ресурсів. Це може спричинити різні простої, вразливості системи безпеки або інші операційні проблеми.
  2. Стійкість. Код Terraform повинен коректно обробляти різноманітні сценарії та зміни. Тестування допомагає виявити та усунути граничні випадки та нестандартні сценарії, які можуть бути неочевидними на етапі початкової розробки. Бо якщо тестів нема, то кожен terraform apply — як лотерея: або спрацює, або тімлід викликає на розмову.
  3. Співпраця. Команди часто працюють спільно над кодом інфраструктури. Тестування забезпечує загальну основу для перевірки змін, внесених різними членами команди.
  4. Валідація модулів. Terraform дозволяє створювати багаторазові модулі. Незалежне тестування цих модулів гарантує, що вони працюють належним чином і можуть безпечно використовуватися в різних конфігураціях інфраструктури.
  5. Запобігання дрифту. З часом реальна інфраструктура може відхилятися від того, що описано в коді Terraform, через ручні зміни або інші фактори. Регулярне тестування допомагає виявити та виправити такі відхилення, повертаючи інфраструктуру у відповідність до коду.
  6. Безпека. Тестування може допомогти виявити потенційні вразливості у вашій конфігурації.
  7. Управління витратами. Тестування може включати перевірку косту, щоб гарантувати, що забезпечення інфраструктури не призведе до непередбачуваних або непотрібних витрат.

Фази тестування

Почнемо з невеликої теоретичної бази. Будь-яка стратегія тестування повинна включати перелік етапів — так звану Піраміду Тестування. Кожен з них відіграє чітко визначену роль. Так виглядає послідовність етапів, що базується на рівні запобігання дефектам (найгрубіші дефекти якомога раніше) і часі виконання кожної фази:

Тож ми маємо визначити мету кожного етапу й інструменти, що будуть використовуватися, для досягнення мети цієї фази. Terraform використовує HCL (Hashicorp Configuration Language) для налаштування вимог цільової інфраструктури, яка і буде нашим тестовим об’єктом.

Базова перевірка

Мета цього етапу — переконатися, що код правильно відформатований, синтаксично вірний і вплине на існуючу інфраструктуру саме так, як ми хочемо, — виділяючи зміни стану, які будемо застосовувати. Для цього є 3 методи:

  1. Terraform fmt: допомагає писати код Terraform у канонічному форматі та стилі відповідно до вимог (Terraform language style conventions).
    terraform fmt -check
    

    Поверне exit code 0, якщо всі вхідні дані відформатовано правильно. Якщо ні, статус exit буде ненульовим, і команда виведе список назв файлів, які не відформатовано належним чином.

  2. Terraform validate: перевіряє, чи конфігурація є синтаксично правильною та внутрішньо узгодженою, незалежно від будь-яких наданих змінних або наявного стану.
    terraform validate
    

    Якщо код неправильний — команда надасть ім’я файлу, рядок коду, який спричиняє помилку, короткий опис помилки та інші деталі.

  3. Terraform plan: дозволяє переглянути зміни, які Terraform планує застосувати до вашої інфраструктури, порівнюючи поточну конфігурацію з попереднім станом. Terraform plan запропонує зміни, які повинні привести віддалені об’єкти у відповідність до конфігурації.
    terraform plan
    

    Якщо інфраструктура ніяк не змінюється, Terraform plan повідомить, що ніяких дій не потрібно. В іншому випадку команда надасть список дій, які буде виконано, якщо ви застосуєте цю конфігурацію.

А тепер до прикладу з життя.

Уявіть: 5 ранку, SRE (Site Reliability Engineer) отримує повідомлення від PagerDuty про те, що рівень помилок перевищує поріг SLI: в регіоні закінчився розмір VM, необхідний для AKS-кластера. Ще переглядаючи останній сон, SRE думає: «Та що там, зараз поправимо» і замість того, щоб створити новий пул з іншим типом VM, він просто змінює розмір поточного. Що відбувається далі? Terraform знищує кластер і створює новий. Тепер ви застрягли на кілька годин, які не вкладаються у жодне розумне вікно SLI.

Як цього уникнути? Навіть найпростіший синтаксичний аналіз terraform plan на ключові слова типу replace чи delete може врятувати вам інфраструктуру і репутацію. Ви можете автоматично виводити попередження: «Це точно той кластер, який ми хочемо поховати?» Але, погодьмося, такий підхід — це скоріше пластир. А треба щось формальніше, системніше, менш залежне від людського фактору. Поговоримо про це далі.

Policy Testing

Політики — це набір інженерних законів, які ніхто не повинен порушувати, навіть якщо дуже хочеться. Вони можуть включати обмеження на конкретні версії модулів, які можна використовувати для розгортання; конкретні регіони, які потрібно використовувати; кількість ресурсів, які потрібно створити, і багато іншого. Кожен обізнаний інженер розуміє, що автоматизована перевірка на відповідність політиці може запобігти людській помилці.

Мета цього етапу — перевірити, чи відповідає Terraform-код політикам, визначеним в рамках проєкту або організації. Немає згоди з політикою — йди до архітектора.

Золотий стандарт перевірки політик з Terraform — Conftest. Він базується на мові Rego з Open Policy Agent для написання політик.

Уявімо кейс із життя:

  1. Розробник пушить новий код Terraform у гілку.
  2. CI запускає нову збірку на основі Git-коміту — формується terraform plan у вигляді бінарного файлу.
  3. Бінарник Conftest перевіряє цей Terraform-plan. Якщо він фейлить, то фейлить і вся збірка, і розробник буде повідомлений про це. Збірка завершується.
  4. Якщо Conftest пройдений успішно, код мержиться у main/master.
  5. Застосовується Terraform і розгортається нова інфраструктура.

І найголовніше — все це працює автоматично. Жодного «ой забув перевірити». Conftest — це ваш адвокат, який завжди питає: «Ви впевнені, що хочете це робити?» — ще до того, як з’явиться «production down» тiкет.

package terraform_limit

# Define the limit for a specific resource type
resource_limit = 2

# Check the number of instances of a specific resource type
resource_count[resource_type] {
    input[resource_type] = _
    count(input[resource_type], _) = count
    count <= resource_limit
}

deny[msg] {
    resource_count[resource_type]
    resource_limit = 2
    msg = sprintf("Number of %v resources exceeds the limit of %v", [resource_type, resource_limit])
}

Ця проста політика обмежує кількість ресурсів до 2.

Тестування на відповідність (compliance testing)

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

Існує великий перелік інструментів для організацій, які прагнуть відповідати вимогам таких стандартів, як PCI-DSS, HIPAA або SOC 2, ефективно знижуючи ризики, пов’язані із загрозами кібербезпеки. Але є нюанс: більшість з цих інструментів — «з коробки», і їх набір правил може не повністю відповідати вашим потребам. Існує два способи розв’язання цієї проблеми: класичний і сучасний.

Класичним рішенням для організацій з індивідуальними вимогами до безпеки є Open Policy Agent, який ми вже згадували. Його можна підлаштувати під свої стандарти: написати власні Rego-правила, які відповідатимуть вашим політикам безпеки або фобіям.

terraform plan -out=tfplan.binary
terraform show -json tfplan.binary > tfplan.json
opa eval --format pretty --data my_policy.rego --input tfplan.json "data.my_policy"

За допомогою цього коду ви просто конвертуєте Terraform plan у JSON і використовуєте opa eval для оцінки його відповідності вашим політикам. Результат покаже будь-які порушення користувацьких політик, що дозволить усунути їх перед застосуванням Terraform plan.

Сучасним рішенням є використання terraform-compliance для Behavior Driven Development (BDD). Ідея проста: замість того, щоб просто перевірити, чи все працює — давайте перевіряти, чи не зробив хтось дурниць. Ви можете написати свої вимоги до відповідності у вигляді функцій у синтаксисі Gherkin.

Наприклад, нам потрібно переконатись, що всі бакети AWS S3 зашифровані:

resource "aws_s3_bucket" "example_bucket" {
  bucket = "my-example-bucket"
  acl    = "private"
}

Маючи цей код Terraform, нам потрібно створити файл проперті, використовуючи синтаксис Gherkin:

Feature: Ensure all S3 buckets are encrypted at rest
  In order to improve security posture
  As engineers
  We'll enforce encryption on all S3 buckets

  Scenario: S3 buckets should have encryption enabled
    Given I have AWS S3 Bucket defined
    Then it must contain server_side_encryption_configuration
    And its value must not be null

Тепер потрібно провести terraform-compliance коду. Спершу ми ініціалізуємо terraform і створюємо план, а потім конвертуємо його у читабельний формат і запускаємо terraform-compliance:

terraform init
terraform plan -out=tfplan.binary

terraform show -json tfplan.binary > tfplan.json
terraform-compliance -p tfplan.json -f features/

Terraform-compliance розбере наш Terraform plan і файли проперті, а потім виведе результати. Якщо бакет S3 не зашифрований, ми побачимо помилку: сценарій «S3 buckets should have encryption enabled» буде зафейлений, оскільки немає server_side_encryption_configuration, або вона є нульовою.

Чому це круто? Ви можете писати перевірки в звичному для BDD стилі. Немає потреби глибоко лізти в Rego. І наостанок — це легко читається іншими колегами, навіть якщо вони з Terraform не на «ти» і навіть не на «ваше величносте».

Статичний аналіз

Сьогодні більшість організацій визнають статичний аналіз як мастхев для всього, що йде в прод. Це стосується і Terraform-коду: він може бути коротким, але, як ми знаємо, «it’s not the size, it’s the impact».

Існує цілий перелік інструментів для виконання цієї задачі. Такі інструменти можна розділити на дві категорії: сканування безпеки/вразливостей та лінтинг + кращі практики. Які найбільш перспективні з них?

Для сканування безпеки та вразливостей ви можете використовувати TFsec, Checkov та Terrascan, які чудово справляються навіть з набором правил за замовчуванням. Ці інструменти можуть виявити багато потенційних проблем і надати докладні рекомендації щодо їх усунення.

Щодо загальних найкращих практик і лінтингу, варто згадати tflint і terraform-ls, які допомагають вловити поширені помилки. Вони застосовують загальні стандарти кодування + кастомні, якщо у вас є власні конвенції кодування. Це як Grammarly, але для інфраструктури.

Unit Testing

Модульне тестування в контексті IaC передбачає тестування окремих модулів або ресурсів Terraform в ізоляції, щоб переконатися, що вони поводяться так, як очікується. Іншими словами — це коли ти хочеш перевірити свій код, перш ніж він перевірить тебе. У класичній розробці ПЗ всі давно знають, що таке Unit-тести: там є pytest, JUnit, xUnit і купа гайдів з приємними графіками покриття. А от з Terraform все дещо складніше, тут модульне тестування (якщо взагалі існує) часто включає поєднання кастомних скриптів, зоопарку інструментів та магії DevOps-інженера, який пішов з компанії кілька років тому.

Існує два «зразкових» інструменти для тестування модулів ІаС — це Terraform Test і Terratest. Terraform test — це вбудований інструмент для модульних та інтеграційних тестів. Працює прямо в Terraform, не треба нічого тягнути зовні, але функціональність поки що, скажімо так, базова. Terratest — це бібліотека на Go, створена для того, щоб писати тести на Terraform, як справжні інженери. Вона надає різноманітні допоміжні функції та патерни для тестування Terraform-коду, Docker-образів, Packer-шаблонів тощо.

package test
 
import (
    "testing"
    "github.com/gruntwork-io/terratest/modules/terraform"
)
 
func TestMyTerraformModule(t *testing.T) {
    terraformOptions := terraform.WithDefaultRetryableErrors(t, &terraform.Options{
        // The path to where your Terraform code is located
        TerraformDir: "../examples/my-terraform-module",
    })
 
    // At the end of the test, run `terraform destroy` to clean up any resources that were created
    defer terraform.Destroy(t, terraformOptions)
 
    // Run `terraform init` and `terraform apply`. Fail the test if there are any errors.
    terraform.InitAndApply(t, terraformOptions)
 
    // Add assertions here to validate the behavior of your module.
}

Звісно, можна застосувати й інші рівні тестування, такі як Integration and System testing для IaC. Утім, я навіть не можу пригадати проєкт, у якому були прийняті всі рівні, згадані вище.

Щоби не перебрати з довжиною статті, пропоную зупинитися на цьому. Інструменти та підходи повинні відповідати меті й визначати, наскільки ретельним може бути тестування на основі ваших цілей і ризиків. Багато компаній використовують комбінацію цих інструментів для досягнення певної мети. Головне — не перетворити свою CI/CD на нову формулу «Terraform + 12 лінтерів = 40 хвилин чеків і нуль деплоїв». Тож краще 2 якісні перевірки, ніж 10, щоб «показати прогрес».

Як може виглядати пайплайн валідації Pull Request

Ваш пайплайн буде залежати від необхідного рівня ретельності тестування, бюджету і, звичайно ж, від терпіння інженерів. Залежно від цілей, які ви намагаєтеся досягти, певні інструменти можуть бути включені або виключені зі списку. Подивімось на картинку нижче — тут зображений досить обширний пайплайн перевірки pull request’a для Terraform-коду.

Усе починається з розробника, який комітить у свій Forked-репозиторій і створює pull request у Origin. Як тільки з’являється новий PR — CI миттєво запускає автоматизований пайплайн перевірки PR.

Пайплайн валідації PR має чотири фази: Basic checks, Policy & Compliance, Static Analysis & Linting і Unit testing. Кожна фаза може включати один або кілька інструментів, що працюватимуть одночасно або паралельно. Зважаючи на конкретні цілі, можна включати й інші фази.

Люди зазвичай не довіряють автоматизованому прийняттю рішень, принаймні на початку, тому в пайплайні передбачена ручна перевірка (code review). Як тільки обидва критерії будуть виконані, PR можна буде об’єднати в гілку Master у репозиторії Origin.

Щодо управління дефектами. Будь-які помилки, виявлені під час пайплайну, повинні перешкоджати мержу PR і вимагати або виправлення, або ручного затвердження цього мержу (залежно від того, як заведено у вас на проєкті).

Business Value та поширені помилки, яких ви можете уникнути

Як і будь-який код, код Terraform може мати дефекти. І не просто «та це minor, давай запаркуємо багрепорт отут, хтось колись пофікисть», а баги, які можуть призвести і до неправильних конфігурацій, і до вразливостей безпеки чи неефективного використання ресурсів. З мого досвіду, витрати на хмарні сервіси можна зменшити до половини, запобігаючи невдалим розгортанням та прихованим дефектам на етапі швидкої розробки, просто запровадивши практику тестування IaC. Просто тому, що ви не розгортаєте «монстра» в кожному пулреквесті. Не кажучи вже про клієнтські невдачі та репутаційні втрати. Копнімо глибше.

Надійність і стабільність

Тестування коду Terraform допомагає виявити та виправити помилки до того, як зміни будуть застосовані у продакшені. Це знижує ризик простоїв і перебоїв в обслуговуванні; гарантує, що зміни в інфраструктурі не матимуть негативного впливу на бізнес-операції та досвід клієнтів.

Одним з найпоширеніших і найсерйозніших дефектів є просто випадкове видалення робочої бази даних. І не кажіть, що це — неможливо. Можливо. Припустимо, у вас є код Terraform, який керує інстансом хмарної бази даних. Ви хочете змінити розмір інстанса для підвищення продуктивності. Але через дефект у коді Terraform інтерпретує цю зміну як запит на знищення та перестворення бази даних — а це вже втрата даних і downtime.

resource "aws_db_instance" "production_db" {
  // Assume the original instance class was db.m4.large
  // and you intended to change it to db.m4.xlarge for better performance
  instance_class = "db.m4.xlarge" // Intended change

  identifier = "prod-db"          // Unique identifier for the DB instance

  // A critical misconfiguration: changing the engine version or specifying
  // a parameter that forces a new resource.

  engine_version = "9.6" // Assume, current version is 9.6.5 and this change is  
  // misinterpreted.
}

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

Можливо, ви хотіли просто зробити апгрейд або оновлення, але через неправильну конфігурацію (engine_version було встановлено неправильно) Terraform plan може показати, що він знищить і перестворить базу даних. Якщо застосувати цей план без ретельної перевірки, це може призвести до видалення бази даних і створення її з нуля.

Зниження вартості на хмарні рішення

Одна з найбільш практичних переваг тестування IaC — гроші. Помилки, виявлені ще до продакшну, це не просто +100 до якості, це ще й мінус кілька нулів в рахунку за хмару. Автоматизоване тестування також зменшує мануальні зусилля, необхідні для перегляду коду та розгортання інфраструктури — а це скорочує операційні витрати ще більше.

Надмірне резервування обчислювальних інстансів — одна з найбезглуздіших помилок, якої можна легко припуститися через брак часу, стислі терміни або просто через втому інженера в п’ятницю ввечері. У цьому прикладі конфігурація Terraform, призначена для розгортання набору compute instances для продакшену, випадково надає більше інстансів, ніж потрібно, через помилку у визначенні змінної або конфігурації циклу. Це може призвести до запуску великої кількості непотрібних інстансів і, відповідно, витрат.

resource "aws_instance" "expensive_instances" {
  count = var.instance_count // Supposed to control the num of instances

  ami           = "ami-123456" // AMI for the instances
  instance_type = "m5.4xlarge" // High-capacity instance type

  tags = {
    Name = "OverProvisionedInstance-${count.index}"
  }
}

variable "instance_count" {
  description = "Number of instances to create"
  // Defect: the default value is higher than intended for the deployment.
  default     = 50 // Accidental high default leading to unexpected cost
}

У цьому сценарії змінна instance_count встановлюється для великого значення за замовчуванням (наприклад, 50, яке могло бути використано для тестування або просто помилково). Якщо цей код буде застосовано без зміни значення instance_count на більш прийнятне для робочого навантаження, це призведе до розгортання 50 інстансів з великою ємністю (m5.4xlarge). Враховуючи вартість таких інстансів, суттєве збільшення рахунку за хмару не змусить довго чекати. Все як в тому анекдоті: «Terraform мовчить. Біл Гейтс радіє. CFO — ні». Можливі й інші типи помилок, наприклад, випадкове розгортання на дорожчу підписку.

Покращення безпеки

Безпека — це не те, що додається в останній день перед релізом. Це те, що має бути вбудовано з першого коміту. Безпекові тести можуть автоматично виявляти неправильні конфігурації або порушення нормативних вимог, що становлять загрозу безпеці. Регулярне тестування гарантує безпечне розгортання інфраструктури.

Ніщо не дратує більше, ніж hardcoded облікові дані у файлах terraform, створені, звісно ж, щоби «швидко все затестити»...

resource "aws_db_instance" "default" {
  allocated_storage    = 20
  engine               = "mysql"
  username             = "admin"
  password             = "hardcodedpasswordthatiforgottoremove"
  instance_class       = "db.m4.large"
}

Згоден, цей дефект можна легко знайти під час першого перегляду коду. Проте, що би ви обрали: побачити, що тест зафейлився, чи показати команді вашу спритність у фіксу на проді?

Співпраця й продуктивність розробників

Фреймворки для тестування можуть допомогти стандартизувати якість коду інфраструктури та практики між командами, покращуючи співпрацю та взаєморозуміння. Розробники та операційні команди працюватимуть ефективніше, знаючи, що їхні зміни перевіряються. Це зменшує кількість переробок і прискорює цикли розробки. Особливо в наш час, коли ви можете працювати з кимось роками й все ще не зустрітися з цим колегою особисто. Осмислене кодування та неймінг можуть прискорити розуміння коду. Так, resource «aws_something» «lol_try2_final_FIX» — це весело рівно до того моменту, коли його потрібно дебажити у проді.

Зниження ризиків

Знову ж таки, рахунки за хмарні послуги можуть робити боляче, особливо якщо ви не очікували, що вони будуть такими великими. Ці та багато інших ризиків слід збирати, відстежувати та управляти ними. Один з найнадійніших способів — це включити автоматизовані тести.

Припустимо, у вас є код Terraform, призначений для налаштування групи автомасштабування для набору compute instances у хмарі. Мета — масштабувати кількість інстансів на основі попиту, зі зваженою мінімальною та максимальною кількістю інстансів. Однак через дефект у конфігурації параметри автомасштабування встановлюються неправильно, що призводить до розгортання більшої кількості інстансів, ніж потрібно.

resource "aws_autoscaling_group" "example" {
  launch_configuration = aws_launch_configuration.example.id
  min_size             = 1   // Intended to have a minimum of 1 instance.
  max_size             = 100 // Mistakenly set to 100 instead of a more reasonable maximum.
}

Дефект: значення `max_size` встановлено значно більшим, ніж необхідно для очікуваного робочого навантаження. Така неправильна конфігурація може призвести до того, що група автомасштабування масштабуватиметься до 100 інстансів під навантаженням або через неправильні метрики, що призведе до неочікувано високих витрат.

До цієї проблеми додається ще одна: агресивні або хибно налаштовані scaling policies. Тобто як тільки CPU трохи підстрибне — автомасштабування каже: «Більше машин! І одразу!» — замість того, щоб трохи зачекати або подумати.

Саме тому необхідно тестувати значення max_size, min_size, desired_capacity, створювати політики за логікою та історією навантажень, і використовувати статичний аналіз або валідацію плану.

Висновки

Тож у цій статті ми розібралися з тестуванням коду Terraform: що це таке й навіщо воно потрібне. Ми пройшлися та описали всі фази такого тестування й склали попередній пайплайн для валідації Pull Request. Ми також розібрали найпоширеніші (та, певно, найбільш дратуючі) помилки, яких можна уникнути під час тестування та як це врешті впливає на результати роботи.

У сучасному бізнес-середовищі цінність функціональних характеристик рішень вважається менш важливою — вони повторюються у різних галузях. Справжнє поле конкурентної боротьби тепер лежить в області нефункціональних характеристик. Надійність інфраструктури рішення безпосередньо впливає на ці нефункціональні атрибути, робить його конкурентоспроможним на ринку, а клієнтів — задоволеними.

Коли мова доходить до тестування — зазвичай все йде не по плану. Адже саме тестування є «першим кандидатом» на скорочення бюджетів, ресурсів або ж відтермінування. Немає значення, що ви плануєте на початку проєкту. Це значить, що має бути перелік «must have» і «nice-to-have» обʼєктів для тестування. Я сподіваюся, що ця стаття змотивує вас включити тестування IaC до списку «must have».

Наостанок підкреслюю: тестування IaC дійсно має бути невід’ємною частиною будь-якого проєкту, який використовує автоматизоване забезпечення та управління інфраструктурою. Але існують конкретні типи проєктів, де таке тестування — критичне: складні розподілені системи, хмарні нативні застосунки, high-availability системи, регульовані галузі, чутливі до безпеки застосунки, великомасштабні деплойменти тощо. Для таких проєктів тестування IaC — це не просто must have, а питання виживання.

👍ПодобаєтьсяСподобалось13
До обраногоВ обраному10
LinkedIn
Дозволені теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter
Дозволені теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter

Також від себе добавлю, що мені як software engineer, бажано протестувати на лише IaC (terraform), а в зв"язці з кодом, а також мати локальне середовище для тестування коду, який буде виконуватися в хмарному середовищі — для пришвидшення розробки, для інтеграційних тестів, перевірки PR (CI частина), і тут бажано з самого початку продумати архітектуру як інфраструктури, так і коду — як полегшити життя всім суміжним командам (по-модульна організація як перший крок), що в результаті дозволить протестувати інфраструктуру на відповідність коду — наприклад, що AWS Lambda може писати в потрібний S3 bucket, і відправляти повідомлення в потрібну SQS чергу — для цього я використовую LocalStack, див. docs.aws.amazon.com/...​localstack-terraform.html

Для тестування в зв«язці з кодом існують 3 оточення — dev/test/prod — де dev — програмісти можуть розгортати та дебажити свій код, після цього він розгортається в test де його ломають вже qa, і якщо останні його не зломали і незнайшли, до чого доколупатися — він вже йде в prod.

Доволі стандартній підхід. Хоч і коштує грошей (на dev/test оточення). Тому часто програмісти і qa працюють на «локальних оточеннях» (своїх машинах). А в «багатому» і ідеальному світі — це точно такі-ж оточення як і prod до останньої крапки IaC коду (ну, може скейлінг менше і тарифи/розміри) ;)

Якщо команда велика і відкривається багато PR одночасно в одній часовій зоні, то лише dev/test environments буде недостатньо.

Якщо є гроші — можна розгорнути кілька ;) ;)

А насправді — кожен свій код все одно тестить якось локально. Це вже перед тим як відправляти до тестувальників код розгортається на DEV щоб подивитись на максимально близькому до робочого оточенні. Ну і для того, щоб ловити якийсь баг теж бажано мати максимально близьке до реального оточення — не завжди ж його можна зловити локально...

Тепер ви застрягли на кілька годин, які не вкладаються у жодне розумне вікно SLI.

^^^ typo, SLA

Наприклад, нам потрібно переконатись, що всі бакети AWS S3 зашифровані

я не devops інженер, але я би це робив через terraform preconditions, developer.hashicorp.com/...​anguage/custom-conditions
вони запускаються кожного разу для `terraform plan|apply`

Тут є проблеми, бо коли складні структури даних передаєш ( map(list(map)) ) - то валідація може давати збій, наприклад, коли один модуль викликає інший і неможливо валідувати значення без apply — тобто зробиш забагато валідацій, частина plan не буде відпрацьовувати. І треба вже робити вибір, що більш важливо — валідувати змінні, чи мати можливість подивитись plan до розгортання ;)

Гарно, що ці питання піднімаються.

Але ви трішечкі намішали тестування terraform-коду як такого і тестування архітектури на відповідність коду і вимогам. Це трішечкі різні речі.

Наприклад, у нас всі модулі terraform обов’язково тестуються (без terraform fmt/validate + tflint + snyk взагалі комміт не приймається) — скажемо так, це статичні тести.

Далі йдуть тести на реальній інфраструктурі — є спеціально виділене оточення, де розгортається реальна інфра за допомогою цих модулів і тестується як можливість створити реальні об’єкти в хмарі, так і відповідність цієї інфраструктури вимогам безпеки та та внітрушнім policy корпорації — наприклад найменування об’єктів (це не так важливо у AWS, але має сенс в Azure) наявність всіх тегів для біллінгу та моніторінгу (так і наявність налаштувань централізованого моніторінгу).

Хочу звернути увагу, що з реальним тестуванням у вигляді розгортання можуть бути проблеми — наприклад, після тестування бажано видалити всі об’єкти, але для деяких примусово налаштовано захист від видалення — його треба відключати, але й треба бути впевненим, що в реальній інфраструктурі воно не буде вимкнене. Крім того, частина налаштувань може бути не доступна в деяких регіонах або мати ще якісь обмеження. Тобто ця частина тестування складніша, коштовніша та значно довша (у нас деяки тести йдуть годинами — і це суто створення та видалення об’єктів в хмарі, не прискориш). Більше того, тут можливі помилки викликані мережею чи проблемами у хмарі, тобто тести можуть впасти, але проблема не в коді. І це треба обробляти — часом, вручну.

Але тільки після всіх цих тестів код йде в головну гілку і може бути використаним для роботи з реальними, робочими оточеннями.

Тестування робочих оточень (той самий drift) — це окрема справа. Ми, наприклад, запускаємо його періодично без змін у коді, просто щоб перевірити, що ніхто нічого «руцями» не міняв. І запускаємо після переходу на нові версії модулів (наприклад, якщо security виставили нові налаштування — вони тестуються в модулях, потім ці модулі оновлюються в робочих оточеннях, дивимось зсув, приміняємо нову конфігурацію.

Тож, я б розділив тести коду як такого, на статичні та реальні. І виділяв тестування «зсуву» як окреме тестування робочих оточень.

І, звісно, не треба забувати, що виключно тестуванням IaC коду не треба обмежуватись — гарно мати на доданок якісно налаштовані policy в хмарі, щоб не коректні об’єкти просто неможливо було створити. Але це зовсім інша тема — і це більше до спеціалістів по безпеці (хоча і не тільки — біллінг теж важлива тема, коли ви вмієте рахувати гроші ;) )

Доречі, «тести на реальній інфраструктурі в тестовому оточенні» — робляться просто кодом terraform/terragrunt — і ми маємо не тільки тести, а ще й реальні приклади коду для розгортання наявних, базових, інфраструктур. Тобто це і тести і документація в «одному флаконі», що доволі зручно. Коли треба розгорнути новий проєкт — частише за все копіюється схожий тест і додаються потрібні зміни. Це значно економить час. А ще це виключає копіювання з інших, схожих проектів, що запобігає копіюванню секретів, особливостей налаштування і всього того, за що хлопці з відділу безпеки точно не скажуть дякую (а скажуть щось інше :D:D:D:D:D)

без terraform fmt/validate + tflint + snyk взагалі комміт не приймається

100% — рекомендую github.com/...​enko/pre-commit-terraform

Звісно, використовується. Але пайплайн все одно це все перевіряє.

Цікава, добротна стаття. Сам маю нагоду працювати в компанії, яка майже всі описані процеси допомагає втілити через UI, набагато зручніше ніж термінал.

Інтегруємся з Checkov, OPA, різними VCS, Slack, Teams, DD, різні SSO та ін.

Підтримуєм terraform/tofu validate та fmt під час init. Є drift detection, хуки, внутрішній module registry, підтримка pull requests та інші штуки. Загалом все для тих, хто працює за GitOps методологією.

Для маленьких проектів може бути цікаво, оскільки на фрі плані функціонал не обрізаний, обмеження лише 50 ранів на місяць.

Підписатись на коментарі