Вступ
У статті Управління 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
міститиме визначення вхідних змінних.
Зараз ви повинні побачити щось подібне до такої структури:
Перше, що ми зробимо, — це перемістимо визначення newrelic_synthetics_monitor
до окремого модуля та дозволимо конфігурувати, чи слід розгортати ресурс, передаючи змінну.
Додайте наступні дві змінні до modules/newrelic_synthetics_monitor/variables.tf
— ping_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. Нам потрібно було розгорнути подібні модулі для різних середовищ з дещо іншими налаштуваннями. Ідея полягала в тому, щоб:
- Використовувати модулі для групування пов'язаних ресурсів (наприклад, синтетичні монітори), де рішення щодо того, які ресурси повинні бути розгорнуті в якому середовищі, приймається на основі змінної модуля.
- Використовувати окрему директорію для кожного середовища, щоб дозволити окреме розгортання для кожного з них.
Ми просто хотіли використовувати модулі як абстракцію для ресурсів, зберігаючи реалізацію в різних файлах, які використовують ці модулі. Для нас “реалізаційні” директорії називалися “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