Che cos’è il cloud native? Quali i suoi vantaggi? Ma, soprattutto, come si realizza? In questo tutorial vi mostriamo come implementare un sistema cloud-native low-cost con dei Raspberry Pi su cui gira GeoServer.
Quando oggi si sente parlare di tecnologie cloud non possiamo fare a meno di indirizzare i nostri pensieri a colossi del web come Google o Amazon Web Services. Questi vendor offrono servizi sempre più comodi e performanti per mettere online applicazioni al massimo delle loro performance.
Ma che cos’è questo cloud native?
Dunque, l’arcano mistero che si cela dietro la nuvola è esattamente quello che molti di voi potrebbero aver già intuito: macchine. Per l’esattezza un sistema cloud native è costituito da più macchine (nodi) raggruppate in unità agglomerate (cluster) disposte in modo tale da poter creare degli agglomerati di computazione, che permettono di avere:
- costi inferiori
- scalabilità e sicurezza
- un ridotto Time To Market
Per farci un’idea più chiara e – consentitemi il termine – meno Orwelliana del concetto di cluster, illustriamo subito quello che potrebbe essere definito un piccolo sistema cloud native:
Questa, di fatto, è una coppia di Raspberry Pi. Ciò che rende questi due microcomputer capaci di comportarsi come un cluster cloud native è Kubernetes! Per chi non lo conoscesse, Kubernetes è la piattaforma open source per la gestione, l’orchestrazione e distribuzione di applicazioni e sistemi cloud native.
Prima di iniziare ad illustrarvi come ho fatto, abbiate un po’ di pazienza nel voler comprendere il funzionamento di Kubernetes, poiché una volta capiti i seguenti concetti il resto verrà da sé.
Kubernetes usa Docker, una tecnologia basata sull’isolamento e la cosiddetta containerizzazione di immagini, il concetto è più o meno simile a quello di una macchina virtuale, solo che una macchina virtuale dispone di uno strato importante chiamato appunto virtualizzatore, mentre un’immagine Docker è un processo Unix inscatolato, isolato dal resto degli altri processi e pronto all’uso, senza virtualizzatore!
Per chi non lo sapesse, il virtualizzatore è il cuore pulsante di una macchina virtuale, uno strato intermedio che traduce istruzioni macchina così da consentire l’istanziazione di una qualsiasi macchina virtuale di parlare “la stessa lingua” della macchina che la ospita. Quindi cosa distingue un Docker da una macchina virtuale? La risposta a questa domanda risiede nell’architettura della CPU in questione.
Avete presente quell’immensa gioia di poter runnare macchine con architettura AMD dentro un PC con architettura Intel? Con Docker questa gioia vi verrà negata, poiché appunto, senza quel famoso virtualizzatore, non potremmo mai far girare immagini AMD su architettura Intel.
Il Raspberry Pi dispone di una CPU ad architettura ARM, quindi in questo tutorial verranno usate solo ed esclusivamente immagini Docker ad-hoc per questa categoria di processori.
STEP 1: Configurare la rete
Per impostare l’indirizzo IP statico di ogni Raspberry fate girare il seguente script per impostare host dinamicamente. Fatelo per ogni Raspberry.
$ nano hostname_and_ip.sh
incollare la seguente porzione di script
#!/bin/sh
hostname=$1 ip=$2 # should be of format: 192.168.1.100 dns=$3 # should be of format: 192.168.1.1
# Change the hostname sudo hostnamectl --transient set-hostname $hostname sudo hostnamectl --static set-hostname $hostname sudo hostnamectl --pretty set-hostname $hostname sudo sed -i s/raspberrypi/$hostname/g /etc/hosts
# Set the static ip
sudo cat <> /etc/dhcpcd.conf interface eth0 static ip_address=$ip/24 static routers=$dns static domain_name_servers=$dns EOT
Salvate ed eseguite lo script parametrizzandolo in modo da avere l’host come primo argomento, l’IP desiderato come secondo e l’IP del router casalingo. In questo modo avrete una cosa del genere:
$ sh hostname_and_ip.sh k8s-master 192.168.1.100 192.168.1.1
Ripetete la procedura per gli altri nodi.
STEP 2: Installare Kubeadm e Docker
Raspbian di default non esce con Docker e Kubeadm, dovremmo installarlo usando uno script esattamente come è stato fatto per la configurazione dell’host
$ nano install.sh
#!/bin/sh
# Install Docker curl -sSL get.docker.com | sh && \ sudo usermod pi -aG docker
# Disable Swap sudo dphys-swapfile swapoff && \ sudo dphys-swapfile uninstall && \ sudo update-rc.d dphys-swapfile remove echo Adding " cgroup_enable=cpuset cgroup_enable=memory" to /boot/cmdline.txt sudo cp /boot/cmdline.txt /boot/cmdline_backup.txt orig="$(head -n1 /boot/cmdline.txt) cgroup_enable=cpuset cgroup_enable=memory" echo $orig | sudo tee /boot/cmdline.txt
# Add repo list and install kubeadm curl -s https://packages.cloud.google.com/apt/doc/apt-key.gpg | sudo apt-key add - && \ echo "deb http://apt.kubernetes.io/ kubernetes-xenial main" | sudo tee /etc/apt/sources.list.d/ kubernetes.list && \
sudo apt-get update -q && \ sudo apt-get install -qy kubeadm
Runnate lo script e prendetevi un caffè.
STEP 3: Configurare Kubeadm e il nodo master
Kubernetes punta ad avere un’architettura master-slave tra i nodi del cluster che poi orchestrerà. Questo vuol dire che:
- Ci può essere solo un nodo master all’interno del cluster
- Nel nodo master non vi sarà deployment alcuno in quanto tutta la logica dell’orchestratore dovrà risiedere soltanto lì.
Per iniziare a configurare il nodo master, posizioniamoci in ssh su di esso ed editiamo quello che sarà il primo file .yaml di configurazione per l’inizializzazione del cluster.
$ nano kubeadm_conf.yaml
incollate la seguente porzione di codice
apiVersion: kubeadm.k8s.io/v1alpha1 kind: MasterConfiguration controllerManagerExtraArgs: pod-eviction-timeout: 10s
$ sudo kubeadm init —-config kubeadm_conf.yaml
una volta terminata l’inizializzazione del nodo master dovreste vedere il seguente output
Your Kubernetes master has initialized successfully!
To start using your cluster, you need to run the following as a regular user:
mkdir -p $HOME/.kube sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config sudo chown $(id -u):$(id -g) $HOME/.kube/config
You should now deploy a pod network to the cluster.
Run “kubectl apply -f [podnetwork].yaml” with one of the options listed at:
https://kubernetes.io/docs/concepts/cluster-administration/addons/
You can now join any number of machines by running the following on each node as root:
kubeadm join --token TOKEN 192.168.1.100:6443 --discovery-token-ca-cert-hash HASH
Seguite le istruzioni per spostare la configurazione di Kubernetes nella vostra home e verificare lo stato del nodo con:
$ kubectl get nodes
STEP 4: Configurare il nodo worker
Per agganciare il nodo worker al nostro cluster, basta collegarsi ad esso tramite ssh e lanciare il comando:
$ sudo kubeadm join --token TOKEN 192.168.1.100:6443 --discovery-token-ca-cert-hash HASH
Tanti più worker node avete quante più volte dovrete ripetere la procedura.
STEP 5: Installare un network container
Kubernetes è una tecnologia Google e se usata in un ambiente Google Cloud fornisce tutte le funzioni tipiche di un orchestratore. Tuttavia la nostra coppia di Raspberry configurati non sono abbastanza, occorre installare un network container, una serie di componenti Kubernetes che istruiscono il cluster su come gestire il networking dei nodi. Io l’ho installo tramite il seguente comando kubectl
$ kubectl apply -f https://git.io/weave-kube-1.6
STEP 6: Geoserver
Siamo ormai a un passo dal nostro primo deploy. A questo punto, ci sono da fare due precisazioni. La prima è che l’immagine cui si fa riferimento nel file .yaml riportato di seguito sarà disponibile a breve su dockerhub. La seconda, invece, riguarda l’architettura del nostro cluster: prima di procedere con quest’ultimo step, mi sembra doveroso spenderci qualche parola in più per spiegarla.
Abbiamo già detto che Kubernetes si basa su tecnologia docker, ovvero sulla containerizzazione e l’isolamento di applicazioni sotto forma di processi Unix. Per essere più precisi, Kubernetes si appoggia al daemon di Docker Engine e lo usa per tirare su o giù containers in modo da orchestrare l’ambiente cloud native.
Il concetto di container in Kubernetes non esiste, esistono soltanto i Pod. Un Pod è un’istanza dalla vita molto breve in cui “vive” il container Docker.
Su Kubernetes possiamo creare Pod o Deployment, delle strutture un po’ più complesse che definiscono il comportamento di un’applicazione e che daranno vita a dei Pod al posto nostro.
Iniziamo! Aprite il vostro editor preferito, il mio come avrete intuito è nano.
$ nano geoserver_deployment.yaml
ora, copiare e incollare il seguente contenuto nel file
apiVersion: apps/v1 kind: Deployment metadata: name: geo-server spec: replicas: 1 selector: matchLabels: role: geo-server template: metadata: labels: role: geo-server spec: containers: - name: geo-server image: geodatahub/geoserver4arm:latest ports: - name: geoserver containerPort: 8080
—
apiVersion: v1 kind: Service metadata: labels: app: geo-server name: geo-server spec: ports: - port: 80 protocol: TCP targetPort: 8080 name: geo-server selector: role: geo-server externalTrafficPolicy: Local type: LoadBalancer sessionAffinity: ClientIP
Non ci resta che applicare il file yaml alla nostra console di kubeadm con:
$ kubectl apply -f geoserver_deployment.yaml
Diamogli qualche minuto per pullare l’immagine e tirare su i Pod.
Verifichiamo infine che il nostro servizio sia raggiungibile tramite l’url: 192.168.1.101:8080
Senior Software Engineer