Modulo 05

Kubernetes

Docker crea ed esegue un container. Kubernetes lo orchestra: gestisce centinaia o migliaia di container distribuiti su più server, li riavvia da solo e li scala secondo il carico.

Introduzione a Kubernetes

Kubernetes, spesso abbreviato in K8s, è una piattaforma open source per orchestrare i container. Tra le sue funzionalità principali: distribuzione automatica dei container, bilanciamento del carico, riavvio automatico in caso di errore, scalabilità automatica e aggiornamenti senza interruzioni del servizio.

In questo modulo useremo Minikube, una versione di Kubernetes pensata per lo sviluppo locale.

Creare il progetto

mkdir kubernetes-demo
cd kubernetes-demo
npm init -y
npm install express

Installare kubectl

kubectl è il programma che permette di comunicare con Kubernetes. Su macOS:

curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/darwin/amd64/kubectl"

Verifica dell'installazione:

kubectl version --client

Installare Minikube

Minikube crea un cluster Kubernetes direttamente sul computer. Su macOS Apple Silicon:

curl -LO https://github.com/kubernetes/minikube/releases/latest/download/minikube-darwin-arm64
sudo install minikube-darwin-arm64 /usr/local/bin/minikube
minikube version

Il server Express

Creiamo index.js:

import express from "express";

const app = express();
const PORT = process.env.PORT || 3000;

app.get("/", (req, res) => {
  res.json({
    message: "Hello from a container!",
    service: "hello-node",
    pod: process.env.POD_NAME || "unknown",
    time: new Date().toISOString()
  });
});

app.get("/readyz", (req, res) => {
  res.status(200).send("ready");
});

app.get("/healthz", (req, res) => {
  res.status(200).send("ok");
});

app.listen(PORT, () => {
  console.log(`Example app listening on port ${PORT}`);
});

Cosa fa questo codice

In package.json cambiamo "type": "commonjs" in "type": "module" e aggiungiamo gli script:

"scripts": {
  "dev": "node --watch index.js",
  "start": "node index.js"
}

Il Dockerfile

FROM node:18-alpine AS base

WORKDIR /app

COPY package.json package-lock.json* ./

RUN npm ci --omit=dev

COPY . .

USER node

EXPOSE 3000

ENV NODE_ENV=production

CMD ["npm","start"]

npm ci --omit=dev installa solo le dipendenze di produzione, USER node evita di eseguire il container come amministratore.

Docker Compose per lo sviluppo

Creiamo docker-compose.yml:

services:
  api:
    build:
      context: .
      dockerfile: Dockerfile

    container_name: node-api

    ports:
      - "3000:3000"

    environment:
      NODE_ENV: production

    volumes:
      - .:/app
      - /app/node_modules

    command: npm run dev

Aggiungiamo .gitignore e .dockerignore:

# .gitignore
node_modules

# .dockerignore
node_modules
npm-debug.log
Dockerfile
.dockerignore
.git
.gitignore

Avviamo tutto con:

docker compose up --build

Aprendo http://localhost:3000 vediamo il JSON restituito dal server.

Il Deployment Kubernetes

Nella cartella k8s, creiamo deployment.yaml:

apiVersion: apps/v1
kind: Deployment

metadata:
  name: kubernetes-demo-api

spec:
  replicas: 2

  selector:
    matchLabels:
      app: kubernetes-demo-api

  template:
    metadata:
      labels:
        app: kubernetes-demo-api

    spec:
      containers:
        - name: kubernetes-demo-api
          image: tuoutente/kubernetes-demo-api:latest

          ports:
            - containerPort: 3000

          env:
            - name: NODE_ENV
              value: "production"

            - name: POD_NAME
              valueFrom:
                fieldRef:
                  fieldPath: metadata.name

          resources:
            requests:
              cpu: "100m"
              memory: "128Mi"

            limits:
              cpu: "500m"
              memory: "512Mi"

          readinessProbe:
            httpGet:
              path: /readyz
              port: 3000

            initialDelaySeconds: 5
            periodSeconds: 10

          livenessProbe:
            httpGet:
              path: /healthz
              port: 3000

            initialDelaySeconds: 10
            periodSeconds: 20

Il Deployment dice a Kubernetes come creare i Pod: replicas: 2 crea due Pod identici, image indica l'immagine Docker da usare, resources definisce quanta CPU e memoria assegnare, readinessProbe e livenessProbe usano gli endpoint /readyz e /healthz per sapere se il Pod è pronto o ancora vivo.

Il Service

Creiamo service.yaml:

apiVersion: v1
kind: Service

metadata:
  name: kubernetes-demo-api-service

spec:
  selector:
    app: kubernetes-demo-api

  ports:
    - protocol: TCP
      port: 3000
      targetPort: 3000

  type: NodePort

Il Service permette ai Pod di comunicare con l'esterno. NodePort espone automaticamente l'applicazione su una porta del nodo Kubernetes.

Avviare Minikube e applicare i file

minikube start
kubectl get nodes
kubectl cluster-info
kubectl apply -f k8s/deployment.yaml
kubectl apply -f k8s/service.yaml

Controllare Pod e servizi

kubectl get pods -w
kubectl get services

Aprire l'applicazione

minikube service kubernetes-demo-api-service

Apre automaticamente il browser sull'applicazione in esecuzione nel cluster.

Fermare Minikube

minikube stop

Automatizzare il deploy

Creiamo deploy.sh:

#!/bin/bash

set -e

NAME="kubernetes-demo-api"
USERNAME="tuoutente"
IMAGE="$USERNAME/$NAME:latest"

echo "Building Docker image..."
docker build -t $IMAGE .

echo "Pushing image to Docker Hub..."
docker push $IMAGE

echo "Applying Kubernetes manifests..."
kubectl apply -f k8s/deployment.yaml
kubectl apply -f k8s/service.yaml

echo "Getting Pods..."
kubectl get pods

echo "Getting Services..."
kubectl get services

echo "Deployment completed successfully."
Lo script fa in un colpo solo tutto il lavoro manuale: costruisce l'immagine, la pubblica su Docker Hub, aggiorna Kubernetes e mostra Pod e servizi risultanti.

In package.json aggiungiamo:

"deploy": "sh deploy.sh"

Da qui in poi basta:

npm run deploy

Cosa abbiamo imparato

Installare kubectl e Minikube, creare un'applicazione Express pronta per il cloud, containerizzarla con Docker, orchestrarla con Deployment e Service Kubernetes, gestire Pod e servizi, e automatizzare l'intero deploy con uno script Bash. Con Kubernetes un'applicazione diventa scalabile, resiliente e gestibile in modo dichiarativo — il livello sopra Docker in una pipeline DevOps completa.