Ciao a tutti! Come parte del mio corso, ho studiato le capacità di una piattaforma cloud domestica come
Cosa vuoi ricevere?
Dopo aver avviato una macchina virtuale con un server Web, puoi andare al suo host e ottenere una bellissima interfaccia utente, specificare i database come fonti per ulteriore lavoro, creare dashboard e grafici.
La versione base ha uno svantaggio significativo: non è affatto tollerante ai guasti. Cioè, l'intera funzionalità dell'applicazione dipende dalla fattibilità di una macchina virtuale. Se rifiuta o 10 persone aprono l'interfaccia utente contemporaneamente, sorgeranno problemi.
Possono essere risolti semplicemente: devi solo... distribuire molte macchine virtuali identiche con un server web e posizionarle sotto un bilanciatore L3. Ma qui non tutto è così chiaro. Grafana memorizza le impostazioni dell'utente (percorsi di database, dashboard, grafici, ecc.) direttamente sul disco della sua macchina virtuale. Pertanto, se modifichiamo alcune impostazioni nell'interfaccia utente, queste modifiche si rifletteranno solo sulla macchina virtuale in cui ci ha inviato il bilanciatore. Ciò porterà a impostazioni incoerenti per la nostra applicazione, causando problemi con l'avvio e l'utilizzo.
Qui verrà in soccorso un altro database, ad esempio MySQL o il suo equivalente. Diciamo a Grafana che dovrebbe memorizzare le impostazioni dell'utente in questo database "di riserva". Successivamente sarà sufficiente specificare il percorso di questo database una volta su ciascuna macchina e modificare tutte le altre impostazioni utente su qualsiasi macchina virtuale; queste si estenderanno alle altre.
Ecco un diagramma dell'infrastruttura dell'applicazione finale:
Impariamo a sollevare con le mani
MySQL e ClickHouse
Prima di implementare un'applicazione del genere con un semplice clic, era necessario imparare a gestire ciascuno dei suoi componenti e ad integrarli tra loro.
Qui ci aiuterà Yandex.Cloud, che fornisce bilanciatori L3, ClickHouse e MySQL come servizi gestiti. L'utente deve solo specificare i parametri e attendere che la piattaforma metta tutto in funzione.
Mi sono registrato, ho creato un cloud e un conto di pagamento. Successivamente sono passato al cloud e ho configurato i cluster MySQL e ClickHouse con impostazioni minime. Ho aspettato finché non sono diventati attivi.
È inoltre necessario ricordarsi di creare un database in ciascun cluster e configurarne l'accesso utilizzando login e password. Non entrerò nei dettagli qui: tutto è abbastanza ovvio nell'interfaccia.
Il dettaglio non ovvio è che questi database hanno molti host, che ne garantiscono la tolleranza agli errori. Tuttavia, Grafana richiede esattamente un host per ciascun database con cui funziona. Lettura lunga c-<cluster_id>.rw.mdb.yandexcloud.net
mappato all'host master attivo corrente del cluster con l'ID corrispondente. Questo è quello che daremo a Grafana.
server web
Ora tocca al server web. Creiamo una normale macchina virtuale con Linux e configuriamo manualmente Grafana su di essa.
Colleghiamoci tramite ssh e installiamo i pacchetti necessari.
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
Successivamente, eseguiamo Grafana sotto systemctl e installiamo il plugin per lavorare con ClickHouse (sì, non è incluso nel pacchetto base).
sudo systemctl start grafana-server
sudo systemctl enable grafana-server
sudo grafana-cli plugins install vertamedia-clickhouse-datasource
Il gioco è fatto, dopodiché con un semplice comando
sudo service grafana-server start
avvieremo il server web. Ora puoi inserire l'indirizzo IP esterno della macchina virtuale nel browser, specificare la porta 3000 e vedere la bellissima interfaccia utente di Grafana.
Ma non affrettarti, prima di configurare Grafana, devi ricordarti di indicargli il percorso di MySQL per memorizzare lì le impostazioni.
L'intera configurazione del server web Grafana è nel file /etc/grafana/grafana.ini
. La riga richiesta è simile alla seguente:
;url =
Impostiamo l'host sul cluster MySQL. Lo stesso file contiene il login e la password per accedere a Grafana nell'immagine sopra, che per impostazione predefinita sono entrambi uguali admin
.
Puoi usare i comandi 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
È ora di riavviare il server web!
sudo service grafana-server restart
Ora nell'interfaccia utente di Grafana specificheremo ClickHouse come DataSource.
Sono riuscito a ottenere una configurazione funzionante con le seguenti impostazioni:
Ho specificato come URL https://c-<cluster_id>.rw.mdb.yandexcloud.net:8443
Tutto! Abbiamo una macchina virtuale funzionante con un server web connesso a CH e MySQL. Puoi già caricare il set di dati su ClickHouse e creare dashboard. Tuttavia, non abbiamo ancora raggiunto il nostro obiettivo e non abbiamo implementato un’infrastruttura completa.
Imballatore
Yandex.Cloud ti consente di creare un'immagine del disco di una macchina virtuale esistente e, sulla base, di un numero qualsiasi di macchine identiche tra loro. Questo è esattamente ciò che useremo. Per assemblare comodamente l'immagine, prendi lo strumento
Il nostro file json sarà composto da due blocchi: builder e provisioner. Il primo blocco descrive i parametri dell'immagine stessa come entità, mentre il secondo blocco descrive le istruzioni per riempirla con il contenuto necessario.
Costruttori
{
"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"
}
],
...
}
In questo modello è necessario impostare l'identificatore della sezione nel cloud in cui si desidera creare l'immagine, nonché il percorso del file con le chiavi dell'account di servizio precedentemente creato in questa sezione. Puoi leggere ulteriori informazioni sulla creazione di account di servizio e chiavi sotto forma di file nella sezione corrispondente
Questa configurazione dice che l'immagine del disco verrà creata in base alla piattaforma ubuntu-1804-lts
, collocato nell'apposita sezione utente nella famiglia di immagini GRAFANA
sotto il nome grafana-{{timestamp}}
.
Fornitori
Ora arriva la parte più interessante della configurazione. Descriverà la sequenza di azioni che dovranno essere eseguite sulla macchina virtuale prima di congelarne lo stato in un'immagine disco.
{
...,
"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"
]
}
]
}
Qui tutte le azioni sono divise in 3 fasi. Nella prima fase viene eseguito un semplice script che crea una directory ausiliaria.
preparare-ctg.sh:
#!/bin/bash
sudo mkdir -p /opt/grafana
sudo chown -R ubuntu:ubuntu /opt/grafana
Nella fase successiva, in questa directory inseriamo uno script, che dovrà essere avviato immediatamente dopo l'avvio della macchina virtuale. Questo script inserirà le variabili utente che devono essere registrate nella configurazione di Grafana e riavvierà il server 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
Fatto questo restano 3 cose da fare:
1) installa i pacchetti
2) esegui Grafana sotto systemctl e installa il plugin ClickHouse
3) inserire lo script setup.sh nella coda di lancio subito dopo aver acceso la macchina virtuale.
pacchetti-installazione.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
esegui-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;
Ora non resta che eseguire Packer e ottenere l'immagine di output posizionata nella sezione specificata. Quando crei una macchina virtuale, puoi selezionarla come disco di avvio e dopo il lancio riceverai un server web Grafana già pronto.
Gruppo di istanze e bilanciatore
Una volta che abbiamo un'immagine del disco che ci consente di creare molti server web Grafana identici, possiamo creare un gruppo di istanze. Sulla piattaforma Yandex.Cloud questo termine si riferisce all'unione di macchine virtuali che hanno le stesse caratteristiche. Quando si crea un gruppo di istanze, viene configurato il prototipo di tutte le macchine di questo gruppo, e poi le caratteristiche del gruppo stesso (ad esempio, il numero minimo e massimo di macchine attive). Se il numero attuale non soddisfa questi criteri, il gruppo di istanze stesso rimuoverà le macchine non necessarie o ne creerà di nuove a propria immagine.
Come parte del nostro compito, creeremo un gruppo di istanze di server web che verranno generati dall'immagine del disco creata in precedenza.
Ciò che è veramente notevole è l'ultima configurazione del gruppo di istanze. Il gruppo target in integrazione con Load Balancer ti aiuterà a configurare un bilanciatore L3 sulle macchine virtuali di questo gruppo facendo clic su un paio di pulsanti.
Durante la configurazione del bilanciatore, ho implementato due punti importanti:
- Ho fatto in modo che il bilanciatore accettasse il traffico degli utenti sulla porta 80 e lo reindirizzasse sulla porta 3000 delle macchine virtuali, esattamente dove vive Grafana.
- Ho impostato il controllo della vitalità delle macchine eseguendo il ping sulla porta 3000.
Mini riassunto
Alla fine, siamo riusciti a implementare manualmente l'infrastruttura applicativa desiderata e ora disponiamo di un servizio Grafana altamente resiliente. È sufficiente conoscere l'indirizzo IP del bilanciatore come punto di ingresso dell'applicazione e host del cluster ClickHouse per caricare il set di dati al suo interno.
Sembrerebbe una vittoria? Sì, la vittoria. Ma qualcosa ancora mi confonde. L'intero processo sopra descritto richiede molti passaggi manuali e non è affatto scalabile; vorrei automatizzarlo se possibile. A questo sarà dedicata la prossima sezione.
Integrazione di Terraform
Utilizzeremo nuovamente uno strumento di HashiCorp chiamato
Tutto il lavoro con Terraform si riduce alla scrittura di un file di configurazione (*.tf
) e la creazione di infrastrutture basate su di esso.
variabili
All'inizio del file includeremo variabili che determinano dove e come verrà implementata la futura infrastruttura.
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>"
}
L'intero processo di distribuzione dell'applicazione si ridurrà alla creazione di un'immagine del disco e all'impostazione di queste variabili. Lasciatemi spiegare di cosa sono responsabili:
oauth_token — un token per accedere al cloud. Può essere ottenuto da
cloud_id — identificatore del cloud in cui distribuiremo l'applicazione
ID_cartella — identificatore della sezione in cui distribuiremo l'applicazione
service_account_id — identificatore dell'account di servizio nella sezione corrispondente del cloud.
ID_immagine — identificatore dell'immagine disco ottenuta utilizzando Packer
nome utente и parola d'ordine — nome utente e password per accedere a entrambi i database e al server web Grafana
nomedb — nome del database all'interno dei cluster CH e MySQL
percorso_chiave_pubblica - percorso del file con la tua chiave ssh pubblica, che puoi utilizzare per connetterti sotto il nome ubuntu
alle macchine virtuali con server web
Configurazione del fornitore
Ora devi configurare il provider Terraform, nel nostro caso Yandex:
provider "yandex" {
token = var.oauth_token
cloud_id = var.cloud_id
folder_id = var.folder_id
zone = "ru-central1-a"
}
Noterai che qui stiamo utilizzando le variabili definite sopra.
Rete e cluster
Ora creeremo una rete in cui comunicheranno gli elementi della nostra infrastruttura, tre sottoreti (una in ciascuna regione) e genereremo cluster CH e 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
}
}
Come puoi vedere, ciascuno dei due cluster è abbastanza tollerante agli errori essendo posizionato in tre zone di disponibilità.
Server Web
Sembrerebbe che potremmo continuare con lo stesso spirito, ma ho incontrato difficoltà. Prima di ciò, ho prima creato un cluster MySQL e solo dopo, conoscendone l'ID, ho raccolto un'immagine del disco con la configurazione richiesta, dove ho specificato l'host del cluster. Ma ora non conosciamo l'ID del cluster prima di avviare Terraform, nemmeno al momento della creazione dell'immagine. Pertanto, ho dovuto ricorrere a quanto segue
Utilizzando il servizio di metadati di Amazon, passeremo alcuni parametri alla macchina virtuale, che accetterà ed elaborerà. Abbiamo bisogno che la macchina acceda ai metadati dietro l'host del cluster MySQL e al nome utente-password, che l'utente ha specificato nel file Terraform, dopo l'avvio. Modifichiamo leggermente il contenuto del file setup.sh
, che viene eseguito quando la macchina virtuale è accesa.
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
Gruppo di istanze e bilanciatore
Dopo aver ricostruito una nuova immagine del disco, possiamo finalmente aggiungere il nostro file per Terraform.
Indichiamo che vogliamo utilizzare un'immagine disco esistente:
data "yandex_compute_image" "grafana_image" {
image_id = var.image_id
}
Ora creiamo un gruppo di istanze:
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"
}
}
Vale la pena prestare attenzione a come lo abbiamo passato ai metadati cluster_uri
, username
и password
. Sono questi che la macchina virtuale eliminerà all'avvio e inserirà nella configurazione di Grafana.
Dipende dal bilanciatore.
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
}
}
}
}
Un po' di zucchero
Ne è rimasto solo un po'. Dopo aver distribuito l'infrastruttura, dovrai andare all'interfaccia utente di Grafana e aggiungere manualmente il cluster CH (il cui ID deve ancora essere ottenuto) come origine dati. Ma Terraform conosce l'ID del cluster. Affidiamo a lui il compito di portare a compimento la questione.
Aggiungiamo un nuovo provider: Grafana e diamogli l'indirizzo IP del bilanciatore come host. Tutte le modifiche apportate da Terraform sulla macchina determinata dal suo sistema di bilanciamento aumenteranno in MySQL e quindi su tutte le altre macchine.
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"
}
Pettiniamoci i capelli
Visualizziamo l'indirizzo IP del bilanciatore e l'host del cluster 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"
}
Puo correre
Tutto! Il nostro file di configurazione è pronto e possiamo, impostando le variabili, dire a Terraform di sollevare tutto ciò che abbiamo descritto sopra. L'intero processo mi ha richiesto circa 15 minuti.
Alla fine puoi vedere un bellissimo messaggio:
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
E nel cloud saranno visibili gli elementi dell'infrastruttura sopraelevata:
Riassumere
Ora, usando Grafana come esempio, ognuno di voi può distribuire applicazioni con un'architettura cloud tentacolare sulla piattaforma Yandex.Cloud. Strumenti utili di HashiCorp come Packer e Terraform possono aiutarti in questo. Spero che qualcuno trovi utile questo articolo :)
PS Di seguito allegherò un collegamento al repository dove potrai trovare ricette già pronte per Packer e Terraform, frammenti delle quali ho fornito in questo articolo.
Fonte: habr.com