Tutorial > Come gestire la Persistenza in Kubernetes

Come gestire la Persistenza in Kubernetes

Cosa sono i Volumes

I Volumes di Kubernetes sono un tipo di oggetto adatto ad astrarre le memorie secondarie. Abbiamo parlato della possibilità di usare memorie all'interno dei container. Infatti, Docker fornisce dei driver che fungono da volumi e che possono essere attaccati all'interno di un container sotto forma di directory. Tuttavia, le funzionalità di questo strumento sono piuttosto limitate.
Al contrario, Kubernetes mette a disposizione diversi tipi di volumi, che i Pod possono usare in vari modi. Un Pod può anche usare più volumi contemporaneamente. In particolare, distinguiamo tra volumi "persistenti" (persistent volumes) e volumi "effimeri" o volatili (ephemeral volumes): i primi implementano delle vere e proprie memorie secondarie, mentre i secondi vivono insieme ai Pod che li usano.
I Volumi sono in pratica dei dischi virtuali che Kubernetes associa ai servizi, che per definizione sono Stateless, in quanto scalabili. I Volumes possono essere considerati come directory accessibili ai Pod del cluster. Tra i Volumes possono anche esistere file overlapping.
Kubernetes supporta diverse tipologie di Volumi, come File System distribuiti (NFS, CephFS, local, altro ...).

Persistent Volume

I persistent volume sono oggetti di storage persistenti, che vengono usati quando si vuole memorizzare dati in maniera permanente. Possono essere creati e gestiti solo dall'amministratore di sistema, non vengono infatti gestiti da Kubernetes stesso. L'uso dei volumi persistenti all'interno di un Application Server è tipicamente associato allo streaming, ma anche a molti altri contesti applicativi.
Per istanziare un persistent volume, l'amministratore deve fornire una risorsa di storage e creare un oggetto Kubernetes PersistentVolume, ad uso degli utenti. Quando un utente fa richiesta per lo storage, viene creato un oggetto dinamico. La creazione dinamica di oggetti Volume è supportata da tutti i cloud provider integrabili con Kubernetes.
Le modalità di utilizzo dei Persistent Volume sono essenzialmente due:
  • Allocare spazi di memoria attraverso la specifica di uno o più oggetti PersistentVolume
  • Creare una richiesta tramite un oggetto PersistentVolumeClaim. In questo caso, un Pod effettua una richiesta per un PersistentVolume e il nodo Master si occupa di gestire tale richiesta.

Un Pod può specificare che tipo di volume utilizzare e dove montarlo. In particolare:
  • Specifica il tipo di Volume nel campo spec.volumes
  • Specifica dove montare il Volume nel campo spec.containers.volumeMounts

Possiamo quindi riassumere la creazione e l'utilizzo dei Volumi nel seguente modo:
  • L'amministratore di sistema definisce degli spazi di memoria, fisici o virtuali, sul cluster o sul cloud provider.
  • Lo sviluppatore, o l'utente, richiede uno spazio di memoria attraverso delle richieste (claim) specificate all'interno del file di Deployment
  • Il nodo Master sceglie i Volumi adatti alle richieste e li monta dinamicamente sui Pod deployati.

Creare un Volume

Definiamo un oggetto PersistentVolume all'interno di un file local_volume.yaml:
apiVersion: v1
kind: PersistentVolume
metadata:
    name: pv-volume
    labels:
	type: local
spec:
    storageClassName: manual
    capacity:
        storage: 10Gi
    accessModes:
	-  ReadWriteOnce
    hostPath:
        path: "/mnt/data"

In ordine, abbiamo definito:
  • kind: il tipo di oggetto (persistent volume)
  • metadata: il nome del disco e un'etichetta che identifica il disco come locale
  • spec: le specifiche del disco, che verranno utilizzate per associare il disco ai claim
  • capacity: definisce la capacità dello storage
  • accessMode: definisce l'accesso alla memoria. In questo caso, abbiamo definito un tipo di accesso ordinato di lettura e scrittura.
  • hostPath: specifica la posizione dei dati

Creare richieste per i Volume

I Pod usano gli oggetti PersistentVolumeClaim per richiedere memoria fisica (o virtuale). Se il nodo Master trova una corrispondenza tra una richiesta e un Volume esistente, instaura una connessione tra il Pod e il Volume. Se non esiste un volume che soddisfi le specifiche della richiesta, il master ritorna un'eccezione.
Definiamo un PersistentVolumeClaim come segue, in un file pvclaim.yaml:
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
    name: pv-claim
spec:
    storageClassName: manual
    accessModes:
	-  ReadWriteOnce
    resources:
	requests:
	    storage: 3Gi

A questo punto, eseguiamo i seguenti comandi sul terminale per deployare gli oggetti pv-volume e pv-claim. Creiamo prima il Persistent Volume.
$ kubectl create -f local_volume.yaml

Output:

persistentvolume/pv-volume created

Ora creiamo il claim:
$ kubectl create -f pvclaim.yaml

Output:

persistentvolumeclaim/pv-claim created

Infine, verifichiamo gli storage del nostro cluster:
$ kubectl get persistentvolumes

Output:


NAME           CAPACITY  ACCESS MODES  RECLAIM POLICY   STATUS  CLAIM                   STORAGECLASS  AGE
pv-nfs-client  10Mi      RWO           Delete           Bound   default/pvc-nfs-client                6d
pv-volume      10Gi      RWO           Retain           Bound   default/pv-claim        manual        108s

Notiamo che, oltre al volume creato, è già presente il volume del file system di NFS, settato in fase di creazione del cluster.
$ kubectl get persistentvolumeclaims

Output:

NAME                STATUS    VOLUME          CAPACITY    ACCESS MODES    STORAGECLASS    AGE
pv-claim            Bound     pv-volume       10Gi        RWO             manual          7m1s
pvc-nfs-client      Bound     pv-nfs-client   10Mi        RWO                             6d1h

A questo punto abbiamo creato un Persistent Volume e un Claim. Ora, qualsiasi Pod può sfruttare l'oggetto claim per fare richiesta per un certo tipo di Persistent Volume. Andiamo quindi a definire un nuovo Pod tramite il seguente manifesto YAML, che chiameremo pv_Pod.yaml:
apiVersion: apps/v1
kind: Deployment
metadata:
    name: pvPod
    labels:
        run: pvPod
spec:
    replicas: 1
    selector:
        matchLabels:
            run: pvPod
    template:
	metadata:
	    labels:
	        run: pvPod
	spec:
	    containers:
	    -  name: pvPod
              image: g1g1/py-kybe:0.2
              imagePullPolicy: Always
		command: ["/bin/bash","-c","while true; do sleep 10; done"]
		volumeMounts:
		-  name: pv-volume
		   mountPath: "/mnt/data"
	    volumes:
           -  name: pv-volume
		persistentVolumeClaim:
		    claimName: pv-claim

Ora applichiamo il Pod al cluster:
$ kubectl apply -f pv_Pod.yaml

Output:

deployment.apps/pvPod created

Molto bene. Ora possiamo finalmente provare a scrivere e memorizzare un file sul path /mnt/data. Per farlo, bisogna eseguire il Pod appena creato. Questo lo si fa tramite il comando kubectl exec. Tuttavia, questo comando richiede di specificare il nome esatto assegnato al Pod da Kubernetes.
Per conoscere il nome esatto del Pod eseguiamo:
$ kubectl get Pod | grep pvPod
Vedremo un output del tipo:
Output:

pvPod-6484fd9d45-9j657             1/1     Running   1          2m22s

Per nostra sfortuna, il deployment assegna ai Pod nomi diversi ogni volta che questi vengono rieseguiti. In questo caso ci vengono incontro le Label, che ci permettono di accedere al nome corretto tramite l'etichetta assegnata al Pod, in questo modo:
$ kubectl get Pod -l pvPod -o custom-columns=NAME:.metadata.name    

Output:

pvPod-6484fd9d45-9j657             
Ora andiamo a eseguire il nuovo Pod, inserendo il nome corretto appena ottenuto:
$ kubectl exec -it    pvPod-6484fd9d45-9j657             
Ed eseguiamo i seguenti comandi per creare un file all'interno del Volume:
root@pvPod-6484fd9d45-9j657: ls /mnt/data
root@pvPod-6484fd9d45-9j657: cd /data/
root@pvPod-6484fd9d45-9j657: echo "tutorial kubernetes" > kube.txt
root@pvPod-6484fd9d45-9j657: cat kube.txt
Ecco l'output:
tutorial kubernetes           

Ora possiamo verificare che il nostro file sia stato creato e inserito nel Volume. Per essere sicuri dell'inserimento, eliminiamo il vecchio Pod e creiamone uno nuovo:
$ kubectl delete Pod pvPod-6484fd9d45-9j657   

$ kubectl create -f pv_Pod.yaml  
Quindi eseguiamo nuovamente:
$ kubectl get Pod -l pvPod -o custom-columns=NAME:.metadata.name    

Output:

pvPod-6484fd9d45-wwtx8 
$ kubectl exec -it    pvPod-6484fd9d45-wwtx8 

root@pvPod-6484fd9d45-wwtx8: cat /mnt/data/kube.txt
Se l'output corrisponde a:
Output:

tutorial kubernetes
Allora l'operazione è avvenuta con successo! Abbiamo montato manualmente il nostro primo storage Kubernetes.
In questa serie abbiamo visto i Volumi di Kubernetes e come definire dei Persistent Volume e richieste per Persistent Volume. I volumi sono oggetti di storage che Kubernetes mette a disposizione degli sviluppatori per creare delle istanze dinamiche di memoria all'interno dei Pod. Un amministratore del cluster può creare dei volumi permanenti fornendo a Kubernetes uno storage esterno, come un file system o uno storage cloud. Nei prossimi capitoli torniamo a parlare di Service Kubernetes e di bilanciamento del carico. Pronti?