Всім привіт! В рамках курсової роботи я займався дослідженнями можливостей такої вітчизняної хмарної платформи, як
Що хочеться здобути?
Після запуску віртуальної машини з веб-сервером можна буде зайти на його хост і отримати гарний UI, вказати бази даних як джерела для подальшої роботи, створити дашборди та графіки.
У базової версії є один істотний недолік - вона зовсім не стійка до відмови. Тобто вся працездатність програми залежить від життєздатності однієї віртуальної машини. Якщо вона відмовить або 10 осіб одночасно відкриють UI, то виникнуть проблеми.
Вирішуються вони просто: потрібно лише… розгорнути багато однакових віртуальних машин з веб-сервером і помістити їх під L3-балансер. Але не тут не все однозначно. Графана зберігає налаштування користувача (шляхи до баз даних, дашборди, графіки і т.д.) прямо на диску своєї віртуальної машини. Таким чином, якщо змінити якісь налаштування в UI, ці зміни відобразяться лише на тій віртуальній машині, куди відправив нас балансер. Це призведе до неконсистентних налаштувань нашої програми, виникнуть проблеми із запуском та використанням.
Тут на допомогу прийде ще одна база даних, наприклад MySQL або її аналог. Говоримо Графані, що вона повинна зберігати налаштування користувача саме в цій "запасний" базі. Після достатньо буде один раз вказати на кожній машині шлях до цієї БД, а всі інші налаштування редагувати на будь-якій з віртуальних машин, вони будуть проростати на інші.
Ось схема підсумкової інфраструктури програми:
Навчимося піднімати руками
MySQL та ClickHouse
Перш ніж розгортати таку програму натисканням однієї кнопки, потрібно було навчитися піднімати ручками кожен його компонент та інтегрувати їх один з одним.
Тут нам допоможе Яндекс.Хмара, яке надає L3-балансери, ClickHouse та MySQL як managed-сервіси. Користувачеві необхідно лише вказати параметри та почекати, поки платформа приведе все до працездатного стану.
Я зареєструвався, створив собі хмару та платіжний обліковий запис. Після цього зайшов у хмару та підняв кластери MySQL та ClickHouse з мінімальними налаштуваннями. Дочекався, доки вони стануть активними.
Також треба не забути створити в кожному кластері базу даних і налаштувати доступ до неї за логіном та паролем. Вдаватися тут деталі не буду — в інтерфейсі все досить очевидно.
Неочевидна деталь була в тому, що у цих БД безліч хостів, які забезпечують їхню відмовостійкість. Однак Графана вимагає рівно один хост для кожної БД, з якою вона працює. Тривале читання c-<cluster_id>.rw.mdb.yandexcloud.net
маппится в поточний активний майстер-хост кластера з відповідним айдішником. Саме його ми й віддамо Графані.
Веб-сервер
Тепер справа за веб-сервером. Піднімемо звичайну віртуальну машину з Linux та руками налаштуємо на ній Графану.
Підключимося по ssh та встановимо необхідні пакети.
sudo apt-get install -y apt-transport-https software-properties-common wget
wget -q -O - https://packages.grafana.com/gpg.key | sudo apt-key add -
sudo add-apt-repository "deb https://packages.grafana.com/enterprise/deb stable main"
sudo apt-get update
sudo apt-get install -y grafana-enterprise
Після цього заведемо Графан під systemctl і встановимо плагін для роботи з ClickHouse (так, в базовій комплектації він не поставляється).
sudo systemctl start grafana-server
sudo systemctl enable grafana-server
sudo grafana-cli plugins install vertamedia-clickhouse-datasource
Все, після цього простою командою
sudo service grafana-server start
ми запустимо веб-сервер. Тепер можна буде в браузері вбити зовнішній IP-адресу віртуальної машини, вказати порт 3000 і побачити красивий UI графани.
Але не варто поспішати, перш ніж налаштовувати графану, треба не забути вказати їй шлях до MySQL, щоб зберігати налаштування там.
Уся конфігурація веб-сервера Графани лежить у файлі /etc/grafana/grafana.ini
. Потрібний рядок виглядає так:
;url =
Виставляємо хост до кластера MySQL. У цьому файлі знаходяться логін і пароль для доступу до Графани на зображенні вище, які за умовчанням обидва рівні admin
.
Можна скористатися sed-командами:
sudo sed -i "s#.*;url =.*#url = mysql://${MYSQL_USERNAME}:${MYSQL_PASSWORD}@${MYSQL_CLUSTER_URI}#" /etc/grafana/grafana.ini
sudo sed -i "s#.*;admin_user =.*#admin_user = ${GRAFANA_USERNAME}#" /etc/grafana/grafana.ini
sudo sed -i "s#.*;admin_password =.*#admin_password = ${GRAFANA_PASSWORD}#" /etc/grafana/grafana.ini
Саме час перезапустити веб-сервер!
sudo service grafana-server restart
Тепер у UI Графани вкажемо ClickHouse як DataSource.
Домогтися працюючої конфігурації у мене вийшло за наступних налаштувань:
Як URL я вказав https://c-<cluster_id>.rw.mdb.yandexcloud.net:8443
Всі! У нас є одна працездатна віртуальна машинка з веб-сервером, підключеним до CH та MySQL. Вже можна завантажувати датасет у ClickHouse та будувати дашборди. Однак ми ще не досягли нашої мети та не розгорнули повноцінну інфраструктуру.
Пакувальник
Яндекс.Хмара дозволяє створити образ диска віртуальної машини, а на його основі — скільки завгодно ідентичних один одному машин. Саме цим ми й скористаємося. Щоб зручно зібрати образ, візьмемо інструмент
Наш json-файл складатиметься з двох блоків: builders та provisioners. Перший блок визначає параметри самого образу як сутності, а другий - інструкцію щодо наповнення потрібним вмістом.
будівельники
{
"builders": [
{
"type": "yandex",
"endpoint": "{{user `endpoint`}}",
"folder_id": "<folder_id>",
"subnet_id": "{{user `subnet_id`}}",
"zone": "{{user `zone`}}",
"labels": {},
"use_ipv4_nat": true,
"use_internal_ip": false,
"service_account_key_file": "<service_account_key_file>",
"image_name": "grafana-{{timestamp}}",
"image_family": "grafana",
"image_labels": {},
"image_description": "GRAFANA",
"source_image_family": "ubuntu-1804-lts",
"disk_size_gb": 3,
"disk_type": "network-hdd",
"ssh_username": "ubuntu"
}
],
...
}
У цьому шаблоні потрібно встановити ідентифікатор розділу в хмарі, де ви хочете створити образ, а також шлях до файлу з ключами від сервісного облікового запису, попередньо заведеного в цьому розділі. Докладніше про створення сервісних облікових записів та ключів у вигляді файлу можна почитати у відповідному розділі
Така конфігурація каже, що образ диска буде зібрано на основі платформи ubuntu-1804-lts
, розміщений у відповідному розділі користувача у сімействі образів GRAFANA
під ім'ям grafana-{{timestamp}}
.
Провізіори
Тепер найцікавіша частина конфігурації. У ній буде описано послідовність дій, які треба буде зробити на віртуальній машині, перш ніж заморозити її стан образ диска.
{
...,
"provisioners": [
{
"type": "shell",
"pause_before": "5s",
"scripts": [
"prepare-ctg.sh"
]
},
{
"type": "file",
"source": "setup.sh",
"destination": "/opt/grafana/setup.sh"
},
{
"type": "shell",
"execute_command": "sudo {{ .Vars }} bash '{{ .Path }}'",
"pause_before": "5s",
"scripts": [
"install-packages.sh",
"grafana-setup.sh",
"run-setup-at-reboot.sh"
]
}
]
}
Тут усі дії поділені на 3 етапи. На першому етапі виконується простий скрипт, який створює допоміжну директорію.
prepare-ctg.sh:
#!/bin/bash
sudo mkdir -p /opt/grafana
sudo chown -R ubuntu:ubuntu /opt/grafana
На наступному етапі до цієї директорії поміщаємо скрипт, який треба буде запустити відразу після запуску віртуальної машини. Цей скрипт покладе змінні користувача, які потрібно прописати, в конфіг Графани і перезапустить веб-сервер.
setup.sh:
#!/bin/bash
CLUSTER_ID="<cluster_id>"
USERNAME="<username>"
PASSWORD="<password>"
sudo sed -i "s#.*;url =.*#url = mysql://${USERNAME}:${PASSWORD}@c-${CLUSTER_ID}.rw.mdb.yandexcloud.net#" /etc/grafana/grafana.ini
sudo sed -i "s#.*;admin_user =.*#admin_user = ${USERNAME}#" /etc/grafana/grafana.ini
sudo sed -i "s#.*;admin_password =.*#admin_password = ${PASSWORD}#" /etc/grafana/grafana.ini
sudo service grafana-server restart
Після цього залишилося зробити 3 речі:
1) встановити пакети
2) завести Графану під systemctl і встановити плагін ClickHouse
3) покласти скрипт setup.sh у черзі на запуск відразу після включення віртуальної машини.
install-packages.sh:
#!/bin/bash
sudo systemd-run --property='After=apt-daily.service apt-daily-upgrade.service' --wait /bin/true
sudo apt-get install -y apt-transport-https
sudo apt-get install -y software-properties-common wget
wget -q -O - https://packages.grafana.com/gpg.key | sudo apt-key add -
sudo add-apt-repository "deb https://packages.grafana.com/enterprise/deb stable main"
sudo apt-get update
sudo apt-get install -y grafana-enterprise
grafana-setup.sh:
#!/bin/bash
sudo systemctl start grafana-server
sudo systemctl enable grafana-server
sudo grafana-cli plugins install vertamedia-clickhouse-datasource
run-setup-at-reboot.sh:
#!/bin/bash
chmod +x /opt/grafana/setup.sh
cat > /etc/cron.d/first-boot <<EOF
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games
@reboot root /bin/bash /opt/grafana/setup.sh > /var/log/yc-setup.log 2>&1
EOF
chmod +x /etc/cron.d/first-boot;
Тепер залишається запустити Packer і на виході отримати образ, вміщений у зазначеному розділі. При створенні віртуальної машини можна вибрати його як завантажувальний диск та після запуску отримати готовий веб-сервер Графани.
Інстанс-група та балансер
Після того, як з'явився образ диска, який дозволяє створювати безліч однакових веб-серверів Графани, ми можемо створити інстанс-групу. На платформі Яндекс.Хмара цим терміном називається поєднання віртуальних машин, що мають однакові характеристики. При створенні інстанс-групи конфігурується прототип всіх машин у цій групі, а потім і характеристики самої групи (наприклад, мінімальна та максимальна кількість активних машин). Якщо поточна кількість не відповідатиме цим критерієм, то інстанс-група сама видалить непотрібні машини або створить нові за образом і подобою.
В рамках нашого завдання ми створимо інстанс-групу веб-серверів, які будуть породжуватись із створеного раніше образу диска.
По-справжньому примітне останнє налаштування інстанс-групи. Цільова група в інтеграції з Load Balancer допоможе натисканням пари кнопок налаштувати L3-балансер поверх віртуальних машин цієї групи.
При налаштуванні балансера я реалізував два важливі моменти:
- Зробив так, щоб балансер приймав трафік користувача на 80 порту, а перенаправляв його на 3000 порт віртуальних машин, саме туди, де живе Графана.
- Налаштував перевірку життєздатності машин, пінгуючи їх у 3000 порт.
Міні-підсумок
Нарешті ми змогли розгорнути бажану інфраструктуру програми, і тепер у нас є високостійкий сервіс Grafana. Необхідно лише знати IP-адресу балансера як точку входу в додаток і хост кластера ClickHouse, щоб завантажити датасет.
Здавалося б, перемога? Так, перемога. Але щось таки бентежить. Весь процес вищий вимагає дуже багато ручних дій і зовсім не масштабується, хочеться його по можливості автоматизувати. Цьому і буде присвячений наступний розділ.
Інтеграція з Terraform
Ми знову скористаємося інструментом від компанії HashiCorp на ім'я
Вся робота з Терраформ зводиться до написання конфігураційного файлу (*.tf
) та створення інфраструктури на його основі.
Змінні
Насамперед файлу винесемо змінні, від яких залежить, де і як буде розгорнута майбутня інфраструктура.
variable "oauth_token" {
type = string
default = "<oauth-token>"
}
variable "cloud_id" {
type = string
default = "<cloud-id>"
}
variable "folder_id" {
type = string
default = "<folder_id>"
}
variable "service_account_id" {
type = string
default = "<service_account_id>"
}
variable "image_id" {
type = string
default = "<image_id>"
}
variable "username" {
type = string
default = "<username>"
}
variable "password" {
type = string
default = "<password>"
}
variable "dbname" {
type = string
default = "<dbname>"
}
variable "public_key_path" {
type = string
default = "<path to ssh public key>"
}
Весь процес розгортки програми зведеться до збирання образу диска та виставляння цих змінних. Поясню, за що вони відповідають:
oauth_token - токен для доступу до хмари. Можна отримати по
cloud_id — ідентифікатор хмари, де розгортатимемо додаток
folder_id — ідентифікатор розділу, де розгортатимемо програму
service_account_id - Ідентифікатор сервісного облікового запису у відповідному розділі хмари.
image_id - Ідентифікатор образу диска, отриманого за допомогою Packer
ім'я користувача и пароль — Ім'я користувача та пароль для доступу до обох баз даних та веб-сервера Графани
dbname - ім'я бази даних усередині кластерів CH та MySQL
public_key_path — шлях до файлу з вашим публічним ssh-ключом, яким можна буде підключатися під ім'ям ubuntu
до віртуальних машин з веб-серверами
Налаштування провайдера
Тепер потрібно налаштувати провайдер Терраформа — у нашому випадку Яндекс:
provider "yandex" {
token = var.oauth_token
cloud_id = var.cloud_id
folder_id = var.folder_id
zone = "ru-central1-a"
}
Можна помітити, що ми використовуємо змінні, задані вище.
Мережа та кластери
Тепер створимо мережу, в якій спілкуватимуться елементи нашої інфраструктури, три підмережі (по одній у кожному регіоні) та піднімемо кластери CH та MySQL.
resource "yandex_vpc_network" "grafana_network" {}
resource "yandex_vpc_subnet" "subnet_a" {
zone = "ru-central1-a"
network_id = yandex_vpc_network.grafana_network.id
v4_cidr_blocks = ["10.1.0.0/24"]
}
resource "yandex_vpc_subnet" "subnet_b" {
zone = "ru-central1-b"
network_id = yandex_vpc_network.grafana_network.id
v4_cidr_blocks = ["10.2.0.0/24"]
}
resource "yandex_vpc_subnet" "subnet_c" {
zone = "ru-central1-c"
network_id = yandex_vpc_network.grafana_network.id
v4_cidr_blocks = ["10.3.0.0/24"]
}
resource "yandex_mdb_clickhouse_cluster" "ch_cluster" {
name = "grafana-clickhouse"
environment = "PRODUCTION"
network_id = yandex_vpc_network.grafana_network.id
clickhouse {
resources {
resource_preset_id = "s2.micro"
disk_type_id = "network-ssd"
disk_size = 16
}
}
zookeeper {
resources {
resource_preset_id = "s2.micro"
disk_type_id = "network-ssd"
disk_size = 10
}
}
database {
name = var.dbname
}
user {
name = var.username
password = var.password
permission {
database_name = var.dbname
}
}
host {
type = "CLICKHOUSE"
zone = "ru-central1-a"
subnet_id = yandex_vpc_subnet.subnet_a.id
}
host {
type = "CLICKHOUSE"
zone = "ru-central1-b"
subnet_id = yandex_vpc_subnet.subnet_b.id
}
host {
type = "CLICKHOUSE"
zone = "ru-central1-c"
subnet_id = yandex_vpc_subnet.subnet_c.id
}
host {
type = "ZOOKEEPER"
zone = "ru-central1-a"
subnet_id = yandex_vpc_subnet.subnet_a.id
}
host {
type = "ZOOKEEPER"
zone = "ru-central1-b"
subnet_id = yandex_vpc_subnet.subnet_b.id
}
host {
type = "ZOOKEEPER"
zone = "ru-central1-c"
subnet_id = yandex_vpc_subnet.subnet_c.id
}
}
resource "yandex_mdb_mysql_cluster" "mysql_cluster" {
name = "grafana_mysql"
environment = "PRODUCTION"
network_id = yandex_vpc_network.grafana_network.id
version = "8.0"
resources {
resource_preset_id = "s2.micro"
disk_type_id = "network-ssd"
disk_size = 16
}
database {
name = var.dbname
}
user {
name = var.username
password = var.password
permission {
database_name = var.dbname
roles = ["ALL"]
}
}
host {
zone = "ru-central1-a"
subnet_id = yandex_vpc_subnet.subnet_a.id
}
host {
zone = "ru-central1-b"
subnet_id = yandex_vpc_subnet.subnet_b.id
}
host {
zone = "ru-central1-c"
subnet_id = yandex_vpc_subnet.subnet_c.id
}
}
Як можна помітити, кожен із двох кластерів створений досить стійким до відмови за рахунок розміщення в трьох зонах доступності.
Веб-сервери
Здавалося б, можна продовжувати так само, але я зіткнувся зі складністю. До цього я спочатку піднімав MySQL кластер і лише після цього, знаючи його ID, збирав образ диска з потрібною конфігурацією, де вказував хост кластера. Але тепер ми не знаємо ID кластера до запуску Терраформу, у тому числі і на момент збирання образу. Тому довелося вдатися до наступного
Використовуючи сервіс метаданих від Amazon, ми передамо у віртуальну машину деякі параметри, які вона прийме та обробить. Нам необхідно, щоб після запуску машина сходила в метадані за хостом MySQL кластера і username-password, які користувач вказував у файлі Terraform. Незначно змінимо вміст файлу setup.sh
, який запускається під час увімкнення віртуальної машини.
setup.sh:
#!/bin/bash
CLUSTER_URI="$(curl -H 'Metadata-Flavor:Google' http://169.254.169.254/computeMetadata/v1/instance/attributes/mysql_cluster_uri)"
USERNAME="$(curl -H 'Metadata-Flavor:Google' http://169.254.169.254/computeMetadata/v1/instance/attributes/username)"
PASSWORD="$(curl -H 'Metadata-Flavor:Google' http://169.254.169.254/computeMetadata/v1/instance/attributes/password)"
sudo sed -i "s#.*;url =.*#url = mysql://${USERNAME}:${PASSWORD}@${CLUSTER_URI}#" /etc/grafana/grafana.ini
sudo sed -i "s#.*;admin_user =.*#admin_user = ${USERNAME}#" /etc/grafana/grafana.ini
sudo sed -i "s#.*;admin_password =.*#admin_password = ${PASSWORD}#" /etc/grafana/grafana.ini
sudo service grafana-server restart
Інтанс-група та балансер
Зібравши новий образ диска, ми можемо нарешті дописати наш файл для Терраформа.
Вкажемо, що ми хочемо використовувати існуючий образ диска:
data "yandex_compute_image" "grafana_image" {
image_id = var.image_id
}
Тепер створимо інстанс-групу:
resource "yandex_compute_instance_group" "grafana_group" {
name = "grafana-group"
folder_id = var.folder_id
service_account_id = var.service_account_id
instance_template {
platform_id = "standard-v1"
resources {
memory = 1
cores = 1
}
boot_disk {
mode = "READ_WRITE"
initialize_params {
image_id = data.yandex_compute_image.grafana_image.id
size = 4
}
}
network_interface {
network_id = yandex_vpc_network.grafana_network.id
subnet_ids = [yandex_vpc_subnet.subnet_a.id, yandex_vpc_subnet.subnet_b.id, yandex_vpc_subnet.subnet_c.id]
nat = "true"
}
metadata = {
mysql_cluster_uri = "c-${yandex_mdb_mysql_cluster.mysql_cluster.id}.rw.mdb.yandexcloud.net:3306/${var.dbname}"
username = var.username
password = var.password
ssh-keys = "ubuntu:${file("${var.public_key_path}")}"
}
network_settings {
type = "STANDARD"
}
}
scale_policy {
fixed_scale {
size = 6
}
}
allocation_policy {
zones = ["ru-central1-a", "ru-central1-b", "ru-central1-c"]
}
deploy_policy {
max_unavailable = 2
max_creating = 2
max_expansion = 2
max_deleting = 2
}
load_balancer {
target_group_name = "grafana-target-group"
}
}
Варто звернути увагу, як ми передали до метадату cluster_uri
, username
и password
. Саме їхня віртуальна машина при запуску дістане і покладе у конфіг Графани.
Справа за балансером.
resource "yandex_lb_network_load_balancer" "grafana_balancer" {
name = "grafana-balancer"
listener {
name = "grafana-listener"
port = 80
target_port = 3000
external_address_spec {
ip_version = "ipv4"
}
}
attached_target_group {
target_group_id = yandex_compute_instance_group.grafana_group.load_balancer.0.target_group_id
healthcheck {
name = "healthcheck"
tcp_options {
port = 3000
}
}
}
}
Трохи цукру
Залишилася зовсім небагато. Після того, як інфраструктура розгорнеться, доведеться сходити в UI Графани і додати до рук кластер CH (ID якого потрібно ще видобути) як Data Source. Але ID кластера знає Терраформ. Доручимо йому довести справу до ладу.
Додамо нового провайдера — Графану, а як хост підсуне їй айпишник балансера. Всі зміни, які зробить Терраформ на машині, куди визначить його балансер, проростуть у MySQL, а отже, і на всі інші машини.
provider "grafana" {
url = "http://${[for s in yandex_lb_network_load_balancer.grafana_balancer.listener: s.external_address_spec.0.address].0}"
auth = "${var.username}:${var.password}"
}
resource "grafana_data_source" "ch_data_source" {
type = "vertamedia-clickhouse-datasource"
name = "grafana"
url = "https://c-${yandex_mdb_clickhouse_cluster.ch_cluster.id}.rw.mdb.yandexcloud.net:8443"
basic_auth_enabled = "true"
basic_auth_username = var.username
basic_auth_password = var.password
is_default = "true"
access_mode = "proxy"
}
Зачешемо
Виведемо айпішник балансера та хост кластера ClickHouse
output "grafana_balancer_ip_address" {
value = [for s in yandex_lb_network_load_balancer.grafana_balancer.listener: s.external_address_spec.0.address].0
}
output "clickhouse_cluster_host" {
value = "https://c-${yandex_mdb_clickhouse_cluster.ch_cluster.id}.rw.mdb.yandexcloud.net:8443"
}
Можна запускати
Всі! Наш конфігураційний файл готовий і можна, виставивши змінні, сказати Терраформ підняти все, що ми описали вище. Весь процес у мене зайняв близько 15 хвилин.
Наприкінці можна побачити гарне повідомлення:
Apply complete! Resources: 9 added, 0 changed, 0 destroyed.
Outputs:
clickhouse_cluster_host = https://c-c9q14ipa2ngadqsbp2iq.rw.mdb.yandexcloud.net:8443
grafana_balancer_ip_address = 130.193.50.25
А у хмарі будуть видні елементи піднятої інфраструктури:
Підіб'ємо підсумки
Тепер на прикладі Графани кожен з вас вміє розгортати програми з розлогою хмарною архітектурою на платформі Яндекс.Хмара. У цьому можуть допомогти такі корисні інструменти від HashiCorp, як Packer і Terraform. Сподіваюся, комусь ця стаття виявиться корисною 🙂
PS Нижче додаю посилання на репозиторій, у якому можна знайти готові рецепти для Пакера та Терраформа, фрагменти яких я наводив у цій статті.
Джерело: habr.com