In questa lezione faremo due cose: approfondiremo il concetto di Pod, visto nella lezione precedente, ed inizieremo a parlare di deployment.
Evidenziamo subito un aspetto importante che dovrà accompagnarci nello studio di Kubernetes per sempre. Il sistema di API di questa piattaforma si basa su una serie di oggetti deputati ognuno ad un ruolo diverso. È assolutamente fondamentale comprendere bene il significato di ognuna di queste tipologie per non rischiare di fare confusione. Si vedrà infatti che in molti casi è possibile raggiungere lo stesso risultato utilizzando oggetti diversi. Ciò che cambia sono i livelli di controllo e le possibilità di gestione che si ottengono utilizzando l'uno invece che l'altro. Facciamo pertanto il punto di quelli visti sino ad ra e che vedremo in questa lezione:
- Pod: uno o più container in esecuzione.
- Deployment: è la componente dedicata alla gestione del rilascio di nuove versioni dell'applicazione.
Pod: un approfondimento
Un Pod è l'unità minimale di deployment in Kubernetes, ciò significa che:
- container singoli o a gruppi non potranno essere sottoposti a deployment se non inseriti in un Pod;
- tutti i container di un Pod saranno collocati su una medesima macchina del cluster.
Abbiamo già visto come avviare un Pod – contenente il server web NGINX – ma non ci siamo collegati ad esso in nessun modo cosa che invece impareremo adesso. Lanciamo il file di configurazione dichiarativa server-web.yaml
:
apiVersion: v1
kind: Pod
metadata:
name: server-web
spec:
containers:
- image: nginx
name: nginx-instance
con il comando apply
:
$ kubectl apply -f server-web.yaml
Ora che abbiamo un Pod di nome server-web
(verificare con kubectl get pods
) possiamo collegarci al server in esecuzione al suo interno con un port forwarding:
$ kubectl port-forward server-web 8080:80
con cui diremo che vogliamo accedere tramite la porta 8080 dell'host alla porta 80 del container. A questo punto impariamo subito un'altra cosa sui Pod: tutti i container in esecuzione nello stesso Pod condividono lo stesso indirizzo IP pertanto condivideranno anche le stesse porte IP, il che sarà utile affinché i vari container dei Pod possano invocarsi tra loro. Quest'ultimo aspetto è conseguenza del fatto che un Pod è un'unità di deployment e quindi il suo contenuto finisce sulla stessa macchina: tutto ciò tornerà utile quando inizieremo a creare delle applicazioni su Kubernetes.
Il primo deployment
Ora passiamo alla configurazione del nostro primo deployment. L'esperimento consisterà nel:
- creare un deployment tramite configurazione dichiarativa. Al suo interno avremo un solo container con il server NGINX in esecuzione;
- modificare la dichiarazione del deployment in modo da richiedere una modifica dell'immagine in esecuzione passando da NGINX al server Apache. Questo meccanismo serve a implementare una versione semplicistica di rollout. In realtà, dovrebbe trattarsi di un cambio tra versioni diverse della stessa applicazione, qui per rendere più macroscopico il cambiamento andiamo da un'applicazione ad un'altra del tutto diversa;
- monitorare i rollout mediante appositi comandi di
kubectl
.
Ora convertiamo il Pod di prima in un deployment creando un nuovo file di configurazione in YAML. Se avete ancora attivo il Pod precedente potete abbatterlo con kubectl delete pod/server-web
. Questo è il nostro deployment (file server-deployment.yaml
):
apiVersion: apps/v1
kind: Deployment
metadata:
name: nostro-server
labels:
server: web
spec:
selector:
matchLabels:
server: web
template:
metadata:
labels:
server: web
spec:
containers:
- name: server-web
image: nginx
La struttura del deployment somiglia a quella del Pod. Al momento, possiamo trascurare alcuni aspetti come selector
e labels
ma notiamo la presenza di template
che contiene la definizione del container che vogliamo lanciare. Attiviamo il deployment (come si vede dai metadati, si chiama nostro-server) con il consueto comando
$ kubectl apply -f server-deployment.yaml
deployment.apps/nostro-server created
e diamo uno sguardo agli oggetti in esecuzione nel cluster:
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
nostro-server-7c5649b769-2g5rp 1/1 Running 0 10s
$ kubectl get deployments
NAME READY UP-TO-DATE AVAILABLE AGE
nostro-server 1/1 1 1 21s
Si vede che il deployment nostro-server
è attivo ed ha avviato esso stesso il Pod di cui ha bisogno. Con il port forwarding possiamo verificare se il server NGINX sta lavorando e possiamo farlo non collegandoci al Pod ma al deployment nel suo complesso:
$ kubectl port-forward deployments/nostro-server 8080:80
Potremo verificare di avere a disposizione il nostro server accessibile all'indirizzo localhost:8080
. Proviamo ora ad aggiornare il deployment modificando solo l'ultima riga del file server-deployment.yaml
impostandola a image: httpd
.
Lanciando di nuovo kubectl apply -f server-deployment.yaml
avremo non più il server NGINX in esecuzione bensì un Apache Server ed il tutto potrà essere verificato sempre tramite port forwarding.
Monitorare i rollout
Modificando il file YAML e chiamando ancora kubectl apply
abbiamo eseguito un rollout del nostro programma. I rollout possono essere monitorati mediante l'apposito comando kubectl rollout
chiedendone lo stato di completamento:
$ kubectl rollout status deployment/nostro-server
deployment "nostro-server" successfully rolled out
verificando quante sue revisioni esistono (nel nostro caso la numero 1 corrisponde al lancio con NGINX e la numero 2 al rollout con Apache Web Server):
$ kubectl rollout history deployment/nostro-server
deployment.apps/nostro-server
REVISION CHANGE-CAUSE
1 <none>
2 <none>
ed infine chiedendo i dettagli di una specifica revisione con il flag --revision
:
$ kubectl rollout history deployment/nostro-server --revision=2
…
Containers:
server-web:
Image: httpd
…
Il campo CHANGE-CAUSE
che si vede nell'output è un'annotazione che può essere usata per descrivere a parole proprie l'azione di rollout: al momento non ci è interessato impostarla. Eseguendo quest'ultimo comando avremo conferma, tra l'altro di quale immagine sia in esecuzione.
Nota interessante: volessimo tornare alla revisione 1 (NGINX) potremmo fare un rollback ma succederebbe quello che vediamo qui:
$ kubectl rollout undo deployments nostro-server --to-revision=1
deployment.apps/nostro-server rolled back
$ kubectl rollout history deployment/nostro-server
deployment.apps/nostro-server
REVISION CHANGE-CAUSE
2 <none>
3 <none>
$ kubectl rollout history deployment/nostro-server --revision=3
...
Containers:
server-web:
Image: nginx
...
Nonostante sia andato in porto il rollback, dalla storia dei rollout è scomparsa la revisione 1: essendo uguale alla 3, Kubernetes non l'ha duplicata bensì la rinominata assegnandole un nuovo identificativo!