Sei sulla pagina 1di 138

KUBERNETES

Track 1

Abstract
Building Advanced Kubernetes track

Lo Castro Nicola
[Email address]
Contents
1. Kubernetes Manifests, Declaratives, & Imperatives................................................................................................3
1.1. Introduzione........................................................................................................................................................3
1.2. Key Features........................................................................................................................................................4
1.3. I templates YAML................................................................................................................................................5
1.4. Installare docker desktop su windows.................................................................................................................6
1.5. Installare minikube (e kubectl) in windows.........................................................................................................8
1.6. Installare un container runtime sotto linux.......................................................................................................10
1.7. Installare kubernates in linux (ubuntu).............................................................................................................11
1.8. Effettuare i bootstrap di un cluster kubernetes.................................................................................................12
1.9. Gestione cluster in modalità imperativa............................................................................................................13
1.10. Oggetti kubernetes........................................................................................................................................16
1.11. Identificare comandi dichiarativi in kubernetes............................................................................................18
1.12. metodi Imperative e declarative a confronto................................................................................................20
1.13. Dimostrazione dei vantaggi chiave dell’approccio dichiarativo.....................................................................21
2. Kubernetes Pods, Deployments, Services, Namespaces, & DaemonSets..............................................................22
2.1. Pods, gli atomi di kubernetes............................................................................................................................22
2.2. Ciclo di vita di un pod........................................................................................................................................25
2.3. L’allocazione delle risorse..................................................................................................................................26
2.4. I namespaces in kubernetes..............................................................................................................................28
2.5. Demo della policy kubernetes “limit”................................................................................................................29
2.6. Ridondanza dei pod, i replicasets......................................................................................................................31
2.7. I daemonset di kubernates................................................................................................................................32
2.8. Deployments.....................................................................................................................................................34
2.9. Deployments in pratica.....................................................................................................................................35
2.10. I services di Kubernetes.................................................................................................................................37
2.11. Laboratorio, il servizio di default clusterIp....................................................................................................41
2.12. Gestione del traffico esterno.........................................................................................................................42
3. Kubernetes Clustering & Containers.....................................................................................................................45
3.1. Clustering motives.............................................................................................................................................45
3.2. I cluster kubernetes:..........................................................................................................................................47
3.3. Demo: usare un cluster di kubernetes reale......................................................................................................48
3.4. La distribuzione dei pod nel cluster (pod topology)..........................................................................................49
3.5. Pod topology in pratica:....................................................................................................................................50
3.6. Secrets in kubernetes........................................................................................................................................52
3.7. Init containers...................................................................................................................................................53
3.8. Network policies................................................................................................................................................54
3.9. Demo: uso di una network policy......................................................................................................................55
3.10. Package management...................................................................................................................................56
3.11. Demo: uso di helm.........................................................................................................................................57
4. Application scaling.................................................................................................................................................58
4.1. Demo: deploy di kubernetes su cloud azure.....................................................................................................60
4.2. Demo: usare kubectl.........................................................................................................................................70
4.3. Demo: usare le configMaps per configurare un container kubernetes.............................................................76
4.4. Demo: configurare liveness e readiness probes per i containers......................................................................79
4.5. Demo: configurare taint e toleration.................................................................................................................83
4.6. Demo: setup dell’autoscaling di kubernetes.....................................................................................................87
4.7. Demo: configurare kubernetes con un multipod deployment..........................................................................90
5. Il packaging di Kubernetes.....................................................................................................................................93
5.1. Demo: Manifesti yaml.......................................................................................................................................94
5.2. Il motore di Google Kubernetes per il continous delivery.................................................................................96
5.3. Amazon EKS e il continous delivery...................................................................................................................97
5.4. Azure per kubernetes........................................................................................................................................98
5.5. Package managers per kubernetes....................................................................................................................99
5.6. Demo: Installare la cli di helm su windows e fare un deploy su kubernetes...................................................100
5.7. Demo: una configMap per una chart di helm..................................................................................................105
5.8. Demo: creazione di una secret usando helm...................................................................................................106
5.9. Demo: deploy di una applicazione usando helm.............................................................................................108
6. Logging e monitoring in kubernetes....................................................................................................................109
6.1. I tool di monitoraggio e logging di Google Kubernetes Engine (GKE)..............................................................111
6.2. I tool di monitoraggio e logging di AWS (EKS).................................................................................................111
6.3. Microsoft AKS log e monitoraggio...................................................................................................................112
6.4. Log e monitoraggio in kubernetes...................................................................................................................113
6.5. Demo: usare i componenti di logging di kubernetes.......................................................................................114
6.6. Demo: monitoraggio usando i log di kubernetes.............................................................................................117
6.7. Demo: il pull degli eventi di kubernetes usando kubectl.................................................................................120
6.8. Collezionare log applicativi usando Fluentd, l’uso di elasticsearch e kibana...................................................122
6.9. Demo: Usare lo stack efk per monitorare i log................................................................................................132
7. Note..................................................................................................................................................................... 134
1. Introduzione a Kubernetes
Kubernetes è un prodotto di Google che viene fuori da esigenze che sono nate in passato

- Reliability/Avaiability applicazioni
- Gestione ottimizzata delle risorse, isolamento delle applicazioni in modo da evitare colli di bottiglia o che le
scarse performance di una applicazione influenzassero negativamente le altr
- Contenimento dei costi

Il primo tentativo per isolare le applicazioni è stato usando le virtual machines o VMs

- più sicurezza
- migliore utilizzo risorse
- maggiore isolamento applicativo
- scalabilità
- riduzione dei costi

Ovviamente non era la soluzione ottimale: ogni vm ha un suo sistema operativo, bisogna pagare le licenze per ogni
server, bisogna gestire la manutenzione e upgrade di ogni vm.

Il miglioramento arriva con i Containers: sono simili alle vm ma condividono il sistema operativo tra applicazioni.

- sono leggeri
- sono indicati per applicazioni cloud-native
- sono scalabili dinamicamente
- garantiscono disaccoppiamento tramite uso api
- in ognuno di essi possiamo definire piccoli microservizi che possono interagire tra loro per erogare un
servizio intero
- sono portabili, possono girare quasi dovunque, supportano il DevOps deployment e l’Agile Deployment

Container IMAGE: software confezionato con tutto l’occorrente per far girare un’applicazione (dipendenze, librerie,
codice, tools. E’ in sola lettura ossia è immutabile. Possiamo pensarla come un template che comprende applicazioni
preconfezionate compreso il virtual operating enviroment. Possono essere invocate in qualsiasi momento tutte le
volte che vogliamo.

Quando eseguiamo una container image di fatto creiamo un container a partire dall’immagine; un container non è
altro che una immagine in esecuzione, aggiungendo di fatto un “writable layer” alla immagine immutabile, il che
significa che la possiamo modificare (e cambiarne il funzionamento) usando degli argomenti di runtime
Perchè usare dunque i containers:

- build fidelity: è garantita la consistenza della build e la fedeltà rispetto all’immagine iniziale, il modo in cui
viene prodotta una immagine è deterministico e altamente riproducibile e l’immagine gira allo stesso modo
ogni volta
- portability: i container possono essere portati e hostati su diverse piattaforme, public clouds, private clouds,
on-premises. A prescindere da dove è deployato il container tutto resta consistente. E’ dunque fatto per
essere scalabile e cloud-native.
- resource segregation: utilizza in modo efficiente le risorse e isola le applicazioni tra di loro. Sono flessibili, è
facile fare rollback, automatizzazione, tutte cose importanti per il mondo devops

Kubernates è di fatto un container orchestrator: ipotizziamo di avere una applicazione che ha una parte di frontend,
una di backend e una di logging

Ognuna di queste parti è deployata come una app-container in un cluster kubernates, per cui può essere scalata e
gestista in modo indipendente dalle altre, è isolata dalle altre, ha una propria configurazione per CPU e risorse di
memoria. Kubernates gestisce questi componenti con un approccio adeguato come il deployment e/o il daemon set.

Non scendiamo nei dettagli, per adesso è sufficiente sapere che Kubernates è in grado di orchestrare questi
container garantendo alta affidabilita, disponibilità e scalabilità. Una sorta di maestro di un’orchestra che fa in modo
che i musicisti suonino in armonia. Adesso scopriremo come.

1.1. Key Features


Adesso esploriamo le key-features che kubernetes include:

- resource segregation: aiuta nell’evitare conflitti nell’uso delle risorse. K. partiziona le risorse di un pc fisico
per un’applicazione, piuttosto che far girare l’applicazione sul server fa girare i container sul server che
hanno accesso a solo una piccola quantità della cpu e della memoria. Il meccanismo dell’isolamento delle
app è inoltre possibile grazie all’uso dei namespaces, range limits, policies messi a disposizione da K.
Possiamo dimensionare le risorse per POD o a livello di container.
- application scaling: kubernetes può creare e distruggere automaticamente i container all’occorrenza
implementando di fatto scalabilità automatizzata. Quando si parla di vertical scaling intendiamo l’incremento
delle risorse hw di un nodo. Nel caso di horizontal scaling presupponiamo un layer di astrazione, replicando
cioè i pod che con K. si può fare. E’ possibile impostare K per scalare orizzontalmente in base a metriche
come l’utilizzo della cpu etc. in pratica la risorsa (misurata) determina il comportamento del controller che
modifica il numero di repliche attive (deployate). E’ possibile, oltre a cpu e ram definire metriche custom. K.
è anche capace di autodiagnosi facendo ripartire container andati in errore, rimpiazzare e terminare
container che non rispondono senza che i client se ne accorgano (finchè i container non sono saliti)

- deployment control: fornisce un eccellente meccanismo per i deploy, è possibile definire per ogni stato le
risorse da usare, effettuare un cambio stato in modo controllato (anche rollback), riusare le risorse non più
allocate per un container

- available resource use optimization: fornisce un meccanismo per ottimizzare l’uso delle risorse che va di pari
passo con la segregation. Il bin packing si riferisce alla gestione delle risorse di molte macchine o nodi e
all’ottimizzazione dell’uso di tali risorse dati differenti containers con differenti esigenze in materia di risorse.
Il nome di bin packing proviene dal classico problema di computing dove differenti volumi devono essere
confezionati in un numero finito di bin containers, ciascuno con un volume fisso (...). Nel nostro caso, a
partire dal numero di macchine fisiche specifichiamo a kubernetes le cpu e ram disponibili e i requisiti di
ciascun container. K. ottimizza la distribuzione dei task dei container su specifici nodi basandosi sull’uso
ottimale delle risorse.

- load balancing: fornisce gratuitamente un bilanciamento di carico. K. rende i container accessibili grazie al
dns ip address. Inizia ad analizzare il traffico di rete e se vede che il traffico è elevato si preoccupa di
distribuirlo per ottimizzare il deployment

- secret management: gestisce e memorizza in modo sicuro le informazioni sensibili. La gestione di password,
chiavi ssh, oauth tokens è possibile in K senza esporre dati sensibili nella configurazione dello stack e senza
bisogno di fare rebuild delle immagini. Ogni volta che hai bisogno di modificare la configurazione di una
applicazione con secret e configuration management non avremo bisogno di fare un redeploy.

2. I templates YAML
YAML è un linguaggio di serializzazione dati focalizzato alla lettura umana. In K. viene usato per definire
l’infrastruttura di cui il nostro cluster ha bisogno per girare. E’ il linguaggio della nostra infrastruttura come Codice.

E’più leggibile del json, è possibile mettere più templates in un solo documento, ciascuno separato da tre punti o tre
trattini.
La spaziatura (indentazione) fa parte della sintassi yaml, a differenza del json non abbiamo bisogno di mettere chiavi
e valori tra virgolette, possiamo usare caratteri speciali senza problemi, non dobbiamo usare quadre e graffe. E’ case
sensitive, può avere estensione YML o YAML.

è possibile usare la tabulazione invece degli spazi ma può creare problemi in alcune situazioni.

Altra differenza con json è che non possiamo definire tutto su una riga.

Usiamo normalmente i manifesti yaml per definire in modo dichiarativo le risorse....

Yaml usa 3 tipi di nodi per rappresentare le strutture dati, mentre in json abbiamo stringhe numeri array e oggetti in
yaml abbiamo

1. scalars: valori letterali come numeri, boolean e stringhe


2. structures: sono simili a oggetti
3. sequences: sono simili agli array

mappings rappresentano il dizionario della struttura dati o hash tables, e le sequenze sono entries ordinate, liste
ordinate di strutture “scalars” o altre liste.

3. Installazione dei componenti su windows


nei paragrafi successivi vedremo come installare i vari componenti di kubernetes su windows

3.1. Installazione docker


Successivamente vedremo le features di kubernates.
dopo l’installazione, l’abilitazione di hyperv, il restart di windows sarà necessario andare a scaricare e installare
(viene proposto link) il kernel update package. Infine “restart” sulla notification che era apparsa suggerendoci di
aggiornare il kernel linux: ripartirà solo docker.

Ecco come si presenta la docker interface:

in basso a sinistra l’icona docker con sfondo verde ci dice che docker sta girando.

Se selezioniamo il menu ‘images’ ci accorgiamo che non abbiamo ancora scaricato alcuna immagine.

Nella parte superiore, icona rotella, ci fa accedere alle impostazioni, tra le varie voci troviamo anche kubernetes

abilitiamo K. diciamo di fare il deploy degli stack docker su k per default, al cliccare “apply and restart” verremo
avvisati dell’installazione del client K. Alla fine dell’istallazione accanto all’icona di docker in basso a sx ci sarà anche
quella di K con sfondo verde (sta girando correttamente).

Apriamo un’istanza di power shell e verifichiamo come K sta lavorando:

“kubectl get no” ci mostrerà i nodi iniziali:


ne abbiamo uno, chiamato docker-desktop: docker desktop include un server e un client K standalone

E’ inoltre integrato nella cli di docker che gira sulla macchina locale così come K server

Limitazioni: K. server non è configurabile ed è un cluster con un nodo solo, ideale per fare test locali.

Proviamo a testare un deployment: “kubectl create deploy demo-deploy –image=ngix –replicas=3” (usiamo l’ultima
immagine di ngix, un web/proxy/mail service)

successivamente “kubectl get po” per avere le info relative ai pod:

3 pods non ancora pronti, stanno scaricando in background le immagini da docker hub

se aggiungiamo l’opzione –w (watch)

ci accorgeremo che una delle istanze è salita, dopo un po’ anche la seconda e la terza saliranno

3.2. Installazione minikube (e kubectl)


Minikube permette di fare un deploy di un nostro cluster personale. Abbiamo bisogno di un container runtime come
docker già installato o un virtual machine enviroment come hyper-v o virtualbox.

In questa demo usiamo hyper-v, feature windows che va abilitata (si trova nel menù disinstallazione programmi).
dall’apposito sito andiamo nel tab dedicato a windows e seguiamo le istruzioni, abbiamo 3 possibili modi per
installare tutto:

nella demo usiamo il windows installer e seguiamo l’installazione di default

andiamo alla pagina web della documentazione e clicchiamo sul link “install kubectl binary with curl on windows”

che ci porterà alla sezione dove troveremo il comando curl per scaricare l’ultima release dell’eseguibile

apriamo una powershell windows con diritti di admin e digitiamo “minikube start” per far partire il nostro primo
cluster:

scaricherà la vm boot image appropriata, le dipendenze etc, infine ci comunicherà il namespace di default del nostro
cluster. Ci avvertirà che non ha trovato kubectl, per cui possiamo usare in alternativa il comando
“minikube kubectl --” seguito da qualsiasi comando valido per kubectl

scaricherà il kubectl integrato e lo eseguirà.

Possiamo creare un alias per eseguire il binario kubectl.exe che avevamo scaricato in precedenza con il browser

a questo punto k sarà l’alias per chiamare kubectl:

facciamo la stessa cosa vista durante l’installazione di docker:

Abbiamo installato minicube su windows.

4. Installazione componenti sotto linux (ubuntu)


di seguito i passi necessari per installare docker e kubernetes sotto linux

4.1. Installazione docker


eseguiremo l’immagine di test hello-world disponibile su docker hub che dopo essere stata deployata restituirà al
demone docker un messaggio, messaggio che verrà passato al docker client e restituito a terminale:

4.2. Installazione kubernates


ecco i comandi da lanciare a terminale che di fatto installano kubelet, kubeadm e kubectl

per prima cosa bisogna disattivare lo swap, aggiornare le librerie per usare il protocollo https, scaricare le chiavi
ufficiali di firma di google, aggiungere la kubernetes repository e infine installare gli applicativi.

nota riga 6: cancellare (tasto K) nel nano-editor la riga corrispondente allo swapfile, successivamente ctrl+x e dare
conferma salvataggio in modo che lo swap sia inattivo anche dopo il reboot
apt-mark serve per bloccare le versioni delle tre app scaricate

Dopo l’installazione kubelet continua a ripartire in loop aspettando che kubeadm gli dica cosa fare

4.3. Bootstrap di un cluster kubernetes


comandi da seguire secondo le best practices

riga due: l’help ci ricorda cosa fare sulla prima macchina e sulle macchine (pod) successive per fare il setup di un
cluster

sulla prima faccio la init, ci ritornerà degli argomenti che useremo su tutte le altre per fare la join al medesimo
cluster:

riga 8-10: per far partire il cluster come utente normale

contenuto del file ./.kube/config:

abbiamo il nome dell’utente di admin e il certificato del client.

adesso facciamo deploy di un pod, avremo bisogno di un addon per comunicare con i pod, andare ad apposita
pagina web:
installeremo weave-net (scorrere in fondo alla lista)

allo step 5, sul terminale relativo al pod (nodo worker), facciamo join del pod con il cluster esattamente come ci era
stato indicato alla init (riga 3)

verifichiamo sul master infine lo stato del cluster:

5. Gestione cluster con approccio imperativo e


dichiarativo
Ci sono molti modi di gestire i cluster kubernetes:

- comandi imperativi
- approccio a configurazione a oggetti imperativi
- approccio a configurazione con oggetti dichiarativi

5.1. Modalità imperativa


Nell’approccio imperativo diciamo a Kubernetes come vogliamo che siano fatte le cose, di fatto è una lista di task che
vogliamo che il sistema completi per raggiungere un determinato obiettivo

Con l’approccio con i comandi imperativi operiamo direttamente sugli oggetti del cluster dicendo a kubectl cosa deve
essere fatto con argomenti e flags

- run per generare un pod e far partire un container


- expose per creare service objects
- autoscale per creare un nuovo oggetto di autoscale (scaling orizzontale tramite deployments)
- create per creazione di specifici tipi di oggetto come un deployment, un servizio, un namespace

Esempio: facciamo partire un ngix container creando un deployment object

Creiamo anzitutto un namespace chiamato demo dentro cui memorizzeremo tutto quello che ci occorre, dentro
isoleremo tutte le risorse di questa demo dal resto delle risorse del cluster: ecco il comando imperativo

kubectl create ns demo

l’output sarà

adesso creiamo un deployment object (il cui nome sarà web e che verrà messo dentro il namespace demo)

kubectl create deploy web –image=nginx –n demo

a questo punto prendiamo i pods

kubectl get po –n demo

e ci accorgeremo che ce ne sta un pod su cui sta girando il nostro deployment

per il fatto che stiamo lavorando su oggetti “live” non esisterà una history della configurazione precedente

adesso possiamo fare update usando un comando verb-based: vogliamo ad esempio scalare

kubectl scale –replicas=5 deploy/web –n demo

(specifichiamo il numero di repliche e li nome del deploy che è web e il namespace)

digitando adesso “kubectl get po –n demo” otteniamo i 5 pod che avevamo chiesto
adesso passiamo all’approccio “imperative object configuration”;

buttiamo giù quanto creato prima (namespace demo) con “kubectl delete ns demo”, scriviamo il manifest del
servizio nel file web.yaml (3 pod con server nginx)

creiamo nuovamente il namespace demo con “kubectl create –n demo” e effettuiamo l’operazione di create (c’è
anche la replace etc) passando come parametri almeno un nome file

“kubectl create –f web.yaml”

se andiamo a verificare i pod (kubectl get po –n demo) vedremo che ce ne sono 3 che stanno girando

a questo punto possiamo definire il service, usando un file chiamato svc.yaml scriviamo
digitiamo “kubectl create –f svc.yaml” e otteniamo

quindi con “kubectl get svc –n demo” otterremo

usiamo adesso il configuration file in modalità imperativa per eliminare il servizio

“kubectl delete –f svc.yaml” ottenendo

adesso facciamo un update degli oggetti definiti in web.yaml mettendo “replicas: 5” infine digitiamo

“kubectl replace –f web.yaml” ottenendo

digitando “kubectl get po –n demo” vedremo come i pods sono diventati 5

5.2. Oggetti kubernetes


sono entità create e persistite nel sistema Kubernetes, che rappresentano lo stato del cluster e descrivono cose tipo
le applicazioni containerizzate, le versioni che stanno girando sul cluster, su quali nodi le applicazioni stanno girando,
quali risorse stanno usando/sono messe a loro disposizione, le policies che incidono sul loro comportamento (es.
alcuni oggetti hanno una restart policy che determina come devono essere ristartati), tutti questi oggetti hanno
differenti attributi secondo il tipo di oggetto.

Possiamo definire questi oggetti in modo dichiarativo con i manifesti yaml, nei primi 2 campi del yaml si specifica
(sempre):

- kind, tipo oggetto (pod, deployment, service)


- api version, necessaria sempre durante la fase di creazione
vediamo un esempio di un manifest per un pod

v1 ci dice che si tratta di uno dei primissimi oggetti che sono esistiti nell’universo di k. proveniente dalle api core

man mano che il progetto k. cresceva gli sviluppatori si sono resi conto di aver bisogno di un modo intuitivo e
intelligente per partizionare ogni nuovo oggetto -> concetto dei sottogruppi di api

alcuni oggetti hanno come api version hanno come prefisso il sottogruppo, vedi tipo il deployment, considerato di
fatto una app; un api subgroup non è altro che una collezione logica di oggetti all’interno delle api di K.; v1 significa
“general availability”, molto stabile, c’è poi v1beta1 etc

Quando creiamo un oggetto in k, bisogna specificare l’object spec, in cui scriviamo lo stato desiderato e altre
informazioni basilari. Scriviamo tutto in un yaml file ed infine usiamo kubectl per creare l’oggetto; a questo punto
verrà letto il file yaml, trasformato in json e inviato all’api di k.

lo spec è differente per ciascun tipo di oggetti in kubernetes in quanto hanno differenti attributi in base alla loro
natura

vediamo un esempio di spec:

avremo una riga spec: una sottosezione che specifica le informazioni del container (containers:) incluso il nome
(name:), l’immagine usata (image:) e un’altra serie di informazioni come ad esempio i limiti da imporre a questa
risorsa e le specifiche in termini di risorse da allocare

consideriamo un altro esempio, questa volta un deployment (subgruppo apps come detto prima)
nella sezione metadata oltre al nome c’è il campo labels, una collection di key-values pairs che vengono mappate
sugli oggetti kubernetes (pods deployments etc) che permettono agli utenti di identificarli meglio organizzandoli in
sottoinsiemi

i labels possono essere mappati sugli oggetti in fase di creazione o in qualsiasi altro momento

tramite il labelselector un client o un utente può così identificare un set di oggetti, è il raggruppamento core
primitivo di k.; ci sono due tipi di labels supportati da k: quality-based e set-based.

Possiamo specificare più requirements in un label selector usando la virgola come separatore e tutti i requirements
devono essere soddisfatti se sono specificati più requirements

I labels ci permettono di definire una sorta di tag objects per esempio per applicare una label a un deployment
name, descrivere pod, taggare in quale env l’oggetto è stato creato.

Nel nostro caso env: dev descrive un oggetto che appartiene all’enviroment dev.

è possibile aggiungere un nome di un prefisso usando la slash, se specificato il prefisso sarà un valido DNS
sottodominio (non più di 253 caratteri). il nome del segmento è obbligatorio e non può essere piu lungo di 63
caratteri, è possibile usare caratteri alfanumerici, punti, trattini, underscore e punti purchè inizino per un carattere
alfanumerico.

5.3. Identificare comandi dichiarativi in


kubernetes
è una tecnica particolare in cui non diciamo a k cosa fare (comandi) ma descriviamo gli oggetti che vogliamo usando i
folders del nostro fs

nella cartella di lavoro non ci sono yaml file, nella cartella config si

in ns.yaml dichiariamo il namespace, nome demo e label demo:


poi abbiamo il web.yaml in cui specifichiamo il deployment di 5 repliche

infine il svc.yaml in cui definiamo un nodePort service sulla porta 31000

a questo punto applichiamo i manifesti contenuti nella cartella config

kubectl apply –f ./config/

con “kubectl get all –n demo” otteniamo la lista degli oggetti del namespace demo:
replicaset è controllato dal deployment, come visto non indichiamo a k le operazioni da fare ma descriviamo
solamente gli oggetti e gli stati finali in cui li desideriamo, se vogliamo ottenere la descrizione completa gli oggetti
che abbiamo creato possiamo usare il comando

kubectl get –f ./config/web.yaml –o yaml

notare:

ci dice qual’è l’ultima configurazione applicata

adesso modifichiamo il web.yaml e cambiamo la versione del server:

se digitiamo kubectl diff –f ./config/ otterremo le diff, file per file dei manifest locali e della configurazione attuale di
k.
se digitiamo nuovamente il comando apply (kubectl apply –f ./config/) kubernetes ci dirà cosa è cambiato

digitando “kubectl get –f ./config/web.yaml –o yaml” vedremo come la last-applied-configuration è cambiata

se adesso imponiamo il numero 3 alle repliche (kubectl scale –replicas=3 deploy/web –n demo)

il comando “kubectl get –f ./config/web.yaml –o yaml” ci restituirà tra le altre cose la last-apply-configuration con
replicas ancora uguale a 5, questo perchè quello che abbiamo fatto è stato esterno all’approccio dichiarativo

per cui non mescolare gli approcci per evitare confusione

5.4. metodi Imperative e declarative a confronto


Imperative è simile ad uno script, diciamo al sistema di effettuare un elenco di task, è l’approccio a cui sono abituati
molti amministratori

Nel Declarative sono specificati i task da fare ma è affidato a kubernetes l’ordine ottimale

Con l’approccio imperativo dobbiamo dire esattamente a kubernetes cosa fare e se sbagliamo l’ordine potremmo
avere un sistema non funzionante, con l’approccio dichiarativo lasciamo a kubernetes decidere cosa fare e quando.
Questa cosa, in un ambiente molto complesso, può essere l’approccio vincente in quanto diciamo a kubernetes solo
lo stato finale desiderato degli oggetti scrivendolo in un file che può essere versionato facilmente. Quando abbiamo
bisogno di aggiornare la configurazione facciamo le modifiche che ci servono e usiamo l’apply, inoltre, se qualcosa va
male è semplice fare rollback ripristinando la versione dei manifest precedenti.

Lo svantaggio dell’approccio dichiarativo è che è molto difficile fare debug (non trattandosi di un elenco di task) e
updates parziali potrebbero trasformarsi in complesse operazioni di merge.

5.5. Dimostrazione dei vantaggi chiave


dell’approccio dichiarativo
siamo su un cluster di 3 nodi e usiamo la macchina master, con il metodo dichiarativo k. automaticamente calcola le
operazioni da fare (create, patch, delete...) per ciascun oggetto.

In poche parole facciamo diverse operazioni necessarie per i vari oggetti dopodichè diremo a k. di processare tutti i
files di configurazione nella cartella config

svc.yaml web.yaml ns.yaml

notare il nodePort modificato a 31001 in svc.yaml

applichiamo le configurazioni: kubectl apply –f ./config/ (partiamo da una configurazione vuota)

nell’approccio imperativo potrebbe essere ad esempio macchinoso cambiare la porta di un servizio, con il metodo
dichiarativo è una modifica ad un file e un apply

se usiamo l’approccio imperativo, ossia kubectl replace –f ./config/svc.yaml otterremo errore

abbiamo cioè scelto il metodo sbagliato per fare le cose, con l’approccio dichiarativo invece funzionerà:

6. Gli oggetti di Kubernetes


Nei paragrafi a seguire faremo una carrellata degli oggetti più importanti di kubernetes: Pods, Deployments,
Services, Namespaces, DaemonSets

6.1. Pods, gli atomi di kubernetes


è la più piccola unità di scheduling di kubernetes:

- è un wrapper, dentro ci finiscono una o più app containerizzate


- può essere pensato come uno “shared execution enviroment” in cui è specificato lo storage condiviso, le
risorse di rete, e come devono girare i containers

lo use case più comune è 1 pod-1container, in questo caso può essere visto come un wrapper di un singolo container

su di esso possiamo fare girare più containers complementari e strettamente legati, che ad esempio hanno bisogno
di condividere risorse, in questo caso i pods che wrappano più container usano risorse di storage e rete come un
unica unità.

Come possono supportare più containers: per come sono concepiti i pod supportano processi multipli
complementari nella forma di containers. Questi containers comprendono una specifica unità di servizi, per cui è
come un gruppo di microservizi orchestrati insieme per ottenere un’applicazione

containers e pod sono schedulati per girare sulla stessa vm o macchina fisica in un cluster, condividono un
enviroment di esecuzione. Nella slide abbiamo ad esempio abbiamo una app containerizzata, un web server per file
su un volume condiviso e un “sidecar content manager container” responsabile di aggiornare i files.

I container in un pod sono schedulati per girare sulla stessa macchina fisica o vm nel cluster, possiamo dire che gli
“abitanti” (tenants) di un pod sono sempre co-locati e co-schedulati mentre girano in un context condiviso.

i pods modellano un host logico per una applicazione, comprendono una o più applicazioni containerizzate che sono
strettamente accoppiate ed è per questo che stanno insieme, sono complementari

docker e il più comune container runtime, kubernates supporta anche altri: supporta qualsiasi container runtime
che risponde alle CRI (container runtime interfaces)

- dalla prospettiva di docker i pod sono gruppi di docker containers, con file system e namespace condiviso
- dalla prospettiva dei pod stiamo parlando di set di linux namespaces e cgroups
cgroups sono delle features del kernel linux che limitano, monitora e isola le risorse di un gruppo di processi

le app individuali possono essere ulteriormente isolate

alcuni pod usano degli init-containers per la sequenza di inizializzazione così come app containers

Tipi di pod containers (che stanno dentro i pod)

- app containers (le abbiamo viste)


- init containers (eseguiti durante lo startup, prima che parta qualsiasi app container)
- ephemeral containers che possono servire per scopi di debug

In kubernetes non creiamo mai direttamente i pods perchè sono disegnati come entità transient e disposable; ogni
pod è schedulato per girare su un nodo dentro un cluster, il pod persiste (è presente) fintanto che

- la sua esecuzione non è terminata


- non viene eliminato
- non viene deallocato (evict, sfrattato) per mancanza di risorse
- il nodo stesso continua a funzionare (node fault -> eliminazione pod)

“Workload resources” possono essere usate per creare e gestire più pods (deployments, jobs e daemonsets),

“Controller” gestisce replica, rollout e autodiagnosi, se un nodo smette di funzionare il controller si accorge che i
pods su quel nodo hanno smesso di funzionare e quindi crea dei pod di rimpiazzo e lo scheduler si occupa di mettere
tali pods sui nodi funzionanti

I Pod templates sono di fatto le specifiche per creare un pod, i controller si occuperanno di creare le “workload
resources” come deployments, jobs e daemonsets basandosi sul pod template.

Altra cosa importante da capire è che quando un pod template modifica una workload resource cambia il pod viene
terminato e rimpiazzato con un nuovi pod aggiornati e patchati

E’ quindi fondamentale comprendere che i pod non sono gestibili direttamente possiamo si modificare alcuni campi
dei pod che stanno girando ma con molte limitazioni relative a patch o replaces in quanto molti metadati dei pod
sono immutabili come ad esempio namespace e nome.
Tra le specifiche di un pod ci sono

- set di volumi di storage, tutti i container dentro quel pod possono accedervi e quindi condividere dati, la
presenza dei volumi permette la sopravvivenza dei dati persistenti, anche nel caso in cui i containers in un
pod deve essere riavviato
- networking: ciascun pod ottiene un unico ip, ciascun container di un pod condivide con gli altri il nw
namespace compreso l’indirizzo ip e la porta, i container dentro lo stesso pod usano localhost per
comunicare tra di loro, oppure i canali standard di inter-process comunication. Container su pod diversi non
possono usare il meccanismo di inter-process comunication (a meno che non sia fatta un’apposita
configurazione) ma usano l’ip networking per comunicare con le entità esterne (comunicazione fatta tramite
apposita configurazione???)
- privilege mode: i container in un pod possono girare in modalità privilegiata intervenendo sull’apposito flag
nel security context, è importante quando i container hanno bisogno di fare operazioni di amministrazione di
sistema come ad esempio accedere a risorse hardware, operazioni che richiedono particolari privilegi.

Come visto i pod sono normalmente amministrati e gestiti dal control plane, ci sono però i pod statici che sono
gestiti esternamente all’api server, sono principalmente usati per eseguire un control plane self-hosted usando il
demone kubelet su un nodo specifico che ci permette di gestire i componenti del control plane. I pod statici vengono
instanziati da una kubelet su un nodo specifico, possiamo dunque affermare che kubelet si occupa di supervisionare
il pod statico direttamente ed è capace di far ripartire il pod se va in errore.

Schema di kubernetes per come l’ho capito io


nodo: -> kubelet
utente amministratore -> comandi a
nodo: -> kubelet
kubectl su istanza master
Kubernetes core nodo: -> kubelet
utente amministratore -> comandi a
nodo: -> kubelet
kubeadm
nodo: -> kubelet
Dalla documentazione ufficiale: https://kubernetes.io/docs/reference/
CLI:
- Kubectl - Main CLI tool for running commands and managing Kubernetes clusters.
- kubeadm - CLI tool to easily provision a secure Kubernetes cluster
COMPONENTS
- kubelet - The primary agent that runs on each node. The kubelet takes a set of PodSpecs and ensures that
the described containers are running and healthy.
- kube-apiserver - REST API that validates and configures data for API objects such as pods, services,
replication controllers.
- kube-controller-manager - Daemon that embeds the core control loops shipped with Kubernetes.
- kube-proxy - Can do simple TCP/UDP stream forwarding or round-robin TCP/UDP forwarding across a set
of back-ends.
- kube-scheduler - Scheduler that manages availability, performance, and capacity

6.2. Ciclo di vita di un pod


Caratteristiche dei pod:

- (Come i containers) sono delle entità effimere ossia vengono considerate entità di breve durata o
“transient”
- non sono dotati di auto diagnosi, se un pod schedulato su un nodo fallisce il pod viene eliminato
- non sopravvivono alla espulsione (“eviction”) a causa di mancanza (“lack”) di risorse o a causa di operazioni
di manutenzione
- sono identificati univocamente da un UID
- sono schedulati sui nodi, su cui restano finchè non vengono terminati (terminated) o rimossi (deleted)
- non vengono mai rischedulati (ne sul nodo dove stavano ne su altri nodi), se accade qualcosa a un pod che
ne impedisce l’esecuzione esso viene rimpiazzato da uno nuovo
- tutti gli oggetti contenuti nel pod possono condividere il medesimo tempo di vita del pod stesso: ad esempio
il volume di storage che esiste finchè esiste il pod, se il pod viene cancellato per qualsiasi ragione anche il
volume storage viene eliminato, se il pod viene nuovamente creato anche il volume di storage viene
nuovamente creato

Ad ogni pod viene associato un podstatus object che contiene un phase field che ne descrive la fase in cui si trova
all’interno del ciclo di vita del pod. La pod phase non è altro che un basic high-level summary in cui il pod si trova
all’interno di un ciclo di vita. La fase non descrive dettagliatamente lo stato del pod e dei suoi container, per cui
dobbiamo fare attenzione a non fare confusione.

Phase values:

- Pending: il pod è stato accettato dal cluster k. ossia l’api server ha creato la risorsa pod che è stata
memorizzata nel etcd backing store di k. per tutti i dati del cluster. Non è stato fatto il setup nemmeno uno
dei containers previsti nel pod. In tale stato il pod è in attesa di essere schedulato o siamo in fase di
download delle immagini del container dalla rete
- Running: il pod è stato schedulato su un nodo e tutti i container previsti sul pod sono stati creati da kubelet
- Succeded: tutti i containers del pod sono stati terminati con successo e non verrano rieseguiti
- Failed: tutti i containers del pod sono stati terminati e almeno uno è andato in errore
- Uknown: se per qualche motivo l’api server non è in grado di determinarne lo stato, accade quando ci sono
problemi di comunicazione con la kubelet che sta sullo stesso nodo dove si presuppone stia girando il pod

Lo stato dei containers (all’interno di un pod)

- Waiting il container è in fase di startup (ad es download immagine): quando chiediamo lo stato a kubelet ci
viene anche ritornata la reason per cui il container si trova in questo stato
- Running il container gira senza problemi: kubelet in questo caso ci fornisce informazioni relative a quando è
entrato in stato running
- Terminated se il container ha smesso di funzionare o ha completato il suo lavoro: kubelet fornisce il motivo
per cui il container si trova in questo stato, l’exit code e i time di inizio e fine elaborazione

Restart policy del container: è prevista tra le specifiche del pod e determina cosa deve fare k. quando un container
esce (completamento con successo):

- allways significa che k. deve riavviare nuovamente il container ogni volta che esce (completamento con
successo)
- on failure significa che k. deve riavviare il container in caso andasse in errore
- never significa che k. non deve ristartare il container

Esempio di quale policy scegliere: se abbiamo un processo che preveda un comportamento di tipo batch (fa delle
cose ed esce) sceglieremo onfailure o never a seconda se vogliamo che venga ritentata l’esecuzione o se ci va bene
che il processo sia stato eseguito con qualsiasi risultato. Se abbiamo un web server non ci aspettiamo la fine
dell’esecuzione dello stesso, a prescindere dell’esito dell’esecuzione, se il server è andato giù ci aspettiamo che
venga ripristinato.

I pod hanno uno podstatus ma anche una serie condizioni in cui il pod è passato o meno

- pod scheduled, significa che è stato schedulato su un nodo


- containers ready, significa che tutti i suoi container sono pronti
- initialized, significa che tutti i suoi container sono stati avviati con successo
- ready, significa che il pod è pronto a ricevere richieste

ecco i campi che descrivono nel dettaglio la condizione di un pod:


6.3. L’allocazione delle risorse: resourceRequest
e resourceLimit
Vediamo adesso come vengono allocate le risorse e ristrette ad ogni singolo container; quando diamo le specifiche di
un pod possiamo dire a k quante e quali risorse allocare a ciascun container. Alcune di queste sono ad esempio cpu e
memoria. In questa demo assegneremo resourceRequest e resourceLimit ad un container definendo quindi quantità
minima e massima da allocare.

con “kubectl create namespace demo” creiamo un namespace per isolare le risorse usate per la demo da quelle del
resto del cluster, prendiamo poi un file di testo e scriviamo il seguente demo-ram.yaml

praticamente al nostro pod chiamato demo-ram nel namespace demo dichiariamo un container (riga 0) con il
medesimo nome, con immagine polinux/stress (disponibile pubblicamente su docker hub) e risorse di memoria
min/max 50/250 megabytes. Nota: se c’è ram a disposizione oltre i primi 50M e il container chiede più ram k.
allocherà più ram, se il container supera il massimo previsto dalle specifiche verrà considerato bad container ossia
candidato a essere terminato. Se continua a usare ram oltre il massimo previsto verrà terminato ed eventualmente
potrà essere riavviato.

Alla riga 16 diamo gli argomenti di avvio del container: il container dovrà provare ad allocare 100Mb, più della
request e meno del limite.

Torniamo adesso alla riga di comando: “kubectl apply –f demo-ram.yaml” otteniamo

“kubectl get po –namespace=demo” restituirà un pod allocato nel ns namespace e in stato running
“kubectl get po –namespace=demo –o wide” restituirà info più dettagliate

il pod sta girando sul nodo worker-2, andiamo su tale nodo e usiamo docker per ottenere prima l’elenco dei
container che stanno girando “sudo docker ps”

notare l’immagine polinux/stress che avevamo deciso di usare che è salita da 2 minuti

adesso chiediamo a docker statistiche relative al container_id che ci interessa (quello dell’immagine polinux/stress)

l’output in tempo reale ci dirà qualcosa del tipo (si va aggiornando con il passare del tempo)

con ctrl+c usciamo da questa schermata, ritorniamo sul master e eliminiamo il pod in questione con “kubectl delete
po demo-ram – namespace=demo”

modifichiamo adesso la riga degli argomenti di demo-ram.yaml sostituiendo --vm-bytes 100M con --vm-bytes 400M
e creiamo un nuovo pod con “kubectl apply –f demo-ram.yaml”

questa volta con il comando “kubectl get po –namespace=demo” verremo informati che il nostro pod è andato in
crash e che è già stato tentato 1 restart

“kubectl get po –namespace=demo -w” ci restituisce la storia di quanto sta accadendo in tempo reale: crash, kill,
crash, kill...in un ciclo infinito perchè la nostra immagine chiede più risorse di ram rispetto a quelle previste dalle
specifiche

a questo punto eliminiamo il nostro pod: “kubectl get po –namespace=demo”

Adesso prendiamo in considerazione un secondo manifest yaml: demo-cpu.yaml


usiamo l’immagine vish/stress per testare le cpu: tra gli argomenti diciamo all’immagine di caricare 4 cpu,
considerando che il nodo ha 2 cpu fisiche il valore limit della cpu nelle specifiche potrà al massimo essere 2, nel
nostro caso abbiamo impostato come limite 1 cpu e come allocata inizialmente 0.5

lanciamo dunque “kubectl apply –f cpu-ram.yaml” e successivamente controlliamo quello che accade digitando
“kubectl get po –namespace=demo –o wide”

andiamo sul nodo worker1 e chiediamo a docker info sull’immagine in questione (sudo docker ps, per recuperare il
container_id)

vediamo le statistiche in tempo reale:

la percentuale di cpu usata arriva al 100% e a tratti la supera

anche se abbiamo detto di usare 4 cpu al container k forza l’immagine ad usare al massimo una cpu (questa volta k
non termina e riavvia il container ma ne limita le risorse)

Per terminare la demo e deallocare tutte le risorse digitiamo “kubectl delete namespace demo”

6.4. Namespaces
i namespaces servono per supportare più cluster virtuali all’interno del medesimo cluster fisico, potremmo pensare i
namespaces come partizioni logiche del cluster, sono l’unico modo di dividere un cluster; possono essere usati per
applicare uno scope a nomi di risorse, che deve essere unico in ciascun namespace; i namespaces non possono
essere annidati e ogni risorsa di k può appartenere al massimo a un namespace.

Quando usare/non usare più namespaces:

- ci sono molti utenti e abbiamo una suddivisione per progetti/teams/aree (non serve se ho pochi utenti)
- non servono per distinguere risorse simili che differiscono di poco (es. versioni di software), in questo caso
usare le label per distinguere le risorse all’interno dello stesso namespace

“kubectl get namespace” torna l’elenco dei namespace del cluster

non usare “kube-“ come prefisso nel namespace in quanto k. usa questo prefisso per definire i propri namespaces
quando k. parte crea 4 namespaces:

- default: usato per oggetti che non hanno un namespace


- kube-system: usato da k per oggetti creati dal sistema
- kube-public: accessibile in lettura da tutti gli utenti, anche quelli non autenticati, riservato per tutte quelle
risorse che devono essere visibili su tutto il cluster
- kube-node-lease: associato ai componenti di k che si occupano di monitorare (heartbeat) i nodi

6.5. Demo della policy kubernetes “limit”


di default i containers girano senza costrutti (constraints) sulle risorse di un cluster per evitare problemi; k mette a
disposizione cluster administrators con dei range-limit, una policy che fissa dei limiti e delle restrizioni circa l’uso di
risorse; la policy è applicata di default a tutte le risorse dei namespaces: pods, containers e storage.

Prima di procedere con la demo prendiamo familiarità con gli shortnames: digitando kubectl api-resources
otteniamo l’elenco delle risorse conosciute da k

tra queste limitranges, shortname limit, namespaces ns, nodes no, pods po, deployments deploy, replicasets rs...

adesso creiamo il namespace demo per isolare delle risorse per questa demo dalle altre risorse del cluster

“kubectl create ns demo”, successivamente con “kubectl describe ns demo” vediamo che

non ci sono ancora imposti dei limitRange alle risorse del namespace

adesso creiamo un oggetto limitRange, il nome non ha limitazioni purchè sia un dns sub-domain name, creiamo un
limit range e un pod
in demo-lr.yaml specifichiamo il limit range demo-lr nel namespace demo impone ad ogni container un min e un max
di memoria allocabile, se facciamo apply del file che descrive il limit range (kubectl apply...) e successivamente
torniamo a scrivere “kubectl describe ns demo” vediamo che è presente un limitrange alla risorsa memoria da
tenere in conto quando facciamo partire un container

se nelle specifiche del container non ci sono richieste specifiche circa la risorsa memoria necessaria (ed eventuale
limite), k esegue il deploy senza problemi applicando request e limit di default, se però tra le specifiche del container
sono specificati request e limit per la memoria k confronta tali specifiche con le policy di tipo limit prima di
procedere con il deploy

se dunque definiamo un container come segue:

in un primo momento request e limits sono commentati, quindi facendo l’apply (kubectl apply –f demo-pod.yaml)
del file, k. creerà il pod e farà il deploy del container senza problemi; “kubectl po demo-pod –n demo –o yaml”
produce in formato yaml le info dettagliate del pod demo-pod del namespace demo, tra queste notiamo che i limiti
alla memoria sono i seguenti:
in pratica viene impostato request e limit massimi consentiti dalla policy; adesso eliminiamo il pod (kubectl delete po
demo-pod –n demo) e andiamo a scommentare le righe relative alle specifiche di memoria per il container (limite
500, minima 300) e rifacciamo l’apply (kubectl apply –f demo-pod.yaml) e andiamo a verificare le info relative al pod
con “kubectl po demo-pod –n demo –o yaml” tra i dettagli vedremo che k ha applicato le specifiche relative al pod

eliminando il pod, impostando come limite massimo 800M nel descrittore del container e rifacendo nuovamente il
giro di apply otterremo il seguente errore:

6.6. Ridondanza dei pod, i replicasets


K. usa i controller per gestire la replication, tali controller includono

- replicasets: ha l’obiettivo di avere un set stabile di pods replicati in esecuzione e disponibili in qualsiasi
momento
- deployments: sono oggetti che gestiscono e agiscono come un wrapper dei replicaset
- daemonsets
- statefulsets

con replicasets k. ci garantisce il numero che vogliamo di pods identici disponibili per i deployments, i deployments
usano i replicasets per l’autodiagnosi e la scalabilità

i replicasets vengono definiti con i yaml files come tutti gli altri oggetti, tra i fields importanti ci saranno

- il pod selector che specifica come identificare i pod che il replicaset gestisce
- il replica fields in cui si specifica il numero dei pod
- il pod template field in cui si specifica il template per il pod che vogliamo far girare con il replicaset in
questione
- il nome del replicaset (obbligatorio insieme a kind e apiVersion)

con l’apply il replicaset inizia il lavoro creando ed eliminando i pod come richiesto dalle specifiche in modo tale da
avere sempre il numero di pod attivi previsti dalla specifica. per creare i nuovi pod il replicaset userà il template
specificato nello yaml

ciascun pod creato dal replicaset ha un field metadata.ownerReferences che ci permette di risalire al replicaset (k ne
ha bisogno per gestire il numero di repliche attive usando le specifiche del replicaset)
se il pod non ha ownerReferences specificato o se l’ownerReferences non è un controller ma fa match con un
replicaset selector (?) il replicaset acquisisce il controllo del pod

normalmente non interagiamo direttamente con il replicaset, normalmente i deployments sono usati per gestire i
replicaset, ad eccezione di due casi

- c’è bisogno di un custom update orchestration (?)


- se non sono necessari update dichiarativi per il pod (?)...

in realtà non si ha mai bisogno di manipolare replicaset direttamente

Possiamo creare sempre naked pods direttamente, è comunque importante che tali pods non abbiano le label che
facciano match con i replicaset selector perchè, come detto, i replicaset se ne impadroniranno

MANIFEST requirements per i replicaset

- apiversion richiesta obbligatoriamente

(NOTA può generare confusione: perchè in alcuni casi abbiamo solo un numero e altre volte abbiamo un
subgrouping come ad es apps? Inizialmente tutti gli oggetti del core di k erano nell’api core group, non c’erano
altri raggruppamenti, ma con il passare del tempo sono arrivati workloads e altri tipi di API per cui è partita
l’usanza di usare i sottogruppi, ad esempio ReplicaSets, Deployments e tutte le altre parti delle app’s api
subgroup)

- kind: ReplicaSet
- metadata, tra cui il nome, obbligatorio, che deve essere un valido dns subdomain (non deve contenere più
di 253 caratteri, caratteri solo lowercase, numeri, trattini, punti, deve iniziare e terminare per carattere
alfanumerico)
- sezione spec: dentro cui dobbiamo indicare la replica, il selector e il template del pod

6.7. I daemonset
I daemonset sono controller che verificano che i pods stiano girando su tutti i nodi, ad esempio il pod di logging.

Quando aggiungiamo nodi al cluster il daemonset fa in modo che i pod replica girino su tutti i nodi aggiunti, quando li
sganciamo dal cluster il daemonset si preoccupa della garbage collection dei pod. Se eliminiamo un daemonset i pod
creati dal daemonset vengono rimossi.

Scenario tipico:

- eseguire un daemone di log collection su ciascun nodo


- eseguire un daemone di cluster storage su ciascun nodo
- eseguire un daemone di monitoraggio su ciascun nodo

normalmente abbiamo un daemonset per ogni tipologia di demone menzionata prima, configurazioni più complesse
prevedono più daemonsets per un tipo di demone ma con flag differenti

Metodi per comunicare con i daemon pods:

- push: i pods del demonset fanno le push degli update ad altri servizi, sono pods che non hanno client
- nodeIp e known Port: i demonset pods possono usare una host port in questo modo sono raggiungibili
tramite nodeip e porta
- dns: in questo caso viene creato un servizio headless usando lo stesso pod selector, i daemonset vengono
“scoperti” usando gli endpoints o A records del dns
- come servizio: in questo caso un servizio è creato con lo stesso pod selector (?), poi il servizio è usato per
comunicare con il demone su un nodo random, in questo specifico pattern non c’è modo di raggiungere uno
specifico nodo

il manifesto yaml che descrive un daemonset deve contenere


- apiversion (come ogni altro oggetto k)
- kind: DaemonSet
- metadata: incluso il nome che deve essere un valido DNS subdomain name
- spec: sezione che deve includere i selector e info circa il pod template

pod template per un daemonset e altre considerazioni:

- .spec.template nodo deve essere specificato, il daemonset controller creerà i pods sui nodi che fanno match
con il node selector
- usa il medesimo schema di un pod
- non ha fields di tipo apiversion e kind
- labels obbligatori, devono essere specificati i pod selector labels
- la restart policy deve essere always (lo è di default)
- lo .spec selector ha 2 campi: match labels e match expressions, la seconda dà modo di costruire selettori più
complessi (specifico una espressione piuttosto che una label), le m.w. includono una chiave, alcuni valori
come un operatore, è una espressione reale
- il nodo su cui gira è selezionato tipicamente dallo scheduler di K., nel caso dei daemonsets i pods non sono
creati dallo scheduler di K ma dal daemonsets controller
- se cambiano le label di un nodo il Daemonset immediatamente aggiunge i pod a ogni nodo che fa match ed
elimina i pods che non fanno più match
- i pod creati dal daemonset possono essere modificati ma non tutti i campi possono essere aggiornati
- i daemonsets possono essere eliminati
- quando si usa kubectl si può impostare il flag cascade a false in modo che i pods restino attivi anche se è
stato eliminato il daemonset
- è possibile effettuare un “rolling update” su un daemonset che può aumentare in modo incrementale i pod
con altre istanze

esempio di demonset di logging:

6.8. I deployments
il Deployment è usato per aggiornamenti dichiarativi su pods e replicasets; come abbiamo già visto dichiariamo lo
stato desiderato. Il deployment può essere usato per definire nuovi replicasets o per rimuovere deployments
esistenti e acquisire le risorse che stavano usando

use cases tipici:


- create a deployment to rollout a replicaset
- roll back to previous (revision) deployment
- scale up for greater load
- declare a new state for the pods
- cleanup old replicaset resources

Esempio:

deployment con nome ngix-deployment, i pod avranno label (tag) app=ngix nella sezione metadati

ricordiamo che i label sono usati per organizzare gli oggetti in subsets (si usa lo slash per separare nome e prefisso), i
ruoli per le labels sono max 63 caratteri, inizio e fine per carattere alfanumerico, possibili underscore, punti e trattini

nella sezione spec vediamo: replica=3, matchLabels è impostato per avere app=nginx

il campo template contiene diversi sottocampi tra cui i pod sono etichettati con app=nginx, nella sezione spec
indichiamo l’esecuzione di un solo container con dentro il server ngnix, immagine docker nginx versione 1.19.0 (di
default k cerca le immagini su docker hub) e porta 80

Esempio di step di deployments

- kubectl apply –f ... --> creiamo il deployment


- kubectl get deployments --> per verificare se il deployment ha avuto successo (opzione –o wide per avere
info più dettagliate, opzione –w per vedere in tempo reale i cambiamenti che vengono fatti al deployment)
- kubectl rollout status, specificando nome e type (nel caso del manifesto precedente sarebbe
deployment/nginx-deployment o deployment nginx-deployment) per vedere lo stato del rollout
- kubectl get rs per vedere lo stato del replicaset di quel deployment, incluso numero di repliche disponibili,
quelle desiderate, stato desiderato etc
- kubectl get pods --show-labels per visualizzare le label (incluse anche quelle generate in automatico) per
ciascun pod

quando si fa update

- kubectl –record (flag record) scriverà l’elenco dei comandi eseguiti come metadata dell’oggetto (ossia il
deployment), che sarà visibile per l’analisi in qualsiasi momento (potremo vedere la storia del rollout le
versioni e le revisioni precedenti
- kubectl rollout status per controllare lo stato di rollout

Poniamo di fare l’update di un deployment mentre c’è un rollout in corso: il deployment crea un nuovo replicaset
come previsto dall’update e inizia a scalare il nuovo replicaset, nel frattempo il vecchio replicaset viene aggiunto alla
lista dei vecchi replicaset e viene tirato giù
L’update dei label selector (non raccomandabile) comporterà l’aggiornamento delle label nei template dei pod:
bisognerà fare l’aggiornamento delle label nella sezione spec del deployment per non incorrere nell’errore di
validazione. Con questo tipo di update il vecchio replicaset diventa orfano e viene creato un nuovo replicaset.

Gli update dei selector cambiano il valore in una selector key, l’effetto è simile a quello di un’aggiunta

La rimozione dei selector causa la rimozione della key dal deployment selector, non richiede alcun cambiamento
nelle pod template labels, i replicaset non diverranno orfane, non verranno creati nuovi replicaset, le label rimosse
continuano ad essere presenti nei pods e nei replicaset

Rollback deployment, qualora il deployment risulti instabile: la storia del rollback viene memorizzata per default, è
possibile limitare la revision history tramite apposite opzioni.

Scale deployment: è possibile tramite comando kubectl scale, se il cluster è abilitato all’auto scale orizzontale può
essere configurato un autoscaler per il deployment in cui si può specificare numero minimo e massimo di pod
eseguibili ammessi basandoci sull’uso della cpu per ciascun pod

Proportional scaling: dobbiamo capire che i rolling deployments permettono l’esecuzione di più versioni della stessa
applicazione contemporaneamente. Durante l’operazione di scaling del deployment il deployment controller bilancia
ogni nuova replica nel replicaset attivo: questo è lo scaling proporzionale.

Pausing/resuming deployments: operazione possibile, in alcune situazioni (updates in corso) possiamo applicare le
opportune fix senza dover fare ulteriori rollouts

6.9. Deployments in pratica


useremo 2 terminali per le operazioni, in una osserveremo lo stato del sistema, nell’altro lanceremo i comandi

- creiamo il namespace per isolare le risorse per il nostr ns dal resto del cluster (kubectl create ns demo)
- usiamo il demo-deploy.yaml che dichiara il deployment (vedi immagine seguente)

notare il labelSelector: tutte le risorse con label “app: nginx” (in particolare i pods e replicaset) saranno oggetto di
questo deployment

- kubectl apply –f demo-deploy.yaml per far partire il rollout del deploy


- kubectl get deploy –n demo per avere news sul deployment in corso

- kubectl get replicaset –n demo per avere news sui replicaset nel namespace

- kubectl get po –n demo –o wide -w per avere info in tempo reale sui pods (5 su nodo 1, 5 su nodo 2)
Adesso inteveniamo sul valore replica e da 10 lo portiamo a 3 e lanciamo di nuovo kubectl apply –f demo-
deploy.yaml sul secondo terminale e sul primo vediamo cosa succede:

vengono terminati i pod non necessari, uscendo da questa schermata e digitando nuovamente kubectl get po –n
demo –o wide (senza il –w) vediamo che adesso i pod sono 3

ci copiamo il nome del primo pod (nginx-deploy....2hf), ci rimettiamo in modalitù di ascolto sui pod con kubectl get
po –n demo –o wide –w e usiamo il seguente comando nell’altro terminale per eliminare il pod che abbiamo preso di
mira: kubectl delete po ngnx-deploy...2hf –n demo

e quello che accade è che il pod viene rimpiazzato rapidamente da uno nuovo, infatti, uscendo dalla schermata in
questione (ctrl+c) e ridigitando kubectl get po –n demo –o wide otteniamo 3 pod attivi

ultima modifica: replica da 3 la facciamo passare a 4, facciamo l’apply del file; quello che viene fatto in tempo reale è
l’aggiunta del pod mancante al gruppo di pod, su nodo 2 per bilanciare le cose (kubectl get po –n demo –o wide –w
sull’altro terminale)

ecco il risultato finale (kubectl get po –n demo –o wide):


6.10. I services
sappiamo che i pod sono effimeri, che ad ogni pod creato viene assegnato un indirizzo ip, inotre che se un pod viene
rimpiazzato, non è detto che a quello nuovo venga dato lo stesso ip. In pratica non possiamo accedere ad un pod
direttamente, ma quello che possiamo fare con un set di pod d k. è chiamare il backend che mette a disposizione una
sorta di servizio di workload interno a altri pods(?) di frontend; se il frontend non può fare affidamento sull’ip del
backend a quale ip dovrà collegarsi per chiamare il backend?

Qui entra in gioco il concetto di service di kubernetes: è una applicazione che gira su un set di pods e che viene
esposta come un servizio di rete, è un’astrazione che definisce

- un set logico di pods


- una policy che governa l’accesso ai pods
- che determina un selector, ossia un metodo per accedere ad un determinato pod

Services provide connectivity between resources and enable communication between various Kubernetes cluster
components and applications within and outside of a cluster. The ClusterIP Service is one of four Service types in
Kubernetes.

Proprio perchè il service è un’astrazione riusciamo a disaccoppiare fe e be.

Cloud-native service discovery:

poniamo una app che usa le api di kubernetes per il service discovery, la app farà una query per ottenere gli
endpoints del server e tali endpoints cambiano ogni volta che il set di pod viene modificato

dietro le quinte ogni cluster ha un server DNS interno che gira su un control plane che ascolta le chiamate alle api del
server per ciascun nuovo servizio, se si accorge che è stato creato un nuovo servizio, registra il nome del servizio nel
dns interno: ogni pod e ogni container conoscono il dns interno, appena un servizio viene registrato nel server dns
interno, ogni pod interno a K saprà dell’esistenza di questo nuovo servizio

per applicazioni non native ci sono metodi di K che utilizzano una porta di rete o un load balancer tra le applicazioni
di frontend e i pods di backend

DEFINIZIONE SERVICE

in kubernetes un service è un oggetto rest (anche un pod lo è), possiamo fare la post di una definizione di un service
all’api server che si occuperà di creare una nuova istanza; il nome del service deve essere un valido DNS LABEL NAME
(contiene max 63 caratteri, lowercase, primo e ultimo alfanumerici, underscore trattino e punto ammessi)
ecco un esempio di manifesto del service: apiversion v1 (fa parte del core delle api), tipo service, nome my-service,
nello spec-selector imponiamo app: svcApp, la targetPort è la porta del container che accetta traffico in ingresso,
port è una porta di servizio astratta che può essere qualsiasi altra porta che altri pod usano per accedere al servizio
(?), protocollo TCP

Services senza selectors:

i services normalmente accedono ai pods di K., possono essere usati per fornire un’astrazione su altri tipi di
backends, pertanto possiamo definire un service senza un pod selector

esempi:

- database esterno in produzione, database interno in test


- puntare a un servizio di un altro namespace o in un altro cluster
- migration workload (vogliamo eseguire parte del carico di lavoro su k.)

siccome il service non ha un selector, non viene creato nessun oggetto di matching endpoint, possiamo mappare
manualmente il servizio ad indirizzo e porta dove sta girando. Il nome degli oggetti di endpoint deve comunque
avere il nome di un sottodominio DNS

Services multi port:

a volte è necessario esporre più porte, con K. possiamo configurare definizioni di porte multiple, in questo caso
bisogna specificare il nome di tutte le porte per evitare ambiguità, come per altri nomi K, questi nomi possono
contenere caratteri alfanumerici lowercase e trattini, devono iniziare e terminare per carattere alfanumerico.

K mette a disposizione 2 metodi di service discovery:

- variabili di ambiente (env variables)


- dns

Quando un pod gira su un nodo kubelet aggiunge variabili d’ambiente per ogni servizio attivo, con tale approccio se
usiamo le variabili d’ambiente per pubblicare l’ip e le porte del cluster al pods dei client, se un pod ha bisogno di
accedere ad un servizio, il servizio deve essere creato prima che venga creato il client del pod altrimenti i pod non
avranno variabili di ambiente popolate con il servizio in questione. Ad esempio, se creiamo il servizio Service redis-
master esposto con protocollo TCP su porta 6379 allocato sull’indirizzo ip del cluster 10.0.0.11, i pod avranno a
disposizione tra le varibili di enviroment i seguenti valori
REDIS_MASTER_SERVICE_HOST=10.0.0.11
REDIS_MASTER_SERVICE_PORT=6379
REDIS_MASTER_PORT=tcp://10.0.0.11:6379
REDIS_MASTER_PORT_6379_TCP=tcp://10.0.0.11:6379
REDIS_MASTER_PORT_6379_TCP_PROTO=tcp
REDIS_MASTER_PORT_6379_TCP_PORT=6379
REDIS_MASTER_PORT_6379_TCP_ADDR=10.0.0.11

L’approccio raccomandato è il metodo con dns tramite un addon: cluster-aware DNS come CoreDNS sono in grado di
usare le api di kubernates e scoprire ogni nuovo servizio creato, quando se ne accorgono generano un set di DNS
records per esso. Se il dns è abilitato per il cluster tutti i pod sono in grado di risolvere i servizi a partire dal dns name.
Se ad esempio abbiamo definito il servizio my-service nel namespace my-ns i pods nello stesso namespace possono
accedervi semplicemente usando il dns name my-service, quelli esterni al namespace il dns name my-service.my-ns

Headless service

in alcuni casi non abbiamo bisogno di load balancing e di un single-service ip, è il caso degli headless services

per poter ottenere un servizio di questo tipo in K dobbiamo non specificare NONE per l’ip del cluster
un servizio headless non è legato al service discovery di K. e puo interfacciare con altri meccanismi di service
discovery; in questo scenario visto che il cluster IP non è allocato kube-proxy non gestisce i servizi headless e non
viene messo in piedi il meccanismo di load balancing e proxy da parte di K

Il DNS viene configurato in base ai selettori che definiamo per il servizio:

- Per tutti gli headless services che hanno dei selector definiti i controller di endpoint genereranno endpoint
records nelle API e configureranno i dns per testituire un A record (un semplice indirizzo ip) che punta
direttamente ai pod che stanno dietro al service
- Per quelli che invece non hanno alcun selector definito, il controller di endpoint non genera endpoint
records, il sistema dns comunque prova a trovare e a configurare dei CNAME records per servizi con nome
esterno o A records per qualsiasi endpoint che condivide il nome con il servizio

Tipi di servizio

- cluster ip: tipo di servizio di default, espone il servizio su un ip interno al cluster, questo servizio è
raggiungibile solo internamente al cluster
- nodePort: espone il servizio su una porta statica sull’ip di ciascun nodo, il nodePort service effettua il routing
delle richieste al servizio di ip del cluster che è creato automaticamente. Il nodePort service è raggiungibile
dall’esterno del cluster usando la combinazione di IP e nodePort
- load Balancer: espone esternamente il servizio usando il load balancer del cloud provider; nodeport e cluster
ip sono creati automaticamente e il loadBalancer esterno redirige le richieste a questi servizi
- external Name: il servizio viene mappato su un valore specificato nel name field esterno, ritornando un
CNAME record e il suo valore; non c’è nessun tipo di proxy configurato, puoi anche usare “ingress” per
esporre un service anche se non è un vero service type

IN PRATICA (DEMO): creeremo un K service

- creiamo un namespace demo con kubectl


- usiamo i seguenti manifest (demo-svc.yaml) per definire il service e il pod (pod.yaml)

il servizio è di tipo NodePort, per cui useremo la combinazione NodeIp:NodePort per accedere al servizio,

con il selector “app: nginx” cattureremo il nostro pod di demo che ha definito tra le labels della sezione metadata lo
stesso set di chiave valore

port/targetPort/nodePort:

- PORT: espone internamente il servizio nel cluster, il servizio diventa disponibile e visibile su tale porta e tutte
le richieste che vengono fatte a questa porta vengono reindirizzate dal servizio ai pod (selezionati da questo
servizio)
- TARGETPORT: è la porta verso cui le richieste vengono inviate dal service, le applicazioni dovranno essere in
ascolto su tale porta in modo da servire le richieste che arrivano, normalmente (default) la target port è
impostata con lo stesso valore di port
- NODE PORT: permette la pubblicazione del servizio esternamente al cluster kubernates usando la tecnica del
nodeIpAddress/nodePort dichiarati nel manifesto (type), se non viene specificato questo campo e il type è
NodePort K. sceglierà un valore tra 30000 e 32767
a questo punto faccio l’apply del manifesto che descrive il pod

poi lancio un comando che fa in modo che il pod accetti lo standard in del terminale: kubectl exec nginx-pod –n
demo –it -- /bin/sh(it sono flag che stanno per interactive terminal), ottenendo così una shell dentro il container,
come se ci fossimo loggati dentro al pod, e infine il nome della shell che vogliamo usare per interagire con il pod

otterremo il prompt dei comandi (una #) del pod, lo useremo per scrivere un /usr/share/nginx/html/index.html
dentro al nostro container con un html molto semplice:

a questo punto con “exit” ci slogghiamo dal pod e procediamo adesso con l’esposizione del service facendo l’apply
del manifest del service

adesso ci serve l’ip del nodo dove sta girando il pod: kubectl get po –n demo –o wide

il pod sta girando sul nodo k8s-worker1, NOTA BENE: l’ip 10.44.0.1 è interno al cluster, noi abbiamo bisogno di quello
esterno. Per far ciò chiediamo a K. info sui nodi con il comando kubectl get no –o wide (no sta per node)

192.168.100.132 è l’ip che ci serve per la nostra richiesta

pertanto chiamando con il browser http://192.168.100.132:31000 avremo come risposta l’index.html che abbiamo
scritto prima.

6.11. Laboratorio, il servizio di default clusterIp


kubectl get services

There is one Service type running in the cluster—the default ClusterIP Service named kubernetes.

kubectl describe svc kubernetes


The ClusterIP Service provides communication between cluster components and the cluster API server.

API requests to the ClusterIP Service IP address 10.96.0.1 are forwarded to the pod that contains the Kubernetes API
server—the kube-apiserver Service—that is listening on port 6443 on the k8s-master1 master node.
kubectl get endpoints

kubectl get nodes -o wide

create a deployment

kubectl create -f https://raw.githubusercontent.com/LODSContent/ChallengeLabs_Resources/master/CKA/


webserver.nginx.deployment.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
name: webserver
spec:
selector:
matchLabels:
run: webserver
replicas: 2
template:
metadata:
labels:
run: webserver
spec:
containers:
- name: webserver
image: nginx
ports:
- containerPort: 80
Il file in questione contiene la definizione di un deployment con nome webserver, due pod di replica che eseguono
un web server nginx. La porta di ascolto del container è la 80.

6.12. Gestione del traffico esterno


in questa demo creeremo un ingress per gestire il traffico esterno per far questo abbiamo bisogno di

- un ingress controller, uno dei controller piu comuni è nginx ingress controller che è normalmente incluso
come addon in minikube su windows
- un ingress object, creato da kubernates usando un manifest dichiarativo yaml

abilitiamo l’add-on di minikube su windows, su un terminale di visual code studio digitiamo minikube addons enable
ingress

poi digitiamo kubectl get po –A –l app.kubernetes.io/name=ingress-nginx per cercare il pod con label (flag –l) relativo
all’addon appena abilitato

nel namespace kube-system abbiamo il controller cercato che sta girando; guardiamo adesso lo yaml che useremo:

prima parte: dichiariamo un pod, useremo l’immagine hashcorp/http-echo con argument –text=greeting from app1”

seconda parte, il service associato a tale pod (selector: app: app1) con la porta di default per l’immagine di prima che
è la 5678
cosa analoga con un secondo pod, cambia solo il messaggio tra gli args e infine il service collegato a tale pod

abbiamo quindi 2 servizi che tornano differenti risposte

su uno yaml a parte definiamo l’oggetto di tipo ingress che fa parte del gruppo di api di networking (rif. apiVersion)
di kubernetes (la versione che si trova in extensions/v1 si riferisce alla v1 beta 1 di kubernetes che è deprecata):

dentro la parte spec definiamo l’elenco dei ruoli, in questo caso solo 1 che ha un campo host (domain.local, di cui
parleremo più tardi) su cui definisco i ruoli e due path che utilizzeranno i due service diversi che ho dichiarato nel
manifest precedente (e che sono in ascolto sulla porta 5678) tramite l’oggetto ingress direzioniamo dunque il traffico
su due distinti services applicando le regole descritte (il path usato nella url).
Creiamo services e pods con apply

verifichiamo che siano saliti (.... get svc –o wide)

adesso creiamo il nostro ingress con kubectl create –f ing-rule.yaml

verifichiamo che stia funzionando con kubectl get ing simple-ingress –o wide –w

inizialmente non abbiamo un indirizzo ip associato perchè sta ancora partendo ed è in attesa di ottenere l’ip, dopo
46 secondi finalmente abbiamo l’ip da usare

da dove ha ottenuto il 172.17.20.26? ...se digitiamo il comando minikube ip otterremo lo stesso ip:

come è possibile che domain.local risolva tale indirizzo visto che non abbiamo un server dns? abbiamo modificato il
file hosts della nostra macchina

adesso usiamo il comando curl e vediamo che se proviamo il /path1 otterremo l’errore 404
questo perchè non c’è alcun ruolo che fa match nel nostro ingress, perchè l’host del nostro ingress è domain.local

adesso otteniamo la risposta del primo pod e del secondo pod al variare del path

7. Kubernetes Clustering & Containers


7.1. Clustering motives
I pod sono schedulati dal control plane o master per girare sui nodi worker, ogni worker node è gestito dal control
plane o master, il cluster comprende diversi nodi incluso più control planes, in modo da avere ridondanza e servizi
altamente disponibili, in modo da garantire sempre la disponibilità del control plane

componenti di un nodo

- kubelet: gira su ogni nodo del cluster, la funzione primaria è registrare il nodo sull’API server
- container runtime: installato su ogni nodo, responsabile a basso livello di far partire e fermare i containers
- network proxy, meglio noto come kube-proxy: è uno dei componenti chiave per la comunicazione nei
cluster kubernetes, si occupa di fare load balancing del traffico diretto ai services sul cluster attraverso gli ip
del cluster e le node ports fino ai pods di backend

un cluster kubernetes è fatto da un master e tanti nodi, sui nodi girano le applicazioni containerizzate, il ruolo del
master o control plane è quello di gestire schedulare e assegnare carichi di lavoro ai nodi, da qui proviene il termine
orchestration. Le applicazioni, piccole e leggere, sono anche considerate microservizi, girano dentro un container che
include codice, logica, dipendenze necessarie per un servizio. Ciascun cluster k ha almeno un control plane e uno o
più nodi “worker”. Il control plane si occupa di mantenere lo stato desiderato del nostro cluster incluso anche quali
applicazioni stanno girando e quali container images stanno usando, numero di repliche, limiti delle risorse etc. Il
master coordina le attività sul cluster, mentre è sui nodi dove gira il carico di lavoro.

Vediamo adesso come K ci aiuta nella sfida della tecnologia di clusterizzazione:

Deployment con 5 repliche del Service Namespace


server web nginx
mettiamo i tre yaml nella stessa cartella e facciamo la build di un deployment con un solo comando con l’approccio
dichiarativo: kubectl apply –f ./config/

Kubernetes nasconde un meccanismo sofisticato per gestire quanto da noi chiesto includendo meccanismi
automatici che facciano fronte anche all’eventualità che uno dei nodi del cluster vada giù. I container per loro natura
sono facilmente trasferibili su altri nodi, che possono girare su virtual machines indifferentemente con sistemi
operativi diversi, su macchine fisiche con tecnologie diverse o su cloud. Sono accessibili grazie ai services objects che
hanno la funzione di bilanciare le richieste sui nodi. Ma non è solo una questione di bilanciamento del traffico, k.
bilancia anche il carico e le risorse agendo sui pods. Nel caso della demo abbiamo 2 nodi che sono due macchine
identiche; vediamo come sono distribuiti i pods: kubectl get po –n demo -o wide

3 pods stanno su un nodo e 2 su un’altro, con l’obiettivo di suddividere il carico in modo ottimale, basta infatti
modificare il numer di repliche da 5 a 6 (riga 9 del manifest del deployment) per accorgerci (comando apply e
successivamente get po...) che i pod vengono distribuiti in modo uguale sui nodi:

k. bilancia dunque il workload in modo da avere il risultato migliore.

Adesso, usando l’approccio imperativo possiamo provare a scalare, nella maggior parte dei casi funziona, ma cosa
accade se invece proviamo a cambiare il nodeport dei nostri servizi?

Modifichiamo quindi il manifesto del servizio, il nodeport lo cambiamo da 31000 a 31001 e usiamo il comando
imperativo per rimpiazzare il servizio kubectl replace –f ./config/svc.yaml
errore: in molti casi l’approccio imperativo mescolato con il dichiarativo combina questi guai, bisogna stare attenti
perchè in questo caso si tratta di un piccolo cluster, figuriamoci cosa accadrebbe su cluster più grandi; kubernetes ci
aiuta a evitare queste situazioni. Con il comando kubectl apply –f ./config invece va tutto bene:

7.2. I cluster kubernetes:


è cruciale, quando siamo in produzione, che la nostra applicazione giri senza downtime, k lo rende possibile tramite il
deployment sui pod essendo dunque un framework per applicazioni distribuite su cloud resiliente: come abbiamo
già visto se un pod va giù su un nodo immediatamente parte un altro pod per rimpiazzarlo. Quando abbiamo bisogno
di scalare la nostra applicazione k si preoccupa di farlo per noi, stessa cosa per il failover.

Il service discovery e il load balancing non sono solo una semplice parte di k, sono il cuore e l’anima: kubernetes
espone i container usando il meccanismo dei nomi DNS o indirizzi ip e quando il traffico sul container è elevato k si
occupa del load balancing sul traffico per distribuirlo in bodo da ottenere dei deployments stabili. K offre i
deployment e gli update su larga scala, molti framework di infrastruttura non supportano il modello devOps che
riguarda la gestione dei deployment e l’aggiornamento del software su larga scala: K e disegnato per lavorare con il
devOps model.

K. riesce a scalare (in and out) in modo relativamente semplice, offre un meccanismo che ci permette di controllare
lo stato dei deployments in qualsiasi momento, contiene il meccanismo del controllo di versionamento in modo da
poter aggiornare i pods in modo efficace ed efficiente e semplifica il lavoro del rollback se la versione corrente del
deployment non è stabile. Flessibilità ed efficienza fanno parte di k., permettendoci di mettere in pausa il
deployment mentre è in fase di processamento, per esempio per permetterci di applicare delle fix al deployment e
successivamente far ripartire il tutto facendo iniziare nuovamente il rollout.

K. ci permette di performare rollouts/rollbacks automatizzati: definiamo lo stato del deployment e k fa il resto per
raggiungere lo stato desiderato, creando ad esempio nuovi container per i nostri deployments, rimuovendo i vecchi
deployments e allocando risorse ai nuovi.

K. include il meccanismo di autodiagnosi monitorando continuamente il cluster e facendo ripartire i container che
vanno in errore o rimpiazzando quelli che smettono di rispondere. K non espone mai ai client i containers finchè non
sono pronti.

K. offre il packing automatico degli eseguibili: quando facciamo il setup di un cluster diciamo a k. cosa ci vogliamo
mettere dentro anche in termini di risorse (cpu, memoria) per ciascun container, k. si occupa di far entrare i nostri
containers sui nostri nodi con una gestione ottimizzata delle risorse.

Con l’approccio dei piccoli container possiamo dividere l’applicazione in fe e be, di conseguenza in piccoli pod, ci
permette uno sviluppo rapido con teams con focus relativamente ridotto responsabile di un container specifico, in
questo modo isoliamo le dipendenze capitalizzando su piccoli componenti ben configurati.

I containers, come già detto, condividono risorse come file system, kernel namespaces, indirizzi ip etc, mentre il
service in kubernetes è usato per organizzare i pod che offrono funzionalità simili, e possono essere acceduti in
modo semplice e configurati per le funzionalità di discovery, visibility, scaling e load balancing

Con k. si può facilmente montare uno storage system compreso storaging locale, storage pubblico su cloud.

Infine, cosa importante, la possibilità di configurare e gestire la sicurezza, memorizzando password, chiavi ssh, token
oauth. Possiamo deployare e aggiornare queste informazioni senza bisogno di rebuilds e senza dover esporre questi
dati nella configurazione dello stack.
7.3. Demo: usare un cluster di kubernetes reale
nella demo lavoreremo su un master con 3 nodi; kubectl get no –o wide

nella colonna role si vede chiaramente il nodo master con ruolo di control-plane e i due worker; creiamo adesso un
deployment

Service Deployment Namespace

se applichiamo le modifiche (approccio dichiarativo) sui 2 worker ci finiranno 3 pods: kubectl get po –n demo –o
wide

Questo è quello che accade di default: distribuzione omogenea dei pods sulle risorse, possiamo però definire regole
e policies per modificare questo comportamento, specificando ad esempio dove i pods devono girare, possiamo
scrivere policies per evitare che due pod si parlino.

I bootstrap tokens sono usati per stabilire un bidirectional trust tra un nodo che vuole entrare a far parte di un
cluster e il master o control plane. Quando lanciamo il comando kubeadm init per far partire il nostro cluster
(bootstrap), viene creato un token iniziale che ha un ttl di 24h, cosa accade dopo? Usando il comando kubeadm
token create --print-join-command generiamo un nuovo token e otteniamo la riga di comando da usare sulle istanze
che vogliamo usare come nodi del cluster

7.4. La distribuzione dei pod nel cluster (pod


topology)
La distribuzione dei pod su un cluster non è una cosa semplice, kubernetes mette a disposizione funzionalità di pod
affinity e anti-affinity che ci permettono di fare in modo che un pod giri solo su un particolare set di nodi,
ovviamente non risolvono completamente la sfida della distribuzione dei pod: la sfida reale è quella di distribuire in
modo omogeneo i pod anche sulle topologie in modo da avere l’utilizzazione ottimale del cluster e availability; il
podTopologySpread scheduling plugin inizialmente preposto per una distribuzione equa dei pod è disegnato per
fare questo tipo di lavoro.

Per ottenere il massimo delle perfomance vanno tenuti in conto

- risorse cpu
- risorse di memoria
- device locality (posizionamento dei device)

K. gestisce le ottimizzazioni tenendo in conto questi parametri grazie a una varietà di componenti, trovando una
maniera di orchestrare questi componenti. Ecco dunque il topology manager che entra in gioco, un componente per
coordinare una collezione di componenti responsabili di tutte le ottimizzazioni.

A prescindere dal topology manager il CPU device manager potrebbe prendere le sue decisioni di allocazione
separatamente, che potrebbero produrre allocazioni indesiderate, ecco perchè tutti i componenti che si occupano di
allocazione fanno fede a quello che dice il topology manager. Il topology manager si occupa di raccogliere le
informazioni sulla topologia dagli Hint Providers e decide successivamente se un pod può essere accettato o meno su
un nodo in base alle policy e le richieste di risorse del pod.

I topology spread constraints possono essere usati per controllare il modo in cui i pods vengono distribuiti sui nodi,
solitamente fatto attraverso i failure domains come le regions, zones, nodes o qualsiasi altro topology domain
definito dall’utente. I topology spread constraints ci aiutano a ottenere una alta disponibilità delle applicazioni
efficientando l’utilizzo delle risorse del cluster, dipendono dai label che diamo ai nodi che ne identificano i topology
domains in cui risiedono. Possiamo definire uno o piu topology spread constraints che indicano al kube-scheduler
come ogni pod da creare debba essere allocato in relazione ai pod già esistenti nel cluster e sono

- maxSkew: descrive come distribuire i pods in modo non omogeneo, è un integer e rappresenta la massima
differenza ammessa di numero di pod schedulati in due topology domains di un particolare tipo di topologia,
il valore non può essere 0 e ha il valore di default 1. In realtà c’è un altro campo correlato a questo valore
per adesso ignoreremo... (?)
- topologyKey: è la chiave della label del nodo: se due nodi hanno la stessa label, lo scheduler considera
entrambi i nodi residenti nella stessa topologia, effettuerà dunque una allocazione di pod bilanciati in
ciascun topology domain
- whenUnsatisfiable: definisce come debba essere gestito un pod se non soddisfa i vincoli imposti (spread
constraint) e puo essere il default “do not schedule”, oppure “schedule anyway” per schedularlo a
prescindere dando priorità al nodo che minimizza il parametro skew
- labelselector: usato per selezionare i pod che fanno match che vengono messi insieme nello stesso topology
domain

7.5. Pod topology in pratica:


Abbiamo 5 pod allocati su 3 nodi worker come segue

abbiamo suddiviso i nodi secondo la seguente topologia:


digitando kubelet labeled --list visualizziamo l’elenco delle label per capire meglio come l’immagine viene tradotta in
pratica; kubelet labeled --list no k8s-worker1 poi facciamo lo stesso per ...worker2, worker3

i primi due worker hanno zone=zone1, il terzo zone=zone2, ciascuno ha node=node1, 2 o 3 coerentemente con il
nome; adesso vediamo come creare questi node labels e fare in modo di creare dei constraints che dipendano da
essi

nel primo caso ad esempio otteniamo la label digitando kubectl label no k8s-worker1 zone=zone1 (se proviamo a
farlo adesso darà errore perchè esiste già tale label sul worker1)

adesso vediamo come impostare le policy di allocazione intervenendo sul manifest di un pod (o di un deployment o
un daemonset); quello che segue è il manifesto per deployare il sesto pod sul nostro cluster, nella sezione spec
troviamo il famoso topologySpreadContraints che contiene 2 constraints (riga 10 e riga 16)

nel primo constraint la topology key è zone, mentre nel secondo la topology key è node

il whenUndstisfiable è impostato in entrambe i casi a DoNotSchedule (che sarebbe il default)

nel labelSelector in entrambe i casi abbiamo env: dev

maxSkew è impostato a 1 per questa prima dimostrazione


vediamo adesso cosa dovrebbe accadere in base a come sono configurati i nodi:

- se non ci fosse stato alcun constraint il nodo 2 sarebbe stato il candidato al deploy in quanto più scarico degli
altri
- per soddisfare il primo constraint il pod6 dovrebbe essere messo nella zone2, ossia sul node3 in quanto
abbiamo indicato a k di applicare i criteri di allocazione basandosi sulla zona e non sui pod deployati su
ciascun nodo (in zone1 abbiamo 3 pod, in zone2 abbiamo 2 pod)
- per soddisfare il secondo constraint il pod6 dovrebbe essere messo sul node2, nella zone1 in quanto il
criterio di distribuzione è quello dei node (che sarebbe uguale al caso in cui non avremmo definito alcun
constraint)

questo crea un conflitto, quindi ci aspetteremo che il deploy di questo pod rimarrà in stato pending

Lanciamo dunque il kubectl apply –f <filename> ottenendo come risposta da kubernetes “pod created”; se però
lanciamo il comando kubectl get po –n demo –o wide il pod lo vedremo in stato pending perchè non c’è modo di
schedularlo seguendo le policy espresse nel manifest

a questo punto elimino il pod con un kubectl delete po –n demo pod6

per risolvere la questione abbiamo 2 strade:

- modificare il whenUsatisfiable di uno dei due constraints per fare in modo che il criterio in conflitto venga
ignorato in caso di conflitto
- modificare il maxSkew aumentandolo

prendiamo la seconda strada: se aumentiamo il maxSkew del criterio a zone e lo impostiamo a 2 il criterio sarà
compatibile con quello seguente perchè la differenza massima di pod deployati tra le due zone può essere 2

quindi dopo aver fatto apply e aver lanciato nuovamente il get po vedremo che...
il pod6 è stato deployato sul worker2.

7.6. Secrets in kubernetes


la k. secret ci permette di memorizzare e gestire informazioni sensibili come passwords chiavi ssh e token oauth in
modo flessibile e sicuro, piuttosto che utilizzare tali informazioni nel manifesto dei pod o dentro le container image;
la funzionalità offerta ci permette di memorizzare poche informazioni molto sensibili: il sistema crea la secret, gli
utenti possono a loro volta generare i propri secrets.

Per default i secrets sono al memorizzati come stringhe non cifrate codificate base64, possono essere visualizzate
come semplice testo dall’utente che che riesce ad accedere all’api o da qualsiasi utente che dispone di accesso al
kubernetes data store principle (at CD?). Per una sicurezza ottimale quando si usa secrets è fortemente
raccomandato abilitare la cifratura a livello rest (at rest?), è una best practice.

Inoltre esistono le policy di accesso basate sui ruoli (RBAC) che è fortemente consigliato abilitare in modo che solo gli
utenti e le entità abilitate abbiano accesso a questo tipo di informazioni.

Le secret possono essere ottenute implicitamente dall’utente che ha i permessi per creare pod. Per utilizzare una
secret il pod deve anzitutto referenziarla. Un pod può memorizzare e fare riferimento ad una secret in tre modi:

- come file in un volume montato su uno o più containers


- come variabile di enviroment del container
- attraverso kubelet nel momento in cui fa la pull delle immagini

Un oggetto di tipo secret deve avere un nome, che deve corrispondere alle specifiche di un dns subdomain name;
possiamo specificare il campo data e/o il campo string data quando creiamo un file di configurazione per una secret.
Questi campi sono opzionali; i valori dei campi “data” devono essere delle stringhe codificate base64, se però la
codifica non è qualcosa di cui abbiamo bisogno possiamo specificare campi “data string” che accettano una stringa
arbitraria come valore. Le chiavi sia di tipo data che string data devono essere dei caratteri alfanumerici, trattini,
underscore o punti. Tutte le coppie chiave/valore nel campo string data sono internamente mergiate nel campo
data, per cui se una chiave compare sia nel campo data che nel string data, il valore specificato nel campo string data
ha la precedenza.

Kubernetes mette a disposizione due categorie di secrets:


- quelle create in automatico dal servizio di sistema (system service account), che vengono associate ai
container insieme alle api credentials, si possono creare secret customizzate per credenziali necessarie per
rendere disponibili i pods. Le secrets built-in sono di diverso tipo e corrispondono agli scenari di uso comune
più popolari:
o opaque, tipo di secret di default, tutte quelle che nel file di configurazione non hanno specificato il
tipo (type statement) sono di questo tipo. Le secrets opaque sono studiate per memorizzare dati
utente arbitrari
o kubernetes.io/service-account-token identificano account di servizio, durante la creazione di un pod
k. crea automaticamente questa secret e l’associa al pod abilitando l’accesso sicuro alle api,
comportamento che può essere disabilitato
o kubernetes.io/dockercfg sono le credenziali necessarie all’accesso della repo docker per prendere le
immagini, normalmente sono usate per memorizzare il file (tilde)/.dockercfg che è il formato legacy
per la configurazione della linea di comando docker, contiene la chiave .dockercfg codificata base 64
o kubernetes.io/dockerconfigjson, anche questa chiave .dockerconfigjson è la versione codificata base
64 del file (tilde)/.docker/config.json una nuova versione del file deprecato .dockercfg
o kubernetes.io/basic-auth è usato per memorizzare dati relativi alla basic authentication (username e
password)
o kubernetes.io/ssh-auth è usato per memorizzare dati relativi connessioni ssh, contiene la coppia di
chiavi ssh private
o kubernetes.io/tls. è usata per memorizzare chiavi e certificati tls. Lo scenario più comune è l’ingress
resource termination ma spesso è usato anche per altre risorse
o bootstrap.kubernetes.io/token i token memorizzati durante il bootstrap del nodo, creato
normalmente nel namespace kube system

possiamo creare delle nostre chiavi di tipo personalizzato assegnando un valore di tipo string come tipo di secret
object, se non specifichiamo nulla come tipo (stringa vuota), la secret viene tratta come opaque

Modi di creare una secret:

- comando kubectl create secret


- usando un generatore come kustomize, un tool standalone di kubernetes che supporta la customizzazione di
oggetti di k. tramite un file di customizzazione
- usando un file di configurazione, specificando la api version v1 e il tipo secret

Come si usano le secret:

possono essere montate come volumi dati, come variabili di enviroment referenziabili da un pod, o usate da altri
componenti di sistema senza essere esposte direttamente al pod, come ad esempio la secret per generare
credenziali per interagire con sistemi esterni come ad esempio un database.

Ogni secret esistente può essere editata a riga di comando (kubectl edit secret), quando accade il comando apre
l’editor di default del sistema operativo e permette di aggiornare i valori del data field codificati base64.

7.7. Init containers


vengono eseguiti prima di qualsiasi app in un pod, possono contenere alcuni tipi di script o utilities non presenti nella
app immagine. Vanno specificati nel manifesto del pod. Ricordarsi che un pod puo avere più container che girano
dentro di esso, pertanto possiamo avere più init container eseguiti prima di eseguire gli app containers.

Gli init containers sono dei banali app containers eccetto il fatto che devono girare fino al completamento e che
l’esecuzione di ciascun init container deve essersi conclusa con successo prima che parta il successivo init container.

Per il fatto che gli init containers usano immagini indipendenti dagli app containers hanno un certo vantaggio legato
allo startup: possono ad es contenere utilities, software custom o script per il setup della configurazione che non
sono presenti nelle stesse immagini delle app.
L’application image builder e i deployer roles possono funzionare in modo indipendente evitando qualsiasi esigenza
di una “jointly built single app image”.Gli init container possono essere eseguiti con una differente vista del file
system rispetto alle app containers dello stesso pod; per esempio possono avere l’accesso garantito alle secret che le
app containers non possono avere.

Sequenzialità: considerando il fatto che girano prima di ogni altra applicazione e che devono essere terminati prima
di poter andare avanti, possono essere usati come metodo per bloccare o ritardare lo startup delle app container
finchè non sono soddisfatte determinate precondizioni. Possono limitare gli attacchi sulla superficie di una app
container image, possono ad esempio eseguire in sicurezza del codice custom o utilities che se venissero fatti da una
app container potrebbe rendere la stessa app container image meno sicura

Per specificare un init container in un pod, dobbiamo aggiungere il field initContainer nel manifest del pod, alla
sezione spec; è un array di oggetti di tipo container e va di pari passo con l’array delle app container. Lo stato dell’init
container è ritornato al sistema nel campo status.initContainerStatuses che, similmente al campo statuses dei
container.

Retry policy: se un init container fallisce, kubelet prova a farlo ripartire finchè non viene raggiunto il successo; ma
dipende anche dalla policy di restart, se è settata a never e l’init container fallisce durante lo startup kubernetes
considera il pod come fallito e non ritenta l’avvio.

Vediamo nel dettaglio la differenza tra init e app container. Gli init containers supportano stessi fields e features
degli app containers, tra cui specification dei volumi, resourceLimits e impostazioni di sicurezza. Le richieste di
risorse e i limiti sono concepiti in modo differente: sono i valori più alti di qualsiasi resource request o limit per tutti
gli init containers. I pods richiedono che tali limiti siano maggiori della somma di tutte quelle specificate per gli app
containers. La schedulazione è fatta basandosi su requests e limits effettivi il che significa che gli init containers
possono riservare risorse per l’inizializzazione che poi non sono usate durante la vita del pod.

Se vengono specificati più init containers per un pod kubelet li esegue in sequenza e tale sequenza viene completata
solo se ciascun container viene eseguito con successo. Solo dopo kubelet prova a far partire gli app containers.

7.8. Network policies


sono utili per gestire il traffico di rete su un indirizzo ip e/o una porta di una particolare applicazione nel cluster, sono
implementate a livello application, e funzionano specificando i permessi ai pod di comunicare con altri oggetti di
rete, oggetti che sono specificati combinando identificatori, ad esempio:

- se è consentito l’accesso da altri pods (non è possibile bloccare l’accesso del pod a se stesso)
- se c’è bloco a livello ip, ...(?)
- namespaces abilitati a parlare con il pod

basicamente le nw policies sono delle network acl implementate a livello di pod o di namespace; usano l’approccio
del label selection per specificare quale traffico è ammesso e quale no come un servizio o un cider ip address range
che è permesso su una particolare porta o protocollo; normalmente funzionano con l’utilizzo di un plugin particolare
come Calico, Flannel o Weave...ma ce ne sono molti altri.

Le n.p. sono implementate con l’uso di uno di questi plugin (la nw solution deve supportare la nw policy); se si crea
una di queste risorse (Network policy) senza che l’apposito plugin sia installato, la n.p. non avrà alcun effetto,
pertanto abbiamo bisogno del controller (plugin) e della definizione della risorsa.

Adesso parliamo di pod isolati e non isolati: il funzionamento di default dei pods è in modo non isolato, viene
accettato tutto il traffico in ingresso da qualsiasi sorgente.

Possiamo isolare i pods con una nw policy che li seleziona, in questo caso il pod rifiuta qualsiasi connessione non
ammessa in modo specifico dalla policy. I pod non selezionati dalla nw policy funzionano accettando il traffico da
qualsiasi sorgente. Affinchè due pod possano parlarsi le nw policy su entrambi devono consentire l’ingresso del
traffico. Se viene negato il traffico in una delle direzioni i pod non si parleranno.
Le nw policies sono additive, se più policy selezionano un pod, vale la union delle policies in ingresso e uscita;
l’ordine in cui vengono applicate non ha valore.

Una risorsa di tipo nw policy deve avere una api version (networkin.k8s.io/v1), il kind (networkPolicy) , e deve
includere alcuni metadati (nome, namespace, il pod selector, policy type ingress o policy type egress, port, protocols
etc)

7.9. Demo: uso di una network policy


Per questa demo useremo il plugin weave o weave-net, il plugin si andrà a installare su ogni nodo del cluster. Per
vedere se il plugin sta girando correttamente digitiamo kubectl get po –n kube-system –l name=weave-net per
controllare le presenza dei pod nel namespace di sistema

Adesso creiamo un nginx deployment per esporlo tramite service e successivamente applichiamo una policy per
restringerne l’accesso:

- kubectl create deploy nginx --image=nginx per creare un deploy con il server web nginx
- kubectl expose deploy nginx --port=80 per esporre il service sulla porta 80

a questo punto proviamo a testare il servizio accedendo da un altro pod nel namespace di default, useremo un
Alpine container e interagiremo tramite una shell:

- kubectl run alpine --rm --ti –image=alpine - - /bin/sh per allocare un pod con immagine alpine, ottenere un
terminale interattivo (-ti); il pod verrà deallocato (--rm) quando chiuderemo il terminale, il - - finale indica
che il nostro comando è terminato e /bin/sh è il binario da eseguire per ottenere una shell
- al prompt digitiamo: wget - - spider - - timeout=1 nginx compando che ci permette di sapere se una
determinata url (nginx) esiste:

- digitiamo infine exit per chiudere la connessione e eliminare il pod alpine

Adesso implementiamo una policy che permetta di accedere al nostro pod (lo selezioniamo grazie con il label
selector app: niginx) a tutti i pod che abbiano una label allow=”true”
il nome della policy, in metadata, è allow-ingress; se il podSelector lo lasciamo vuoto la policy verrà applicata a tutti i
pod del namespace; applichiamo la policy con kubectl apply –f policy.yaml

usiamo nuovamente il pod alpine per interagire con un pod kubectl run alpine --rm --ti –image=alpine - - /bin/sh e
ottenuto il prompt digitiamo nuovamente il comando wget - - spider - - timeout=1 nginx

e ci accorgiamo che la nostra policy sta funzionando correttamente, ossia ci stiamo collegando da un pod senza
alcuna label e quindi la connessione in ingresso al server nginx è bloccata

a questo punto usciamo dal nostro pod (comando exit) e ne creiamo uno con label allow=true (-l sta per label):
kubectl run alpine --rm -ti -l allow=true --image=alpine - - /bin/sh

il comando wget - - spider - - timeout=1 nginx questa volta produrra il seguente output:

7.10. Package management


Un deploy in k. solitamente implica l’allocazione e la creazione di diverse risorse indipendenti, come pods, services,
deployments etc; ciascuno ha il proprio manifest yaml; Helm è un package manager di kubernetes che usa varie
astrazioni per rendere più semplice e veloce il packaging, la configurazione e il deploy di applicazioni e servizi su un
cluster k. Helm è un progetto Kubernetes ufficiale supportato dalla Cloud Native Computing Foundation,
associazione no profit che supporta il codice open source associato all’ecosistema di k.

Come sappiamo ogni sistema operativo ha il proprio package manager che aiuta a installare e aggiornare le
applicazioni, Helm fa questo per K. e offre funzionalità molto simili come node NPM o Debian Linux. Helm permette

- l’installazione di sw
- il download automatico delle relative dipendenze necessarie al sw che ci serve
- il download (fetch) dei package sw dalle repo
- la configurazione del sw deployment
- l’upgrade sel sw

I componenti che usa sono:

- helm command line tool: la user interface per accedere a tutte le funzionalità di helm
- tiller: un server component che gira sul cluster di k. e ascolta i comandi di helm per gestire le configurazioni
e il deploy di una release sw.
- Il packaging format usato da helm è chiamato charts, ed esiste un charts repository ufficiale contenente
charts pre-pacchettizzati per i più comuni progetti opensource

Gli helm packages sono normalmente chiamati charts e i charts hanno una configurazione yaml e dei template yaml

La struttura base di un chart su file system è una cartella dove manualmente le dipendenze di charts vengono
memorizzate; è meglio cmq usare requirements.yaml ub nidi da linkare dinamicamente le dipendenze.

Poi abbiamo una sottocartella template che contiene i files template che sono combinati con le configurazioni e
renderizzati nei manifest di Kubernetes. I templates usano il linguaggio di programmazione GO, linguaggio
introdotto da google; abbiamo poi il chart.yaml manifest con i metadati relativi al chart, poi il licence, il readme, i
requirements.yaml e i valori yaml (è la configurazione di default)

Il comando helm permette l’installazione di un chart da una cartella locale o da un tar.gz (versione compressa della
struttura) . I charts, come detto, possono essere scaricati e installati dalle charts repos.

Una chart repository altro non è che un sito web con un index.yaml e i file impacchettati in un tar.gz

I comandi/sottocomandi disponibili per charts sono svariati e aiutano a creare i package e i file index.yaml necessari
che possono essere distribuiti in svariati modi, tra cui un web server, un object storage server o un sito di hosting
come github. Helm è preconfigurato con la default chart repo chiamata stable, che punta a uno storage bucket, i
sorgenti di tale repo stanno su GitHub. Tramite il comando add possiamo aggiungere altre repos, ce ne sono alcune
con package sperimentali/instabili.

7.11. Demo: uso di helm


nella demo installeremo helm e lo useremo per installare i package; helm si installa usando la riga di comando sul
nodo master: sudo snap install heml - - classic l’opzione classic serve per metterlo in modalità classic e disabilitare il
security confinement.

Adesso aggiungiamo la stable helm repo add http://charts.helm.sh/stable successivamente, dopo l’installazione
digitiamo helm search repo stable per visualizzare il contenuto (i charts) della repo appena aggiunta:

adesso proviamo a installare un chart, mysql: helm install stable/mysql - -generate-name (il flag generate-name
indica a heml di generare il nome per questa releas in automatico)
come vediamo dall’output la chart è deprecata, ma va bene, perchè stiamo facendo una demo; adesso vediamo
l’elenco delle charts installate da helm con il comando helm ls

la release è stata piazzata nel namespace di default è ha il nome autogenerato mysql-.....; la chart specifica usata è la
mysql-1.6.9 con app version 5.7.30

Proviamo a guardare dentro la chart: helm pull stable/mysql (per scaricare il tar), con ls mysql* otteniamo il
seguente output

se lo scompattiamo (tar –zvxf) vedremo la struttura di templates descritta prima:

chart.yaml, values.yaml, il readme e la cartella template con il file con le note di rilascio.

Infine, per disinstallare usiamo l’apposito comando indicando il nome della chart: helm uninstall mysql....

8. Application scaling
Per scaling si intende la capacità di offrire risposta adeguata all’aumentare alle richieste, ossia di aumentare
dinamicamente le risorse hardware, il n. di applicazioni che girano in parallelo ma soprattutto la capacità del nostro
sistema di cambiare all’aumentare del workload.

Con l’approccio a containers abbiamo la flessibilità di cui abbiamo bisogno per implementare la scalabilità: i
container offrono alle app una sorta di sistema operativo virtuale, con i tool essenziali al funzionamento delle app
stesse (es. nessuna user interface), inoltre sono portabili e leggeri.

E facile fare il setup dei containers, basta un file con un manifest e poco altro, è possibile fare rapidi test per
verificare che tutto funzioni e infine deployare in un ambiente cloud per rendere le applicazioni disponibili e
accessibili.

Rappresentano un passo di modernizzazione senza necessariamente modificare le nostre applicazioni legacy. Ma il


lavoro migliore lo fanno se smontiamo la applicazione e la dividiamo in microservizi.

Elementi di kubernetes:

- cluster, fatto da un determinato numero di nodi


- nodi, macchine virtuali gestite da kubernetes
- pods, unità atomiche che vengono instanziate sui nodi
- control plan, il componente di kubernetes che si occupa di gestire pods e nodi

Componenti di kubernetes:

- kube api server: permette di interagire con le api sottostanti, in sostanza è la user interface per kubernetes
control plane
- etcd: è il key-value store per memorizzare i dati per il cluster, se decidiamo di usare etcd è meglio pensare
anche a uno store di backup
- kube scheduler: si occupa di schedulare i pod sui nodi e di trovare il nodo ideale per il deploy di ciascun pod
- kube controller manager: si occupa di monitorare le performance del sistema
- cloud controller manager: si occupa di gestire il cluster in base allo specifico cloud provider

Il kube scheduler si occupa, abbiamo detto, di selezionare un determinato nodo per ciascun pod: quando definiamo i
nodi del nostro cloud possiamo anche definirne la loro caratteristica, ossia uno o più taints che descrivono le
capacità del nodo, come ad esempio la cpu della macchina. Quando definiamo i pods possiamo specificare quella che
è chiamata la tolerance cioè quali sono le caratteristiche che i nodi devono avere per poter far girare un pod;
pertanto il kube scheduler, con il meccanismo del filtering si occupa di selezionare per ogni pod il nodo su cui può
girare (ossia quello in cui il taint fa match con la tolerance). Abbiamo poi il meccanismo dello scoring: ci può essere
infatti un nodo che risponde meglio alle caratteristiche di un pod che un altro, il kube scheduler seleziona il nodo che
ha lo score più alto per quel pod.

Componenti di un nodo che possiamo usare:

- kubelet: un agent che gira su ogni nodo, che si occupa di verificare che tutti i pod su quel nodo stiano
girando correttamente
- kube-proxy: lavora come un network proxy e gira su tutti i nodi, fa in modo che tutte le regole di rete siano
implementate e seguite, basicamente permette la comunicazione continua tra tutti i pod del cluster e anche
fuori dal cluster se necessario
- container runtime: è il software che permette l’esecuzione dei container, molto simile a docker, ma in più
kubernetes supporta containerd, CRI-O e altro

Nodi kubernetes: sono comunemente delle virtual machines ma nessuno vieta che siano delle macchine fisiche; tutti
i nodi sono gestiti da un control plane. Normalmente un cluster ha molti nodi che girano, ma cosa accade se
vogliamo aggiungere altri nodi. Ci sono 2 strategie:

- usare il componente kubelet: in pratica si fa partire l’agent sul nodo che si occuperà di registrare questo sul
control plane
- manualmente: possiamo usare le api del api server per registrare a mano il nodo sul cluster
Addon di kubernetes che si possono agganciare al nostro deployment di kubernetes:

- Web ui, la dashboard grafica che ci permette una gestione semplificata


- dns: il server per risolvere i nomi di rete; ogni nodo che parte ha incluso in automatico un server dns
- container resource monitoring: registra continuamente le risorse del sistema e le memorizza in un database,
è inclusa una ui che permette all’utente di controllare tali dati
- cluster level loggin: grazie a questo componente i log dei vari nodi sono raccolti in un unico store
centralizzato, una ui ne permette la consultazione

Adesso introduciamo l’argomento controllers: sono dei “control loops” utilizzati per monitorare lo stato di vari
aspetti del cluster, cioè nodi, jobs etc. Usando lo state information, i controller possono chiedere o anche effettuare
cambi a tali componenti quando serve; fanno in modo di spostare il sistema allo stato desiderato (portare online
nodi...).

Il controller pattern: i controller sono usati per determinare lo stato dei componenti e effettuare azioni su di essi per
portarli allo stato desiderato. Per esempio possiamo vedere un pod che fallisce, e il controller automaticamente lo fa
ripartire. Ma un buon controller pattern consiste nell’inviare messaggi all’api server e lasciare che l’api server
gestisca gli effetti conseguenti. C’è una leggera differenza, molto sottile: se ad un fallimento di un pod corrisponde un
messaggio all’api server, l’api server può intraprendere azioni del tipo notification, auditing, logging, metric
collection etc. Una delle operazioni ovviamente sarà quella di dire al controller di far ripartire il pod: nel frattempo
sono state fatte molte altre azioni importanti

8.1. Demo: deploy di kubernetes su cloud azure


Useremo il portale microsoft azure per effettuare un deployment. Assumeremo di avere accesso ad un cloud azure e
di avere i privilegi sufficienti per creare risorse. Entriamo nel portale azure con il browser e, considerando che
kubernetes è una risorsa, faremo click sul bottone “Create a resource”

A questo punto ci verrà chiesto di scegliere cosa creare: cercheremo kubernetes nel campo di ricerca e....
faremo click su create nel box corrispondente al service kubernetes;

nella pagina che segue sceglieremo di allocare il ns servizio kubernetes nel suo proprio gruppo di risorse che
chiameremo sbdemo01

successivamente diamo il nome al cluster (campo kubernetes cluster name): sbdemo01

possiamo lasciare la maggior parte dei campi con il loro default (notare possiamo scegliere una versione di
kubernetes, che può essere utile se abbiamo del sw che dipende da una specifica versione di kubernetes)

scendendo nella schermata possiamo scegliere la grandezza del nodo, ossia le grandezze delle vm che costituiranno i
nodi del nostro cluster
cliccando sul link “change size” passeremo a un pannello dove possiamo scegliere le varie tipologie di macchine:

in questa demo sceglieremo il size DS2_v2 (selezioniamo la riga e premiamo select in fondo alla pagina)

tornando alla pagina precedente notiamo come sono presenti anche le opzioni di autoscaling e quanti nodi sono
ammessi se scatta l’autoscaling:

nel nostro caso manterremo l’autoscaling attivo e il massimo numero di nodi lo imposteremo a 2; clicchiamo su next

nel tab “node pools” possiamo decidere il tipo di pools dei ns nodi; possiamo decidere di lasciare il node pool
esistente oppure crearne uno nuovo cliccando sul bottone add node pool

creiamo dunque un nuovo node pool, ci verranno chieste le seguenti info:


nome node pool (sbdemo01), node size (click su choose a size e scegliamo la tipologia DS2_v2), lasciamo invariato
tutto il resto e clicchiamo “add” in fondo allo schermo, torneremo alle schermata precedente e selezioneremo il
checkbox corrispondente:

passiamo alla tab successiva, “autentication tab” dove possiamo scegliere come autenticare l’utente; possiamo
scegliere “service principal” come utente o applicazione o System assigned identity (raccomandato, ms gestirà lo
strato di autenticazione, meno lavoro per noi)

possiamo scegliere anche di usare il role-based access control e configurare anche le impostazioni di cifratura per
ogni disco usato dai nodi: lasciamo tutto com’è e passiamo al tab successivo: networking
in questo tab possiamo decidere come gestire il traffico da e verso il cluster: possiamo usare kubenet che creerà la
propria vnet oppure Azure cni per agganciare tutto ad una vnet esistente. Il load balancing è già gestito, ma
possiamo decidere di abilitare l’application routing da http, settare un range di ip autorizzati ad accedere al cluster;
lasciamo tutto com’è e clicchiamo su next per passare al tab “integrations” dove possiamo scegliere ulteriori servizi
di azure che si integrano con il cluster kubernetes. Normalmente k. è usata per gestire e tirare su immagini, per cui
la prima cosa da fare è collegarla ad un container registry o crearne uno nuovo; nel nostro caso lasciamo “none”;

nella sezione successiva possiamo abilitare il monitoraggio del cluster con azure monitor e le policy per aumentare i
controlli di sicurezzaal nostro cluster; lasciamo tutto com’è e clicchiamo sul bottone review + create;

uscirà una schermata riassuntiva e se tutto torna possiamo cliccare sul bottone create per procedere alla creazione;

dopo la creazione del cluster (ci vorranno alcuni minuti) ci verrà mostrata la schermata in cui ci viene proposto di
andare alla pagina della ns risorsa o collegarsi al cluster; clicchiamo sul go to resource
Azure mostrerà tante info relative al ns cluster come se fosse qualsiasi altra risorsa con activity logs, access control,
tags etc

Ci sono anche alcune risorse specifiche di kubernetes come i namespaces, workloads, storage etc; ancora più sotto i
settings per effettuare il tuning che adesso vediamo.

Ipotizziamo di essere tornati nella schermata principale di azure; per riprendere il lavoro ci basta cercare il nostro
cluster sbdemo01 nella’apposito campo di ricerca:
Tra i risultati vediamo che c’è il nostro kubernetes service, ci clicchiamo su...entriamo nella schermata di overview
con le info relative alla versione di k. l’indirizzo dell’api server, la localtion, il tipo di sottoscrizione; seguono dei tab di
dettaglio...

tab properties: vengono mostrate info di alto livello del ns cluster tra cui la strategia di encryption, i pools, tipologia
dei nodi, informazioni di rete relativi a pod, dns, services etc

tab monitoring: vengono mostrate alcune metriche (cpu, traffico, memoria etc) del nostro cluster

tab capabilities: mostra le feature che abbiamo abilitato sul cluster, monitoring e autoscaling, non abbiamo abilitato
la azure policy, la possiamo abilitare andando a cliccare sul link configure se vogliamo, oppure disabilitare quando lo
desideriamo le feature attive
tab recommendation: qui ci stanno le notifiche che ci aiutano a seguire le best practice per avere performance
ottimali (costi, availability etc)

Adesso nella barra dei menù di sx clicchiamo su namespaces:

qui vediamo graficamente quello che avevamo visto a riga di comando in precedenti demo: i namespaces di k di un
cluster appena creato, default, public, system, node-lease; notare come azure ci permette di definire facilmente
nuovi namespaces senza usare la riga di comando. Andiamo al menù workloads (sotto namespaces):
qui possiamo vedere altre info dettagliate del nostro cluster separati per tab: deployments, pods, replicasets...per il
momento vediamo solo roba di sistema, usata da kubernetes per manutenere il nostro cluster; passiamo al menù
services e ingresses

è una sezione molto importante su cui vale la pena perdere un po’ di tempo in quanto sono impostazioni che ci
permettono la comunicazione tra i componenti di kubernetes: i servizi sono legati a un namespace, è buona pratica
legare sempre un servizio a un namespace (evitando quello di default); ogni service è visualizzato con un link,
attraverso cui possiamo accedere alla schermata di dettaglio. Vediamo ad esempio “kubernetes”:

vediamo porte, pods e altri dettagli del servizio in una schermata riassuntiva, cliccando su yaml accediamo al
manifest del servizio
Ogni servizio è configurabile con un yaml, da qui possiamo intervenire sulla configurazione del servizio e
successivamente cliccare sul bottone review + save. Torniamo adesso alla schermata generale e ci spostiamo nella
sezione node-pools: qui possiamo controllare lo stato dei pool di nodi, in questo caso abbiamo creato il node pool
sbdemo01 che è funzionante (vediamo info come il node size, il sistema operativo...)

Possiamo visualizzare i singoli nodi cliccando sul tab nodes, qui vediamo le nostre vm e il loro stato

da questa schermata possiamo fare lo scaling del notro pool o anche aggiungere nuovi pool; passiamo al menù
cluster configuration:
qui possiamo modificare la versione di k e attivare il servizio aggiuntivo di autenticazione Azure Active Directory
(operazione non reversibile).

8.2. Demo: usare kubectl


Adesso vediamo come si usa il kube control command line tool per eseguire comandi sul cluster di kubernetes.
Useremo la powershell (c’è da tenere presente che la riga di comando è presente anche in altri prodotti come google
e aws) assumendo che sul nostro sistema abbiamo già installato Docker e Kube control.

Collegamento al cluster kubernetes: con azure per prima cosa dobbiamo autenticarci, per farlo dobbiamo lanciare un
Azure Cli Kubernetes command per configurare le credenziali e specificare il kube control che vogliamo usare:

- az login effettua la login su azure, su ambiente windows otterremo un popup che ci consentirà la login in
ambiente microsoft, chiuso il browser e ritornati alla power shell

Adesso dobbiamo configurare la sessione per collegarci al cluster kubernetes:


- az aks get-credentials - - resource-group sbdemo01 - - name sbdemo01 dove “name” viene seguito dal
nome del cluster e “resource-group” dal gruppo di risorse dove sta girando

Le nostre credenziali sono adesso configurate, a questo punto possiamo interagire con kubelet, il nostro kube
control

- kubelet get nodes per ottenere l’elenco dei nodi disponibili nel cluster (4 nodi, tutti funzionanti)

- kubelet get services per ottenere l’elenco dei servizi attualmente attivi (c’è solo quello di default, ossia
kubernetes)

- kubectl describe node <nomenodo> ci fornisce diversi dettagli di un nodo tra cui, nella parte finale le risorse
usate dal nodo:

nell’ipotesi di avere docker attivo digitiamo git clone https://github.com/Azure-Samples/azure-voting-app-redis.git


per scaricare i sorgenti e successivamente esploriamo la cartella appena creata
cd .\azure-voting-app-redis\

usiamo docker-compose up –d per creare l’immagine e dopo aver ottenuto nuovamente il prompt verifichiamo la
presenza della immagine con docker images

l’immagine è presente, adesso vediamo se i containers stanno girando digitando docker ps

abbiamo 2 containers: azure-vote-front e azure-vote-back; adesso verifichiamo il funzionamento tramite browser


andando alla url http://localhost:8080/
si tratta di una semplice app per votare per cane o gatto; nulla di speciale ma è cmq un container che sta girando in
locale e che adesso deployeremo sul nostro cluster; torniamo dunque alla powershell per creare una azure container
registry dentro cui mettere la nostra immagine:

- az acr create - - resource-group sbdemo01 - - name sbdemo01 - - sku Basic crea un Azure container registry
chiamato sbdemo01 nel nostro resource group sbdemo01 usando il Basik sku

A questo punto possiamo effettuare la login dentro questo container registry:

- az acr login - - name sbdemo01

A questo punto dobbiamo taggare la nostra docker image locale sul container registry:

- az acr list --resource-group sbdemo01 --query space "[].{acrLoginServer:loginServer}" --output table per
visualizzare l’acrLoginServer ossia il server di login per accedere al container registry remoto

adesso leghiamo l’immagine (la lista si ottiene digitando docker images) al server sbdemo01.azureacr.io

- docker tag mcr.microsoft.com/azuredocs/azure-vote-front:v1 sbdemo01.azurecr.io/azure-vote-front:v1


cambiando così il tag dalla versione data da microsoft a quella nostra personalizzata
- docker push sbdemo01.azurecr.io/azure-vote-front:v1 per fare la push dell’immagine sul container registry
non abbiamo agganciato ancora il nostro container registry al nostro cluster, per fare questo digitiamo

- az aks update -n sbdemo01 -g sbdemo01 --attach-acr sbdemo01 agganciando il nostro container registry
sbdemo01 al nostro cluster sbdemo01

adesso possiamo interagire con kubernetes per fare il deploy del manifest, ma prima di tutto dobbiamo aggiornarlo
in quanto è configurato per puntare all’immagine locale e non a quella del nostro container registry: editiamo
dunque azure-vote-all-in-one-redis.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: azure-vote-back
spec:
replicas: 1
selector:
matchLabels:
app: azure-vote-back
template:
metadata:
labels:
app: azure-vote-back
spec:
nodeSelector:
"beta.kubernetes.io/os": linux
containers:
- name: azure-vote-back
image: mcr.microsoft.com/oss/bitnami/redis:6.0.8
env:
- name: ALLOW_EMPTY_PASSWORD
value: "yes"
ports:
- containerPort: 6379
name: redis
---
apiVersion: v1
kind: Service
metadata:
name: azure-vote-back
spec:
ports:
- port: 6379
selector:
app: azure-vote-back
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: azure-vote-front
spec:
replicas: 1
selector:
matchLabels:
app: azure-vote-front
strategy:
rollingUpdate:
maxSurge: 1
maxUnavailable: 1
minReadySeconds: 5
template:
metadata:
labels:
app: azure-vote-front
spec:
nodeSelector:
"beta.kubernetes.io/os": linux
containers:
- name: azure-vote-front
image: mcr.microsoft.com/azuredocs/azure-vote-front:v1
ports:
- containerPort: 80
resources:
requests:
cpu: 250m
limits:
cpu: 500m
env:
- name: REDIS
value: "azure-vote-back"
---
apiVersion: v1
kind: Service
metadata:
name: azure-vote-front
spec:
type: LoadBalancer
ports:
- port: 80
selector:
app: azure-vote-front

- nella sezione deployment va modificato il nome dell’immagine per puntare al nostro container registry:
sbdemo01.azurecr.io/azure-vote-front:v1 (era mcr.microsoft.com/azuredocs/azure-vote-front:v1)

a questo punto digitiamo kubectl apply –f azure-vote-all-in-one-redis.yaml e aspettiamo che il deploy venga
completato:

a questo punto verifichiamo a quale ip risponde la ns applicazione: kubectl get service azure-vote-front - - watch

l’ip esterno del load balancer è 20.150.155.177; se lo digitiamo nella barra degli indirizzi del nostro browser
otterremo la stessa schermata dell’immagine che girava in locale su docker.
8.3. Demo: usare le configMaps per configurare
un container kubernetes
useremo le config maps per configurare un container con parametri di esecuzione dinamici piuttosto che cablati
dentro: è il caso di un software che vogliamo vendere a diversi clienti che hanno la medesima necessità, quello che
serve è cambiare la configurazione (e non il codice) al cambio di cliente: entra in gioco la configMap

per fare la demo useremo il portale azure; apriamo il browser, entriamo in azure e andiamo a prendere il cluster che
abbiamo messo in piedi nelle demo precedenti: adesso imposteremo una configMap. Andiamo nel menu
“configuration” e visualizziamo le configMaps già presenti, ne esistono di versioni differenti, noi metteremo in piedi
quella più semplice basata su key-value pairs (coppia chiave-valore)

clicchiamo su add e ci viene mostrato l’editor

a questo punto possiamo scrivere un yaml o un json per definire la nostra config map

definiamo una ConfigMap (kind) con nome (name) sbdemo01-config, avente come dati foo: bar e baz: qaz
cliccando in basso sul bottone add la configMap viene aggiunta all’elenco precedente, adesso la faremo usare da un
pod dummy; passiamo alla sezione workloads del portale azure e clicchiamo sulla tab “pods”

possiamo editare uno di quelli che compaiono nella lista e passare la ns configurazione oppure crearne uno nuovo
cliccando sul bottone add in cima; ancora una volta ci viene proposto l’editor yaml dentro cui scriviamo la definizione
del ns pod: (kind pod, name sbdemo01-pod)

nella sezione spec abbiamo definito un container con nome sbdemo01-container che punta all’immagine
gcr.io....busybox e che dovrà essere eseguito con il comando /bin/sh –c env

subito dopo descriviamo cosa dovrà contenere l’enviroment (sezione env):

la prima variabile, chiamata SPECIAL_LEVEL_KEY prenderà il valore dalla configMap con nome sbdemo01-config, ed
esattamente dal valore corrispondente alla chiave foo;

la seconda, SPECIAL_TYPE_KEY prenderà il valore dalla configMap con nome sbdemo01-config, ed esattamente dal
valore corrispondente alla chiave baz

infine, nella sezione envFrom mostriamo un altro modo di fornire le variabili di enviroment specificando
esplicitamente il nome della configMap
A questo punto facciamo click sul bottone add per aggiungere il pod. Torneremo all’elenco dei pod, cerchiamo quello
appena aggiunto e ci clicchiamo sopra:

scorrendo verso il basso lo schermo vediamo che il pod è in stato “terminated” in quanto è stato eseguito tutto
quello che c’era da eseguire; andiamo a vedere sui live-logs (quarta voce del menù) che cosa è accaduto

ci verrà chiesto di selezionare un pod specifico

siccome il pod è terminato non vedremo altri log in tempo reale, abbiamo bisogno di vedere gli historical logs
cliccando sull’apposito link (log analytics). Nella schermata dei log analitici vediamo una query precompilata per
visualizzare i log scritti negli ultimi 20 minuti (riga2) dal container in questione (riga 3) ordinati cronologicamente
(riga 4)
nel caso delle variabili di enviroment, per ogni variabile impostata viene scritta una riga di log; modifichiamo la query
per visualizzare la riga di log (logEntry) e le ordiniamo per logEntry piuttosto che cronologicamente

lanciando la query ecco che vediamo le entry che ci interessano: sono state impostate le variabili foo, baz, prese
direttamente dalla nostra configMap e le variabili SPECIAL_... andando a pescare i valori dalla ns configMap

8.4. Demo: configurare liveness e readiness


probes per i containers
modificheremo i nostri attuali deployments e guarderemo i log in tempo reale per verificare quello come funziona il
probing, vedremo inoltre i nostri probes in azione quando un container si trova in uno stato non valido

apriamo una powershell; nella demo precedente abbiamo deployato una immagine docker che ci permette di votare
per cane o gatto; l’immagine è renderizzata come una semplice pagina web; quello che vogliamo crare adesso è una
readiness probe ossia un oggetto che farà una query al pod dove sta girando la nostra immagine per verificare se sta
girando correttamente; riapriamo il nostro yaml e andiamo nella sezione deployment del front-end
...
apiVersion: apps/v1
kind: Deployment
metadata:
name: azure-vote-front
spec:
replicas: 1
selector:
matchLabels:
app: azure-vote-front
strategy:
rollingUpdate:
maxSurge: 1
maxUnavailable: 1
minReadySeconds: 5
template:
metadata:
labels:
app: azure-vote-front
spec:
nodeSelector:
"beta.kubernetes.io/os": linux
containers:
- name: azure-vote-front
image: mcr.microsoft.com/azuredocs/azure-vote-front:v1
ports:
- containerPort: 80
resources:
requests:
cpu: 250m
limits:
cpu: 500m
env:
- name: REDIS
value: "azure-vote-back"
...

vediamo che viene deployato 1 container, azure-vote-front; esattamente in questa sezione aggiungeremo un probe
readiness che ci dirà se il container è pronto o meno; il probe readiness serve a evitare che altri sistemi accedano al
nostro container finchè non è dichiarato pronto ecco quello che aggiungeremo tra “- containerPort: 80” e
“resources:” con l’opportuna indentazione
readinessProbe:
httpGet:
path: /
port: 80
periodSeconds: 3
timeoutSeconds: 10

in pratica questo probe dice di monitorare la root sulla porta 80, ogni 3 secondi, con un timeout massimo di 10 per la
risposta; chiudiamo questo file, salvandolo, e nel prompt digitiamo kubectl apply –f <filename>

il sistema ci notificherà la modifica del deployment azure-vote-front; adesso andiamo sulla pagina azure e andiamo a
navigare nella sezione kubernetes del workload:
chicchiamo sul nostro “azure-vote-front” deployment e nella schermata che otteniamo andiamo nella sezione live-
logs e selezioniamo un pod dal menù a tendina

i log che vedremo in basso provengono dal probe che abbiamo definito poco fa

otteniamo una risposta http 200...con questo readiness probe sappiamo quando il nostro pod è pronto a
rispondere...ma come sappiamo se il pod sta funzionando ed è in salute? abbiamo bisogno di un liveness probe;
nello yaml di poco fa, togliamo sostiuiamo il readiness probe con un liveness probe:
livenessProbe:
httpGet:
path: /
port: 80
periodSeconds: 3
timeoutSeconds: 10
non è cambiato molto: la sostanza è la stessa ma il probe è di diversa natura: se il nostro container non è
funzionante, grazie a questo probe partirà la procedura di restart

ancora una volta usiamo il comando kubectl apply –f <filename> per applicare le modifiche
torniamo sul nostro portale azure nella sezione overview del nostro deployment:

questa volta in fondo alla schermata vedremo che il pod sta girando e funzionando correttamente:

nella sezione live-logs:


il nostro probe sta funzionando correttamente e il nostro pod è in salute; adesso proviamo a vedere in azione il
nostro liveness probe come si occupa di riavviare il nostro pod: basta semplicemente modificare il path su cui fare il
check e usare path: /foo ossia una url che non esiste (il nostro container tornerà un http 404) facendo fallire il nostro
livenessProbe; ancora una volta salviamo il file e facciamo l’apply

nella sezione overview dopo aver fatto refresh più volte vedremo che il restart count inizia a salire e che il pod da
ready passerà a not ready e lo stato sarà crashLoopBackOff che significa che non riavvierà all’infinito: dopo 3 riavvii
se il pod non viene ritenuto funzionante verrà disattivato

8.5. Demo: configurare taint e toleration


Taints e tolerations si riferiscono sempre ad un pool di nodi; nel nostro portale azure abbiamo un paio di node pools;
clicchiamo sul pool sbdemo01

notare come nella schermata che viene fuori c’è una sezione con taints e labels:
nessun taint e label è definito; i taints sono applicati a tutti i nodi di un pool, una toleration specifica quale pool di
nodi o quali nodi possono essere usati per eseguire i nostri lavori. Immaginamo ad esempio di voler far girare un pod
su nodi che hanno la proprietà noSchedule sulla gpu:

creiamo un nuovo pool di nodi digitando: az aks nodepool add --resource-group sbdemo01 --cluster-name
sbdemo01 --name sbdemo0111 --node-count 1 --node-taints foo=bar:NoExecute --no-wait

questo comando chiede ad azure di aggiungere (add) un nuovo nodepool al resource group sbdemo01 e al cluster
sbdemo01; tale nodepool si chiamerà sbdemo0111, avrà un solo nodo e su di esso verrà definito il taint
foo=bar:NoExecute

il taint definito su questo pool in buona sostanza dice a kubernetes di non eseguire alcun pod su tale nodo a meno
che non venga fatto in un modo particolare (che vedremo).

Andiamo a vedere cos’è successo sul portale:

nella lista dei node pools c’è un nuovo nodepool in fase di creazione, entriamo dentro il nodepool appena creato e
vediamo che nel dettaglio dei taints c’è il taint definito prima:

Solo i pod che “tollereranno” tale taint potranno essere deployati su tale node-pool, altrimenti finiranno sull’altro
che non ha taint definiti. Adesso modifichiamo tale pool definendo un taint anche per esso:

anzitutto con kubectl get nodes vediamo quali sono i nodi del nostro cluster:

adesso aggiungiamo un taint a un singolo nodo: kubectl taint nodes aks-sbdemo01-32189398-vms000000 foo=
123:NoExecute

ripetiamo la stessa cosa per i nodi ...01 e ...02 con taints diversi:
a questo punto riprendiamo il nostro vecchio yaml e guardiamo la porzione che riguarda il deploy del frontend
(togliamo il probe della precedente demo)
...
apiVersion: apps/v1
kind: Deployment
metadata:
name: azure-vote-front
spec:
replicas: 1
selector:
matchLabels:
app: azure-vote-front
strategy:
rollingUpdate:
maxSurge: 1
maxUnavailable: 1
minReadySeconds: 5
template:
metadata:
labels:
app: azure-vote-front
spec:
nodeSelector:
"beta.kubernetes.io/os": linux
containers:
- name: azure-vote-front
image: mcr.microsoft.com/azuredocs/azure-vote-front:v1
ports:
- containerPort: 80
resources:
requests:
cpu: 250m
limits:
cpu: 500m
env:
- name: REDIS
value: "azure-vote-back"
...
Adesso aggiungiamo la toleration dopo la sezione env
...
env:
- name: REDIS
value: "azure-vote-back"
tolerations:
- key: "foo"
operator: "Equal"
value: "bar"
effect: "NoExecute"
...
questo significa che questo deployment girerà su pod di un nodo che ha un taint definito in questo modo o che non
ha alcun taint; pertanto se faremo l’apply di questo manifesto il container girerà solo sul nuovo nodo appena
definito. Dopo aver applicato il manifest andiamo su azure nella sezione workloads:
chicchiamo sul deployment azure-vote-front, cliccheremo sul link in fondo alla schermata (dove ci stanno i pod
instanziati dal nostro deployment):

notare come alla voce “node” c’è il nodo sbdemo0111, che è il nodo che ha il taint che fa match con la toleration
definita nel deployment

altro esempio:
...
env:
- name: REDIS
value: "azure-vote-back"
tolerations:
- key: "foo"
operator: "Equal"
value: "789"
effect: "NoExecute"
...
questa volta la toleration farà match con la taint definita nel node-pool sbdemo01; facciamo la apply

sul portale azure torniamo al deployment:


andiamo in basso, clicchiamo sul pod

ed ecco che alla voce node vediamo come nodo aks-sbdemo01....002

8.6. Demo: setup dell’autoscaling di kubernetes


Per fare questa cosa andiamo sul portale azure e modificare il cluster che abbiamo creato nelle demo precedenti
nel menù del nostro cluster sbdemo01 andiamo in basso e selezioniamo la voce node pools:

Con il node pool abbiamo la possibilità di scalare il numero di nodi all’occorrenza: il pool sbdemo01 ha per adesso 3
nodi, se ci clicchiamo su entriamo nella schermata di configurazione del node pool

esiste la voce autoscaling “disabled”; ipotizziamo di avere un sito che con traffico che cambia, per cui abbiamo
bisogno di un certo numero di nodi in base alle esigenze del momento, i nodi costano pertanto bisogna inventare
una cosa dinamica; in alto nella pagina abbiamo la voce “scale node pool”, se ci clicchiamo su si aprirà una schermata
come quella che segue
il metodo di scalin in questo momento è impostato a manual, e abbiamo 3 nodi allocati a prescindere dalle nostre
necessità reali; se ad esempio la ns necessità è 1, possiamo modificare a mano il valore 3 e fare apply

nella parte alta dello schermo verrà mostrata una notification, ci verrà comunicato l’evento di scaling; aspettiamo
finchè il lavoro non è completato

nella schermata di overview del nostro node pool il numero di nodi è diventato 1;

se clicchiamo nuovamente sul bottone scale node pool, proviamo adesso a passare alla modalità di scaling
automatica:

adesso possiamo scegliere numero minimo e massimo di nodi; è sconsigliato mettere a 0 il numero minimo di nodi,
perchè significa spegnere tutte le macchine virtuali e se quindi arriva una richiesta ci vorrà un po’ prima che venga
evasa (deve essere fatto lo startup di una vm); nella parte di destra settiamo a 2 il numero massimo di nodi e
clicchiamo su apply nella parte bassa della schermata; tra le notification troveremo il messaggio che ci dice che lo
scaling è stato applicato

nella sezione overview non è cambiato nulla: abbiamo solo 1 nodo che sta girando:

8.7. Demo: configurare kubernetes con un


multipod deployment
riprendiamo quanto abbiamo lasciato sul portale azure nelle precedenti demo e agiamo sul kubectl per aggiornare le
impostazioni:
andiamo nella sezione workload per vedere il deployment:

clicchiamo sul nostro azure-vote-front deployment

in basso vediamo l’elenco dei pod su cui sta girano la nostra immagine, in questo caso il pod è uno solo, su un solo
nodo; adesso proviamo a deployare l’immagine su più pods (scaling orizzontale): basta modificare il numero di
repliche per avere il numero di pods che desideriamo sul manifest yaml
...
apiVersion: apps/v1
kind: Deployment
metadata:
name: azure-vote-front
spec:
replicas: 1
selector:
matchLabels:
app: azure-vote-front
strategy:
rollingUpdate:
maxSurge: 1
maxUnavailable: 1
minReadySeconds: 5
template:
metadata:
labels:
app: azure-vote-front
spec:
nodeSelector:
"beta.kubernetes.io/os": linux
containers:
- name: azure-vote-front
image: mcr.microsoft.com/azuredocs/azure-vote-front:v1
ports:
- containerPort: 80
resources:
requests:
cpu: 250m
limits:
cpu: 500m
env:
- name: REDIS
value: "azure-vote-back"
...
modifichiamo replicas a 2, facciamo l’apply del nostro manifest con kubectl apply –f ....

torniamo sul nostro portale azure e facciamo refresh della schermata del nostro deployments vediamo che i pods
sono diventati 2:

stessa cosa nella schermata dei deployments:


se passiamo al tab dei pods vedremo nella lista 2 pods di tipo azure-vote-front:

9. Il packaging di Kubernetes
l’utilizzo dei package oggiggiorno è molto diffuso perchè ci evita di scrivere inutilmente codice per implementare
comunicazioni con una api o un database; in pratica la soluzione è integrare i package già disponibili nella nostra
applicazione (la nostra applicazione stessa può essere gestita come un package) e qui entra in gioco il package
management grazie ad una serie di tools. Usiamo ad esempio il package manager per

- installare nuovi package nella ns soluzione


- installare la nostra applicazione come package in kubernetes
- aggiornare i packages della nostra soluzione all’ultima versione
- configurare/riconfigurare i package con particolari impostazioni personalizzate
- rimuovere i package
- risolvere le dipendenze necessarie di ogni package (e della ns applicazione) affinchè tutto funzioni

Il package managemet è una cosa complessa, ecco perchè abbiamo diversi tools a disposizione.

- le software repositories: sono componente essenziale per il package management, dentro ci stanno le varie
versioni dei package e gli eventuali aggiornamenti
- binary repository managers: non tutto è codice sorgente, in alcuni casi abbiamo codice compilato
- app stores: molto utile per applicazioni pubbliche, i package managers si integrano con gli app stores per
tirare giù il software di cui abbiamo bisogno

Funzionalità di base del package manager:

- estrazione archivi compressi


- verifica checksum e certificati digitali per l’autenticità e integrità del software
- download/install/update software
- raggruppamento packages per funzionalità
- gestione delle dipendenze tra packages

Quando dunque parliamo di container il packaging diventa importante; ipotizziamo di avere un enviroment di
staging dove facciamo i nostri test dell’applicazione e un ambiente di produzione dove ci finisce la versione finale; in
questo scenario abbiamo differenti configurazioni che vanno gestite opportunamente (es in ambiente di testing
avremo bisogno di packages di debug che in prod non useremo); tramite il sistema di packaging di kubernetes
possiamo deployare il nostro codice su ambienti diversi con enviroment diversi, usando per ciascun ambiente un set
di packages personalizzato.

Di conseguenza i container possono usare templates differenziati per ciascun tipo di ambiente; i templates
includono le istruzioni per il deploy e le variabili di ambiente; il problema potrebbe essere la gestione di questi
templates e in alcuni casi non riescono a soddisfare tutte le esigenze senza contare che lo yaml potrebbe diventare
complicato e illegibile, la cosa si complica se gli enviroments sono più di due (staging e prod).

Altra complicazione ce la da la gestione del versionamento dei package sui diversi ambienti.

Tutte le problematiche sono gestibili grazie al packaging di kubernetes: helm.

Risolve molti dei problemi descritti prima tra cui la gestione dei package tra deployments:

- meccanismo di dependency management: per permettere il funzionamento corretto del sw


- sottostistema di gestione delle secrets: per mantenere segrete le password e le info sensibili
- templating language (yaml e json): tramite cui diciamo a helm cosa fare
- repository support, aiuta a impacchettare e versionare la nostra applicazione in modo da poter essere
installata anche su enviroments remoti

9.1. Demo: Manifesti yaml


In questa demo vedremo come funzionano i manifesti yaml, utilizzeremo azure e kubernetes.

Iniziamo aprendo la nostra powershell: useremo la linea di comando (kubectl) per fare il deploy di una applicazione
su kubernetes tramite un semplice file manifesto; partiremo da qualcosa di già pronto, un deployment chiamato
sbdemo02 a cui adesso ci colleghiamo digitando az login (connessione ad azure, si aprirà browser per
l’autenticazione, poi si ritorna alla nostra powershell)

adesso possiamo chiamare kubectl per controllare la nostra istanza di kubernetes: az aks get-credentials –g
sbdemo02 –n sbdemo02 (resource group e cluster chiamati sbdemo02) in questo modo kubernetes aggiunge al file
locale di configurazione la definizione del cluster e seleziona il cluster come default, per evitare di compiere azioni su
altri cluster.

Adesso vediamo quali sono i nodi definiti nel cluster: kubectl get nodes

adesso chiediamo al nostro cluster l’esecuzione di una applicazione, lo facciamo creando un manifest file tramite
notepad o con il comando code <nomefile>.yml per utilizzare visual studio

apiVersion: apps/v1
kind: Deployment
metadata:
name: azure-vote-back
spec:
replicas: 1
selector:
matchLabels:
app: azure-vote-back
template:
metadata:
labels:
app: azure-vote-back
spec:
nodeSelector:
"kubernetes.io/os": linux
containers:
- name: azure-vote-back
image: mcr.microsoft.com/oss/bitnami/redis:6.0.8
env:
- name: ALLOW_EMPTY_PASSWORD
value: "yes"
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 250m
memory: 256Mi
ports:
- containerPort: 6379
name: redis
---
apiVersion: v1
kind: Service
metadata:
name: azure-vote-back
spec:
ports:
- port: 6379
selector:
app: azure-vote-back
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: azure-vote-front
spec:
replicas: 1
selector:
matchLabels:
app: azure-vote-front
template:
metadata:
labels:
app: azure-vote-front
spec:
nodeSelector:
"kubernetes.io/os": linux
containers:
- name: azure-vote-front
image: mcr.microsoft.com/azuredocs/azure-vote-front:v1
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 250m
memory: 256Mi
ports:
- containerPort: 80
env:
- name: REDIS
value: "azure-vote-back"
---
apiVersion: v1
kind: Service
metadata:
name: azure-vote-front
spec:
type: LoadBalancer
ports:
- port: 80
selector:
app: azure-vote-front
abbiamo definito 2 deployment (backend e frontend) e 2 services (uno per ciascun deployment)

nella sezione spec notare

nodeSelector:
"kubernetes.io/os": linux

che dice che abbiamo bisogno per il deploy di nodi linux, l’immagine che il backend userà è
mcr.microsoft.com/oss/bitnami/redis:6.0.8, ossia una distribuzione di redis, nella sezione resource spieghiamo quali
e quante risorse abbiamo bisogno (mem cpu)

la separazione tra deploy e service viene fatta con tre linee; il service che identifica i pod di backend (rif. selector)
rende pubblico l’accesso ai pod attraverso la porta 6397 sul nostro cluster

nel blocco successivo definiamo il deployment di frontend chiamato azure-vote-front che necessita di nodi con
sistema operativo linux (sezione spec->nodeSelector), l’immagine, come quella del backend, la prendiamo dalla repo
di microsoft: la versione v1 dell’immagine azure-vote-front; definiamo poi (sez. resources) le risorse che ci servono,
la porta alla quale il container risponderà (containerPort), infine, nella sezione env diciamo al frontend dove trovare
il database Redis di cui ha bisogno (name: REDIS, value: azure-vote-back) indicando il servizio che deve cercare nel
cloud per accedere ai pod di backend

infine il servizio di load balancing che espone il frontend all’esterno sulla porta 80

adesso passiamo tutto a kubernetes semplicemente con il comando kubectl apply –f <nomefile yaml>

aspettiamo che kubernetes faccia il suo lavoro, digitando periodicamente kubectl get service azure-vote-front

quando tutto sarà pronto otterremo l’informazione relativa a ip e porta accessibili dall’esterno (tramite browser)

9.2. Il motore di Google Kubernetes per il


continous delivery
Considerando il fatto che kubernetes è il motore più popolare per orchestrare container management e deployment,
è supportato dai maggiori distributori di servizi: google cloud platform (GCP) ha una implementazione di kubernetes
chiamata google kubernetes engine (GKE) un tool che offre

- enviroment gestito per maneggiare i containers


o deployment
o gestione configurazione, liveness, readyness, etc
o scaling
- gestione di più macchine: sono raggruppate in un cluster, nel caso di google sarà chiamato GKE cluster,
google gestisce il cluster a modo suo;
o compute engine instances sono le macchine che formano parte del cluster GKE

Parliamo dunque dei cluster GKE: i cluster GKE sono di fatto cluster kubernetes (non tutti i cluster k. sono cluster
gke); i benefici offerti sono quelli classici di kubernetes

- gestione automatizzata del cluster (nodi, pods etc)


- monitoraggio e liveness probes
- scaling automatizzato
- gestione del rolling update del software con tempi di down minimi

ma ci sono differenze rispetto a kubernetes “regular”:

- load balancing gestito da google e built-in (pronto all’uso)


- node pools: in kubernetes non c’è il concetto dei pool di nodi, con gke possiamo gestire più nodi in una sola
volta
- scaling automatico di nodi e pools
- upgrade automatico delle risorse di computazione che stanno sotto
- node auto-repair: se il nodo ha problemi viene messo offline e riparato
- logging e monitoring: dei vari componenti del cluster, in modo da poter fare diagnosi e troubleshooting

per quanto riguarda l’upgrade automatico della versione anche kubernetes viene aggiornato appena esce una nuova
versione; google ci permette di decidere se aggiornare k. con le release alfa e testare quindi le ultime funzionalità,
oppure le versioni beta, stabili ma non pienamente testate oppure se effettuare l’aggiornamento solo con versioni
stabili

Altro concetto introdotto da gke è i gke workloads: anche nel k. normale applicazioni e componenti sono spesso
classificati come “workload”, ma deployare un workload in gke significa anzitutto impacchettare il workload dentro
un container usando ad esempio docker

- la feature di continous integration di google permette di fare la build dei containers; possiamo inoltre
collegare google a diverse source repositories che ci aiutano a costruire i ns container a partire dal codice
- la feature di continous delivery permette il deploy dei container implementando così il loop CI/CD
- possiamo memorizzare i ns container in artifact registries o container registries e collegare google per
deployare le nostre container images

Dipende da flessibilità, tempi di risposta e controllo, possiamo decidere un “mode of operation” del nostro cluster,
con gke ne abbiamo 2:

- Autopilot: il cluster è interamente gestito in automatico permettendoci di concentrarci sul workload (tutto
preconfigurato per avere rese ottimali ready-to-go), pagheremo solo per le risorse che andremo ad usare
- Standar: abbiamo il controllo completo del cluster e dei suoi componenti, può essere più costoso in quanto
paghiamo i nodi che usiamo ma abbiamo controllo di tutto quello di cui abbiamo bisogno

9.3. Amazon EKS e il continous delivery


Anche amazon mette a disposizione la sua versione personalizzata (Distro) di kubernetes: Elastic Kubernetes Service.
Possiamo usare gli aws outpost per avere dei cluster on-premise o on the edge, abbiamo anche aws wavelength e
EKS anywhere nuovo tool che consentono una esperienza di gestione cluster.

Benefici di eks:

- availability: il cluster è distribuito tra diverse availability zones e il control plane ripristina velocemente i nodi
non funzionanti riducendo gli sla di downtime
- provision efficient: eks gestisce i gruppi di nodi come node-pools, possiamo usare fargate per provisionare in
automatico il servizio di compute-as-demand usando le Amazon EC2 Spot instances e risparmiando in costi
- enviroment sicuro: aks automaticamente applica le patch al nostro cluster, incluse le patch di sicurezza
(hanno una community molto attiva)

Kubernetes deployments

- deploy eks nel cloud enviroment aws: usiamo eks per creare un cluster kubernetes, fargate per il deploy di
serveless containers, ec2 deploy per i nodi e infine far partire il nostro cluster kubernetes. Sono dei cloud
tools che ci permettono di usare a ns vantaggio la eks dashboard e la aws console per monitorare ed
esplorare il nostro cloud k.
- eks anywhere è un altro modo di deployare kubernetes, ci permette di usare eks distro clusters per gestire il
nostro k. cluster: è chiamato anywhere perchè i nodi possono essere cloud-based, on premises virtual
machines o istanze AWS EC2. Dopo il setup del cluster, kubernetes funziona come sempre, possiamo usare la
eks dashboard e la aws console
- your own tools: è la terza opzione, download e build del cluster kubernetes con il nostro sistema e i nostri
tools, gestione manuale dei nodi in enviroment personalizzato; è simile a eks anywhere eccetto per il fatto
che siamo responsabili della ns infrastruttura

EKS Hybrid Deployment: è il caso di compagnie che migrano progressivamente a cluster amazon, possiamo usare eks
che permette la gestione di cluster kubernetes anche se le applicazioni o i server stanno nel nostro data center;
usando eks anywhere possiamo far girare kubernetes contemporaneamente su cloud e nel ns datacenter: vengono
usate delle eks distro che sono delle distribuzioni kubernetes deployate da Amazon nel cloud, i deployments locali
sembrano identici a quelli del cloud. Possiamo usare eks su aws outpost un servizio fully managed di amazon che
estende l’infrastruttura aws, i servizi aws, api e altri tool a qualsiasi sito virtuale connesso.

Eks Machine Learning: usando aws inferencia possiamo implementare kubeflow con eks e modellare i nostri
machine learning workflows. Inferencia gira sulle ec2 di ultima generazion che le rendono efficienti. Altra opzione è
aws deep learning containers che possono eseguire training e inferences usando kubeflow

Eks batch processing: kubernetes può eseguire batch jobs in modo sequenziale (ci vogliono nodi molto potenti per
ridurre i tempi) oppure in parallelo i job che non sono collegati tra di loro (scaleremo i nodi per ridurre i tempi)

Eks web applications: possiamo creare webapps che scalano automaticamente in più regions ottenendo benefici in
termini di performance, in termini di scalabilità e disponibilità (reliability e availability)

eks big data: usando la integrazione ems con eks possiamo automatizzare i sistemi di provisioning come apache
spark, hadoop e altre applicazioni big data; eks può gestire automaticamente tali risorse per noi, incluso il
provisioning su cluster kubernetes (?). Tool di analisi (deep analytics data) molto potenti e sistemi di machine
learning completano il set di servizi per i nostri servizi big data

9.4. Azure per kubernetes


Anche azure supporta kubernetes, parliamo di Azure Kubernetes Service (AKS): i vantaggi offerti sono il portale azure
per gestire il cluster, la cli per interagire a linea di comando (gli azure resource manager templates) oppure la
powershell, i templates per strategie replicabili (processo CD/CI). Aks è gratuito, si paga solo per i nodi nel cluster
non per kubernetes.

Le identities e la sicurezza sono gestibili tramite l’azure active directory che permette abilitare feature come RBAC,
tramite cui puoi configurare accessi a risorse o namespaces garantendo ruoli ad alcune identities (user, groups,
applications) associandole alle azure AD identities senza bisogno di inventare nulla di nuovo.

Log e monitoring è integrato nella console Azure Monitor, si possono collezionare metriche su memoria, cpu etc.

I nodi aks girano su vm, usando questi nodi possiamo collegare storage, fare setup di pods, aggiornare componenti
del cluster e configurare requirements di basso livello come gpus.

Anche qui abbiamo node pools, possiamo avere nodi sistemi operativi misti che possono far girare anche windows
server containers. Possiamo scalare automaticamente nodi e pod, possiamo scalare orizzontalmente o verticalmente
in base alla configurazione. Possiamo far girare più versioni di kubernetes.

AKS può anche aggiornare automaticamente il cluster quando viene rilasciata una nuova versione

AKS supporta nodi con gpu-enabled, per cui se hai servizi che richiedono vm con questa feature, per computazioni
complesse, per grafica intensiva etc.
Confidential computing nodes: è una feature di aks, è ancora sperimentale, praticamente permette ai container di
girare in un hardware-based trusted execution enviroment o “enclave”, in pratica c’è l’isolation tra container e
l’integrità del codice

Storage volume support: i workloads possono essere montati su storage statico o dinamico e possono accedere ai
dati persistenti, tutti pod hanno tutti accesso ai ns dati persistenti.

Un cluster aks è normalmente deployato in una rete virtuale azure, ad ogni pod viene assegnato un ip nella virtual
nw, in modo da poter comunicare senza problemi nella rete. I pod possono comunicare nello stesso cluster così
come nodi possono comunicare con altri nodi nella medesima virtual nw (anche se associati a cluster differenti!).

Tra i tools messi a disposizione abbiamo Helm e una estensione per visual studio code.

Devops starter è una soluzione scaricabile dalla git repo di microsoft, include un set di azioni che permettono
rapidamente e facilmente creare le risorse azure necessarie per far girare aks, possiamo configurare una build
pipeline da usare per la continous integration. Infine devops starter può essere usato per integrare altre risorse
come azure application insights per catturare logs e altre metriche.

Abbiamo il supporto alle immagini docker, possiamo preparare il lavoro con docker e poi fare la push su aks, esiste
un azure container registry (ACR) in cui possiamo pubblicare o mantenere private le nostre immagini.

Azure ha la certificazione kubernetes, per cui possiamo stare sicuri di avere una implementazione di k corretta.

Azure risponde a diverse compliance (soc, iso ...) che garantisce la sicurezza e la garanzia del deploy

9.5. Package managers per kubernetes


Ci sono diversi tools in kubernetes, li suddividiamo per categorie.

Categoria dei package managers, offrono funzioni per gestire configurazioni, aggiornamenti all’interno del cluster.

Categoria dei tool di continous integration

Categoria dei tools di continous delivery.

Tra i package managers parliamo dei più popolari: helm, jsonnet (ideato da google, i templates sono scritti in json
tramite riga di comando) e ksonnet (kube control port della jsonnet library); tramite questi ultimi due è possibile fare
rilasci su kubernetes usando dei semplici files di configurazione.

Parliamo di helm: open source, manutenuto dalla community di kubernetes, è l’applicazione standard per il deploy
su kubernetes. Possiamo produrre releases configurabili: se abbiamo una nuova versione di un deployment helm
riesce a gestire il passaggio alla nuova versione attivando nuovi pod e spegnendo quelli vecchi.

Possiamo usare helm per cancellare release esistenti che non usiamo più, inoltre se siamo sviluppatori o degli sre,
potremmo essere interessati ad ispezionare le releases per essere sicuri che funzionino, e helm ci aiuta su questo.

Con helm abbiamo 2 parti del puzzle: helm è la parte client che sta sul sistema di sviluppo, tiller invece è la parte
server che sta deployata sul cluster. Helm agisce come un template engine, mentre il Tiller è il release manager che
aggiorna la history delle versioni e monitora il funzionamento.

Parliamo di jsonnet e ksonnet, ciascuno capace di generare templates consumabili dalla kube command line utility,
considerando che rispettano gli standard yaml di kubernetes, possono generare più file per rappresentare ciascuno
dei componenti necessari al deploy su k. ma non saremo capaci di fare un deploy efficiente riducendo la
duplicazione. Possiamo eseguirli per produrre un singolo file che include tutti i componenti necessari, opzione che
non ha bisogno di altri tool di kubernetes.

I tools di continous improvement sono in giro da un po di tempo, anche più di kubernetes, sono compatibili con
kubernetes:
- jenkins
- travis
- circle ci
- gitlab
- azure devops e github (better fit nel mondo azure)
- ....

Tools di CD:

- weave cloud (gestisce in sicurezza le credenziali)


- shippable
- codefresh
- harness
- azure devops e github (better fit nel mondo azure)
- ....

9.6. Demo: Installare la cli di helm su windows e


fare un deploy su kubernetes
Anzitutto con il browser andiamo su helm.sh e clicchiamo sul link get started, scendiamo alla sezione “install helm” e
andiamo alla “official release page” tramite apposito link, finiremo su github. Essendo open source possiamo
scaricare i sorgenti oltre che i binari compilati per i vari sistemi operativi. Scarichiamo la versione stabile per
windows, un file zip che estrarremo per comodità nella cartella c:\demo

A questo punto apriamo una powershell e andiamo alla cartella c:\demo dopodichè, prima di lanciare helm ci
colleghiamo al cluster azure tramite il solito comando az login e successivamente ci colleghiamo al cluster e resource
group già pronto sbdemo02 con az aks get-credentials –g sbdemo02 –n sbdemo02

eseguiamo poi un kubectl get nodes per essere sicuri che kubernetes stia girando:

adesso ci occupiamo di helm e ci facciamo dare la lista dei package che possiamo installare: helm.exe search hub

funziona, a questo punto proviamo a cercare un package in particolare: helm.exe search hub aspnet
vediamo che ci sono solo un paio di package che rispondono al criterio di ricerca e tail package sono considerati
come charts (o packages) in helm che possono essere installati nel nostro cluster kubernetes. Vediamo adesso come
fare.

Inizializziamo la repo per prima cosa. Le repo contengono i charts che possiamo scaricare e installare, ce ne sono
diverse che includono cose come Grafana, certificates managers, databases etc. In questo caso faremo il setup di un
mysql database da bitnami, quindi anzitutto inizializziamo la repo bitnami: helm repo add bitnami
https://charts.bitnami.com/bitnami

(la repo esisteva già) scrivendo helm search repo bitnami otterremo l’elenco dei chart disponibili su tale repo (c’è
tanta roba)

altra cosa da fare periodicamente è aggiornare la repo, in questo caso l’abbiamo collegata da poco ma è bene tenere
presente il comando: helm repo update

adesso cerchiamo mysql per l’installazione: helm search hub mysql

mysql esiste ed è presente nella repo bitnami: helm install bitnami/mysql --generate-name installerà la chart sul
sistema locale. Verrà prodotto un output abbastanza lungo in cui viene spiegato come procedere con l’installazione
tra cui il setup delle credenziali, e le istruzioni per farlo girare su un pod di kubernetes
digitando helm list o helm ls otteniamo i charts scaricati in locale

in questo caso abbiamo solo il deployment mysql, ci viene detto anche l’ultima volta in cui è stata aggiornato il chart

se non vogliamo più un chart possiamo digitare helm uninstall <nome chart> - -keep-history in questo caso <nome
chart> è mysql-1630622403

il flag keep-history serve a mantenere traccia delle info della release per il futuro; se digitiamo helm status mysql-
1630622403 grazie al flag precedente potremo vedere che una volta abbiamo installato il chart

Adesso proveremo a fare il deploy di un chart di helm nel nostro cluster kubernetes tramite gli helm chart hooks un
meccanismo per effettuare azioni al momento giusto durante il ciclo di vita della nostra applicazione, in alcuni casi
sono anche chiamati lifecycle hooks

Gli helm hooks sono agganciati ai charts, per cui per prima cosa abbiamo bisogno di un chart in locale, ne creiamo
uno che chiamiamo foo: helm create foo se digitiamo adesso ls per vedere cosa c’è nel nostro folder vediamo che c’è
una cartella chiamata foo

dentro, vedremo che ci sono alcuni files che userà helm tra cui un paio di cartelle e il file Chart.yaml:
adesso creiamo un hooks in questa cartella, gli hooks sono memorizzati nella cartella templates, entriamo dentro
tale cartella: e vediamo cosa c’è dentro

creiamo una cartella chiamata hooks (mkdir hooks) e ci entriamo dentro, adesso siamo pronti a creare i nostri hooks;
ce ne sono di diverso tipo: pre-install, post-install, pre-delete, post-delete, pre-updated, post-updated, pre-rollback,
post-rollback e tests.

Creeremo in questa demo pre-install e post-install; creiamo anzitutto il file hook-preinstall.yaml


apiVersion: v1
kind: Pod
metadata:
name: foo-hook-preinstall
annotations:
"helm.sh/hook": "pre-install"
spec:
containers:
- name: foo-hook-container
image: busybox
imagePullPolicy: IfNotPresent
command: ['sh', '-c', 'echo The pre-install hook Pod is running - foo-
hook-preinstall && sleep 5']
restartPolicy: Never
terminationGracePeriodSeconds: 0
abbiamo definito un pod kubernetes chiamato foo-hook-preinstall, con annotation “hekm.sh/hook”: “pre-install”
scriviamo che si tratta di un hook di helm; nella sezione spec/tontainers abbiamo dichiarato un container che usa la
busybox come immagine e successivamente abbiamo dichiarato il comando da eseguire:

sh –c echo The pre-install hook Pod is running - foo-hook-preinstall && sleep 5

stamperemo un messaggio dopodichè attenderemo 5 secondi prima di terminare il container

adesso tocca al hook-postinstall.yaml


apiVersion: v1
kind: Pod
metadata:
name: foo-hook-postinstall
annotations:
"helm.sh/hook": "post-install"
spec:
containers:
- name: foo-hook-container
image: busybox
imagePullPolicy: IfNotPresent
command: ['sh', '-c', 'echo post-install hook Pod is running - foo-hook-
postinstall && sleep 5']
restartPolicy: Never
terminationGracePeriodSeconds: 0
molto simile al pre install in questo caso cambia il messaggio stampato a video

torniamo nella cartella demo e lanciamo il comando helm per fare il deploy sul pod di kubernetes, controllando
prima di avere kubernetes funzionante

installiamo la chart con i nostri hooks: helm install foo .\foo\

con helm list verifichiamo cosa è installato

e anche su kubernetes....

i pods sono in stato completed, adesso controlliamo che hanno fatto:

kubectl describe pod/foo-hook-preinstall | Select-String –pattern ‘Anno|Started:|Finished:’

notare che tra inizio e fine esecuzione sono passati i famosi 5 secondi che avevamo messo di sleep;

kubectl describe pod/foo-hook-postinstall | Select-String –pattern ‘Anno|Started:|Finished:’


notare che tra inizio e fine esecuzione sono passati i famosi 5 secondi che avevamo messo di sleep, e che il pod è
partito dopo che il pod di preinstall si è concluso;

infine controlliamo il pod foo-69d....

kubectl describe pod/foo-69d6f54... | Select-String –pattern ‘Anno|Started:|Finished:’

notare come il pod è partito esattamente dopo 6 secondi che il pod di postinstall aveva girato

infine helm delete foo per eliminare la nostra installazione su helm e kubectl delete pod foo-hook-preinstall foo-
hool-postinstall per togliere i pod da kubernetes

9.7. Demo: una configMap per una chart di


helm
Diamo per scontato l’aver installato helm sulla nostra macchina e di avere a disposizione un cluster azure su cui
abbiamo kubernetes. In questa demo creeremo una nuova chart ma senza hooks. Creiamo una chart vuota che
chiameremo bar: helm create bar

come per la chart foo helm creerà la cartella bar con dentro l’occorrente: files yaml, cartella templates etc

ci posizioniamo dentro la cartella templates per poter creare adesso una configmap da usare nel nostro cluster
kubernetes: creiamo il file configmap.yaml e ci copiamo dentro il seguente contenuto
apiVersion: v1
kind: ConfigMap
metadata:
name: bar-configmap
data:
greeting: "Hello World"
con tale manifest diciamo a kubernetes che si tratta di una config map, di nome bar-configmap e il cui contenuto è
una coppia chiave valore greetin/Hello World; adesso abbiamo bisogno di un pod che la possa usare; creiamo un
nuovo file barpod.yaml e ci scriviamo dentro quanto segue
apiVersion: v1
kind: Pod
metadata:
name: bar-pod
spec:
containers:
- name: bar-container
image: k8s.gcr.io/busybox
command: [ "/bin/echo", "Configured greeting: $(GREETING_KEY)" ]
env:
- name: GREETING_KEY
valueFrom:
configMapKeyRef:
name: bar-configmap
key: greeting
restartPolicy: Never
si tratta di un pod con nome bar-pod con un container la cui immagine è una busybox e il cui comando di avvio è
/bin/echo Configured greeting: $(GREETING_KEY) nella sezione env definiamo una variabile di enviroment
GREETING_KEY visibile dal nostro container il cui valore lo andiamo a prendere dalla configmap chiamata bar-
configmap ed esattamente dal valore corrispondente alla chiave greeting. Infine nell’ultima riga diciamo a
kubernetes che il pod una volta eseguito non andrà riavviato.

Procediamo dunque a installare il chart su kubernetes tornando alla cartella padre di bar e digitando:

helm install bar .\bar\

a questo punto controlliamo se il pod è stato installato con kubectl get pods

il pod è stato installato e ha girato con successo senza andare in errore; digitiamo adesso il comando per visualizzare
i log (l’output) del pod eseguito

kubectl logs bar-pod

infine, per pulire l’ambiente eliminiamo il pod da kubernetes helm delete bar

ci accerteremo con kubectl get pods che il nostro pod non è più su kubernetes

9.8. Demo: creazione di una secret usando helm


partiamo dalla demo precedente, in cui avevamo definito una configmap per aggiungere questa volta una secret:
come abbiamo visto per valori sensibili, come ad esempio le password, abbiamo bisogno della feature della secret

torniamo dunque nella cartella bar\templates e scriviamo il file secret.yaml come segue
apiVersion: v1
kind: Secret
metadata:
name: bar-secret-password
type: Opaque
data:
password: bm9fb25lX3dpbGxfZXZlcl9ndWVzc190aGlz
in pratica istruiamo kubernetes con una secret, chiamata bar-secret-password, di tipo opaque (è quello base) e nella
sezione data inseriamo la password codificata base 64; adesso andiamo a modificare il pod della demo precedente,
aggiungendo il riferimento alla nostra secret; ci sono diversi modi di farlo ma in questo caso semplicemente ci serve
avere una variabile d’ambiente con la nostra password. Modifichiamo il comando da eseguire nella ns immagine
usando questa volta la shell (/bin/sh) e passando ad essa più comandi (flag –c) separati da &&
apiVersion: v1
kind: Pod
metadata:
name: bar-pod
spec:
containers:
- name: bar-container
image: k8s.gcr.io/busybox
command: [ "/bin/sh", "-c", "echo Configured greeting: $(GREETING_KEY)
&& echo password $(PASSWORD_KEY)" ]
env:
- name: GREETING_KEY
valueFrom:
configMapKeyRef:
name: bar-configmap
key: greeting
- name: PASSWORD_KEY
valueFrom:
secretKeyRef:
name: bar-secret-password
key: password
restartPolicy: Never
installiamo dunque il nostro chart con il solito comando helm install bar .\bar\ eseguito nella cartella padre di bar

andiamo a verificare cosa ha combinato kubernetes con kubectl get pods


il nostro pod è stato eseguito con successo; vediamone i log con kubectl logs bar-pod e scopriamo qual’era la
password che avevamo memorizzato nella nostra secret :-)

Nota: esistono diversi plugin che aggiungono sicurezza alle nostre informazioni sensibili cifrando le password e
mantenendole al sicuro, abbiamo visto un metodo semplice ma poco sicuro.

Ricordiamoci infine di disinstallare il nostro pod:

9.9. Demo: deploy di una applicazione usando


helm
ultima demo di questo modulo: deploy di una applicazione multipod in kubernetes usando helm; riprendiamo il chart
usato nella precedente demo, in cui avevamo visto l’uso di configMap e secret e modifichiamo il manifest in modo
da ottenere una applicazione multipod, splittando la nostra applicazione in due pod, uno visualizzerà il valore della
configMap, uno quello della secret: editiamo in \bar\templates il file barpod.yaml
apiVersion: v1
kind: Pod
metadata:
name: bar-pod1
spec:
containers:
- name: bar-container
image: k8s.gcr.io/busybox
command: [ "/bin/sh", "-c", "echo Configured greeting: $(GREETING_KEY)"
]
env:
- name: GREETING_KEY
valueFrom:
configMapKeyRef:
name: bar-configmap
key: greeting
restartPolicy: Never
e aggiungiamo barpod1.yaml
apiVersion: v1
kind: Pod
metadata:
name: bar-pod2
spec:
containers:
- name: bar-container
image: k8s.gcr.io/busybox
command: [ "/bin/sh", "-c", "echo password $(PASSWORD_KEY)" ]
env:
- name: GREETING_KEY
valueFrom:
configMapKeyRef:
name: bar-configmap
key: greeting
restartPolicy: Never
installiamo il chart con helm install bar .\bar\ eseguito nella cartella padre di bar e verifichiamo i pod su kubernetes

a questo punto vediamo i log dei due pod

infine disinstalliamo la ns applicazione da kubernetes

10. Logging e monitoring in kubernetes


Sappiamo bene che il logging è una parte importante delle app per molte categorie di utenti:

- sviluppatori: per capire se il codice è efficiente


- amministratori: per sapere se le applicazioni stanno funzionando bene
- security teams: per capire se le applicazioni sono sicure e se c’è qualche attacco in corso

Altra cosa importante nello sviluppo delle applicazioni è il monitoring, ci sono tante cose che possiamo monitorare e
tanti modi in cui possiamo farlo:

- l’approccio più usato è quello degli alerts, con cui avvisi e fai intraprendere azioni a certe persone quando
accade qualcosa (es: sto usando troppa memoria, avviso gli amministratori)
- altra strategia è quella delle dashboards tramite cui abbiamo un’immagine complessiva dello stato di salute
del sistema: se tutto verde è ok, se qualcosa non va bene salta subito agli occhi

in generale il monitoraggio è strettamente legato alla user experience: se l’utente non è soddisfatto della app e
migra verso qualcos’altro ce ne possiamo accorgere in questo modo, oltre al fatto che possiamo individuare
problemi sconosciuti durante il funzionamento e eventuali attacchi informatici; il monitoraggio dipende anche dal
logging, di fatto il monitoring è l’ennesimo utilizzatore dei log.

Uno dei problemi principali del logging è che i file di log possono essere tanti, molto verbosi (incide sulla leggibilità),
e spesso mancanti di correlazione tra loro (più azioni eseguite in contemporanea, le righe di queste che si intrecciano
nello stesso log...)

Cosa importante quando si scrive un’applicazione: monitoraggio e logging devono essere progettati
contestualmente.

Estendiamo il concetto al monitoraggio di un cloud: in questo caso oltre alle applicazioni abbiamo bisogno di
monitorare nodi e altre risorse, se lo facciamo bene riusciamo a prevedere attacchi o problemi di sistema.

Tipi di monitoraggio necessari nel cloud:

- databases (througput, tempi di risposta etc)


- website (origine richieste, tempi di risposta...)
- virtual network (traffico dati etc)
- cloud storage, molto simile al database
- virtual machines (uso di memoria, cpu, performance...)

La maggior parte dei cloud providers offrono i tool necessari

Benefici del cloud monitoring:

- troubleshoot in generale, la cosa più ovvia


- scaling: possiamo considerare possibilità di scalare in automatico in determinate situazioni, grazie al
monitoraggio possiamo capire quando e come fare
- strumenti dedicati: non ci dobbiamo inventare nulla, abbiamo già strumenti pronti ed efficaci
- accessibilità: possiamo fare monitoraggio anche da tablet o telefoni
- installazione: normalmente in fase di installazione vengono predisposti in automatico tutti gli strumenti per il
monitoraggio
- availability: tali strumenti sono sempre disponibili
- costi: non dobbiamo spendere per avere questi strumenti, sono spesso inclusi nella soluzione gratuitamente
e il valore che forniscono è molto alto

In una soluzione ibrida con cloud più macchine private ci dobbiamo ingegnare a spedire i log delle nostre macchine
private al tool, se invece abbiamo tutto in cloud pubblico non dobbiamo fare nulla, abbiamo tutto pronto. La
soluzione più costosa ovviamente è il cloud privato in cui ci dobbiamo attrezzare manualmente per individuare cosa
e come monitorare.

Best practices di monitoraggio

Osservare l’utilizzo del sistema e i suoi costi: lo spreco di risorse non fa bene a nessuno, dall’altro lato un sistema che
non risponde bene per mancanza di risorse causa danni costosi

Individuare bene metriche ed eventi: sapere in anticipo cosa monitorare ci aiuta a manutenere il sistema senza
troppi sforzi

Usare singola piattaforma: è più semplice guardare i dati in un singolo posto che attraverso più sistemi

Individuare ruoli con i dati (trigger rules with data): ossia capire quali azioni intraprendere se accadono certi eventi,
creando ruoli che ci informano di un determinato evento possiamo agire in modo efficace
Separare e partizionare i dati centralizzati: non è una buona idea mettere tutti i dati nello stesso contenitore è buona
cosa differenziarli per applicazione/sistema in modo da fare query efficaci (è buono mettere tutto il cibo in cucina ma
non è buona idea metterlo nello stesso contenitore!)

Monitorare la user experience: se alcuni aspetti sono soggettivi, altri non lo sono, ad esempio quanto è veloce il
sistema, quali sono i tempi di risposta, quanto è disponibile e raggiungibile, da quanto tempo è up; sono tutti dati
che influiscono sulla user experience.

Creare standard e testarli: possiamo quantificare il concetto di sistema funzionante? Se si, possiamo stabilire uno
standard e testarlo per verificare che il sistema risponda a tale standard

10.1. I tool di monitoraggio e logging di Google


Kubernetes Engine (GKE)
Native integration: quando creiamo un cluster google automaticamente abbiamo integrato il monitoraggio e il
logging dello stesso.

Cloud operation for GKE: il tool include una dashboard di monitoraggio, disegnata per i dati di kubernetes, non è un
semplice dump di dati ma vediamo dati specifici legati allo stato di salute, del nostro cluster. Il tool ci permette di
collezionare i log di una applicazione come preferiamo.

Configuring Cloud operation for gke: è una fase importante del setup del sistema.

Nel Cloud operation for GKE possiamo vedere metriche chiave come gpu e memoria, vedere informazioni a vari livelli
(infrastruttura, workload, service) ma anche dei componenti di kubernetes (namespaces, nodi, pod).

Configurazione del tool: collezionare log di sistema piuttosto che di applicazione, il tool è abilitato a farlo di default.

Caratteristiche:

- nella dashboard abbiamo una filter bar che ci permette di cercare determinate risorse di gke
- l’aler timeline che traccia ogni problema in ordine cronologico presentandoli come una timeline,
- le dashboard tables ci permettono di vedere le informazioni in modo tabulato (tra queste le metriche),
- infine la visualizzazione dei log con possibilità di filtrarli.

Audit logs dai nodi per questioni di sicurezza: è importante avere a disposizione log, warn etc dei sistemi sottostanti
a kubernetes, ma abbiamo anche tracciati i tentativi di login e l’esecuzione di binari (servizi ed eseguibili)

Prometheus: è un tool di monitoraggio usato con kubernetes che può essere abilitato per collezionare e analizzare
metriche, verranno prodotte nel formato “prometheus exposition” e possono essere esportate nel cloud monitoring
(?)

10.2. I tool di monitoraggio e logging di AWS


(EKS)
Come google ha dei tool built-in per logging e monitoraggio:

- EKS control plane logging: colleziona e fa audit direttamente dal control plane passandole immediatamente
a CloudWatch.
- ciascun log di ciascun cluster viene inviato a Amazon CloudWatch sottoforma di stream, i dati non sono
copiati e memorizzati in più posti, eks plane control logga direttamente verso amazon cloud watch
- amazon eks authenticator logs: possiamo vedere i log suddivisi per ruoli (...?)
- amazon cloud trail: memorizza le azioni effettuate da utenti, ruoli e servizi, catturando le info relative alle
chiamate alle api come eventi
Eks control plane logging: possiamo abilitare o disabilitare vari tipi di log, addirittura per singolo cluster. Il setup della
configurazione può essere fatto in vari modi: tramite la aws management console, la cli di aws oppure usando le aws
eks api. Quando abilitiamo il control plane logging tutti i log del cluster eks sono automaticamente inviati sottoforma
di stream a CloudWatch del medesimo account aws. Ma cosa viene raccolto?

- kubernetes api server component logs: i log del servizio kubernetes stesso
- audit informations: login avvenuti con successo o meno per tutti i componenti dotati di identità, non solo
utenti e amministratori
- authenticator: log unici di eks, sono relativi a informazioni a richieste RBAC, cioè se un utente è autenticato
non è detto che sia autorizzato e l’autenticator ci dà questo tipo di info sulle azioni eseguite da utenti e
sistemi
- controller manager logs: è quello che si occupa di gestire i core control loops
- scheduler logs: possiamo ad esempio capire se il sistema ha problemi a selezionare i nodi per schedulare i
pod.

Amazon eks include una api a cui una app può fare chiamate per interagire con il cluster eks. Eks può essere
integrato con Amazon CloudTrail, per cui le chiamate alle api possono essere loggate e collezionate con cloudTrail,
per cui abbiamo records di ogni azione intrapresa; le info possono essere cercate per utente, per ruolo e anche per
servizio AWS. CloudTrail visualizza anche i dati della chiamata che possono essere visti come eventi.

Se abilitiamo la continous delivery a cloudtrail possiamo consegnare gli eventi di log ad un bucket S3 di amazon per
memorizzare tutto su storage. Se non vogliamo abilitare questa feature possiamo però continuare a visualizzare gli
eventi più recenti tramite la cloudTrail console, per farlo abbiamo bisogno dell’event history area.

La cosa bella di eks è che tutte le azioni sono loggate su cloudtrail, anche quelle dello stesso cloud stesso come
creazione. I log includono sempre l’identità e l’access management per chi ha richiesto quell’azione: conosceremo
chi o cosa ha generato la richiesta anche se è fallita la creazione del cluster(?). Se la richiesta è stata fatta con
credenziali temporanee possiamo sapere chi ha fatto la richiesta in quanto le info circa il ruolo o l’utente federato
sono disponibili. Nel caso in cui la richiesta è stata generata da un servizio aws, tale info è collezionata allo stesso
modo di quella degli utenti e memorizzata nei log.

Eks control plane produce tantissime metriche utili che potremo collezionare in modo naturale.

Anche EKS si integra con prometheus per collezionare, formattare e esporre certe metriche per l’utilizzo in
downstream. Prometheus è un tool di monitoraggio e un time-series database, integrato con aws mette a
disposizione endpoint per collezionare dati richiesti, aggregarli in vari modi e fare addirittura istogrammi, esporre
una user interface per permetterci di filtrare proiettare grafici e fare query della collezione di dati delle metriche
usando le utilities di kubernetes come kubectl. Eseguendo infatti kubectl get --raw/metrics possiamo vedere le
metriche collezionate del nostro sistema

10.3. Microsoft AKS log e monitoraggio


Anche azure mette a disposizione tool di log e monitoraggio built-in. Quello di default è Elastic Stack for kubernetes
molto primitivo, tramite cui possiamo monitorare i cluster con log lineari. Se vogliamo entrare nel contesto in modo
più approfondito e analizzare il significato dietro ai log allora è meglio usare un servizio di Azure che usa al meglio i
log di kubernetes, li scarica in modo centralizzato e li analizza.

Uno dei tool di cui parliamo è l’Azure Monitor for containers disegnato non solo per kubernetes ma per ogni tipo di
motore di orchestration, come DC/OS, Docker swarm, Red Hat openshift. I log e metriche sono recuperati non solo
dai containers ma anche dal cluster stesso in modo da permetterci di fare correlazione tra log e individuare più
rapidamente eventuali problemi a prescindere dal sistema operativo che sta eseguendo l’orchestrator. In pratica il
formato dei log sarà ‘familiare’ a prescindere dalla sorgente.

Altro tool è l’AKS Container insights, disegnato in modo specifico per monitorare lo stato di salute e le performance
dei carichi di lavoro (una applicazione va considerata un carico di lavoro). Valgono gli stessi ragionamenti di prima:
cambia il motore di orchestration sotto (non necessariamente kubernetes) ma il tool funziona uguale. Il set di
features messo a disposizione per il monitoraggio permette di conoscere lo stato e le performance del cluster
kubernetes a prescindere dal sistema operativo che stiamo usando: possiamo controllare i nodi, identificare l’utilizzo
medio di cpu e memoria per ciascun container, o più ad alto livello, raggruppando per containers. Possiamo capire in
che pod un container risiede, se ci sono colli di bottiglia, controllare le performance di un controller o di un pod.

Possiamo monitorare l’utilizzo globale di una risorsa dai vari workloads, host, pods etc. Abbiamo l’integrazione con
prometheus (aprendo la possibilità di usare molte più metriche), possiamo configurare alerts, capire il
comportamento dei cluster, monitorare in modo continuo i carichi di lavoro.

Prometheus è un open source disegnato come soluzione di monitoraggio delle metriche; possiamo mettere insieme
le metriche che vogliamo, aggregarle in base alle esigenze. Fa parte del Cloud Native Compute Foundation per cui è
compatibile per lavorare con piattaforme cloud come Azure. Ha bisogno di un server e uno store per funzionare, ma
usando Azure Monitor for containers abbiamo una integrazione diretta con gli endpoints di metrica di prom., azure è
in grado di fare da server e store.

10.4. Log e monitoraggio in kubernetes


Come abbiamo detto prima Prometheus è una soluzione bella e popolare per avere un servizio di monitoraggio delle
metriche. Normalmente per funzionare dobbiamo fare il setup del suo server e del suo store, ma integrato con
azure, questo passaggio non è necessario, è sufficiente esporre l’endpoint della metrica di Prometheus e il Container
Insight farà il resto del lavoro.

Prometheus può funzionare in due modi:

- collezionando metriche da servizi di kubernetes come kube-dns e kube-state-metrics (Cluster-wide), tramite


apposita configurazione nel componente ConfigMap
- collezionando metriche da nodi definiti nel componente ConfigMap (node-wide)

La differenza di funzionamento è che in modo cluster-wide le metriche riguardano l’intero cluster, mentre in modo
node-wide riguardano il solo nodo.

In alcuni scenari, quando entrano in gioco molti containers e il sistema è complesso, è necessario monitorare ogni
layer di kubernetes in quanto il controllo di piu microservizi che girano insieme è diverso rispetto a quello di una
singola applicazione, pertanto avere il giusto tool di monitoraggio di kubernetes è essenziale.

I vari layer di k. monitorabili sono i nodi, i pod, i cluster e gli stessi control planes. Vediamo adesso cosa possiamo
monitorare a basso livello:

- uso cpu
- uso disco
- risorse di ciascun pod
- i servizi di kubernetes sia quelli deployati che quelli nativi come kube-scheduler (o etcd) che sono critici per il
benessere non solo della nostra applicazione ma anche per il cluster stesso (se etcd non funziona non ci sarà
nessuno a scegliere il nodo dove far girare il pod di cui abbiamo bisogno)

Le risorse devono essere monitorate direttamente per individuare in tempo i problemi, kubernetes mette insieme
molte metriche relative a risorse di vari layer, tra cui stato e utilizzo (della risorsa). I tool di k. possono essere
configurati per monitorare direttamente le applicazioni e per determinare se il sistema ha raggiunto lo stato
desiderato e intraprendere le opportune azioni come inviare alerts ai team o correggere il comportamento del
sistema.

Tra i 5 tool più importanti di kubernetes per il monitoraggio c’è cAdvisor sta dentro kubelet pertanto è disponibile
automaticamente in quasi tutti i sistemi kubernetes. Colleziona informazioni relative all’utilizzo di risorse di ciascun
container in esecuzione. Altro tool è Sensu Go una utility di telemetria e un servizio di controllo stato di
funzionamento particolarmente utile per soluzioni multi cloud, controlla server, containers, servizi e applicazioni
anche insieme a Prometheus un tool molto facile da customizzare senza dover degradare le performance del
sistema. Abbiamo poi Fluentd, tool molto semplice che possiamo usare per catturare i log nativi generati dalle varie
risorse di kubernetes e spedirli a qualsiasi sevizio di processing a valle. Infine abbiamo elastic stack che include tool
come fluentd, logstash, elastichsearch e kibana per catturare, trasformare e visualizzare le informazioni prese dai
vari componenti di kubernetes.

Infine parliamo di un altro tool di monitoraggio di kubernetes: netapp cloud insights un tool di monitoraggio di
infrastruttura che ci dà una visione completa dell’infrastruttura che monitora e su cui gira kubernetes. Con questo
tool abbiamo a disposizione molti strumenti che ci permetteranno di ottimizzare i costi del nostro deployment,
prevedere in anticipo possibili problemi del sistema, prevenire e individuare attacchi oltre che a capire e tenere sotto
controllo l’architettura di kubernetes (visualizzazione topologia, stato del cluster etc).

10.5. Demo: usare i componenti di logging di


kubernetes
In questa demo vedremo come funzionano i log di kubernetes; kubernetes usa klog per i log strutturati, vedremo
come sono fatti. Anzitutto useremo helm per fare il deploy di un pod, sucessivamente kubectl per vedere i risultati.

Per questa demo useremo azure kubernetes service, aks, abbiamo già un cluster pronto, chiamato sbdemo03. Come
fatto nelle precedenti demo ci colleghiamo al cluster tramite una powershell, anzitutto facciamo la login al servizio
con il comando az login e successivamente ci colleghiamo al cluster e resource group già pronto con az aks get-
credentials –g sbdemo03 –n sbdemo03 ossia otteniamo le credenziali di accesso al cluster e le memorizziamo sul file
locale di kubernetes e facciamo in modo che il cluster sbdemo03 sia quello di default. Infine con kubectl get nodes
vediamo se la connessione a kubernetes sta funzionando:

ok, adesso creiamo una semplice applicazione da deployare nel cluster utilizzando helm: helm create sbdemo03
creerà una cartella con dentro l’occorrente per creare un chart di helm; entreremo nella cartella
sbdemo03/templates e creeremo un file yaml per descrivere quanto andremo a deployare nel nostro cluster: pod-
counter.yaml
apiVersion: v1
kind: Pod
metadata:
name: sbdemo03-counter
spec:
containers:
- name: count
image: busybox
args: [/bin/sh, -c,
'i=0; while true; do echo "$i: $(date)"; i=$((i+1)); sleep 1;
done']
nel manifest diciamo a kubernetes di creare un pod chiamato sbdemo03-counter mettendoci dentro un container
con l’immagine di una busybox che useremo per lanciare il comando/batch che ad ogni loop farà l’echo della data di
sistema seguito da un valore che incrementa ogni secondo. Torniamo sulla cartella demo e lanciamo il comando
helm install sbdemo03 .\sbdemo03\
chart installato, verifichiamo l’installazione con kubectl get pods

Adesso parliamo di log: esistono due tipi di log creati dopo l’installazione del pod, il primo è quello generato dalla
nostra applicazione nel nostro pod (gli echo che produce sta producendo la nostra app sullo standard output e che
vengono catturati da kubernetes). Per visualizzarlo è sufficiente digitare kubect logs sbdemo03-counter

esistono molti tool che producono logging appropriato piuttosto che il semplice echo, ma sono dipendenti
dall’applicazione, in questo caso usiamo il metodo più semplice per fare la demo, usando per l’appunto “echo”

Il secondo tipo di log viene prodotto da kubernetes stessa, che monitora e logga qualsiasi cosa stia accadendo nel
sistema; per fare ciò usa klog, un meccanismo di logging che struttura l’output in modo che ogni pezzo di log possa
essere categorizzato e capito. Useremo il portale azure per visualizzarlo meglio: usando il browser navighiamo fino
alla pagina del nostro cluster. Azure logga il comportamento di qualsiasi componente, in questo caso siamo
interessati ai soli log di kubernetes, pertanto scendiamo nel menù del nostro cluster kubernetes a sinistra fino alla
voce log e ci clicchiamo sopra
la prima cosa che vediamo è un popup con una serie di query che comunemente si usano per cercare tra i log,
categorizzate su un menù a sinistra:

andiamo sulla categoria performance:

vediamo le query più comuni in materia di perfomance: la cpu del container, la memoria...cliccando sul run parte la
query...clicchiamo sul run di “avg node cpu usage percentage per minute query box”:

la schermata si dividerà in due, nella parte alta la query, nella parte bassa i risultati

i log che stiamo visualizzando riguardano l’uso medio di cpu per minuto per il nostro cluster nell’ultima ora (rif riga 6
ago(1h)). Oltre alla schermata dei results, c’è anche il tab Charts, se ci clicchiamo sopra visualizzeremo il risultato
sottoforma di grafico
10.6. Demo: monitoraggio usando i log di
kubernetes
Adesso ci occupiamo di auditing, partendo dalla demo precedente. Auditing in kubernetes significa fornire records
relativi alla sicurezza che documentano una serie di azioni fatte in un cluster. Il cluster stesso fa audit delle attività
generate da utenti, applicazioni e dagli stessi control plane: in pratica ci servono a capire chi ha fatto cosa e quando.

Con kubernetes possiamo usare i policy objects per catturare le info di audit relative al cluster, con azure kubernetes
possiamo abilitare la feature facilmente: nella schermata del nostro cluster andiamo nel menù a sx e clicchiamo sulla
voce policy

nel nostro caso l’addon è gia stato abilitato in precedenza durante il processo di creazione:

clicchiamo su “go to azure policy” per vedere com’è configurato il servizio di policy:
quando è stato creato il cluster è stato abilitato il servizio Azure Policy che ha iniziato a guardare il cluster sotto certi
aspetti: in questo momento ci sta dicendo quali sono le policy configurate e se il cluster è compliant in tali aree; di
default non ci sono molte policy abilitate, abbiamo la possibilità di abilitarle in questo contesto cliccando sulla voce
“assign policy” in alto a sx:

nella schermata che compare possiamo adesso definire lo scope della policy (=la risorsa a cui la colleghiamo), in
questo caso il ns cluster kubernetes ed eventuali esclusioni (ad esempio se non vogliamo includere certi pod).

nella sezione policy definition, cliccando sui tre puntini a destra possiamo scegliere tra diverse proposte disponibili:
ne possiamo aggiungere una personalizzata, ma ce ne sono molte già predefinite da cui poter scegliere. Esistono altri
modi di creare policies in kubernetes usando ad esempio files yaml, tali policies possono controllare certe azioni
fatte da applicazioni o utenti; tali azioni possono dunque essere loggate. In Azure il log è centralizzato, possiamo
accedere ai log usando i comandi di kubectl ma il portale azure rende le cose più semplici.

Torniamo alla schermata principale del nostro cluster e andiamo alla sezione logs e creiamo la nostra query
chiudendo la schermata “queries” contenente query predefinite:

Digitiamo adesso la query che desideriamo nella parte superiore della pagina:

ContainerLog poi, alla riga seguente

| where LogEntry contains “audit” e ancora, alla riga seguente

order by TimeGenerated desc

la query in questione filtra i log tirando fuori solo i riferimenti alle righe di auditing ordinandole cronologicamente;

eseguiamo la query cliccando sul bottone run e...


ecco i risultati delle ultime azioni registrate. Immaginiamo di aver creato un web API che viene contenuta da
Kubernetes, ipotizziamo di aver creato alcuni requisiti di autenticazione sulla API: se l’utente è autenticato ottiene
accesso all’endpoint della ns api, se non lo è ottiene un errore http 401 “rejected”. Bene, possiamo configurare tale
policy in modo tale che ad ogni tentativo da parte di un utente l’accesso (o il tentativo) viene registrato. Ogni volta
che verrà fatto un tentativo avremo una nuova entry tra i log di kubernetes e nella schermata precedente di azure.

Adesso vediamo alcune caratteristiche proprie più di Azure che di kubernetes, la sezione “activity log” relativa al ns
cluster (che, come tutte le risorse di azure, è una risorsa)

in questa schermata vedremo tutte le azioni effettuate sul ns cluster: se un utente ha accesso alla risorsa azure e
decide di modificarla, tale info verrà loggata qui: è utile agli amministratori per capire cosa è stato fatto da chi.

10.7. Demo: il pull degli eventi di kubernetes


usando kubectl
In questa demo esploreremo i pod events in Kubernetes usando il comando kubectl. Torniamo alla app della demo
6.5 e vediamo gli eventi che sono stati generati da tale app (la app è stata disinstallata). Digitando kubectl get pods
vediamo che non c’è nulla che sta girando:

reinstalliamo la nostra app con helm install sbdemo03 .\sbdemo03\ e verifichiamo che l’app sia stata installata
digitando kubectl get pods
adesso controlliamo gli eventi associati ai ns pods; immaginiamo lo scenario in cui abbiamo fatto il deploy ma i ns
pods non riescono a partire (fail o timeout); catturare tali eventi ci aiuterebbe a intervenire. Digitiamo dunque:
kubectl describe pod sbdemo03-counter ottenendo info dettagliate del nostro pod, se andiamo in cima possiamo
verificare se il ns container sta girando (riga status):

se scendiamo in basso vediamo la sezione events con gli eventi ordinati dal più vecchio al più recente:

- assegnazione del pod a un nodo


- pull dell’immagine di busybox
- creazione del container
- avvio del container

se ci fossero stati problemi relativi all’avvio del container in questa sezione avremmo visto altre righe
ma non è l’unica maniera di ottenere i dati relativi agli eventi; ipotizziamo di avere tanti pod e di volere qualcosa di
centralizzato; possiamo usare un altro comando kubectl get events

vediamo gli eventi che ci sono stati nel ns cluster; adesso proviamo a filtrarli, ad esempio per un singolo pod: kubectl
get events --field-selector ‘involvedObject.name=sbdemo03-container’

possiamo creare query più sofisticate come argomento del field selector, in questo caso abbiamo selezionato solo
per nome.

Infine eliminiamo il ns deploy sbdemo03 con helm delete sbdemo03

10.8. Collezionare log applicativi usando Fluentd,


l’uso di elasticsearch e kibana
Per vedere come funziona fluentd useremo un progetto di esempio, vedremo un po di codice, useremo helm per
fare il deploy del progetto in kubernetes e infine vedremo i log generati. Installeremo una applicazione che ha i
componenti dello stack EFK inclusi, per EFK si intende ElasticSearch, Fluentd e Kibana. Ci concentreremo su Fluentd
per catturare i log relativi alla ns applicazione che gira su kubernetes. Iniziamo dalla Helm chart di cui vogliamo fare il
deploy su kubernetes:
nello zip fornito andiamo a vedere la cartella templates il file sbdemo03efk-app.yaml
apiVersion: v1
kind: Pod
metadata:
name: app
namespace: sbdemo03efk
spec:
containers:
- name: count
image: busybox
args: [/bin/sh, -c,
'i=0; while true; do echo "$i: $(date)"; i=$((i+1)); sleep 1;
done']
è la famosa applicazione che stampa la data e un contatore ogni secondo, l’abbiamo chiamata app e l’abbiamo
messa nel namespace sbdemo03efk; adesso vediamo sbdemo03efk-namespace.yaml
kind: Namespace
apiVersion: v1
metadata:
name: sbdemo03efk
in esso semplicemente abbiamo definito il namespace sbdemo03efk; adesso vediamo sbdemo03efk-fluentd.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
name: fluentd
namespace: sbdemo03efk
labels:
app: fluentd
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: fluentd
labels:
app: fluentd
rules:
- apiGroups:
- ""
resources:
- pods
- namespaces
verbs:
- get
- list
- watch
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: fluentd
roleRef:
kind: ClusterRole
name: fluentd
apiGroup: rbac.authorization.k8s.io
subjects:
- kind: ServiceAccount
name: fluentd
namespace: sbdemo03efk
---
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: fluentd
namespace: sbdemo03efk
labels:
app: fluentd
spec:
selector:
matchLabels:
app: fluentd
template:
metadata:
labels:
app: fluentd
spec:
serviceAccount: fluentd
serviceAccountName: fluentd
tolerations:
- key: node-role.kubernetes.io/master
effect: NoSchedule
containers:
- name: fluentd
image: fluent/fluentd-kubernetes-daemonset:v1.4.2-debian-
elasticsearch-1.1
env:
- name: FLUENT_ELASTICSEARCH_HOST
value: "elasticsearch.sbdemo03efk.svc.cluster.local"
- name: FLUENT_ELASTICSEARCH_PORT
value: "9200"
- name: FLUENT_ELASTICSEARCH_SCHEME
value: "http"
- name: FLUENTD_SYSTEMD_CONF
value: disable
- name: FLUENT_CONTAINER_TAIL_PARSER_TYPE
value: /^(?<time>.+) (?<stream>stdout|stderr) [^ ]* (?<log>.*)$/
resources:
limits:
memory: 512Mi
requests:
cpu: 100m
memory: 200Mi
volumeMounts:
- name: varlog
mountPath: /var/log
- name: varlibdockercontainers
mountPath: /var/lib/docker/containers
readOnly: true
terminationGracePeriodSeconds: 30
volumes:
- name: varlog
hostPath:
path: /var/log
- name: varlibdockercontainers
hostPath:
path: /var/lib/docker/containers
abbiamo definito un serviceAccount per fluentd; fluentd cattura i log dai vari pod abbiamo bisogno di un
serviceAccount per fornire i permessi a fluentd per usare le api di kubernetes e parlare con tali pods
abbiamo poi definito un clusterRole per fluentd dando a fluentd la possibilità di fare get list e whatch su pod e
namespaces, ci servono per accedere ai files di log

abbiamo definito successivamente un clusterRoleBinding per legare il serviceAccount ai ruoli dichiarati prima, infine
i daemonSet fluentd; nella parte iniziale assegnamo nome e namespace, alle righe “serviceAccount” e
“serviceAccountName” associamo l’oggetto al nostro serviceAccount, l’immagine che andremo ad usare sarà la
versione 1.1 di fluentd che gira con debian: fluent/fluentd-kubernetes-daemonset:v1.4.2-debian-elasticsearch-1.1

per quanto concerne l’enviroment (sezione env) diciamo a fluentd dove prendere i log e a chi mandarli: la sezione
relativa al serviceAccount dice da dove prenderli, la sezione env istruisce fluentd per inviare i log a elasticsearch che
si trova nello stesso cluster alla porta 9200, in http

linea più importante di tutte è quella che segue name: FLUENT_CONTAINER_TAIL_PARSER_TYPE: in cui diciamo a
fluentd dove e come trovare i log: la nostra app logga sullo standard output, in questa configurazione stiamo dicendo
a fluentd di fare il pull dei log dallo stream di standard out con (?<stream>stdout|stderr)

nella la parte rimanente definiamo lo storage delle informazioni collezionate

a questo punto vediamo che succede lanciando il comando helm install sbdemo03efk .\sbdemo03efk\

verifichiamo lo stato dell’installazione con kubectl get pods

c’è un solo pod che sta girando...è corretto? Abbiamo messo tutto sotto un namespace, cerchiamo dunque le risorse
sotto quello specifico namespace con kubectl get pods --namespace=sbdemo03efk

ed ecco il pod “app”, il pod fluentd etc... proviamo a vedere se la ns app sta girando correttamente con kubectl logs
app --namespace=sbdemo03efk

adesso se fluentd è configurato correttamente dovrebbe mandare le info a elasticsearch, vediamo dunque come
abbiamo configurato elasticsearch; nel file sbdemo03efk-es-svc.yaml leggiamo
kind: Service
apiVersion: v1
metadata:
name: elasticsearch
namespace: sbdemo03efk
labels:
app: elasticsearch
spec:
selector:
app: elasticsearch
clusterIP: None
ports:
- port: 9200
name: rest
- port: 9300
name: inter-node
abbiamo esposto un service di nome elasticsearch sul namespace sbdemo03efk che usa tutti i pod la cui label
(labelselector) sia app: elasticsearch; il servizio espone due porte, la 9200 su cui elasticesearch espone le rest api e la
9300 che serve alla comunicazione tra nodi; clusterip è settato a none in quanto il service è headless ossia che non
ha una ui o qualcosa di simile.

Adesso guardiamo il contenuto di sbdemo03efk-es-statefulset.yaml


apiVersion: apps/v1
kind: StatefulSet
metadata:
name: es-cluster
namespace: sbdemo03efk
spec:
serviceName: elasticsearch
replicas: 1
selector:
matchLabels:
app: elasticsearch
template:
metadata:
labels:
app: elasticsearch
spec:
containers:
- name: elasticsearch
image: docker.elastic.co/elasticsearch/elasticsearch:7.2.0
resources:
limits:
cpu: 1000m
requests:
cpu: 100m
ports:
- containerPort: 9200
name: rest
protocol: TCP
- containerPort: 9300
name: inter-node
protocol: TCP
volumeMounts:
- name: data
mountPath: /usr/share/elasticsearch/data
env:
- name: cluster.name
value: k8s-logs
- name: discovery.seed_hosts
value: "es-cluster-0.elasticsearch"
- name: cluster.initial_master_nodes
value: "es-cluster-0"
- name: ES_JAVA_OPTS
value: "-Xms512m -Xmx512m"
initContainers:
- name: fix-permissions
image: busybox
command: ["sh", "-c", "chown -R 1000:1000
/usr/share/elasticsearch/data"]
securityContext:
privileged: true
volumeMounts:
- name: data
mountPath: /usr/share/elasticsearch/data
- name: increase-vm-max-map
image: busybox
command: ["sysctl", "-w", "vm.max_map_count=262144"]
securityContext:
privileged: true
- name: increase-fd-ulimit
image: busybox
command: ["sh", "-c", "ulimit -n 65536"]
securityContext:
privileged: true
volumeClaimTemplates:
- metadata:
name: data
labels:
app: elasticsearch
spec:
accessModes: [ "ReadWriteOnce" ]
storageClassName: default
resources:
requests:
storage: 100Gi
abbiamo definito un oggetto statefulSet un oggetto che permette di assegnare delle identità ai pod in modo da poter
dare loro uno storage stabile e persistente necessario a elasticsearch per memorizzare i dati che possano resistere ai
restart dei pods; il nome dello statefulSet è es-cluster, mentre il namespace sbdemo03efk. Nella sezione spec
diciamo che vogliamo una sola replica (non è un multipod deployment), non è una grande idea ma si tratta di una
demo, nella parte selector/metadata abbiamo la configurazione relativa ai label e i label selector

più sotto specifichiamo l’immagine da usare, versione 7.2.0 (ogni versione può avere configurazioni diverse) presa da
docker

nella sezione ports dichiariamo la porta di ascolto 9200 per le api rest e la 9300 per la comunicazione tra nodi

nella sezione env dichiariamo un po di variabili di ambiente come il nome del cluster, i nomi dei nodi master (in
questo caso è solo es-cluster-0 perchè abbiamo una sola replica, se ne avessimo avute di più ci sarebbe stato
anche ...-1 e ...-2)

nella sezione initContainer eseguiamo alcuni comandi di configurazione del pod prima che parta elasticsearch e
infine il volume da usare per fare lo storaging etc

notare che elasticsearch.sbdemo03efk.svc.cluster.local definito nel manifest di fluentd come host da contattare per
inoltrare i log catturati ha il formato <nomeservizio>.<nomeNamespace>.svc.cluster.local

torniamo adesso alla powershell e vediamo se elasticsearch sta lavorando correttamente:

kubectl rollout status sts/es-cluster --namespace=sbdemo03efk per verificare lo stato del rollout dello statefulset
definito prima

digitiamo kubectl port-forward es-cluster-0 9200:9200 --namespace=sbdemo03efk


in modo da poter fare il forward delle richieste dalla porta locale al cluster remoto, e precisamente al pod es-cluster-
0; adesso con il browser proviamo a chiamare localhost:9200

ecco elasticsearch che ci sta rispondendo...aggiungiamo alla url /_cluster/state per conoscere lo stato:

sembra dunque che elasticsearch stia girando correttamente, nella sezione nodi vedremo che è collegato ad un
gruppo di nodi e che ci sono degli indici per logstash, il che vuol dire che elasticsearch sta ricevendo correttamente i
log da fluentd
Adesso passiamo ad analizzare kibana, il terzo applicativo del gruppo efk che ci permetterà di vedere i log catturati
da fluentd e indicizzati da elasticsearch; analizziamo il manifest di kibana. Anzitutto definiamo un service di nome
kibana nel nostro namespace sbdemo03efk, usiamo la label app: kibana (label selector) per individuare tutti i pod a
cui si potrà accedere tramite questo servizio. La porta esposta da questo servizio e rimappata sui pod è la 5601.
apiVersion: v1
kind: Service
metadata:
name: kibana
namespace: sbdemo03efk
labels:
app: kibana
spec:
ports:
- port: 5601
selector:
app: kibana
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: kibana
namespace: sbdemo03efk
labels:
app: kibana
spec:
replicas: 1
selector:
matchLabels:
app: kibana
template:
metadata:
labels:
app: kibana
spec:
containers:
- name: kibana
image: docker.elastic.co/kibana/kibana:7.2.0
resources:
limits:
cpu: 1000m
requests:
cpu: 100m
env:
- name: ELASTICSEARCH_URL
value: http://elasticsearch:9200
- name: ELASTICSEARCH_HOSTS
value: http://elasticsearch:9200
ports:
- containerPort: 5601
successivamente abbiamo il deployment dell’immagine di kibana, versione 7.2.0, il deployment con nome kibana
label app: kibana verrà collocato nel namespace sbdemo03efk, anche qui usiamo il label selector per individuare tutti
i pod con la stessa funzione (label app: kibana), sebbene abbiamo definito una sola replica del deployment.

si tratta di una demo, normalmente sia per elasticsearch che per kibana si definiscono più repliche in quanto la
mission di queste app, quella di fare monitoraggio dei log, è molto critica

per quanto riguarda la sezione env definiamo la url del servizio elasticsearch che ci permetterà di accedere ai pod di
elasticsearch (ricordiamo che abbiamo definito un servizio chiamato elasticsearch e che quindi il dns di kubernetes sa
risolvere)

infine dichiariamo che il nostro deployment sarà accessibile alla porta 5601, porta che è stata esposta dal service
“kibana” precedentemente dichiarato

per poter accedere al service di kibana abbiamo bisogno di un port forwarding dalla nostra macchina locale al cluster
di kubernetes, esattamente come abbiamo fatto per il servizio di elasticsearch

abbiamo anzitutto bisogno di conoscere il nome del pod da accedere, pertanto usiamo il comando che ci dà l’elenco
kubectl get pod --namespace=sbdemo03efk

a questo punto accediamo al pod kibana-56fd46 da una porta locale sulla nostra macchina

kubectl port-forward kibana-56fd46.... 5601:5601 --namespace=sbdemo03efk

adesso con un browser normale accediamo a localhost:5601 e otteniamo risposta da kibana

adesso andiamo a vedere i nostri logs cliccando sul tasto discover nel menù di sinistra
dobbiamo adesso creare il link ai dati che stanno su elasticsearch; notare che kibana già ci suggerisce due indici che
ha trovato su elasticsearch “logstash-...” il che significa che kibana e elasticsearch si stanno parlando

digitando nel campo index pattern logstash-* selezioniamo tutti gli indici trovati, clicchiamo sul tasto next step che
nel frattempo si è attivato, nello step successivo ci viene chiesto di selezionare un timestamp corrispondente al log

selezioniamo @timestamp dalle opzioni disponibili per filtrare in base al timestamp, infine clicchiamo sul bottone
“create index pattern” per terminare la configurazione: adesso abbiamo un indice da cui leggere i dati

torniamo a cliccare sul bottone “discover” nella parte alta del menu di sx:
ed ecco un istogramma e nella parte inferiore un log specifico; se guardiamo attentamente, ogni riga di log ha un
timestamp e un id

ipotizziamo adesso di avere un gruppo di pod che stanno loggando e di voler vedere solo i log di un pod, ad esempio
quello che abbiamo chiamato “app”: nel campo filter basterà digitare kubernetes.pod_name:app

ed ecco il risultato

10.9. Demo: Usare lo stack efk per monitorare i


log
riprendiamo quanto fatto nelle demo precedenti, abbiamo visualizzato i log di kubernetes tramite kibana, ma la
nostra app sta girando sotto kubernetes, pertanto vogliamo adesso visualizzare i log di kubernetes e non solo quelli
della nostra applicazione

ritorniamo sulla nostra pagina kibana visualizzata nel browser (vedi demo precedente) e ripartiamo dalla schermata
in cui avevamo definito il filtro kubernetes.pod_name:app per accedere ai log della nostra app e modifichiamo il
filtro aggiungendo un not davanti al nostro filtro per escludere i log della nostra app

ma non si tratta cmq di log di kubernetes; proviamo adesso a vedere i log creati da kubernetes stesso aggiornando il
filtro kubernetes.namespace_name:”kube-system”

quello che adesso vediamo sono i log relazionati in modo specifico con il sistema kubernetes, notare il nome del pod
non è quello della nostra app

vediamo adesso se ci sono degli errori che si sono verificati in kubernetes a livello di sistema modificando il filtro in
stream:stderr
proviamo a espandere uno dei record per vedere cosa è accaduto nel dettaglio e ci accorgiamo come, grazie al log
strutturato, la leggibilità è molto semplice

infine, visto che abbiamo usato helm per installare tutto, rimuoviamo tutto usando helm: helm delete sbdemo03efk

con un solo comando abbiamo tolto tutto quanto

11. Note
Padre di kubernetes: borg

Strategie di deployment di kubernetes

- rolling update (o ramped): processo di sostituzione dei pod graduale senza downtime, è la strategia di
default usata da kubernetes, corrisponde nello yaml a .spec.strategy.type: RollingUpdate nella
spec strategy specifichiamo maxSurge e maxUnavailable per definire il numero di pod che possono essere
non attivi o in più durante il processo
- recreate: si tratta di fare shutdown della vecchia versione e setup della nuova, è un processo di tipo tutto o
niente, abbiamo il problema del downtime di sistema; nello yaml specificheremo .spec.strategy.type:
Recreate

- blu/green si mettono in piedi entrambe le versioni con lo stesso numero di istanze, spegniamo la versione
più vecchia solo quando siamo sicuri che la nuova versione risponde ai requisiti. Rapido rollback/rollout, non
ci sono problemi relazionati alle versioni. Di contro dobbiamo spendere molte risorse per tenere in piedi
entrambe le versioni, bisogna ritestare l’intera piattaforma, è complicato con applicazioni statefull
- canary si redirige gradualmente il traffico verso la nuova versione. Il vantaggio è che possiamo monitorare
l’error rate e che il rollback è rapido, di contro abbiamo un rollout molto lento, dobbiamo definire due
manifests
- A/B testing facciamo il routing di alcuni utenti alle nuove funzionalità sotto specifiche condizioni,
mantenendo così sotto controllo la distribuzione del traffico e facendo girare diverse app in parallelo. Di
contro abbiamo bisogno di un load balancer evoluto ed è difficile risolvere alcuni tipi di errori
- shadow inviamo le richieste contemporaneamente a entrambe le versioni, facciamo il rollout solo quando la
nuova versione è stabile e risponde alle performace attese; l’utente non si accorge di nulla. Ovviamente
dovremo usare molte risorse, i test potrebbero essere falsati e il setup complesso.
Beats (Filebeat, Metricbeat, and Packetbeat) è un tool per collezionare dati osservabili da indicizzare su elasticsearch
per le operazioni di monitoraggio e logging

Potrebbero piacerti anche