☵️

K8s per Svogliati

La guida a Kubernetes che non volevi leggere ma che ti salverà il culo il giorno che il cluster esplode alle 3 di notte.

Da zero a "so quello che sto facendo (più o meno)"

Alla fine K8s non è poi così terribile, basta che qualcuno te lo spieghi senza il tono da "senior architect che ha fatto 14 certificazioni".

Capitolo 01

Che Cavolo è Kubernetes?

Spoiler: non è magia, è solo un sacco di YAML

🍔 Analogia per Svogliati

Immagina di gestire una pizzeria. Hai dei forni (server), delle pizze da fare (container), e dei pizzaioli (processi). Kubernetes è il direttore di sala che decide quale pizza va in quale forno, se un forno si rompe sposta le pizze in un altro, e se arrivano 200 clienti accende altri forni.

📦 Container

Un pacchetto che contiene la tua app + tutto quello che le serve. Come una scatola da trasloco con l'etichetta "FRAGILE" che nessuno rispetta.

💡 Docker crea i container. Kubernetes li orchestra — cioè decide dove, quando e quanti farne girare.

☵️ Kubernetes (K8s)

Un sistema di orchestrazione di container. Il nome viene dal greco "timoniere" — e il numero 8 sta per le 8 lettere tra la K e la S. Sì, i dev sono pigri quanto te.

⚠️ K8s NON è: un hosting, un linguaggio, una religione (anche se a volte sembra).

💔 Senza K8s

SSH sul server → docker run → preghiera → "funziona sulla mia macchina" → il server muore alle 3AM → sveglia → panico → SSH → docker run di nuovo

VS

💚 Con K8s

Definisci cosa vuoi → K8s lo fa → qualcosa muore → K8s lo ricrea da solo → tu dormi → il tuo capo pensa che sei un genio

Capitolo 02

Architettura di K8s

Ovvero "chi fa cosa in questo casino"

🏗️ Architettura del Cluster Kubernetes
CONTROL PLANE (Il Cervello) API Server La reception Tutto passa da qui etcd Il database Ricorda tutto Scheduler L'assegnatore Pod → Nodo giusto Controller Manager Il supervisore Stato desiderato = reale kubectl / API WORKER NODES (Le Braccia) 🖥️ Nodo 1 kubelet Agente locale kube-proxy Networking Pod A 📦 nginx Pod B 📦 api Pod C 📦 redis 🖥️ Nodo 2 kubelet Agente locale kube-proxy Networking Pod D 📦 app Pod E 📦 db 🖥️ Nodo 3 kubelet Agente locale kube-proxy Networking Pod F 📦 worker Pod G 📦 cache

🧠 Control Plane

Il "cervello" del cluster. Prende tutte le decisioni. Non ci gira la tua app (di solito).

  • API Server — la porta d'ingresso. Ogni comando passa da qui
  • etcd — il database. Se muore etcd, muore tutto
  • Scheduler — decide su quale nodo piazzare un Pod
  • Controller Manager — controlla che la realtà corrisponda al desiderio

💪 Worker Nodes

Le "braccia" del cluster. Qui gira la tua app per davvero.

  • kubelet — l'agente sul nodo, esegue gli ordini
  • kube-proxy — gestisce il networking sul nodo
  • Container Runtime — containerd/CRI-O, quello che fa girare i container
  • Pods — i tuoi container, finalmente al lavoro
Capitolo 03

I Concetti Base

Le parole che dovrai fingere di capire alle riunioni

🔗 Come si Relazionano le Risorse K8s
Deployment "Voglio 3 repliche della mia app" crea/gestisce ReplicaSet "Mantengo sempre 3 Pod attivi" Pod 1 📦 container(s) Pod 2 📦 container(s) Pod 3 📦 container(s) Service (ClusterIP/NodePort/LB) "IP stabile per raggiungere i Pod"
📦

Pod

L'unità più piccola. Uno o più container che condividono rete e storage. Come un coinquilino che non paga l'affitto.

🔄

ReplicaSet

Mantiene N copie del tuo Pod. Ne muore uno? Ne crea un altro. Come un'idra.

🚀

Deployment

Gestisce i ReplicaSet. Rolling update, rollback. Il tuo migliore amico.

🌐

Service

Un IP stabile per raggiungere i Pod. Perché i Pod muoiono e rinascono con IP diversi.

📎

Namespace

Cartelle virtuali per organizzare le risorse. Tipo "dev", "staging", "prod-che-non-toccare".

🔒

ConfigMap / Secret

Configurazioni e credenziali. I Secret sono base64, NON criptati. Sì, lo so.

📂

PV / PVC

Storage persistente. PV = il disco. PVC = "mi serve un disco così". Come Tinder ma per lo storage.

🚦

Ingress

Espone i Service all'esterno con regole HTTP. Il buttafuori del cluster.

🛠️

DaemonSet

Un Pod su OGNI nodo. Perfetto per monitoring e log. Come la muffa: ovunque.

💥 Regola d'oro: In K8s tutto è dichiarativo. Non dici "crea 3 pod". Dici "voglio 3 pod" e K8s si arrangia. Se ne muore uno, lo ricrea. Se ne vuoi 5, ne aggiunge 2. Tu dichiari lo stato desiderato, lui ci arriva.
Capitolo 04

Installazione

Il primo passo per il tuo nuovo hobby non richiesto

💻 Opzione 1: minikube 💤 Più Facile

Un cluster K8s single-node sulla tua macchina. Perfetto per imparare senza distruggere niente (di importante).

bash — Installazione minikube
# macOS (con Homebrew)
brew install minikube

# Linux
curl -LO https://storage.googleapis.com/minikube/releases/latest/minikube-linux-amd64
sudo install minikube-linux-amd64 /usr/local/bin/minikube

# Avvia il cluster (ci mette un po', vai a fare caffè)
minikube start

# Verifica che funzioni
kubectl get nodes
NAME       STATUS   ROLES           AGE   VERSION
minikube   Ready    control-plane   42s   v1.31.0

Opzione 2: kind (Kubernetes IN Docker)

Cluster K8s dentro container Docker. Più leggero di minikube, ottimo per test e CI/CD.

bash — kind
brew install kind
kind create cluster --name svogliati-cluster
kubectl cluster-info

☁️ Opzione 3: Cloud Managed

Per chi ha il budget (o il free tier). Qualcun altro gestisce il control plane, tu gestisci solo i worker.

bash — Cloud providers
# Google GKE
gcloud container clusters create svogliati --num-nodes=3

# AWS EKS
eksctl create cluster --name svogliati --nodes 3

# Azure AKS
az aks create -g myResourceGroup -n svogliati --node-count 3

🐄 Opzione 4: RKE2 (Rancher) 😎 Per Veri Svogliati

RKE2 è il K8s di Rancher/SUSE. Un comando, un cluster production-ready. Niente Docker da installare prima, niente dipendenze strane, niente 47 pagine di documentazione. Uno script, un servizio, via. È quello che usi quando hai server bare-metal o VM e vuoi un cluster serio senza vendere l'anima al cloud.

🚀 Perché RKE2? Hardened di default (CIS benchmark), usa containerd (niente Docker), include tutto nel binario. Lo installi, parte, funziona. Il sogno di ogni svogliato che però vuole fare bella figura in audit.
bash — Server (Master) — 3 comandi e hai un cluster
# ===== NODO SERVER (MASTER) =====

# 1. Installa RKE2 server
curl -sfL https://get.rke2.io | sudo sh -

# 2. Abilita e avvia
sudo systemctl enable rke2-server
sudo systemctl start rke2-server

# 3. Aspetta un minuto (vai a fare caffè, di nuovo)
# Controlla che sia partito:
sudo journalctl -u rke2-server -f

# 4. Configura kubectl
mkdir -p ~/.kube
sudo cp /etc/rancher/rke2/rke2.yaml ~/.kube/config
sudo chown $(id -u):$(id -g) ~/.kube/config
export PATH=$PATH:/var/lib/rancher/rke2/bin
echo 'export PATH=$PATH:/var/lib/rancher/rke2/bin' >> ~/.bashrc

# 5. Verifica
kubectl get nodes
NAME        STATUS   ROLES                       AGE   VERSION
server-01   Ready    control-plane,etcd,master    60s   v1.30.x+rke2r1

# Prendi il token per aggiungere i worker
sudo cat /var/lib/rancher/rke2/server/node-token
K10abc123...::server:xyz789...
bash — Worker (Agent) — 2 comandi
# ===== NODO WORKER (ripeti per ogni worker) =====

# 1. Installa RKE2 in modalità agent
curl -sfL https://get.rke2.io | INSTALL_RKE2_TYPE="agent" sudo sh -

# 2. Configura: dove è il server e qual è il token
sudo mkdir -p /etc/rancher/rke2
sudo tee /etc/rancher/rke2/config.yaml <<EOF
server: https://IP-DEL-SERVER:9345
token: IL-TOKEN-CHE-HAI-COPIATO-PRIMA
EOF

# 3. Avvia
sudo systemctl enable rke2-agent
sudo systemctl start rke2-agent

# 4. Torna sul server e controlla
kubectl get nodes
NAME        STATUS   ROLES                       AGE    VERSION
server-01   Ready    control-plane,etcd,master    5m     v1.30.x+rke2r1
worker-01   Ready    <none>                       30s    v1.30.x+rke2r1
worker-02   Ready    <none>                       15s    v1.30.x+rke2r1

# 🎉 Cluster production-ready in 5 minuti. Andiamo a dormire.
bash — Config avanzata (opzionale, per fare i fighi)
# /etc/rancher/rke2/config.yaml sul SERVER
# (crea PRIMA di avviare rke2-server)

# HA: più server per alta disponibilità
tls-san:
  - "lb.esempio.com"         # DNS del load balancer
  - "10.0.0.100"              # VIP

# CNI: di default usa Canal, ma puoi cambiare
cni: cilium                    # canal | cilium | calico | none

# Disabilita componenti che non ti servono
disable:
  - rke2-ingress-nginx        # se usi un altro ingress controller

# CIDR custom
cluster-cidr: "10.42.0.0/16"
service-cidr: "10.43.0.0/16"
💡 RKE2 vs K3s: K3s è il fratellino leggero (IoT, edge, Raspberry Pi). RKE2 è il fratello serio (produzione, compliance, enterprise). Se il tuo capo dice "CIS hardened" e tu non vuoi configurare 200 cose a mano, RKE2 è la risposta. Se vuoi K8s su un Raspberry Pi, prendi K3s.

🔧 kubectl — Il Tuo Coltellino Svizzero

L'unico comando che dovrai imparare. Tutto il resto è contorno.

bash — Installa kubectl
# macOS
brew install kubectl

# Linux
curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"
sudo install kubectl /usr/local/bin/kubectl

# Verifica
kubectl version --client

# Pro tip: alias per svogliati
echo 'alias k=kubectl' >> ~/.bashrc
echo 'complete -o default -F __start_kubectl k' >> ~/.bashrc
🚀 Da ora in poi puoi scrivere k invece di kubectl. Hai appena risparmiato 6 lettere per comando. Con 100 comandi al giorno, sono 600 lettere. Ringraziami.
Capitolo 05

Primi Passi

Da "cosa sto facendo" a "forse so cosa sto facendo"

1

Crea il tuo primo Pod

Un Pod con nginx. Niente di più facile.

bash
# Crea un pod al volo (modo imperativo)
kubectl run mio-primo-pod --image=nginx

# Vedi se è vivo
kubectl get pods
NAME             READY   STATUS    RESTARTS   AGE
mio-primo-pod    1/1     Running   0          10s

# Bravo! Hai appena deployato qualcosa!
2

Ispeziona il Pod

Guarda dentro. Come un dottore, ma per container.

bash
# Dettagli completi
kubectl describe pod mio-primo-pod

# Solo lo YAML (il DNA del Pod)
kubectl get pod mio-primo-pod -o yaml

# Log del container
kubectl logs mio-primo-pod

# Entra DENTRO il container (come SSH ma più figo)
kubectl exec -it mio-primo-pod -- /bin/bash
3

Crea un Deployment

Ora facciamo le cose per bene. Un Deployment gestisce più repliche e gli aggiornamenti.

bash
# Crea un deployment con 3 repliche
kubectl create deployment mia-app --image=nginx --replicas=3

# Guarda i pod che spuntano
kubectl get pods -w  # -w = watch, si aggiorna live
NAME                       READY   STATUS    RESTARTS   AGE
mia-app-6d8f5c7b9d-abc12   1/1     Running   0          5s
mia-app-6d8f5c7b9d-def34   1/1     Running   0          5s
mia-app-6d8f5c7b9d-ghi56   1/1     Running   0          5s

# Prova a uccidere un pod. K8s ne crea subito un altro
kubectl delete pod mia-app-6d8f5c7b9d-abc12
# ...aspetta 2 secondi...
kubectl get pods
# Tadaaa! 3 pod di nuovo. K8s non molla mai.
4

Esponi con un Service

Il tuo deployment gira ma nessuno lo vede. Serve un Service.

bash
# Esponi il deployment
kubectl expose deployment mia-app --port=80 --type=NodePort

# Vedi il servizio creato
kubectl get svc
NAME      TYPE       CLUSTER-IP     EXTERNAL-IP   PORT(S)        AGE
mia-app   NodePort   10.96.45.123   <none>        80:31234/TCP   5s

# Con minikube, apri nel browser
minikube service mia-app

# Oppure port-forward per test veloce
kubectl port-forward svc/mia-app 8080:80
# Ora apri http://localhost:8080 🎉
5

Scala e Aggiorna

Il bello di K8s: scalare è un comando, aggiornare pure.

bash
# Scala a 5 repliche
kubectl scale deployment mia-app --replicas=5

# Aggiorna l'immagine (rolling update!)
kubectl set image deployment/mia-app nginx=nginx:1.25

# Guarda il rollout in tempo reale
kubectl rollout status deployment/mia-app

# Qualcosa è andato storto? ROLLBACK!
kubectl rollout undo deployment/mia-app
# Come se niente fosse successo 😎
Capitolo 06

Benvenuto nello YAML Hell

Dove l'indentazione sbagliata ti rovina la giornata

🔥 La Verità sullo YAML

In K8s tutto è YAML. Il deployment? YAML. Il service? YAML. Il tuo dolore? Probabilmente causato da YAML con un'indentazione sbagliata di uno spazio. MAI usare TAB nello YAML. Solo spazi. Due spazi. Sempre.

📄 Anatomia di un Deployment YAML

deployment.yaml — commentato per svogliati
apiVersion: apps/v1          # Versione dell'API. Ogni risorsa ha la sua
kind: Deployment              # Tipo di risorsa
metadata:                      # Info sulla risorsa
  name: mia-app                # Nome del deployment
  labels:                      # Etichette (tipo hashtag)
    app: mia-app
spec:                          # Le specifiche (cosa vuoi)
  replicas: 3                  # Quante copie
  selector:                    # Come trova i Pod da gestire
    matchLabels:
      app: mia-app              # Deve matchare con template.labels
  template:                    # Template del Pod
    metadata:
      labels:
        app: mia-app            # Label del Pod (deve matchare selector!)
    spec:
      containers:              # Lista dei container nel Pod
      - name: nginx
        image: nginx:1.25      # Immagine Docker
        ports:
        - containerPort: 80    # Porta esposta
        resources:             # SEMPRE settare le risorse!
          requests:            # Minimo garantito
            cpu: "100m"        # 100 milliCPU = 0.1 CPU
            memory: "128Mi"   # 128 MB RAM
          limits:              # Massimo consentito
            cpu: "500m"        # Se sfora, viene throttlato
            memory: "256Mi"   # Se sfora, viene UCCISO (OOMKilled)

🌐 Service YAML

service.yaml
apiVersion: v1
kind: Service
metadata:
  name: mia-app-service
spec:
  type: ClusterIP             # ClusterIP = interno | NodePort = porta su nodo | LoadBalancer = IP esterno
  selector:                   # Quali Pod "cattura"
    app: mia-app               # Pod con label app=mia-app
  ports:
  - port: 80                  # Porta del Service
    targetPort: 80            # Porta del container

🚦 Ingress YAML

ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: mia-app-ingress
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
spec:
  rules:
  - host: mia-app.esempio.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: mia-app-service
            port:
              number: 80
bash — Applica i file YAML
# Applica un singolo file
kubectl apply -f deployment.yaml

# Applica tutto in una cartella
kubectl apply -f ./manifests/

# Dry-run: vedi cosa succederebbe SENZA farlo davvero
kubectl apply -f deployment.yaml --dry-run=client

# Genera YAML da un comando (per svogliati che non vogliono scrivere YAML)
kubectl create deployment test --image=nginx --dry-run=client -o yaml > deployment.yaml
# 👆 Questo trucco vale ORO. Genera lo YAML base e tu lo modifichi.
💡 Pro tip per svogliati: kubectl create ... --dry-run=client -o yaml genera lo YAML per qualsiasi risorsa. Non serve ricordare la struttura a memoria. Copia, incolla, modifica. Fatto.
Capitolo 07

Networking

Come i Pod si parlano (e come parli tu con loro)

🌐 Tipi di Service a Confronto
🌐 Internet LoadBalancer Service IP esterno pubblico (cloud only) NodePort Service Porta 30000-32767 su ogni nodo ClusterIP Service Solo interno al cluster (default) 📦 Pod 1 📦 Pod 2 📦 Pod 3 tipo: LoadBalancer tipo: NodePort tipo: ClusterIP

💬 DNS Interno

Ogni Service ha un nome DNS automatico. I Pod si parlano così:

DNS
# Formato DNS di un Service:
<nome-service>.<namespace>.svc.cluster.local

# Esempi:
curl http://mia-app-service.default.svc.cluster.local
curl http://mia-app-service  # abbreviato, se stesso namespace

# Nel tuo codice, connettiti al DB così:
postgres://db-service:5432/mydb

🔓 Network Policies

Firewall per Pod. Di default tutti parlano con tutti. Cattiva idea in prod.

network-policy.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-only-frontend
spec:
  podSelector:
    matchLabels:
      app: backend
  ingress:
  - from:
    - podSelector:
        matchLabels:
          app: frontend
Capitolo 08

Storage

Perché i container sono effimeri come le tue motivazioni

💾 Il Problema

Quando un Pod muore, tutti i dati dentro muoiono con lui. Se il tuo database gira in un Pod senza storage persistente, ogni restart è un factory reset. Non ideale.

📂 Flusso dello Storage Persistente
Pod "Mi serve storage!" monta PVC "Voglio 10Gi RWO" binda PV "Ecco 10Gi disco" Disco EBS/GCE/NFS StorageClass provisiona automaticamente

📂 PVC + Deployment

pvc-example.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: mio-storage
spec:
  accessModes:
    - ReadWriteOnce          # RWO = un nodo alla volta
  resources:
    requests:
      storage: 5Gi
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: app-con-storage
spec:
  replicas: 1
  selector:
    matchLabels:
      app: app-con-storage
  template:
    metadata:
      labels:
        app: app-con-storage
    spec:
      containers:
      - name: app
        image: nginx
        volumeMounts:
        - name: dati
          mountPath: /data      # Dove appare nel container
      volumes:
      - name: dati
        persistentVolumeClaim:
          claimName: mio-storage
Capitolo 09

Troubleshooting

Quando le cose vanno male (e andranno male)

🔍 Flowchart: "Il mio Pod non funziona"
Pod non funziona! kubectl get pods Qual è lo STATUS? Pending Cause comuni: - Nodo senza risorse - PVC non bindato - NodeSelector errato CrashLoopBackOff Cause comuni: - App crasha all'avvio - Config/env sbagliato - OOMKilled (poca RAM) ImagePullBackOff Cause comuni: - Immagine non esiste - Tag sbagliato - Registry privato senza secret kubectl describe pod guarda Events kubectl logs <pod> --previous log dell'istanza crashata kubectl describe pod controlla immagine + secret kubectl describe + kubectl logs = 90% dei problemi risolti kubectl get events --sort-by=.metadata.creationTimestamp timeline di tutto quello che è successo Problema trovato!

CRITICO Pod in CrashLoopBackOff

Il container crasha e K8s continua a riavviarlo con intervalli crescenti (backoff).

bash — Debug
# 1. Guarda i log del crash precedente
kubectl logs <pod-name> --previous

# 2. Guarda gli eventi
kubectl describe pod <pod-name> | tail -20

# 3. Se OOMKilled, aumenta memory limits
# 4. Se errore app, fixa il codice/config
# 5. Se manca un env/secret:
kubectl get configmap,secret -n <namespace>

COMUNE Pod stuck in Pending

Lo Scheduler non riesce a piazzare il Pod su nessun nodo.

bash — Debug
# 1. Guarda gli eventi del Pod
kubectl describe pod <pod-name>
# Cerca "FailedScheduling" negli Events

# 2. Controlla le risorse disponibili nei nodi
kubectl describe nodes | grep -A5 "Allocated resources"

# 3. Il PVC è bindato?
kubectl get pvc
# Se dice "Pending", il problema è lo storage, non il Pod

# 4. Controlla taints e tolerations
kubectl describe nodes | grep Taint

COMUNE Service non raggiungibile

Hai creato il Service ma non risponde. Le label sono il problema nel 99% dei casi.

bash — Debug
# 1. Il Service ha degli endpoints?
kubectl get endpoints <service-name>
# Se la lista è vuota, le label non matchano!

# 2. Confronta le label
kubectl get svc <service-name> -o yaml | grep -A3 selector
kubectl get pods --show-labels

# 3. Testa la connessione dall'interno
kubectl run test-debug --image=busybox --rm -it -- wget -qO- http://<service-name>

# 4. La porta è giusta?
kubectl get svc <service-name> -o yaml | grep -A5 ports

AVANZATO Node NotReady

Un nodo è andato in vacanza. I Pod sopra verranno ri-schedulati su altri nodi (dopo ~5 min).

bash — Debug
# 1. Stato dei nodi
kubectl get nodes
kubectl describe node <node-name>

# 2. Sul nodo (se accessibile via SSH)
systemctl status kubelet
journalctl -u kubelet -f

# 3. Riavvia kubelet
systemctl restart kubelet

# 4. Drain del nodo prima di manutenzione
kubectl drain <node-name> --ignore-daemonsets --delete-emptydir-data
# Questo sposta tutti i Pod su altri nodi

PANICO etcd down / API Server non risponde

Se l'API Server non risponde, non è la fine del mondo. I Pod già in esecuzione continuano a girare. Non puoi solo fare modifiche.

bash — Panico Controllato
# 1. Controlla i componenti del control plane
kubectl get componentstatuses  # potrebbe non funzionare se API è down

# 2. Se hai accesso SSH al master
crictl ps | grep -E "etcd|apiserver|scheduler|controller"

# 3. Controlla i log del control plane
journalctl -u kubelet -f  # su nodo master

# 4. Backup etcd (FAI QUESTO REGOLARMENTE!)
ETCDCTL_API=3 etcdctl snapshot save backup.db

# 5. Se managed (GKE/EKS/AKS): apri un ticket. È il loro problema.
Capitolo 10

Cheat Sheet Definitivo

Stampalo, appendilo, tatuatelo

👀 Ispezionare

Cosa vuoi fareComando
Vedi tutti i podkubectl get pods -A
Vedi pod con più dettaglikubectl get pods -o wide
Dettaglio di un podkubectl describe pod <nome>
Log di un podkubectl logs <pod> -f
Log del container precedente (crash)kubectl logs <pod> --previous
Entra in un containerkubectl exec -it <pod> -- /bin/sh
Vedi eventi recentikubectl get events --sort-by='.lastTimestamp'
Risorse dei nodikubectl top nodes
Risorse dei podkubectl top pods
Vedi tutto in un namespacekubectl get all -n <ns>

🛠️ Creare e Modificare

Cosa vuoi fareComando
Applica un manifestkubectl apply -f file.yaml
Crea un deploymentkubectl create deploy <nome> --image=<img>
Scala replichekubectl scale deploy <nome> --replicas=N
Aggiorna immaginekubectl set image deploy/<nome> <c>=<img:tag>
Rollbackkubectl rollout undo deploy/<nome>
Edita risorsa livekubectl edit deploy/<nome>
Cancella risorsakubectl delete -f file.yaml
Port-forward localekubectl port-forward svc/<nome> 8080:80
Genera YAML senza crearekubectl create deploy x --image=y --dry-run=client -o yaml

🔒 Secrets e Config

Cosa vuoi fareComando
Crea ConfigMap da filekubectl create cm <nome> --from-file=config.txt
Crea ConfigMap da literalkubectl create cm <nome> --from-literal=KEY=val
Crea Secretkubectl create secret generic <nome> --from-literal=pw=xxx
Decodifica un Secretkubectl get secret <nome> -o jsonpath='{.data.pw}' | base64 -d
Vedi tutti i contextkubectl config get-contexts
Cambia contextkubectl config use-context <nome>
Cambia namespace defaultkubectl config set-context --current --namespace=<ns>
🚀 Alias da Svogliato Professionista:
~/.bashrc o ~/.zshrc
# Alias base
alias k=kubectl
alias kg='kubectl get'
alias kgp='kubectl get pods'
alias kgpa='kubectl get pods -A'
alias kd='kubectl describe'
alias kl='kubectl logs -f'
alias kx='kubectl exec -it'
alias kaf='kubectl apply -f'
alias kdf='kubectl delete -f'
alias kns='kubectl config set-context --current --namespace'

# Il più utile di tutti: mostra pod con nodo e IP
alias kwide='kubectl get pods -o wide'

# Watch continuo dei pod
alias kwatch='kubectl get pods -w'
Capitolo 15

GUI, Dashboard & Tools

Perché il terminale è bello, ma cliccare è meglio (non ditelo a nessuno)

🕵️ La Verità Scomoda

I puristi ti diranno che "il vero sysadmin usa solo la CLI". Sì vabbè. Intanto loro scrivono 47 comandi kubectl per capire cosa sta succedendo, e tu con due click hai la dashboard con grafici, log in tempo reale e il bottone "riavvia". Chi è lo svogliato intelligente adesso?

🐄 Rancher GRATIS

La Rolls Royce delle dashboard K8s. Gestisci più cluster da un'unica interfaccia web. Crei cluster, aggiungi nodi, fai deploy, gestisci RBAC, monitori tutto. E puoi anche importare cluster già esistenti (EKS, GKE, AKS, RKE2, qualsiasi cosa).

Punti di forza:

  • Multi-cluster management
  • Catalogo app (tipo app store per K8s)
  • RBAC con interfaccia grafica
  • Monitoring integrato (Prometheus/Grafana)
  • Gestione GitOps con Fleet

Ideale per:

  • Team che gestiscono più cluster
  • Aziende che vogliono una UI seria
  • Chi ha RKE2 e vuole il pacchetto completo
  • Svogliati che vogliono fare tutto da browser
bash — Installa Rancher con Helm (su un cluster K8s esistente)
# 1. Aggiungi il repo Helm
helm repo add rancher-stable https://releases.rancher.com/server-charts/stable
helm repo update

# 2. Crea il namespace
kubectl create namespace cattle-system

# 3. Installa cert-manager (serve per i certificati TLS)
kubectl apply -f https://github.com/cert-manager/cert-manager/releases/latest/download/cert-manager.yaml
# Aspetta che sia pronto
kubectl wait --for=condition=Available deployment --all -n cert-manager --timeout=120s

# 4. Installa Rancher
helm install rancher rancher-stable/rancher \
  --namespace cattle-system \
  --set hostname=rancher.tuodominio.com \
  --set bootstrapPassword=admin

# 5. Aspetta che sia pronto
kubectl rollout status deployment rancher -n cattle-system

# 6. Apri https://rancher.tuodominio.com e goditi la GUI 🎉

# Alternativa rapida per test locale:
docker run -d --restart=unless-stopped \
  -p 80:80 -p 443:443 \
  --privileged \
  rancher/rancher:latest

🔎 Lens (OpenLens) DESKTOP APP

L'IDE per Kubernetes. Un'app desktop che si connette ai tuoi cluster e ti mostra tutto con una GUI pazzesca. Log in tempo reale, shell nei pod, metriche, tutto. È tipo Visual Studio Code ma per K8s.

Punti di forza:

  • UI bellissima e veloce
  • Log in real-time con filtri
  • Shell nei container con un click
  • Editor YAML integrato
  • Multi-cluster con tab

Ideale per:

  • Sviluppatori che vogliono capire K8s
  • Debug veloce senza ricordare i comandi
  • Chi vuole una visione d'insieme rapida
  • Svogliati che odiano la CLI
bash — Installa
# macOS
brew install --cask openlens

# Linux (Snap)
sudo snap install kontena-lens --classic

# Oppure scarica da: https://github.com/MuhammedKalworsky/OpenLens/releases

# Apri e lui legge automaticamente il tuo ~/.kube/config
# Tutti i tuoi cluster appaiono. Click, esplora, fatto.
💡 Lens vs OpenLens: Lens (Mirantis) è diventato a pagamento. OpenLens è il fork open-source e gratuito. Fa quasi tutto uguale. Usa quello.

🐶 k9s TERMINALE 💤 Top per Svogliati

Per chi vuole restare nel terminale ma non vuole scrivere kubectl get pods 400 volte al giorno. k9s è una TUI (Terminal UI) che ti fa navigare il cluster come un file manager. Frecce, invio, e vedi tutto. Il compromesso perfetto tra CLI e GUI.

bash — Installa e usa
# Installa
brew install k9s        # macOS
sudo snap install k9s   # Linux

# Lancia
k9s

# Comandi dentro k9s:
#   :pods          → vedi i pods
#   :deploy        → vedi i deployments
#   :svc           → vedi i services
#   :ns            → cambia namespace
#   /qualcosa      → filtra/cerca
#   l              → log del pod selezionato
#   s              → shell nel pod
#   d              → describe
#   ctrl-d         → delete
#   :q             → esci (come vim, ma almeno qui funziona)
🔥 Hot take: k9s è probabilmente lo strumento che userai di più dopo kubectl. È veloce, leggero, gira ovunque (anche via SSH su un server), e ti fa fare in 2 tasti quello che con kubectl richiede 30 caratteri. Se installi una sola cosa di questa lista, installa k9s.

📊 Kubernetes Dashboard UFFICIALE

La dashboard ufficiale del progetto Kubernetes. Basica ma funzionale. Se non vuoi installare niente di terze parti, questa c'è sempre.

bash — Deploy in un comando
# Installa la dashboard
kubectl apply -f https://raw.githubusercontent.com/kubernetes/dashboard/v2.7.0/aio/deploy/recommended.yaml

# Crea un utente admin per accedere
kubectl create serviceaccount dashboard-admin -n kubernetes-dashboard
kubectl create clusterrolebinding dashboard-admin \
  --clusterrole=cluster-admin --serviceaccount=kubernetes-dashboard:dashboard-admin

# Genera il token
kubectl create token dashboard-admin -n kubernetes-dashboard

# Avvia il proxy
kubectl proxy
# Apri: http://localhost:8001/api/v1/namespaces/kubernetes-dashboard/services/https:kubernetes-dashboard:/proxy/
# Incolla il token. Finito.

💫 Portainer WEB UI

Nato per Docker, cresciuto per K8s. Interfaccia web pulitissima, perfetta per chi viene dal mondo Docker e vuole gestire K8s senza impazzire. Community Edition gratuita.

bash — Installa su K8s
kubectl apply -n portainer -f https://downloads.portainer.io/ce2-19/portainer.yaml

# Accedi su https://NODO-IP:30779
# Crea utente admin al primo accesso

💡 Headlamp GRATIS

Dashboard moderna e leggera di Kinvolk/Microsoft. Desktop app o in-cluster. Interfaccia pulita, plugin system, e non chiede la carta di credito. L'alternativa a Lens per chi vuole restare open source al 100%.

bash — Installa
# Desktop app
brew install --cask headlamp

# Oppure in-cluster con Helm
helm repo add headlamp https://headlamp-k8s.github.io/headlamp/
helm install headlamp headlamp/headlamp -n headlamp --create-namespace
⚖️ Confronto Rapido: Quale Scegliere?
Tool Tipo Multi-cluster Costo Per chi
🐄 Rancher Web UI (server) ✅ Top Gratis Team, enterprise, multi-cluster
🔎 OpenLens Desktop app ✅ Sì Gratis Developer, debug quotidiano
🐶 k9s Terminale (TUI) ✅ Sì Gratis Sysadmin, chi vive nel terminale
📊 K8s Dashboard Web UI (in-cluster) ❌ No Gratis Chi vuole l'ufficiale, basico
💫 Portainer Web UI (in-cluster) ✅ Sì CE gratis Chi viene da Docker
💡 Headlamp Desktop / Web ✅ Sì Gratis Chi vuole open source leggero
💡 Consiglio dello svogliato esperto: Installa k9s per il lavoro quotidiano (veloce, ovunque), Rancher se gestisci più cluster o lavori in team, e tieni OpenLens sul portatile per quando vuoi cliccare invece di scrivere. Non servono tutti, ma averne almeno uno ti cambia la vita. Il terminale puro è per i masochisti o per chi vuole fare colpo al colloquio.
Capitolo 16

Survival Kit

Le regole d'oro per sopravvivere in produzione

⚠️ SEMPRE settare le risorse

Un Pod senza resources.requests e resources.limits è un Pod che può mangiare tutta la RAM del nodo. E lo farà. Alle 3 di notte.

💚 SEMPRE usare liveness e readiness probe

Senza probe, K8s non sa se la tua app è viva o morta. Con le probe, rileva i problemi e riavvia automaticamente.

yaml
livenessProbe:
  httpGet:
    path: /healthz
    port: 8080
  initialDelaySeconds: 15
  periodSeconds: 10
readinessProbe:
  httpGet:
    path: /ready
    port: 8080
  initialDelaySeconds: 5
  periodSeconds: 5

🔒 MAI usare :latest come tag

image: nginx:latest è la roulette russa del deploy. Non sai cosa stai deployando. Usa sempre tag specifici: nginx:1.25.3

📎 Usa i Namespace

Non buttare tutto in default. Crea namespace per ambiente (dev, staging, prod) o per team. È gratis e ti salva la vita.

💾 Backup di etcd

etcd è il cervello del cluster. Se lo perdi, perdi tutto. Backup automatico, minimo giornaliero. Se usi managed K8s, il provider lo fa (forse).

📊 Monitoring

Installa Prometheus + Grafana. Se non monitori, non sai cosa succede. Se non sai cosa succede, non puoi fixare. Se non puoi fixare... buona fortuna.

bash — Stack rapido con Helm
helm repo add prometheus-community \
  https://prometheus-community.github.io/helm-charts
helm install monitoring prometheus-community/kube-prometheus-stack \
  -n monitoring --create-namespace
🔄 Ciclo di Vita di un Deploy in Produzione
Code git push CI/CD build + test Image push registry Deploy kubectl apply Monitor Prometheus 😴 Dormi K8s si arrangia
🏆 La Regola Finale dello Svogliato

Se devi fare qualcosa più di due volte, automatizzalo.
Se devi spiegare qualcosa più di due volte, documentalo.
Se qualcosa ti sveglia alle 3 di notte, metti una probe.

Capitolo 12

Helm: il Package Manager

Perché scrivere 14 file YAML a mano è da masochisti

🍪 Analogia per Svogliati

Helm sta a Kubernetes come apt/brew sta a Linux/Mac. Vuoi installare Prometheus con 47 file YAML, 12 ConfigMap, 8 Service e 3 sacrifici al dio del YAML? Oppure vuoi scrivere helm install prometheus e andare a fare merenda? Ecco.

📦 Cos'è un Chart

Un Chart è un pacchetto Helm. Contiene tutti i template YAML, i valori di default, e le istruzioni per installare un'applicazione. È tipo un .deb o un .pkg ma per K8s.

  • Chart.yaml — metadati (nome, versione)
  • values.yaml — configurazione di default
  • templates/ — i file YAML con variabili
  • charts/ — dipendenze (chart dentro chart, inception)

🤔 Perché Usarlo

Senza Helm devi copincollare YAML tra ambienti, cambiare a mano le variabili, e pregare di non aver dimenticato niente. Con Helm:

  • Template — un YAML, N ambienti
  • Versioning — rollback a una versione precedente
  • Dipendenze — "la mia app ha bisogno di Redis" e Helm lo installa
  • Community — migliaia di chart pronti all'uso

🔧 Comandi Essenziali

bash — Helm in 5 minuti
# Installa Helm
brew install helm  # macOS
curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash  # Linux

# Aggiungi un repository (tipo aggiungere un PPA)
helm repo add bitnami https://charts.bitnami.com/bitnami
helm repo update

# Cerca un chart
helm search repo nginx
NAME                 CHART VERSION  APP VERSION  DESCRIPTION
bitnami/nginx        15.3.4         1.25.3       NGINX is a web server...

# Installa (una riga, un intero stack)
helm install mio-nginx bitnami/nginx

# Installa con valori custom
helm install mio-nginx bitnami/nginx -f miei-valori.yaml

# Oppure override inline
helm install mio-nginx bitnami/nginx --set replicaCount=3,service.type=LoadBalancer

# Vedi cosa hai installato
helm list
NAME       NAMESPACE  REVISION  STATUS    CHART          APP VERSION
mio-nginx  default    1         deployed  nginx-15.3.4   1.25.3

# Aggiorna una release
helm upgrade mio-nginx bitnami/nginx --set replicaCount=5

# Qualcosa è andato storto? Rollback!
helm rollback mio-nginx 1  # torna alla revisione 1

# Disinstalla tutto pulito
helm uninstall mio-nginx

# Vedi i valori di default di un chart (per sapere cosa puoi cambiare)
helm show values bitnami/nginx | less

# Dry-run: vedi lo YAML che verrebbe generato SENZA installare
helm template mio-nginx bitnami/nginx -f miei-valori.yaml

📄 Esempio: values.yaml Custom

miei-valori.yaml — Override dei default
# Sovrascrivi solo quello che ti serve, il resto rimane default
replicaCount: 3

image:
  tag: "1.25.3"     # Mai :latest, ricordi?

service:
  type: LoadBalancer
  port: 80

resources:
  requests:
    cpu: "100m"
    memory: "128Mi"
  limits:
    cpu: "250m"
    memory: "256Mi"

ingress:
  enabled: true
  hostname: mia-app.esempio.com
💡 Regola dello svogliato: prima di scrivere YAML a mano, cerca se esiste un chart Helm. Il 90% delle cose che vuoi installare (database, monitoring, ingress controller, message queue) ha già un chart mantenuto dalla community. Non reinventare la ruota, sei troppo pigro per quello.
Capitolo 13

RBAC: Chi Può Fare Cosa

Perché dare admin a tutti è come dare le chiavi della Ferrari al tirocinante

🏢 Analogia per Svogliati

RBAC è il sistema di permessi di K8s. Pensa a un palazzo con tanti uffici. Il CEO (cluster-admin) ha le chiavi di tutto. Il developer ha le chiavi solo del suo piano (namespace). Lo stagista può guardare dalla finestra ma non toccare niente (get/list ma non delete). Se non configuri RBAC, tutti sono CEO. E questo finisce male.

🔒 Come Funziona RBAC
Chi? User ServiceAccount RoleBinding "Lega chi a cosa" (namespace-scoped) Role "Cosa può fare" (namespace-scoped) Risorse pods, svc, deploy, secret... ClusterRoleBinding (cluster-wide) ClusterRole (cluster-wide) RBAC = Chi + Cosa + Dove Role/ClusterRole definiscono i permessi. I Binding li assegnano.

👤 Esempio: Developer che può solo leggere

rbac-readonly.yaml — Lo stagista può guardare ma non toccare
# Step 1: Crea un Role (cosa può fare)
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  namespace: dev
  name: role-sola-lettura
rules:
- apiGroups: [""]             # "" = core API (pods, services, etc)
  resources: ["pods", "services", "configmaps"]
  verbs: ["get", "list", "watch"]  # Solo leggere, niente delete/create
- apiGroups: ["apps"]
  resources: ["deployments"]
  verbs: ["get", "list", "watch"]
---
# Step 2: Lega il Role all'utente (chi)
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  namespace: dev
  name: binding-stagista
subjects:
- kind: User
  name: "mario.rossi@azienda.com"  # Lo stagista
  apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: Role
  name: role-sola-lettura          # Il ruolo creato sopra
  apiGroup: rbac.authorization.k8s.io

💻 Esempio: ServiceAccount per la tua App

Le app dentro K8s usano ServiceAccount per autenticarsi. Non far girare tutto con il SA di default che ha troppi permessi.

bash — ServiceAccount in 30 secondi
# Crea un ServiceAccount
kubectl create serviceaccount mia-app-sa -n dev

# Crea un Role che può leggere i secret
kubectl create role secret-reader -n dev \
  --verb=get,list --resource=secrets

# Lega il SA al Role
kubectl create rolebinding mia-app-binding -n dev \
  --role=secret-reader --serviceaccount=dev:mia-app-sa

# Verifica: cosa PUÒ fare questo SA?
kubectl auth can-i get secrets --as=system:serviceaccount:dev:mia-app-sa -n dev
yes

# Cosa NON può fare?
kubectl auth can-i delete pods --as=system:serviceaccount:dev:mia-app-sa -n dev
no

🔍 Debug RBAC: "Perché non posso fare X?"

bash — Quando ricevi "Forbidden"
# Cosa posso fare io?
kubectl auth can-i --list

# Cosa può fare un utente specifico?
kubectl auth can-i --list --as=mario.rossi@azienda.com

# Test specifico
kubectl auth can-i create deployments -n production

# Vedi tutti i RoleBinding in un namespace
kubectl get rolebindings -n dev -o wide

# Vedi i ClusterRoleBinding (attenzione, qui si gioca pesante)
kubectl get clusterrolebindings -o wide | grep mario
💥 Regola d'oro RBAC: Principio del minimo privilegio. Dai solo i permessi necessari, niente di più. Se qualcuno ha bisogno di più permessi, li chiede e tu valuti. Non fare il pigro qui — un cluster-admin dato a chi non serve è una bomba a orologeria. Lo stagista che cancella la produzione non è una barzelletta, è un martedi.
Capitolo 14

🚨 SOS alle 3 di Notte

La checklist da panico per quando PagerDuty ti sveglia e il tuo cervello è al 3%

📸 PRIMA DI TOCCARE QUALSIASI COSA: Respira. Bevi acqua. Apri il terminale. Non fare niente di avventato. I Pod già in esecuzione probabilmente stanno ancora girando. Hai tempo.

1️⃣ Valuta la Situazione (30 secondi)

Non fixare. Prima capisci.

bash — Triage rapido
# I nodi sono vivi?
kubectl get nodes

# Quali pod sono in difficoltà?
kubectl get pods -A | grep -v Running | grep -v Completed

# Cosa è successo di recente?
kubectl get events -A --sort-by='.lastTimestamp' | tail -30

# Qualcuno ha fatto un deploy?
kubectl rollout history deployment -n <namespace>

2️⃣ Pod Crashano? (il caso più comune)

bash — Indaga il crash
# Log del pod che crasha
kubectl logs <pod> --previous -n <ns>

# OOMKilled? Controlla qui
kubectl describe pod <pod> -n <ns> | grep -A3 "Last State"

# Se OOMKilled: qualcuno ha bisogno di più RAM
# Fix temporaneo (compra tempo per dormire):
kubectl set resources deployment/<nome> -n <ns> --limits=memory=512Mi

# Se è un deploy recente che ha rotto tutto: ROLLBACK
kubectl rollout undo deployment/<nome> -n <ns>
# Ora torna a dormire. Domani si indaga.

3️⃣ Nodo NotReady?

bash — Nodo giù
# Quale nodo è down?
kubectl get nodes
kubectl describe node <nodo> | grep -A10 Conditions

# I pod si stanno spostando su altri nodi? (dopo ~5 min)
kubectl get pods -A -o wide | grep <nodo-down>

# Se cloud: il nodo è ancora vivo nell'infrastruttura?
# GKE: gcloud compute instances list
# AWS: aws ec2 describe-instances

# Forza lo spostamento dei pod
kubectl drain <nodo> --ignore-daemonsets --delete-emptydir-data --force

# Se managed K8s: scala il node pool per aggiungere capacità

4️⃣ Il Servizio non Risponde dall'Esterno?

bash — Debug connettività
# I pod della app girano?
kubectl get pods -l app=<nome> -n <ns>

# Il service ha endpoints?
kubectl get endpoints <service> -n <ns>
# Vuoto = le label non matchano. Il problema è lì.

# L'ingress punta al service giusto?
kubectl describe ingress <nome> -n <ns>

# Test dall'interno del cluster
kubectl run debug --rm -it --image=busybox -- wget -qO- http://<service>.<ns>

# Il certificato TLS è scaduto?
kubectl get secret <tls-secret> -n <ns> -o jsonpath='{.data.tls\.crt}' | \
  base64 -d | openssl x509 -noout -dates

5️⃣ Disco Pieno / Storage Issues?

bash — Storage panic
# PVC bloccato in Pending?
kubectl get pvc -A | grep Pending

# Disco pieno nel nodo?
kubectl describe node <nodo> | grep -A5 "Conditions"
# Cerca DiskPressure = True

# Spazio usato nel container
kubectl exec <pod> -- df -h

# Fix d'emergenza: elimina pod vecchi/completati che occupano spazio
kubectl delete pods --field-selector=status.phase==Succeeded -A
kubectl delete pods --field-selector=status.phase==Failed -A

🔥 Regola d'Oro delle 3AM

Alle 3 di notte non si fanno fix permanenti. Si fa triage, si mette una pezza (rollback, scale up, restart), si torna a dormire, e domani mattina con il cervello acceso si fa il fix vero. Il fix fatto alle 3AM col cervello al 3% diventa il bug di domani alle 10AM. Garantito.

Checklist pezza notturna:
☑ C'è stato un deploy recente? → rollback
☑ OOMKilled? → aumenta memory limits
☑ Pod crashano? → scala le repliche funzionanti
☑ Nodo down? → drain + scala il node pool
☑ Non capisci cosa è successo? → rollback all'ultimo stato stabile
☑ Torna a dormire → domani si fa post-mortem