Здравейте всички! Като част от моята курсова работа проучвах възможностите на такава вътрешна облачна платформа като
Какво искате да получите?
След като стартирате виртуална машина с уеб сървър, можете да отидете на нейния хост и да получите красив потребителски интерфейс, да посочите бази данни като източници за по-нататъшна работа, да създадете табла за управление и графики.
Основната версия има един съществен недостатък - тя изобщо не е устойчива на грешки. Тоест, цялата производителност на приложението зависи от жизнеспособността на една виртуална машина. Ако тя откаже или ако 10 души отворят потребителския интерфейс едновременно, тогава ще възникнат проблеми.
Те се решават просто: просто трябва да разположите много идентични виртуални машини с уеб сървър и да ги поставите под L3 балансьор. Но тук не всичко е толкова ясно. Grafana съхранява потребителските настройки (пътища към бази данни, табла, графики и др.) директно на диска на своята виртуална машина. По този начин, ако промените някои настройки в потребителския интерфейс, тогава тези промени ще бъдат показани само на виртуалната машина, където балансьорът ни изпрати. Това ще доведе до непоследователни настройки за нашето приложение, проблеми при стартирането и използването.
Тук друга база данни ще дойде на помощ, например MySQL или негов еквивалент. Казваме на Grafana, че трябва да съхранява потребителските настройки в тази "резервна" база данни. След това ще бъде достатъчно да посочите пътя до тази база данни на всяка машина веднъж и да редактирате всички други потребителски настройки на която и да е от виртуалните машини, те ще растат на останалите.
Ето диаграма на крайната инфраструктура на приложението:
Научете се да повдигате с ръце
MySQL и ClickHouse
Преди да внедрите такова приложение с натискането на един бутон, беше необходимо да се научите как да повдигате всеки от неговите компоненти с дръжки и да ги интегрирате един с друг.
Тук ще ни помогне Yandex.Cloud, който предоставя L3 балансьори, ClickHouse и MySQL като управлявани услуги. Потребителят трябва само да посочи параметрите и да изчака, докато платформата приведе всичко в работно състояние.
Регистрирах се, създадох облак и сметка за таксуване за себе си. След това отидох в облака и повдигнах MySQL и ClickHouse клъстери с минимални настройки. Изчакайте, докато станат активни.
Също така трябва да запомните да създадете база данни във всеки клъстер и да конфигурирате достъпа до нея чрез потребителско име и парола. Тук няма да навлизам в подробности - всичко е съвсем очевидно в интерфейса.
Неочевидната подробност беше, че тези бази данни имат много хостове, които осигуряват тяхната толерантност към грешки. Grafana обаче изисква точно един хост за всяка база данни, с която работи. Дълго четене c-<cluster_id>.rw.mdb.yandexcloud.net
Картира към текущия активен главен хост на клъстера със съответния идентификатор. Това ще дадем на Графана.
уеб сървър
Сега всичко зависи от уеб сървъра. Нека създадем обикновена виртуална машина с Linux и да настроим Grafana върху нея с нашите ръце.
Нека се свържем чрез 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
След това ще стартираме Grafana под 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 grafana.
Но не бързайте, преди да настроите Grafana, не забравяйте да посочите пътя до MySQL, за да съхранява настройките там.
Цялата конфигурация на уеб сървъра Grafana е във файла /etc/grafana/grafana.ini
. Необходимият ред изглежда така:
;url =
Ние излагаме хоста на MySQL клъстера. Същият файл съдържа потребителското име и паролата за достъп до Grafana на снимката по-горе, които са еднакви по подразбиране 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
Сега в потребителския интерфейс на Grafana ще посочим ClickHouse като DataSource.
Успях да постигна работеща конфигурация със следните настройки:
Дадох като URL https://c-<cluster_id>.rw.mdb.yandexcloud.net:8443
Всичко! Имаме една работеща виртуална машина с уеб сървър, свързан към CH и MySQL. Вече можете да качите набора от данни в ClickHouse и да създадете табла за управление. Все още обаче не сме постигнали целта си и не сме изградили пълноценна инфраструктура.
Опаковчик
Yandex.Cloud ви позволява да създадете дисково изображение на съществуваща виртуална машина и въз основа на него можете да създадете колкото искате идентични машини. Точно това ще използваме. За да сглобите удобно изображението, вземете инструмента
Нашият json файл ще се състои от два блока: builders и providers. Първият блок описва параметрите на самото изображение като цяло, а вторият описва инструкциите за попълването му с необходимото съдържание.
Строителите
{
"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 етапа. На първия етап се изпълнява прост скрипт, който създава спомагателна директория.
подготви-ctg.sh:
#!/bin/bash
sudo mkdir -p /opt/grafana
sudo chown -R ubuntu:ubuntu /opt/grafana
На следващия етап поставяме скрипт в тази директория, който ще трябва да се стартира веднага след стартиране на виртуалната машина. Този скрипт ще зададе потребителските променливи в конфигурацията на 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) стартирайте Grafana под 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 и да получите изходното изображение, поставено в посочения дял. Когато създавате виртуална машина, можете да я изберете като диск за зареждане и след стартиране да получите готов уеб сървър на Grafana.
Група екземпляри и балансьор
След като има дисково изображение, което ви позволява да създавате много идентични уеб сървъри на Grafana, можем да създадем група екземпляри. В платформата Yandex.Cloud този термин се отнася до обединението на виртуални машини, които имат еднакви характеристики. При създаването на група екземпляри се конфигурира прототипът на всички машини в тази група и след това характеристиките на самата група (например минималния и максималния брой активни машини). Ако текущият брой не отговаря на този критерий, тогава самата група екземпляри ще премахне ненужните машини или ще създаде нови по образ и подобие.
Като част от нашата задача ще създадем група екземпляри от уеб сървъри, които ще бъдат създадени от предварително създаденото дисково изображение.
Настройката на последната инстанционна група е наистина забележителна. Целевата група в интеграция с Load Balancer ще ви помогне да настроите L3 балансьор върху виртуалните машини от тази група, като натиснете няколко бутона.
Когато настройвах балансира, внедрих две важни точки:
- Направих го така, че балансьорът да приема потребителски трафик на порт 80 и да го пренасочва към порт 3000 на виртуални машини, точно където живее Grafana.
- Настройте проверки на изправността на машината, като ги пингвате на порт 3000.
мини-общо
Най-накрая успяхме ръчно да внедрим желаната инфраструктура на приложенията и сега имаме изключително устойчива услуга Grafana. Необходимо е само да знаете IP адреса на балансира като входна точка към приложението и хоста на клъстера ClickHouse, за да заредите набора от данни в него.
Изглежда ли победа? Да, победа. Но все нещо ме притеснява. Целият процес по-горе изисква много ръчни действия и изобщо не се мащабира, искам да го автоматизирам, ако е възможно. Това ще бъде фокусът на следващия раздел.
Terraform интеграция
Отново ще използваме инструмент от HashiCorp, наречен
Цялата работа с Terraform се свежда до писане на конфигурационен файл (*.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
потребителско име и парола - потребителско име и парола за достъп до двете бази данни и уеб сървъра на Grafana
dbname - име на база данни в CH и MySQL клъстери
публичен_ключ_път - пътя до файла с вашия публичен ssh ключ, който може да се използва за свързване под името ubuntu
към виртуални машини с уеб сървъри
Настройка на доставчика
Сега трябва да конфигурирате доставчика на Terraform - в нашия случай Yandex:
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, събрах дисково изображение с желаната конфигурация, където посочих хоста на клъстера. Но сега не знаем идентификатора на клъстера преди стартирането на Terraform, включително по време на изграждането на изображението. Така че трябваше да прибегна до следното
Използвайки услугата за метаданни от Amazon, ние ще предадем някои параметри на виртуалната машина, които тя ще приеме и обработи. Нуждаем се от машината, за да отиде до метаданните за MySQL хоста на клъстера и за потребителското име-парола, които потребителят е посочил във файла 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
Intance група и балансьор
След като възстановихме новото дисково изображение, най-накрая можем да добавим нашия файл за Terraform.
Посочваме, че искаме да използваме съществуващо дисково изображение:
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
. Тяхната виртуална машина ще го получи при стартиране и ще го постави в конфигурацията на Grafana.
Става дума за балансьора.
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
}
}
}
}
Малко захар
Оставаше много малко. След като инфраструктурата бъде разгърната, ще трябва да отидете в потребителския интерфейс на Grafana и ръчно да добавите CH клъстера (чийто идентификатор все още трябва да получите) като източник на данни. Но Terraform знае идентификатора на клъстера. Нека го накараме да довърши работата.
Да добавим нов доставчик - Grafana, и да мушнем IP-то на балансира като хост. Всички промени, които Terraform прави на машината, където нейният балансьор определя, ще растат в 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"
}
Да се срешем
Дисплей балансиращ IP адрес и 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"
}
Може да бяга
Всичко! Нашият конфигурационен файл е готов и можем, като зададем променливи, да кажем на Terraform да повдигне всичко, което описахме по-горе. Целият процес ми отне около 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
И в облака ще се виждат елементи от повдигнатата инфраструктура:
Сумирайте
Сега, използвайки примера на Grafana, всеки от вас може да внедри приложения с обширна облачна архитектура на платформата Yandex.Cloud. Полезни инструменти от HashiCorp като Packer и Terraform могат да ви помогнат с това. Надявам се някой да намери тази статия за полезна 🙂
PS По-долу ще прикача връзка към хранилището, където можете да намерите готови рецепти за Packer и Terraform, фрагменти от които цитирах в тази статия.
Източник: www.habr.com