Kubernetes Ingress: Come Esporre i Tuoi Servizi al Mondo

Guida completa a Kubernetes Ingress. Ingress controller a confronto, configurazione TLS, routing avanzato. Esempi pratici e best practices.

Hai deployato la tua applicazione su Kubernetes. I pod girano, i service funzionano. Ma come fai ad accederci dall'esterno? Con un Service di tipo LoadBalancer ottieni un IP pubblico, ma paghi un load balancer per ogni servizio. Con NodePort esponi una porta su ogni nodo, ma e scomodo e poco flessibile.

Ingress e la soluzione: un singolo punto di ingresso che gestisce routing, TLS, e load balancing per tutti i tuoi servizi HTTP/HTTPS.

Cos'e Ingress

Ingress e una risorsa Kubernetes che definisce regole per il traffico HTTP/HTTPS in entrata. Dice cose come: "le richieste per api.example.com vanno al service api", "le richieste per example.com/blog vanno al service blog".

Ma Ingress da solo non fa niente. E solo una definizione. Per farlo funzionare serve un Ingress Controller — un componente che legge le risorse Ingress e le implementa effettivamente.

E come la differenza tra un file di configurazione e il programma che lo usa. L'Ingress e la configurazione, l'Ingress Controller e il programma.

Ingress Controller: Le Opzioni

Ci sono molti ingress controller. I piu usati:

NGINX Ingress Controller — Il piu comune. Usa NGINX sotto il cofano, ha quasi tutte le feature che ti servono, buona documentazione. E quello che consiglio di default se non hai requisiti specifici.

Traefik — Popolare, buona integrazione con Docker e Kubernetes, configurazione dinamica nativa. Default su K3s.

HAProxy Ingress — Performance eccellenti, feature enterprise. Scelta solida per workload critici.

Contour — Basato su Envoy, buono per ambienti che gia usano Envoy/Istio.

Kong — Ha feature di API Gateway integrate. Se ti servono rate limiting, authentication, etc., Kong le ha built-in.

Per la maggior parte dei casi, NGINX o Traefik vanno benissimo. La scelta spesso dipende da cosa gia conosci o usi.

Installare NGINX Ingress Controller

# Via Helm (consigliato)
helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
helm repo update
helm install ingress-nginx ingress-nginx/ingress-nginx

Dopo l'installazione, avrai un Service di tipo LoadBalancer che e il punto di ingresso. Su cloud provider, ottieni un IP pubblico. On-premise, potresti dover configurare MetalLB o simili.

Verifica che funzioni:

kubectl get pods -n ingress-nginx
kubectl get svc -n ingress-nginx

La Prima Regola Ingress

Supponiamo di avere un deployment e un service:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-app
spec:
  replicas: 2
  selector:
    matchLabels:
      app: my-app
  template:
    metadata:
      labels:
        app: my-app
    spec:
      containers:
        - name: my-app
          image: nginx:latest
          ports:
            - containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
  name: my-app-svc
spec:
  selector:
    app: my-app
  ports:
    - port: 80
      targetPort: 80

Ora creiamo un Ingress:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: my-app-ingress
spec:
  ingressClassName: nginx
  rules:
    - host: my-app.example.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: my-app-svc
                port:
                  number: 80

Applica e verifica:

kubectl apply -f ingress.yaml
kubectl get ingress

Ora le richieste a my-app.example.com (assumendo che il DNS punti all'IP dell'ingress controller) arrivano al tuo service.

Routing Basato su Path

Un singolo Ingress puo gestire routing multipli:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: multi-path-ingress
spec:
  ingressClassName: nginx
  rules:
    - host: example.com
      http:
        paths:
          - path: /api
            pathType: Prefix
            backend:
              service:
                name: api-service
                port:
                  number: 80
          - path: /web
            pathType: Prefix
            backend:
              service:
                name: web-service
                port:
                  number: 80
          - path: /
            pathType: Prefix
            backend:
              service:
                name: default-service
                port:
                  number: 80

L'ordine conta. I path piu specifici dovrebbero venire prima. /api viene matchato prima di /.

pathType

  • Prefix: Matcha il path come prefisso. /api matcha /api, /api/users, /api/v1/users.
  • Exact: Matcha esattamente. /api matcha solo /api, non /api/users.
  • ImplementationSpecific: Comportamento dipende dall'ingress controller.

Routing Basato su Host

Puoi servire host diversi con lo stesso Ingress:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: multi-host-ingress
spec:
  ingressClassName: nginx
  rules:
    - host: api.example.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: api-service
                port:
                  number: 80
    - host: web.example.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: web-service
                port:
                  number: 80

Ogni host puo avere regole di path indipendenti.

TLS/HTTPS

In produzione, vuoi HTTPS. Due modi per gestirlo.

Certificato Manuale

Crea un Secret con il certificato:

kubectl create secret tls my-tls-secret \
  --cert=path/to/cert.pem \
  --key=path/to/key.pem

Referenzialo nell'Ingress:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: tls-ingress
spec:
  ingressClassName: nginx
  tls:
    - hosts:
        - my-app.example.com
      secretName: my-tls-secret
  rules:
    - host: my-app.example.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: my-app-svc
                port:
                  number: 80

cert-manager (Consigliato)

cert-manager automatizza tutto: richiede certificati a Let's Encrypt, li rinnova, li gestisce.

# Installa cert-manager
kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.14.0/cert-manager.yaml

Crea un ClusterIssuer per Let's Encrypt:

apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: letsencrypt-prod
spec:
  acme:
    server: https://acme-v02.api.letsencrypt.org/directory
    email: your-email@example.com
    privateKeySecretRef:
      name: letsencrypt-prod
    solvers:
      - http01:
          ingress:
            class: nginx

Ora l'Ingress puo richiedere certificati automaticamente:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: auto-tls-ingress
  annotations:
    cert-manager.io/cluster-issuer: letsencrypt-prod
spec:
  ingressClassName: nginx
  tls:
    - hosts:
        - my-app.example.com
      secretName: my-app-tls  # cert-manager crea questo
  rules:
    - host: my-app.example.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: my-app-svc
                port:
                  number: 80

cert-manager vede l'annotation, richiede il certificato, lo salva nel Secret, e lo rinnova prima della scadenza. Zero manutenzione.

Annotations Utili (NGINX)

Gli ingress controller supportano annotation per configurazioni avanzate. Queste sono specifiche per NGINX Ingress Controller:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: annotated-ingress
  annotations:
    # Redirect HTTP a HTTPS
    nginx.ingress.kubernetes.io/ssl-redirect: "true"

    # Rate limiting
    nginx.ingress.kubernetes.io/limit-rps: "10"

    # Proxy timeout
    nginx.ingress.kubernetes.io/proxy-read-timeout: "300"
    nginx.ingress.kubernetes.io/proxy-send-timeout: "300"

    # Rewrite path
    nginx.ingress.kubernetes.io/rewrite-target: /$2

    # Client max body size (upload)
    nginx.ingress.kubernetes.io/proxy-body-size: "50m"

    # CORS
    nginx.ingress.kubernetes.io/enable-cors: "true"

    # Basic auth
    nginx.ingress.kubernetes.io/auth-type: basic
    nginx.ingress.kubernetes.io/auth-secret: basic-auth
spec:
  # ...

Ogni ingress controller ha le sue annotation. Controlla la documentazione del tuo.

Rewrite Target

Un pattern comune: vuoi che /api vada al service, ma il service si aspetta richieste a /.

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: rewrite-ingress
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /$2
spec:
  ingressClassName: nginx
  rules:
    - host: example.com
      http:
        paths:
          - path: /api(/|$)(.*)
            pathType: ImplementationSpecific
            backend:
              service:
                name: api-service
                port:
                  number: 80

Richiesta a example.com/api/users → service riceve /users.

Ingress Class

Da Kubernetes 1.18+, IngressClass e il modo consigliato per specificare quale controller gestisce un Ingress.

apiVersion: networking.k8s.io/v1
kind: IngressClass
metadata:
  name: nginx
  annotations:
    ingressclass.kubernetes.io/is-default-class: "true"
spec:
  controller: k8s.io/ingress-nginx

Se marchi una IngressClass come default, gli Ingress senza ingressClassName usano quella.

Default Backend

Cosa succede se una richiesta non matcha nessuna regola? Puoi definire un default backend:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: default-backend-ingress
spec:
  ingressClassName: nginx
  defaultBackend:
    service:
      name: default-service
      port:
        number: 80
  rules:
    # regole normali...

Utile per servire una pagina 404 custom o redirect a una landing page.

Troubleshooting

L'Ingress non funziona:

  1. Verifica che il controller sia running: kubectl get pods -n ingress-nginx
  2. Controlla gli eventi dell'Ingress: kubectl describe ingress my-ingress
  3. Verifica che il service backend esista e funzioni
  4. Controlla i log del controller: kubectl logs -n ingress-nginx deployment/ingress-nginx-controller

Certificato TLS non funziona:

  1. Verifica che il Secret esista: kubectl get secret my-tls-secret
  2. Controlla che i nomi host matchino il certificato
  3. Con cert-manager, controlla lo stato del Certificate: kubectl get certificates

404 anche se il path dovrebbe matchare:

  1. Verifica pathType (Prefix vs Exact)
  2. Controlla l'ordine dei path (piu specifici prima)
  3. Verifica che il service abbia endpoint: kubectl get endpoints my-service

Best Practices

Usa sempre TLS in produzione. Con cert-manager e praticamente gratis. Non c'e scusa per non avere HTTPS.

Un Ingress per applicazione. Rende piu facile gestire lifecycle e permessi indipendenti.

Monitora l'ingress controller. E un single point of failure. Configura alerting su CPU, memoria, e error rate.

Rate limiting. Proteggi i tuoi service da abusi. Meglio impostarlo dall'inizio che dopo un incidente.

Replica il controller. Di default molti controller girano con una replica. In produzione, almeno 2-3.

Backup delle configurazioni. Ingress sono risorse Kubernetes, quindi GitOps. Ma assicurati che i Secret TLS siano gestiti correttamente (non committare chiavi private!).

Conclusione

Ingress e il modo standard per esporre servizi HTTP su Kubernetes. Una volta capito il concetto (Ingress = regole, Ingress Controller = implementazione), il resto e configurazione.

Inizia semplice: NGINX Ingress Controller, cert-manager per TLS, regole base. Aggiungi complessita (rewrite, rate limiting, etc.) quando ti serve.

Non e rocket science, ma ci sono abbastanza dettagli da far perdere tempo se non li conosci. Spero che questa guida ti risparmi qualche ora di debugging.

L'infrastruttura ben fatta e invisibile. Quando l'Ingress funziona, nessuno ci pensa. Ed e cosi che dev'essere.