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:

cluster cloud native low cost di Raspberry Pi con Geoserver

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