Управління New Relic за допомогою Terraform: Частина II — Всеохопний посібник

Вступ

У статті Управління New Relic за допомогою Terraform: Частина I — Детальний посібник я пояснив, як використовувати Terraform для керування конфігурацією та об'єктами New Relic. Тут я покажу приклад того, як можна структурувати репозиторій, щоб не заплутатись в численних конфігураціях.

Репозиторій

Попередня стаття закінчувалась одним файлом main.tf, який містить усе необхідне для створення моніторингу та сповіщень для синтетичних моніторів. Нижче наведено файл (також можна завантажити git-репозиторій тут):

terraform {  
 required_version = ">= 0.12"  
 required_providers {  
 newrelic = {  
 source = "newrelic/newrelic"  
 }  
 }  
}  

provider "newrelic" {  
 api_key = "XXX"  
 region = "EU"  
 account_id = "XXXX"  
}  

resource "newrelic_synthetics_monitor" "google" {  
 status = "ENABLED"  
 name = "Google ping"  
 period = "EVERY_MINUTE"  
 uri = "https://google.com"  
 locations_public = ["AWS_EU_WEST_3"]  
 type = "SIMPLE"  
}  

resource "newrelic_synthetics_monitor" "onet" {  
 status = "ENABLED"  
 name = "Onet ping"  
 period = "EVERY_MINUTE"  
 uri = "https://onet.pl"  
 locations_public = ["AWS_EU_WEST_3", "AWS_EU_WEST_2"]  
 type = "SIMPLE"  
}  

resource "newrelic_alert_policy" "policy" {  
 name = "prod"  
 incident_preference = "PER_CONDITION_AND_TARGET"  
}  

resource "newrelic_synthetics_alert_condition" "google" {  
 policy_id = newrelic_alert_policy.policy.id  
 name = "Googl ping failed"  
 monitor_id = newrelic_synthetics_monitor.google.id  
}  

resource "newrelic_synthetics_alert_condition" "onet" {  
 policy_id = newrelic_alert_policy.policy.id  
 name = "Onet ping failed"  
 monitor_id = newrelic_synthetics_monitor.onet.id  
}  

resource "newrelic_notification_destination" "email" {  
 name = "User email"  
 type = "EMAIL"  
 property {  
 key = "email"  
 value = "[email protected]"  
 }  
}  

resource "newrelic_notification_channel" "emailchannel" {  
 name = "email-channel"  
 type = "EMAIL"  
 destination_id = newrelic_notification_destination.email.id  
 product = "IINT"  

 property {  
 key = "subject"  
 value = "{{issueId}}"  
 }  

 property {  
 key = "customDetailsEmail"  
 value = "issue id - {{issueId}}"  
 }  
}  

resource "newrelic_workflow" "workflow" {  
 name = "send-email-workflow"  
 destination {  
 channel_id = newrelic_notification_channel.emailchannel.id  
 }  

 muting_rules_handling = "NOTIFY_ALL_ISSUES"  

 issues_filter {  
 name = "filter-name"  
 type = "FILTER"  

 predicate {  
 attribute = "accumulations.policyName"  
 operator = "EXACTLY_MATCHES"  
 values = [newrelic_alert_policy.policy.name]  
 }  

 predicate {  
 attribute = "priority"  
 operator = "EQUAL"  
 values = ["CRITICAL"]  
 }  
 }  
}

Цей підхід не має достатньої гнучкості, особливо коли ви хочете повторно використовувати ресурси або встановити якісь стандарти. Щоб вирішити цю проблему, Terraform надає механізм абстракції, званий «модулі». Коротко кажучи — ви можете виділити ресурси в модуль, який потім можна буде використовувати повторно. Модуль повинен бути самодостатнім, що означає, що всі необхідні ресурси повинні бути включені в модуль або передані як вхідні змінні. З іншого боку, модуль може публікувати змінні Output, щоб його можна було використовувати в інших місцях.

Використання модулів для модульності

Більш формальне визначення модуля можна знайти в документації Terraform:

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

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

Давайте почнемо зі створення нашого першого модуля.

Вступ

Завантажте файл з частини I та збережіть його в робочу директорію на вашому локальному комп'ютері. Тепер у тій самій директорії створіть нову папку modules, а в ній — папку newrelic_synthetics_monitor.

У цій папці створіть три порожні файли: main.tf, outputs.tf та variables.tf.

У кожному з цих файлів ми додамо різні типи об'єктів Terraform:

  • main.tf міститиме інформацію про ресурси.
  • outputs.tf міститиме інформацію про змінні виводу, опубліковані модулями.
  • variables.tf міститиме визначення вхідних змінних.

Зараз ви повинні побачити щось подібне до такої структури:

pic

Перше, що ми зробимо, — це перемістимо визначення newrelic_synthetics_monitor до окремого модуля та дозволимо конфігурувати, чи слід розгортати ресурс, передаючи змінну.

Додайте наступні дві змінні до modules/newrelic_synthetics_monitor/variables.tfping_google та ping_onet. Змінні можуть бути досить потужними, але для нашого випадку ми можемо просто створити змінні типу bool без значень за замовчуванням. Визначення виглядають наступним чином:

variable "google_ping" {  
 type = bool  
 description = "Бульова змінна, що вказує, чи слід увімкнути синтетичний моніторинг пінгу для сайту google.com. Встановіть значення 'true', щоб виконати пінг, або 'false', щоб вимкнути його."  
}  

variable "onet_ping" {  
 type = bool  
 description = "Бульова змінна, що вказує, чи слід увімкнути синтетичний моніторинг пінгу для сайту onet.pl. Встановіть значення 'true', щоб виконати пінг, або 'false', щоб вимкнути його."  
}

Тепер давайте перемістимо визначення ресурсів з головного файлу main.tf на корінь у modules/newrelic_synthetics_monitor/main.tf. Нам доведеться зробити кілька коригувань, щоб все працювало коректно.

Перш за все, в модулі main.tf для кожного ресурсу додайте властивість count, як показано нижче. Це дозволить створювати чи не створювати ресурси в залежності від змінної. Загалом, count дозволяє створювати кілька екземплярів одного й того ж об'єкта, тож, наприклад, якщо ви хочете створити кілька схожих ресурсів, можна використовувати count для їх створення. У Terraform для читання значення змінної використовуйте var.variable_name. Ви навіть можете створити нову змінну, яка буде масивом всіх URL, які New Relic повинен пінгувати.

resource "newrelic_synthetics_monitor" "google" {  
 count = var.google_ping ? 1 : 0  
 status = "ENABLED"  
 name = "Google ping"  
 period = "EVERY_MINUTE"  
 uri = "https://google.com"  
 locations_public = ["AWS_EU_WEST_3"]  
 type = "SIMPLE"  
}  

resource "newrelic_synthetics_monitor" "onet" {  
 count = var.onet_ping ? 1 : 0  
 status = "ENABLED"  
 name = "Onet ping"  
 period = "EVERY_MINUTE"  
 uri = "https://onet.pl"  
 locations_public = ["AWS_EU_WEST_3", "AWS_EU_WEST_2"]  
 type = "SIMPLE"  
}

Після переміщення додайте визначення змінних виводу, щоб ми могли використовувати ідентифікатори моніторів Synthetics за межами модуля. Для цього у файлі outputs.tf додайте визначення змінних:

output "google_id" {  
 value = var.google_ping ? newrelic_synthetics_monitor.google[0].id : null  
 description = "Google Synthetics ping ID"  
}  

output "onet_id" {  
 value = var.onet_ping ? newrelic_synthetics_monitor.onet[0].id : null  
 description = "Google Synthetics onet ID"  
}

Як ви можете побачити, в value ми посилаємось на властивості всередині модуля newrelic_synthetics_monitor. Крім того, оскільки визначення ресурсу використовує властивість count, змінна виводу повинна бути вказана так: var.onet_ping ? newrelic_synthetics_monitor.onet[0].id : null, що означає, що якщо var.onet_ping істинне, то повернеться значення newrelic_synthetics_monitor.onet[0].id, інакше значення буде null.
Ці значення будуть використані пізніше.

У коріньовому файлі main.tf додайте посилання на цей модуль, визначивши об'єкт модуля.

module "synthetic" {  
 source = "./modules/newrelic_synthetics_monitor"  
 google_ping = true  
 onet_ping = true  
}

Ключове слово module вказує, що на рівні кореневого файлу main.tf потрібно буде підключити модуль під час виконання. source вказує на кореневу директорію модуля, а ping_google/ping_onet — це значення змінних, переданих у модуль.

Якщо ви використовуєте розширення або IDE, яке перевіряє синтаксис, ви помітите, що умови сповіщень (alert conditions) для monitor_id потребують оновлення, тому змініть monitor_id на module.synthetic.google_id/onet_id.

Якщо все оновлено, давайте спробуємо розгорнути рішення. Якщо ви дотримувались першої частини, можливо, у вашому обліковому записі New Relic вже є деякі ресурси. Для спрощення виконайте команду terraform destroy з кореневої конфігурації, щоб очистити всі існуючі ресурси. Якщо ви цього не зробите, можуть виникнути додаткові проблеми. У реальному сценарії, замість очищення ресурсів, вам доведеться використати команду terraform import, щоб імпортувати ресурси в Terraform.

Якщо розгортання не вдалося і з'явилося повідомлення про помилку Error: Failed to query available provider packages, це може бути через те, що Terraform шукає реєстр постачальників за замовчуванням. Щоб вирішити цю проблему, переконайтесь, що ви вказали правильну конфігурацію постачальника у вашому модулі. За замовчуванням Terraform намагається знайти модуль у реєстрі постачальників hashicorp/newrelic, але ми використовуємо реєстр newrelic/newrelic. Тому потрібно додати конфігурацію постачальника всередині модуля. Додайте наступну частину в modules/newrelic_synthetics_monitor/main.tf.

terraform {  
 required_version = ">= 0.12"  
 required_providers {  
 newrelic = {  
 source = "newrelic/newrelic"  
 }  
 }  
}

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

Ми почнемо з переміщення policy в окремі модулі. Як швидкий шлях, ви можете просто скопіювати модуль newrelic_synthetics_monitor і перейменувати його в newrelic_alerty_policy. Вміст файлів можна знайти на моєму GitHub.

Коли все буде готово, потрібно буде оновити policy id та policy name у файлі main.tf на рівні кореня. Для цього змініть newrelic_alert_policy.policy.id на module.policy.id, а newrelic_alert_policy.policy.name на module.policy.name у файлі main.tf. Виконайте terraform init, terraform apply, і ваші зміни повинні бути застосовані. Майте на увазі, що деякі ресурси будуть потребувати перезавантаження, у реальному житті вам слід подумати, чи можна перезавантажити ці ресурси.

Ту ж саму процедуру потрібно буде виконати для решти ресурсів, витягнувши їх у власні модулі.

Оточення

У попередньому розділі ми перемістили ресурси з кореневого файлу main.tf до модулів. В цьому розділі я покажу альтернативний спосіб, який ми реалізували під час нашого PoC. Нам потрібно було розгорнути подібні модулі для різних середовищ з дещо іншими налаштуваннями. Ідея полягала в тому, щоб:

  1. Використовувати модулі для групування пов'язаних ресурсів (наприклад, синтетичні монітори), де рішення щодо того, які ресурси повинні бути розгорнуті в якому середовищі, приймається на основі змінної модуля.
  2. Використовувати окрему директорію для кожного середовища, щоб дозволити окреме розгортання для кожного з них.

Ми просто хотіли використовувати модулі як абстракцію для ресурсів, зберігаючи реалізацію в різних файлах, які використовують ці модулі. Для нас “реалізаційні” директорії називалися “environment” (середовище), оскільки ми намагались проєктувати налаштування середовища через файлову систему в Git-репозиторії.

Перед наступним кроком очистіть NewRelic — просто виконайте команду terraform destroy.

Створіть нову директорію і в ній ще одну підкатегорію example1. Перемістіть main.tf з кореня в environments\example1. Оновіть шляхи до модуля (замініть ./modules на ../../modules). Ми могли б залишити це так, але ми також перемістимо всі значення змінних з main.tf в файл terraform.tfvars.
Створіть ще один файл variables.tf та оголосіть наступні змінні:

variable "deploy_google_check" {  
 type = bool  
 description = "Булева змінна для включення або вимкнення синтетичного монітору пінгу для сайту google.com у New Relic. Встановіть в 'true', щоб створити монітор, або в 'false', щоб пропустити його."  
}  

variable "deploy_onet_check" {  
 type = bool  
 description = "Булева змінна для включення або вимкнення синтетичного монітору пінгу для сайту onet.pl у New Relic. Встановіть в 'true', щоб створити монітор, або в 'false', щоб пропустити його."  
}  

variable "email_address" {  
 type = string  
 description = "Електронна адреса, на яку будуть надсилатися сповіщення у разі спрацювання тривог або виявлення проблем синтетичними моніторами у New Relic."  
}  

variable "api_key" {  
 type = string  
 description = "API ключ New Relic, необхідний для автентифікації та взаємодії з API New Relic для створення та керування ресурсами."  
}  

variable "account_id" {  
 type = string  
 description = "ID облікового запису New Relic, пов'язаний з організацією, в якій будуть створюватися та керуватися синтетичні монітори."  
}

У файлі terraform.tfvars додайте значення для змінних:

api_key = "YourNewRelicApiKey"  
account_id = "YourNewRelicAccountId"  
deploy_google_check = true  
deploy_onet_check = true  
email_address = "[email protected]"

Щоб розгорнути конфігурацію, потрібно спочатку відкрити директорію environments\example1, оскільки це поточний корінь для нашого випадку розгортання. Змінивши значення deploy_google_check на false, ви видалите монітор пінгу для Google і пов'язане з ним сповіщення.

Висновок

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

Я планую підготувати ще одну статтю про налаштування простого CI/CD пайплайна за допомогою GitHub Actions. Ось також посилання на першу частину Управління New Relic за допомогою Terraform: Частина I — Всеохопний посібник та на репозиторій, де ви можете знайти всі файли, які використовувалися в цій частині.

Всі файли та фінальна структура репозиторію, запропонована в цій статті, доступні в цьому GitHub репозиторії.

Перекладено з: Managing New Relic with Terraform: Part II — A Comprehensive Guide

Leave a Reply

Your email address will not be published. Required fields are marked *