Salta al contenuto principale

05 - Service Mesh: Istio vs Linkerd, mTLS e Traffic Management

Service Mesh: Istio vs Linkerd, mTLS e Traffic Management In un'architettura a microservizi su Kubernetes, ogni servizio comunica con decine di altri.

Cosa ne pensi di questo articolo?

05 - Service Mesh: Istio vs Linkerd, mTLS e Traffic Management
Kubernetes AvanzatoFoto:Christina MorilloPexels

In Breve

Service Mesh: Istio vs Linkerd, mTLS e Traffic Management In un'architettura a microservizi su Kubernetes, ogni servizio comunica con decine di altri.

Service Mesh: Istio vs Linkerd, mTLS e Traffic Management

In un'architettura a microservizi su Kubernetes, ogni servizio comunica con decine di altri. Chi garantisce che queste comunicazioni siano cifrate? Chi gestisce il retry automatico quando un servizio e momentaneamente irraggiungibile? Chi fornisce le metriche di latenza e tasso di errore per ogni coppia sorgente-destinazione? Senza un service mesh, rispondere a queste domande richiede codice custom in ogni servizio.

Un service mesh risolve questi problemi a livello di infrastruttura, trasparente all'applicazione. I due player dominanti in ambiente Kubernetes sono Istio, il piu ricco di funzionalita, e Linkerd, progettato per semplicita e overhead minimo. Questo articolo mostra come installare entrambi, configurare mTLS automatico, gestire il traffico con canary deployment e circuit breaking, e quando scegliere l'uno o l'altro.

Cosa Imparerai

  • Come funziona un service mesh: data plane (sidecar) e control plane
  • mTLS automatico: cosa significa, come verificarlo, come gestire le eccezioni
  • Istio: installazione, VirtualService, DestinationRule, canary e blue/green
  • Linkerd: installazione lightweight, SMI TrafficSplit, extensions
  • Circuit breaking con Istio OutlierDetection
  • Retry e timeout a livello di infrastruttura
  • Osservabilita: metriche golden signals dal service mesh
  • Istio vs Linkerd: quando scegliere quale

Come Funziona un Service Mesh

Il service mesh si basa sull'iniezione automatica di un sidecar proxy (Envoy per Istio, Linkerd2-proxy per Linkerd) in ogni Pod. Il sidecar intercetta tutto il traffico in entrata e uscita dal container dell'applicazione, senza richiedere modifiche al codice.

Il data plane e l'insieme di tutti i sidecar proxy che gestiscono il traffico effettivo. Il control plane (Istiod per Istio, linkerd-control-plane per Linkerd) distribuisce la configurazione ai proxy, gestisce i certificati per mTLS e raccoglie telemetria.

Confronto Istio vs Linkerd

Caratteristica Istio Linkerd
Sidecar proxy Envoy (C++, 50-100 MB) linkerd2-proxy (Rust, 10-20 MB)
Overhead memoria per Pod 100-200 MB 20-30 MB
Latency overhead P99 2-5 ms 0.5-1 ms
mTLS automatico Si (cert-manager o built-in) Si (rotazione automatica 24h)
Traffic management Completo (VirtualService, DR) Base (HTTPRoute, TrafficSplit)
L7 policy HTTP, gRPC, TCP HTTP, gRPC
Ingress Gateway API + Istio Gateway Gateway API
Curva di apprendimento Ripida Moderata
Maturita produzione Alta (Google, Airbnb) Alta (Shopify, Microsoft)

Istio: Installazione e Configurazione Base

Installazione con istioctl

# Scarica e installa istioctl
curl -L https://istio.io/downloadIstio | ISTIO_VERSION=1.22.0 sh -
export PATH=$PWD/istio-1.22.0/bin:$PATH

# Installa Istio con profilo di produzione
istioctl install --set profile=production -y

# Il profilo production abilita:
# - HA con multiple repliche del control plane
# - Affinity rules per distribuire su nodi diversi
# - Risorse CPU/memoria adeguate
# - Solo le feature necessarie (no addon come Kiali, Jaeger)

# Abilita l'iniezione automatica del sidecar nel namespace
kubectl label namespace production istio-injection=enabled

# Verifica lo stato del mesh
istioctl proxy-status

# Analizza la configurazione per possibili problemi
istioctl analyze --namespace production

mTLS con Istio: PeerAuthentication e DestinationRule

Istio implementa il mTLS automatico tra tutti i Pod con sidecar iniettato. Con PeerAuthentication si puo rendere il mTLS STRICT (obbligatorio) a livello di namespace o di mesh intero:

# mtls-strict.yaml
# Abilita mTLS STRICT per tutto il namespace production
apiVersion: security.istio.io/v1
kind: PeerAuthentication
metadata:
  name: default-mtls
  namespace: production
spec:
  mtls:
    mode: STRICT  # DISABLE, PERMISSIVE, STRICT
---
# Eccezione: un servizio legacy che non ha sidecar
apiVersion: security.istio.io/v1
kind: PeerAuthentication
metadata:
  name: legacy-service-exception
  namespace: production
spec:
  selector:
    matchLabels:
      app: legacy-service
  mtls:
    mode: PERMISSIVE  # accetta anche connessioni plain-text

# Verifica mTLS
kubectl exec -n production frontend-pod -c istio-proxy -- \
  pilot-agent request GET /config_dump | grep -A5 "tls_context"

# Visualizza lo stato mTLS con istioctl
istioctl x describe pod frontend-pod.production

VirtualService: Routing e Traffic Splitting

Il VirtualService definisce come il traffico verso un servizio viene instradato, con regole basate su header HTTP, pesi, path matching:

# virtual-service-canary.yaml
# Canary deployment: 90% traffico a v1, 10% a v2
apiVersion: networking.istio.io/v1
kind: VirtualService
metadata:
  name: api-service-vs
  namespace: production
spec:
  hosts:
    - api-service  # nome del Kubernetes Service
  http:
    # Routing per header: dev e QA ricevono sempre v2
    - match:
        - headers:
            x-user-group:
              exact: "beta-testers"
      route:
        - destination:
            host: api-service
            subset: v2
    # Traffico generale: 90/10 split
    - route:
        - destination:
            host: api-service
            subset: v1
          weight: 90
        - destination:
            host: api-service
            subset: v2
          weight: 10
      # Timeout e retry a livello di infrastruttura
      timeout: 5s
      retries:
        attempts: 3
        perTryTimeout: 2s
        retryOn: "5xx,reset,connect-failure,retriable-4xx"
---
apiVersion: networking.istio.io/v1
kind: DestinationRule
metadata:
  name: api-service-dr
  namespace: production
spec:
  host: api-service
  trafficPolicy:
    connectionPool:
      tcp:
        maxConnections: 100
      http:
        http2MaxRequests: 1000
        maxRequestsPerConnection: 10
    loadBalancer:
      simple: LEAST_CONN  # ROUND_ROBIN, RANDOM, LEAST_CONN
  subsets:
    - name: v1
      labels:
        version: v1
    - name: v2
      labels:
        version: v2

Circuit Breaker con OutlierDetection

Il circuit breaker rimuove temporaneamente gli host "unhealthy" dal load balancing pool quando superano soglie di errore definite:

# destination-rule-circuit-breaker.yaml
apiVersion: networking.istio.io/v1
kind: DestinationRule
metadata:
  name: payment-service-circuit-breaker
  namespace: production
spec:
  host: payment-service
  trafficPolicy:
    connectionPool:
      tcp:
        maxConnections: 50
        connectTimeout: 3s
      http:
        http1MaxPendingRequests: 100
        http2MaxRequests: 500
        maxRetries: 3
    outlierDetection:
      # Rimuovi un host se in 10 secondi riceve 5 errori 5xx
      consecutiveGatewayErrors: 5
      consecutive5xxErrors: 5
      interval: 10s
      # Tienilo fuori per 30 secondi
      baseEjectionTime: 30s
      # Massimo 50% degli host puo essere rimosso
      maxEjectionPercent: 50
      # Analizza solo richieste con 100ms o piu di latenza
      minHealthPercent: 50

Istio Gateway per il Traffico Ingress

# istio-gateway.yaml
apiVersion: networking.istio.io/v1
kind: Gateway
metadata:
  name: production-gateway
  namespace: istio-system
spec:
  selector:
    istio: ingressgateway
  servers:
    - port:
        number: 443
        name: https
        protocol: HTTPS
      tls:
        mode: SIMPLE
        credentialName: wildcard-tls-cert  # Secret con cert TLS
      hosts:
        - "*.federicocalo.dev"
    - port:
        number: 80
        name: http
        protocol: HTTP
      tls:
        httpsRedirect: true  # redirect tutto HTTP a HTTPS
      hosts:
        - "*.federicocalo.dev"
---
# Collega il Gateway al VirtualService
apiVersion: networking.istio.io/v1
kind: VirtualService
metadata:
  name: api-gateway-vs
  namespace: production
spec:
  hosts:
    - "api.federicocalo.dev"
  gateways:
    - istio-system/production-gateway
    - mesh  # anche per traffico interno al mesh
  http:
    - route:
        - destination:
            host: api-service
            port:
              number: 8080

AuthorizationPolicy: Controllo degli Accessi

# authorization-policy.yaml
# Solo il frontend puo chiamare il backend
apiVersion: security.istio.io/v1
kind: AuthorizationPolicy
metadata:
  name: backend-access-policy
  namespace: production
spec:
  selector:
    matchLabels:
      app: backend
  action: ALLOW
  rules:
    - from:
        - source:
            principals:
              - "cluster.local/ns/production/sa/frontend-service-account"
      to:
        - operation:
            methods: ["GET", "POST"]
            paths: ["/api/v1/*"]
---
# Blocca tutto il resto (default deny)
apiVersion: security.istio.io/v1
kind: AuthorizationPolicy
metadata:
  name: deny-all
  namespace: production
spec:
  action: DENY
  # Nessun selector = si applica a tutti i Pod nel namespace
  # Nessuna rule = blocca tutto

Linkerd: Installazione e Configurazione

Linkerd privilegia la semplicita operativa e le performance. Il suo proxy scritto in Rust ha un overhead di ~1ms di latenza P99 e ~20MB di memoria per Pod, rendendolo ideale per cluster con molti microservizi o vincoli di risorse.

Installazione Linkerd

# Installa Linkerd CLI
curl --proto '=https' --tlsv1.2 -sSfL https://run.linkerd.io/install | sh
export PATH=$HOME/.linkerd2/bin:$PATH

# Prerequisiti: verifica che il cluster sia compatibile
linkerd check --pre

# Installa il control plane
linkerd install --crds | kubectl apply -f -
linkerd install | kubectl apply -f -

# Verifica installazione
linkerd check

# Abilita injection sul namespace
kubectl annotate namespace production linkerd.io/inject=enabled

# Installa l'estensione Viz per osservabilita
linkerd viz install | kubectl apply -f -
linkerd viz check
linkerd viz dashboard &

Canary con Linkerd e HTTPRoute (Gateway API)

# linkerd-canary-httproute.yaml
# Linkerd usa la Gateway API per il traffic splitting
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: api-service-canary
  namespace: production
spec:
  parentRefs:
    - name: api-service
      kind: Service
      group: core
      port: 80
  rules:
    - backendRefs:
        - name: api-service-v1
          port: 80
          weight: 90
        - name: api-service-v2
          port: 80
          weight: 10

# Monitora il canary con linkerd viz
linkerd viz stat httproute/api-service-canary -n production
linkerd viz routes deploy/api-service-v2 -n production

Osservabilita con Linkerd

# Visualizza metriche golden signals per tutti i deployment
linkerd viz stat deploy -n production

# Output tipico:
# NAME              MESHED   SUCCESS     RPS   LATENCY_P50   LATENCY_P99   TCP_CONN
# api-service       4/4      99.8%       245   1ms           12ms          42
# backend-service   3/3      98.2%       180   3ms           45ms          28
# payment-service   2/2      100.0%      65    8ms           89ms          12

# Visualizza il traffico per un singolo Pod
linkerd viz tap pod/api-service-xyz -n production

# Genera un report di osservabilita
linkerd viz check --proxy -n production

Istio Telemetry: Metriche, Tracing e Logging

Istio emette automaticamente le golden signals (latenza, traffico, errori, saturazione) per ogni coppia source-destination. Le metriche sono esposte in formato Prometheus e visibili in Grafana con le dashboard ufficiali Istio.

# telemetry-config.yaml
# Configura il sampling per il distributed tracing
apiVersion: telemetry.istio.io/v1
kind: Telemetry
metadata:
  name: mesh-tracing
  namespace: istio-system
spec:
  tracing:
    - providers:
        - name: tempo  # o jaeger, zipkin
      randomSamplingPercentage: 1.0  # campiona 1% del traffico in produzione
---
# Metriche custom per un servizio specifico
apiVersion: telemetry.istio.io/v1
kind: Telemetry
metadata:
  name: payment-service-metrics
  namespace: production
spec:
  selector:
    matchLabels:
      app: payment-service
  metrics:
    - providers:
        - name: prometheus
      overrides:
        - match:
            metric: REQUEST_COUNT
          tagOverrides:
            destination_version:
              value: "request.headers['x-version'] | 'unknown'"

# Importa le dashboard Grafana ufficiali Istio
# Dashboard ID: 7639 (Mesh Overview), 11829 (Service), 12378 (Workload)

Service Mesh vs Cilium eBPF: Quando Usarli

Con l'avvento di Cilium, che offre mTLS e L7 policy senza sidecar (via eBPF nel kernel), la scelta si e complicata. Ecco una guida pratica:

Scenario Scelta Consigliata Motivo
Microservizi con traffic management avanzato (canary, header-based routing) Istio VirtualService/DestinationRule insostituibili
Sicurezza mTLS con overhead minimo Linkerd o Cilium Proxy Rust o eBPF minimale
Cluster con centinaia di microservizi, risorse limitate Linkerd o Cilium mTLS Overhead sidecar moltiplicato per ogni Pod
Multi-cluster o multi-cloud Istio Mesh federation built-in
Team nuovo a service mesh Linkerd Curva di apprendimento nettamente inferiore

Best Practices per il Service Mesh

Checklist per il Service Mesh in Produzione

  • Inizia in modalita PERMISSIVE: abilita mTLS in PERMISSIVE prima di renderlo STRICT, per identificare servizi che non hanno ancora il sidecar
  • Imposta timeout su tutti i VirtualService: senza timeout, una dipendenza lenta blocca l'intera catena di chiamate
  • Circuit breaker per ogni dipendenza esterna: database, API esterne, servizi di pagamento
  • Usa retry solo per errori idempotenti: non fare retry automatico su POST senza idempotency key, altrimenti crei ordini duplicati
  • Monitora il sidecar CPU/memoria: in cluster con molti Pod, l'overhead aggregato del sidecar puo essere significativo
  • Testa il failover: disabilita manualmente un pod e verifica che il circuit breaker e i retry funzionino come atteso
  • Mantieni aggiornato il mesh: le versioni di Istio e Linkerd hanno cicli di vita brevi (6-12 mesi); pianifica upgrade regolari

Anti-Pattern Comuni

  • Retry infiniti con amplificazione: se A richiama B con 3 retry, e B richiama C con 3 retry, un singolo errore genera 9 tentativi verso C (retry storm)
  • Timeout troppo generosi: un timeout di 60 secondi su una API critica significa che durante un'outage tutti i thread/goroutine dell'applicazione sono bloccati per 60s
  • Ignorare il control plane overhead: Istiod consuma CPU/memoria significativa per ogni proxy connesso; in cluster con 1000+ Pod, serve un nodo dedicato
  • Injection selettiva senza piano: se solo alcuni Pod hanno il sidecar, il mTLS STRICT fallisce per le comunicazioni verso Pod senza sidecar

Conclusioni e Prossimi Passi

Il service mesh ha smesso di essere un "nice to have" e e diventato un requisito per qualsiasi architettura a microservizi seria. mTLS automatico, osservabilita granulare, circuit breaking e traffic management dichiarativo risolvono problemi reali che senza un service mesh richiederebbero codice custom in ogni servizio.

La scelta tra Istio e Linkerd dipende dalle esigenze: Istio per scenari complessi con traffic management avanzato e multi-cluster, Linkerd per semplicita operativa e overhead minimo. In entrambi i casi, l'investimento iniziale nell'apprendimento e nella configurazione e ripagato dalla riduzione di codice infrastrutturale nelle applicazioni e dalla visibilita senza precedenti sul traffico interno al cluster.

Serie Correlate

Prova Questi Strumenti

Strumenti gratuiti correlati a questo argomento.

Commenti

Caricamento commenti...

Accedi per commentare

Discuti questo articolo nella community

Unisciti al gruppo Telegram per discutere con altri sviluppatori, fare domande e condividere le tue esperienze.

Ti è piaciuto questo articolo?

Esplora altri contenuti sul blog o scopri i miei progetti