Journey to Kubernetes - PARTE 9000 - DigitalOcean

November 11, 2019 0 Comments Terraform, Kubernetes, AWS, Route53, Mysql, helm, digitalocean

Voglio un k8s worry free!

Ciao a tutti!
Come potete aver visto, non ho mai completato le guide per avere il cluster kubernetes su hetzner.
Un pò per mancanza di tempo, un pò perché hetzner non è un sistema chiavi in mano.
Al momento ho ancora tutta la mia infrastruttura li, ma ho intenzione di migrare altrove.

Per questo ho studiato DigitalOcean, che finalmente ha dei servizi maturi per ospitare un cluster kubernetes.

A questo link https://gitlab.com/nutellino-digitalocean-kubernetes-playground/terraform trovate la repository di questo articolo, contenente tutta la ricetta necessaria per creare un cluster kubernetes e i deployment delle app utilizzando una sola tecnologia: terraform

Ormai per me è il de facto standard per gestire infrastrutture. Sopratutto perché è chiaro, funziona bene, ed è versatile. In pochissimi passi sono passato da AWS a DigitalOcean semplicemente utilizzando terraform, senza dovermi studiare comandi CLI.

Vi spiegherò quindi in italiano (nel readme.md sono spiegati in inglese) tutti i passi necessari per deployare il vostro cluster!

Introduzione

Il cluster che andremo a creare sarà formato da più attori di DigitalOcean:

  • Cluster K8s
  • Database managed mysql
  • Load balancer
  • Volumi
  • AWS DNS

Come potete vedere ho messo anche AWS DNS, questo perché terraform rende facile usare risorse multicloud. Inoltre utilizzerò AWS S3 per il salvataggio degli state di terraform.

Prerequisiti necessari nel proprio pc:

  • terraform (v0.11)
  • kubectl command line
  • helm command line
  • mysql command line
  • doctl command line
  • S3 Bucket su AWS S3 per il salvataggio degli state di terraform. Questi stati saranno usati come stati remoti e i loro output saranno usati come variabili nei progetti successivi

Struttura delle directory

  • 1_kubernetes: Crea un cluster kubernetes con l'autoscaling abilitato, custom cert manager CRDs pre-installati e i prerequisiti per helm.
  • 2_database: Crea un database mysql su digital ocean
  • 3_app-databases: Crea utenti e database nel database appena creato
  • 4_helm_system: effettua il deploy di tutti i chart di sistema richiesti per avere un cluster completamente funzionante.
  • 5_dns: Progetto che recupera l'ip del load balancer creato in automatico da nginx-ingress e lo usa per impostare due record A su AWSD
  • 6_helm_apps: Progetto helm per il deploy di due siti wordpress completi all'interno del cluster

Creare l'infrastruttura

In sintesi per creare l'infrastruttura dovremo quindi posizionarci in ogni cartella seguendo l'ordine, configurare variabili e backend e lanciare terraform apply.

Distruggere l'infrastruttura

Seguire l'ordine inverso delle cartelle ed eseguire terraform destroy. Verificare sulla console web di digital ocean che tutto sia stato eliminato.


Iniziamo il deploy

Di seguito faremo passo passo la configurazione e il deploy di ogni progetto.

Dovrete avere l'aws cli configurata nella vostra shell corrente. Vedere qui: https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-configure.html

Avrete bisogno anche del token di digital ocean. Potete crearlo tramite il menu API:

do_menu

Per passare esplicitamente il token nel provider terraform, eseguire l'export della variabile:

export TF_VAR_digital_ocean_token="123123123123123123123123"  

Potete anche salvare la riga nel vostro .bash_profile o chi come me usa zsh .zshrc.


1 kubernetes

Questo progetto si occupa della creazione del cluster kubernetes.
Inoltre verranno creati:

  • Helm namespace
  • cert-manager CRDs
  • doks-metrics-server (digitalocean metrics server per abilitare hpa autoscaling)

 Configuration

Create un file terraform_backend.tf seguendo l'esempio:

terraform {  
  backend "s3" {
    bucket = "terraform.nutellino-playground"
    key    = "terraform-do/kubernetes/tfstate"
    region = "eu-west-1"
  }
}
  • terraform.nutellino-playground è il mio bucket s3 che conterrà il file di stato
  • terraform-do/kubernetes/tfstate è il path della risorsa
  • eu-west-1 è la regione dove si trova il bucket

Poi potete procedere a configurare le variabili nel vostro file name.auto.tfvars :

Nel mio caso

aws_region = "eu-west-1"  
do_region = "fra1"  
kubeconfig_path = "~/.kube/configdigitalocean"  
namespace = "nut-auriga"  

Avviare la creazione

Lanciamo quindi i due comandi:

terraform init  
terraform apply  

Una volta completato, possiamo cercare il cluster nella nostra cloud console:

after_kubernetes

Avremo inoltre il file kubeconfig salvato nel pc, nel mio esempio ~/.kube/configdigitalocean

Possiamo quindi attivare il contesto di kubectl con il comando:

export KUBECONFIG=/Users/samuelechiocca/.kube/configdigitalocean  

2 database

In questo progetto verrà creato un database mysql managed.

Ci saranno anche degli outputs (configurati in outputs.tf) che serviranno nei progetti successivi.

 Configuration

Create un file terraform_backend.tf seguendo l'esempio:

OBBLIGATORIO cambiare la key del backend!

terraform {  
  backend "s3" {
    bucket = "terraform.nutellino-playground"
    key    = "terraform-do/database/tfstate"
    region = "eu-west-1"
  }
}

Poi potete procedere a configurare le variabili nel vostro file name.auto.tfvars :

Nel mio caso

aws_region = "eu-west-1"  
do_region = "fra1"  
namespace = "nut-auriga"  

Avviare la creazione

Lanciamo quindi i due comandi:

terraform init  
terraform apply  

Cerchiamo il nostro database su digitalocean:

after_database


3 app-database

Questo progetto serve per creare utenti e database all'interno del nostro database mysql. Siccome sono pigro non ho intenzione di sporcarmi le mani collegandomi al database e lanciando le query. Preferisco sia terraform a pensarci.

Create un file terraform_backend.tf seguendo l'esempio:

OBBLIGATORIO cambiare la key del backend!

terraform {  
  backend "s3" {
    bucket = "terraform.nutellino-playground"
    key    = "terraform-do/app-databases/tfstate"
    region = "eu-west-1"
  }
}

Create inoltre il file remote_states.tf con i riferimenti allo stato del progetto 2_database

data "terraform_remote_state" "database" {  
  backend = "s3"
  config {
    bucket = "terraform.nutellino-playground"
    key    = "terraform-do/database/tfstate"
    region = "eu-west-1"
  }
}

Questo esempio contiene le password non cryptate, nel mondo reale dovrete cambiare la gestione delle password, per evitare che siano commitate in un sistema di version control. Una delle possibilità è git-crypt.

Poi potete procedere a configurare le variabili nel vostro file name.auto.tfvars :

Nel mio caso

aws_region = "eu-west-1"  
namespace = "nut-auriga"  
fix = "1"  

fix è un parametro che se cambiato in un apply seguente, triggera il deploy delle null_resource che cambiano l'accesso dell'utente a mysql_native_password.

Avviare la creazione

Lanciamo quindi i due comandi:

terraform init  
terraform apply  

In questo esempio due utenti e due database saranno creati.

Possiamo poi verificare l'esistenza di questi utenti/db in console:

after_db_users


4 helm_system

Possiamo finalmente passare alla parte divertente. Il deploy via Helm chart di tutti i sottosistemi necessari a k8s per gestire ingress, volumi, certificati ssl.

Deployeremo quindi:

Come sempre create un file terraform_backend.tf seguendo l'esempio:

OBBLIGATORIO cambiare la key del backend!

terraform {  
  backend "s3" {
    bucket = "terraform.nutellino-playground"
    key    = "terraform-do/helm-system/tfstate"
    region = "eu-west-1"
  }
}

Poi potete (come sempre) procedere a configurare le variabili nel vostro file name.auto.tfvars :

Nel mio caso

region="eu-west-1"  
kubeconfig_path = "~/.kube/configdigitalocean"  
email_cert_issuer = "samuele@nutellino.it"  

Avviare la creazione

Lanciamo quindi i due comandi:

terraform init  
terraform apply  

Probabilmente dovrete eseguire terraform apply perché a volte l'apiserver risponde un unable to handle request

* helm_release.nfs-provisioner: rpc error: code = Unknown desc = Could not get apiVersions from Kubernetes: unable to retrieve the complete list of server APIs: webhook.cert-manager.io/v1beta1: the server is currently unable to handle the request

Un secondo terraform apply sistemerà i problemi.

Controlliamo in console due cose:

Per prima cosa troveremo il nuovo fiammante load balancer:

after_loadbalancer

e come seconda cosa un volume che è stato attaccato al nostro nfs-provisioner

after_nfs

E con questo abbiamo finito il setup del cluster! Ora possiamo iniziare a deployare al suo interno un paio di wordpress.


5 dns

Questo progetto si occupa di recuperare l'ip del load balancer appena creato e di usarlo nei due record A che andremo a creare. Il dominio che userò sarà tuttorotto.it, e creerò due record collegati:

  • website1.tuttorotto.it
  • website2.tuttorotto.it

Come sempre create un file terraform_backend.tf seguendo l'esempio:

OBBLIGATORIO cambiare la key del backend!

terraform {  
  backend "s3" {
    bucket = "terraform.nutellino-playground"
    key    = "terraform-do/dns/tfstate"
    region = "eu-west-1"
  }
}

Poi potete (come sempre) procedere a configurare le variabili nel vostro file name.auto.tfvars :

Nel mio caso

aws_region = "eu-west-1"  
domain = "tuttorotto.it"  
kubeconfig_path = "~/.kube/configdigitalocean"  

Avviare la creazione

Lanciamo quindi i due comandi:

terraform init  
terraform apply  

In questo modo avremo i nostri due record dns creati e pronti all'uso. Attendete un pò la propagazione e poi procedete allo step successivo.


6 helm_apps

In quest'ultimo progetto, possiamo FINALMENTE fare il deploy dei nostri due siti wordpress! :)

Saranno dei siti con:
* volume su pvc dinamica nfs * database su cluster mysql di digitalocean * certificato SSL da let's encrypt gestito da cert-manager

Come sempre create un file terraform_backend.tf seguendo l'esempio:

OBBLIGATORIO cambiare la key del backend!

terraform {  
  backend "s3" {
    bucket = "terraform.nutellino-playground"
    key    = "terraform-do/helm-apps/tfstate"
    region = "eu-west-1"
  }
}

Create inoltre il file remote_states.tf con i riferimenti allo stato del progetto 3_app-databases per recuperare utenti, password, endpoint, etc.

data "terraform_remote_state" "app-databases" {  
  backend = "s3"
  config {
    bucket = "terraform.nutellino-playground"
    key    = "terraform-do/app-databases/tfstate"
    region = "eu-west-1"
  }
}

Configuriamo il file name.auto.tfvars :

Nel mio caso

aws_region="eu-west-1"  
kubeconfig_path = "~/.kube/configdigitalocean"  

Avviare la creazione

Lanciamo quindi i due comandi:

terraform init  
terraform apply  

Questo deploy creerà inoltre uno scale-up del nostro pool di worker a 2, essendo stati configurati come nodi molto piccoli (1vcpu and 2Gb ram) la cpu richiesta dai due deployment wordpress è troppa per un solo nodo.

Una volta completato il deploy, verifichiamo il funzionamento dei due siti:

after_helm


Conclusioni

Probabilmente deciderò di migrare tutto il mio cluster su digitalocean. Le possibili risorse da utilizzare non sono tante come AWS, ma sono il minimo indispensabile per avere un cluster worry free e con tutte le best practice di k8s!

Samuele Chiocca
Padova, italy Website