Una prova su strada di k0s, Kubernetes in un singolo eseguibile by Mirantis, che ha raggiunto la versione 1.27

0

Quando abbiamo parlato la prima volta di k0s, nell’articolo “Per voi è troppo k8s? Avete provato k3s e lo trovate esoso? Arriva Mirantis con il suo k0s, Kubernetes in un eseguibile“, avevamo analizzato in maniera approssimativa le funzionalità di questa soluzione, senza approfondire.

L’occasione di rimediare viene però data dall’annuncio da parte di Mirantis della disponibilità della versione 1.27 di k0s che segue chiaramente Chill Vibes, la nuova release di Kubernetes.

L’aspetto su cui punta Mirantis per promuovere k0s è in prima istanza quello della sicurezza. Infatti ciò che è garantito dall’azienda proprietaria di Docker è il fatto che l’eseguibile viene distribuito senza alcuna CVE conosciuta riscontrabile.

Per chiarire meglio riportiamo le parole di Miska Kaipiainen, VP of engineering, product strategy, and open source in Mirantis:

Before 1.27, k0s relied on system images published by various upstream projects, this has worked pretty well, but there are some downsides. For one thing, it’s understood (sadly) that most upstream system images used by Kubernetes contain CVEs.

Prima della 1.27 k0s si basava sulle immagini di sistema pubblicate dai vari progetti upstream, e questo ha funzionato bene, ma con qualche controindicazione. Per prima cosa, è risaputo che (tristemente) la maggioranza delle immagini di sistema upstream usate da Kubernetes contiene CVE.

E la soluzione a questo è citata appunto nella nuova modalità con cui k0s è prodotto:

Starting with this 1.27 release, k0s will run all system components with images that we build ourselves. We still use pure upstream functionality and do not use any custom forks of project components. Essentially what we do is take the upstream components as-is and rebuild the images in a way that mitigates as many known CVEs as possible.

A partire da questa release 1.27 tutte le componenti di sistema con cui funzionerà k0s si baseranno su immagini che costruiremo per nostro conto. Usiamo ancora le funzionalità upstream, senza fare fork delle componenti di progetto. Quello che facciamo è essenzialmente prendere le componenti upstream come sono e farne la rebuild in modo da mitigare più CVE possibili.

Le premesse quindi sembrano interessanti, ma nella pratica come funziona questo k0s?

Installare k0s

L’installazione di k0s potrebbe essere fatta lanciando direttamente un file .sh da internet, ma non essendo particolarmente fan di questa modalità, qualcosa di più canonico funziona ugualmente:

rasca@ubuntu-jammy:~$ wget https://github.com/k0sproject/k0s/releases/download/v1.27.1%2Bk0s.0/k0s-v1.27.1+k0s.0-amd64
...

rasca@ubuntu-jammy:~$ sudo mv k0s-v1.27.1+k0s.0-amd64 /usr/local/bin/k0s

rasca@ubuntu-jammy:~$ sudo chmod +x /usr/local/bin/k0s

Una volta che l’eseguibile è disponibile si può avviare la creazione del controller e l’avvio del cluster:

rasca@ubuntu-jammy:~$ sudo k0s install controller --single --verbose
INFO[2023-05-05 13:21:12] creating user: etcd                          
INFO[2023-05-05 13:21:12] creating user: kube-apiserver                
INFO[2023-05-05 13:21:12] creating user: konnectivity-server           
INFO[2023-05-05 13:21:12] creating user: kube-scheduler                
INFO[2023-05-05 13:21:12] Installing k0scontroller service 

rasca@ubuntu-jammy:~$ sudo k0s start

rasca@ubuntu-jammy:~$ sudo k0s status
Version: v1.27.1+k0s.0
Process ID: 1822
Role: controller
Workloads: true
SingleNode: true
Kube-api probing successful: true
Kube-api probing last error: 

Da qui in poi il Kubernetes di k0s sarà accessibile mediante sudo k0s kubectl o, se avete già kubectl disponibile sul vostro sistema (come in questo caso) andando a creare il file kubeconfig di k0s che conterrà tutte le informazioni di accesso:

rasca@ubuntu-jammy:~$ sudo k0s kubeconfig admin > kubeconfig

rasca@ubuntu-jammy:~$ export KUBECONFIG=kubeconfig

rasca@ubuntu-jammy:~$ kubectl get nodes
NAME STATUS ROLES AGE VERSION
ubuntu-jammy Ready control-plane 6m32s v1.27.1+k0s

rasca@ubuntu-jammy:~$ kubectl get namespaces
NAME              STATUS   AGE
kube-system       Active   110s
kube-public       Active   110s
kube-node-lease   Active   110s
default           Active   110s
k0s-autopilot     Active   107s

Il cluster è quindi pronto all’uso.

Deploy di un’applicazione

k0s è Kubernetes ed in questo senso la creazione di un deployment e del relativo servizio può essere fatta nella consueta modalità:

rasca@ubuntu-jammy:~$ kubectl create namespace mynamespace
namespace/mynamespace created

rasca@ubuntu-jammy:~$ kubectl -n mynamespace create deployment nginx --image=nginx:latest
deployment.apps/nginx created

rasca@ubuntu-jammy:~$ kubectl -n mynamespace create service clusterip nginx --tcp=80:80
service/nginx created

rasca@ubuntu-jammy:~$ kubectl -n mynamespace set selector service nginx 'app=nginx'
service/nginx selector updated

In questo test k0s è stato installato in una macchina virtuale non particolarmente potente (2 CPU, 4 GB RAM), ma i tempi di risposta sono davvero eccellenti, ed in pochi secondi l’applicazione di test (il webserver nginx) è disponibile:

rasca@ubuntu-jammy:~$ kubectl -n mynamespace get all
NAME                         READY   STATUS    RESTARTS   AGE
pod/nginx-7bf8c77b5b-rxxjv   1/1     Running   0          31s

NAME            TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)   AGE
service/nginx   ClusterIP   10.106.130.151   <none>        80/TCP    20s

NAME                    READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/nginx   1/1     1            1           31s

NAME                               DESIRED   CURRENT   READY   AGE
replicaset.apps/nginx-7bf8c77b5b   1         1         1       31s

A proposito di servizi

A questo punto, nella maggioranza degli ambienti Kubernetes verrebbe il dubbio in merito a come esporre i servizi all’esterno del cluster poiché il servizio creato qui sopra è di tipo ClusterIP, quindi visibile unicamente all’interno della rete cluster. Ci vorrebbe una componente ingress e verosimilmente un load balancer, entrambi non presenti di default in k0s.

C’è però un aspetto molto interessante di k0s che vale la pena citare, e lo si introduce andando a stampare lo stato delle interfacce di rete presenti nel sistema dopo l’installazione, ponendo attenzione sul device kube-bridge:

rasca@ubuntu-jammy:~$ ip a
...
...
5: kube-bridge: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
    link/ether 72:ff:99:76:bc:ec brd ff:ff:ff:ff:ff:ff
    inet 10.244.0.1/24 brd 10.244.0.255 scope global kube-bridge
       valid_lft forever preferred_lft forever
    inet6 fe80::24ce:51ff:fee5:a65b/64 scope link 
       valid_lft forever preferred_lft forever
...
...

E visualizzando le regole di NAT del firewall, create in automatico mediante nft, relative all’indirizzo IP del servizio che abbiamo creato, ossia 10.106.130.151:

rasca@ubuntu-jammy:~$ kubectl -n mynamespace get service
NAME    TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)   AGE
nginx   ClusterIP   10.106.130.151   <none>        80/TCP    59m

rasca@ubuntu-jammy:~$ sudo nft list table nat | grep -C 2 10.106.130.151

	chain KUBE-SERVICES {
		meta l4proto tcp ip daddr 10.106.130.151  tcp dport 80 counter packets 0 bytes 0 jump KUBE-SVC-DHME6VD2S2S3MUZC
		meta l4proto tcp ip daddr 10.96.0.1  tcp dport 443 counter packets 0 bytes 0 jump KUBE-SVC-NPX46M4PTMTKRN6Y
		meta l4proto udp ip daddr 10.96.0.10  udp dport 53 counter packets 0 bytes 0 jump KUBE-SVC-TCOU7JCQXEZGVUNU
--

	chain KUBE-SVC-DHME6VD2S2S3MUZC {
		meta l4proto tcp ip saddr != 10.244.0.0/16 ip daddr 10.106.130.151  tcp dport 80 counter packets 5 bytes 300 jump KUBE-MARK-MASQ
		 counter packets 5 bytes 300 jump KUBE-SEP-ELPBDVDUOHBNVX3R
	}

Quindi sì, kube-bridge viene usata dalle regole per mettere in comunicazione l’host con la rete cluster di Kubernetes, consentendo quindi l’accesso diretto ai servizi di tipo ClusterIP:

rasca@ubuntu-jammy:~$ curl 10.106.130.151
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>

<p><em>Thank you for using nginx.</em></p>
</body>
</html>

In questo modo il test di eventuali applicazioni risulta assolutamente immediato e non vi è necessità di effettuare il deploy di componenti aggiuntive, comunque supportate da k0s, quali Ingress NGINX come ingress controller o MetalLB come load balancer.

Conclusioni

Se le esigenze poi esulano dai semplici test va sottolineato come k8s sia a tutti gli effetti adatto alla produzione, infatti mediante l’utilizzo di k0ctl è possibile creare e gestire istanze multiple di k0s attraverso un semplice yaml, in maniera centralizzata, andando a risolvere anche la problematica di gestione multi-cluster.

Mica male per un singolo eseguibile!

Da sempre appassionato del mondo open-source e di Linux nel 2009 ho fondato il portale Mia Mamma Usa Linux! per condividere articoli, notizie ed in generale tutto quello che riguarda il mondo del pinguino, con particolare attenzione alle tematiche di interoperabilità, HA e cloud.
E, sì, mia mamma usa Linux dal 2009.