Salutare tuturor! Ca parte a activității mele de curs, am cercetat capabilitățile unei astfel de platforme cloud domestice precum
Ce vrei sa primesti?
După lansarea unei mașini virtuale cu un server web, puteți merge la gazda acesteia și puteți obține o interfață de utilizare frumoasă, puteți specifica bazele de date ca surse pentru lucrări ulterioare, puteți crea tablouri de bord și grafice.
Versiunea de bază are un dezavantaj semnificativ - nu este deloc tolerantă la erori. Adică, întreaga funcționalitate a aplicației depinde de viabilitatea unei mașini virtuale. Dacă refuză sau 10 persoane deschid interfața de utilizare în același timp, atunci vor apărea probleme.
Ele pot fi rezolvate simplu: trebuie doar să... implementați multe mașini virtuale identice cu un server web și să le plasați sub un echilibrator L3. Dar nu totul este atât de clar aici. Grafana stochează setările utilizatorului (căi către baze de date, tablouri de bord, grafice etc.) direct pe discul mașinii sale virtuale. Astfel, dacă modificăm unele setări în UI, aceste modificări se vor reflecta doar pe mașina virtuală la care ne-a trimis echilibratorul. Acest lucru va duce la setări inconsecvente pentru aplicația noastră, provocând probleme cu lansarea și utilizarea.
Aici o altă bază de date va veni în ajutor, de exemplu, MySQL sau echivalentul său. Îi spunem lui Grafana că ar trebui să stocheze setările utilizatorului în această bază de date „de rezervă”. Ulterior, va fi suficient să specificați calea către această bază de date o dată pe fiecare mașină și să editați toate celelalte setări ale utilizatorului pe oricare dintre mașinile virtuale; acestea se vor extinde la celelalte.
Iată o diagramă a infrastructurii finale a aplicației:
Să învățăm să ridicăm cu mâinile noastre
MySQL și ClickHouse
Înainte de a implementa o astfel de aplicație cu un clic pe un buton, a fost necesar să învățați cum să gestionați fiecare dintre componentele sale și să le integrați între ele.
Aici ne va ajuta Yandex.Cloud, care oferă echilibrare L3, ClickHouse și MySQL ca servicii gestionate. Utilizatorul trebuie doar să specifice parametrii și să aștepte până când platforma aduce totul în stare de funcționare.
M-am înregistrat, am creat un cloud și un cont de plată. După aceea, am mers în cloud și am configurat clustere MySQL și ClickHouse cu setări minime. Am așteptat până au devenit activi.
De asemenea, trebuie să vă amintiți să creați o bază de date în fiecare cluster și să configurați accesul la aceasta folosind un login și o parolă. Nu voi intra în detalii aici - totul este destul de evident în interfață.
Detaliul neevident a fost că aceste baze de date au multe gazde, care le asigură toleranța la erori. Cu toate acestea, Grafana necesită exact o gazdă pentru fiecare bază de date cu care lucrează. Lectură lungă c-<cluster_id>.rw.mdb.yandexcloud.net
mapat la gazda principală activă curentă a clusterului cu ID-ul corespunzător. Asta îi vom oferi lui Grafana.
server web
Acum depinde de serverul web. Să ridicăm o mașină virtuală obișnuită cu Linux și să configuram manual Grafana pe ea.
Să ne conectăm prin ssh și să instalăm pachetele necesare.
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
După aceea, să rulăm Grafana sub systemctl și să instalăm pluginul pentru lucrul cu ClickHouse (da, nu este furnizat în pachetul de bază).
sudo systemctl start grafana-server
sudo systemctl enable grafana-server
sudo grafana-cli plugins install vertamedia-clickhouse-datasource
Gata, dupa aceea cu o simpla comanda
sudo service grafana-server start
vom porni serverul web. Acum puteți introduce adresa IP externă a mașinii virtuale în browser, puteți specifica portul 3000 și puteți vedea frumoasa interfață Grafana.
Dar nu vă grăbiți, înainte de a configura Grafana, trebuie să vă amintiți să îi spuneți calea către MySQL pentru a stoca setările acolo.
Întreaga configurație a serverului web Grafana se află în fișier /etc/grafana/grafana.ini
. Linia necesară arată astfel:
;url =
Am setat gazda la clusterul MySQL. Același fișier conține login și parola pentru accesarea Grafana din imaginea de mai sus, care implicit sunt ambele egale admin
.
Puteți folosi comenzile 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
Este timpul să reporniți serverul web!
sudo service grafana-server restart
Acum, în UI Grafana, vom specifica ClickHouse ca sursă de date.
Am reușit să realizez o configurație de lucru cu următoarele setări:
Am specificat ca URL https://c-<cluster_id>.rw.mdb.yandexcloud.net:8443
Toate! Avem o mașină virtuală funcțională cu un server web conectat la CH și MySQL. Puteți încărca deja setul de date în ClickHouse și puteți crea tablouri de bord. Cu toate acestea, încă nu ne-am atins obiectivul și nu am implementat o infrastructură cu drepturi depline.
Ambalator
Yandex.Cloud vă permite să creați o imagine de disc a unei mașini virtuale existente și, pe baza acesteia, orice număr de mașini identice între ele. Acesta este exact ceea ce vom folosi. Pentru a asambla în mod convenabil imaginea, luați unealta
Fișierul nostru json va consta din două blocuri: constructori și furnizori. Primul bloc descrie parametrii imaginii în sine ca entitate, iar al doilea bloc descrie instrucțiuni pentru umplerea acesteia cu conținutul necesar.
Constructori
{
"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"
}
],
...
}
În acest șablon, trebuie să setați identificatorul secțiunii din cloud în care doriți să creați imaginea, precum și calea către fișierul cu cheile din contul de serviciu creat anterior în această secțiune. Puteți citi mai multe despre crearea conturilor de serviciu și a cheilor sub forma unui fișier în secțiunea corespunzătoare
Această configurație spune că imaginea de disc va fi construită pe baza platformei ubuntu-1804-lts
, plasat în secțiunea de utilizator corespunzătoare din familia de imagini GRAFANA
sub nume grafana-{{timestamp}}
.
Furnizorii
Acum vine partea mai interesantă a configurației. Acesta va descrie secvența de acțiuni care vor trebui efectuate pe mașina virtuală înainte de a-și îngheța starea într-o imagine de disc.
{
...,
"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"
]
}
]
}
Aici toate acțiunile sunt împărțite în 3 etape. În prima etapă, se execută un script simplu care creează un director auxiliar.
prepare-ctg.sh:
#!/bin/bash
sudo mkdir -p /opt/grafana
sudo chown -R ubuntu:ubuntu /opt/grafana
În etapa următoare, plasăm un script în acest director, care va trebui să fie lansat imediat după pornirea mașinii virtuale. Acest script va pune variabilele utilizator care trebuie înregistrate în configurația Grafana și va reporni serverul web.
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
După aceasta, mai sunt 3 lucruri de făcut:
1) instalați pachete
2) rulați Grafana sub systemctl și instalați pluginul ClickHouse
3) puneți scriptul setup.sh în coada de lansare imediat după pornirea mașinii virtuale.
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;
Acum tot ce rămâne este să rulați Packer și să obțineți imaginea de ieșire plasată în secțiunea specificată. Când creați o mașină virtuală, o puteți selecta ca disc de pornire și după lansare veți primi un server web Grafana gata făcut.
Grup de instanțe și echilibrator
Odată ce avem o imagine de disc care ne permite să creăm multe servere web Grafana identice, putem crea un grup de instanțe. Pe platforma Yandex.Cloud, acest termen se referă la unirea mașinilor virtuale care au aceleași caracteristici. La crearea unui grup de instanțe, prototipul tuturor mașinilor din acest grup este configurat și apoi caracteristicile grupului însuși (de exemplu, numărul minim și maxim de mașini active). Dacă numărul curent nu îndeplinește aceste criterii, atunci grupul de instanțe în sine va elimina mașinile inutile sau va crea altele noi în propria imagine.
Ca parte a sarcinii noastre, vom crea un grup de instanțe de servere web care vor fi generate din imaginea de disc creată anterior.
Ceea ce este cu adevărat remarcabil este configurarea grupului de ultimă instanță. Grupul țintă în integrare cu Load Balancer vă va ajuta să configurați un echilibrator L3 deasupra mașinilor virtuale ale acestui grup făcând clic pe câteva butoane.
Când am configurat echilibrul, am implementat două puncte importante:
- Am făcut ca echilibrerul să accepte traficul utilizatorului pe portul 80 și să îl redirecționez către portul 3000 al mașinilor virtuale, exact acolo unde locuiește Grafana.
- Am configurat verificarea viabilității mașinilor prin ping-ul la portul 3000.
Mini rezumat
În cele din urmă, am reușit să implementăm manual infrastructura de aplicații dorită, iar acum avem un serviciu Grafana foarte rezistent. Trebuie doar să cunoașteți adresa IP a echilibrantului ca punct de intrare în aplicație și gazda clusterului ClickHouse pentru a încărca setul de date în acesta.
S-ar părea o victorie? Da, victorie. Dar ceva încă mă încurcă. Întregul proces de mai sus necesită o mulțime de pași manuali și nu este deloc scalabil; aș dori să îl automatizez dacă este posibil. Acesta este ceea ce va fi dedicată următoarea secțiune.
Integrarea terraformei
Vom folosi din nou un instrument de la HashiCorp numit
Toate lucrările cu Terraform se rezumă la scrierea unui fișier de configurare (*.tf
) și crearea infrastructurii pe baza acesteia.
variabile
La începutul fișierului, vom include variabile care determină unde și cum va fi implementată viitoarea infrastructură.
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>"
}
Întregul proces de implementare a aplicației se va reduce la construirea unei imagini de disc și setarea acestor variabile. Permiteți-mi să explic de ce sunt responsabili:
oauth_token — un token pentru accesarea cloud-ului. Poate fi obținut prin
cloud_id — identificatorul cloud unde vom implementa aplicația
folder_id — identificatorul secțiunii în care vom implementa aplicația
service_account_id — identificatorul contului de serviciu în secțiunea corespunzătoare din cloud.
imagine_id — identificatorul imaginii de disc obținute folosind Packer
nume de utilizator и parola — nume de utilizator și parolă pentru a accesa atât bazele de date, cât și serverul web Grafana
dbname — numele bazei de date în cadrul clusterelor CH și MySQL
cale_cheie_publică — calea către fișierul cu cheia ssh publică, pe care o puteți folosi pentru a vă conecta sub nume ubuntu
la mașini virtuale cu servere web
Configurarea furnizorului
Acum trebuie să configurați furnizorul Terraform - în cazul nostru, Yandex:
provider "yandex" {
token = var.oauth_token
cloud_id = var.cloud_id
folder_id = var.folder_id
zone = "ru-central1-a"
}
Veți observa că aici folosim variabilele definite mai sus.
Rețea și clustere
Acum vom crea o rețea în care elementele infrastructurii noastre vor comunica, trei subrețele (una în fiecare regiune) și vor ridica clustere CH și 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
}
}
După cum puteți vedea, fiecare dintre cele două clustere este creat destul de tolerant la erori, fiind plasat în trei zone de disponibilitate.
Servere web
S-ar părea că am putea continua în același spirit, dar am avut dificultăți. Înainte de aceasta, am ridicat mai întâi un cluster MySQL și abia după aceea, cunoscându-i ID-ul, am colectat o imagine de disc cu configurația necesară, unde am specificat gazda clusterului. Dar acum nu cunoaștem ID-ul clusterului înainte de a lansa Terraform, inclusiv în momentul construirii imaginii. Prin urmare, a trebuit să recurg la următoarele
Folosind serviciul de metadate al Amazon, vom transmite niște parametri mașinii virtuale, pe care o va accepta și procesa. Avem nevoie ca mașina să meargă la metadatele din spatele gazdei cluster MySQL și numele de utilizator-parola, pe care utilizatorul le-a specificat în fișierul Terraform, după pornire. Să modificăm ușor conținutul fișierului setup.sh
, care rulează când mașina virtuală este pornită.
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
Grup de intanță și echilibrator
După ce am reconstruit o nouă imagine de disc, putem adăuga în sfârșit fișierul nostru pentru Terraform.
Să indicăm că vrem să folosim o imagine de disc existentă:
data "yandex_compute_image" "grafana_image" {
image_id = var.image_id
}
Acum să creăm un grup de instanțe:
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"
}
}
Merită să acordăm atenție modului în care l-am transmis în metadate cluster_uri
, username
и password
. Acestea sunt pe care mașina virtuală le va scoate la pornire și le va introduce în configurația Grafana.
Depinde de echilibrator.
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
}
}
}
}
Puțin zahăr
A mai rămas doar puțin. După ce infrastructura este implementată, va trebui să accesați interfața de utilizare Grafana și să adăugați manual clusterul CH (al cărui ID încă trebuie să fie obținut) ca sursă de date. Dar Terraform cunoaște ID-ul clusterului. Să-i încredințăm să ducă problema la bun sfârșit.
Să adăugăm un nou furnizor - Grafana și să-i dăm adresa IP a echilibrantului ca gazdă. Toate modificările pe care Terraform le face pe mașina în care echilibrul său o stabilește vor crește în MySQL și, prin urmare, pe toate celelalte mașini.
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"
}
Să ne pieptănăm părul
Să afișăm adresa IP a echilibratorului și gazda clusterului 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"
}
Poți să alergi
Toate! Fișierul nostru de configurare este gata și putem, prin setarea variabilelor, să îi spunem Terraform să ridice tot ce am descris mai sus. Întregul proces mi-a luat aproximativ 15 minute.
La final puteți vedea un mesaj frumos:
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
Și în cloud vor fi vizibile elemente ale infrastructurii ridicate:
Însumați
Acum, folosind Grafana ca exemplu, fiecare dintre voi poate implementa aplicații cu o arhitectură cloud extinsă pe platforma Yandex.Cloud. Instrumentele utile de la HashiCorp, cum ar fi Packer și Terraform, vă pot ajuta în acest sens. Sper că cineva va găsi acest articol util :)
PS Mai jos voi atașa un link către depozitul unde puteți găsi rețete gata făcute pentru Packer și Terraform, fragmente din care am furnizat în acest articol.
Sursa: www.habr.com