La Guida Definitiva ai Pod e ai Servizi in Kubernetes

Kubernetes è una potente piattaforma di orchestrazione dei container, ma per sfruttarne appieno il potenziale, è importante comprendere i suoi componenti principali, con i pod e i servizi che svolgono ruoli fondamentali. In questo articolo, esploreremo cosa sono e come lavorano insieme per esporre e gestire l’accesso alle applicazioni in esecuzione all’interno di un cluster Kubernetes.

Cosa è un Pod?

Pod sono le unità di calcolo più piccole che puoi creare e gestire in Kubernetes.

Prima di creare un pod, controlliamo le risorse API disponibili nel tuo cluster Kubernetes; puoi utilizzare il comando kubectl api-resources. Questo comando elenca le risorse API supportate dal server API di Kubernetes, inclusi i loro nomi abbreviati, i gruppi API e se sono o meno namespace. Questo è utile per comprendere le capacità del tuo cluster, specialmente quando si lavora con risorse personalizzate o si esplorano nuove funzionalità di Kubernetes. Il comando kubectl explain completa questo fornendo informazioni dettagliate sulle singole risorse.

Creiamo un file di configurazione di base per un pod che esegue un semplice container Nginx.

Crea un file denominato nginx-pod.yaml:

YAML

 

apiVersion: v1
kind: Pod
metadata:
  name: nginx-pod
  labels:
    app: nginx
spec:
  containers:
  - name: nginx
    image: nginx:latest
    ports:
    - containerPort: 80

Ecco una breve spiegazione dei termini utilizzati:

  • apiVersion: v1: Specifica la versione API.
  • kind: Pod: Indica che questa configurazione è per un pod.
  • metadata:name: Il nome del pod.
  • metadati: etichette: Coppie chiave-valore che possono essere utilizzate per organizzare e selezionare il pod.
  • spec:contenitori: Un elenco di contenitori che verranno eseguiti nel pod.
  • spec:contenitori:nome: Il nome del contenitore.
  • spec:contenitori:immagine: L’immagine del contenitore da utilizzare (in questo caso, l’ultima immagine di Nginx).
  • spec:contenitori:porte: Le porte da esporre dal contenitore.

Usa kubectl per applicare il file di configurazione e creare il pod:

  • kubectl apply -f nginx-pod.yaml

Controlla lo stato del pod per assicurarti che sia stato creato e sia in esecuzione:

  • kubectl get pods

Dovresti vedere un output simile a questo:

Shell

 

NAME         READY   STATUS    RESTARTS    AGE
nginx-pod    1/1     Running   0           10s

Successivamente, elimina il pod:

  • kubectl delete pod nginx-pod

L’output dovrebbe assomigliare al seguente:

Shell

 

kubectl get pod
No resources found in default namespace.

Cos’è un Servizio?

Creare un servizio per un pod Nginx in Kubernetes ti consente di esporre l’applicazione Nginx e renderla accessibile all’interno o all’esterno del cluster. Ecco una guida passo dopo passo per creare un Servizio per un pod Nginx.

Per assicurarti di avere un pod Nginx in esecuzione, se non ne hai già uno, crea un file YAML chiamato nginx-pod.yaml:

YAML

 

apiVersion: v1
kind: Pod
metadata:
  name: nginx-pod
  labels:
    app: nginx
spec:
  containers:
  - name: nginx
    image: nginx:latest
    ports:
    - containerPort: 80

Applica la configurazione del Pod:

  • kubectl apply -f nginx-pod.yaml

Crea un file YAML chiamato nginx-service.yaml per definire il Servizio:

YAML

 

apiVersion: v1
kind: Service
metadata:
  name: nginx-service
spec:
  selector:
    app: nginx
  ports:
    - protocol: TCP
      port: 80
       targetPort: 80
  type: ClusterIP

Qui ci sono alcune cose da notare:

  • selector: Un selettore di etichette per abbinare il pod Nginx.
  • app: nginx: Questo dovrebbe corrispondere all’etichetta nel pod Nginx.
  • type: ClusterIP: Il tipo del Servizio. ClusterIP rende il Servizio accessibile solo all’interno del cluster. Puoi anche usare NodePort o LoadBalancer per esporre il Servizio esternamente.

Applica la configurazione del Servizio utilizzando kubectl:

  • kubectl apply -f nginx-service.yaml
  • kubectl get services
Shell

 

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

Poiché il Servizio è di tipo ClusterIP, è accessibile solo all’interno del cluster. Per accedervi dall’esterno del cluster, puoi cambiare il tipo di Servizio in NodePort o LoadBalancer.

Per esporre il Servizio esternamente utilizzando NodePort, modifica il file nginx-service.yaml:

YAML

 

apiVersion: v1
kind: Service
metadata:
  name: nginx-service
spec:
  selector:
    app: nginx
  ports:
    - protocol: TCP
      port: 80
       targetPort: 80
      nodePort: 30007  # Specify a node port in the range 30000-32767 (optional)
  type: NodePort

Applica la configurazione aggiornata del Servizio:

  • kubectl apply -f nginx-service.yaml

Ora puoi accedere all’applicazione Nginx utilizzando l’indirizzo IP del nodo e la porta del nodo (ad esempio, http://<node-ip>:30007).

Pod Multi-Container

L’utilizzo di un singolo contenitore per pod fornisce la massima granularità e disaccoppiamento. Tuttavia, ci sono scenari in cui il deployment di più contenitori, talvolta definiti come contenitori compositi, all’interno di un singolo pod risulta vantaggioso. Questi contenitori secondari possono svolgere diverse funzioni: gestire i log o potenziare il contenitore primario (concetto di sidecar), agire come proxy per sistemi esterni (concetto di ambasciatore) o modificare i dati per adattarli a un formato esterno (concetto di adattatore). Questi contenitori secondari complementano il contenitore primario eseguendo compiti che quest’ultimo non gestisce.

Di seguito è riportato un esempio di configurazione del pod Kubernetes che include un contenitore primario in esecuzione di un server Nginx e un contenitore secondario che funge da sidecar per gestire i log. Il contenitore sidecar utilizza un semplice contenitore di log come BusyBox per dimostrare come è possibile visualizzare i log di accesso di Nginx.

YAML

 

apiVersion: v1
kind: Pod
metadata:
  name: nginx-with-logging-sidecar
spec:
  containers:
  - name: nginx
    image: nginx:latest
    ports:
    - containerPort: 80
    volumeMounts:
    - name: shared-logs
      mountPath: /var/log/nginx
  - name: log-sidecar
    image: busybox:latest
    command: ["sh", "-c", "tail -f /var/log/nginx/access.log"]
    volumeMounts:
    - name: shared-logs
      mountPath: /var/log/nginx
  volumes:
  - name: shared-logs
    emptyDir: {}

In seguito, eseguirai le seguenti operazioni:

  • Salva la configurazione YAML in un file, ad esempio nginx-with-logging-sidecar.yaml
  • Applica la configurazione utilizzando kubectl:kubectl apply -f nginx-with-logging-sidecar.yaml
  • Verifica che il pod sia in esecuzione: kubectl get pods
  • Controlla i log del contenitore sidecar per visualizzare i log di accesso di Nginx: kubectl logs -f <nome-pod> -c log-sidecar.

Seguendo questi passaggi, eliminerai il Pod esistente e applicherai la nuova configurazione, impostando un Pod con un container Nginx e un container sidecar per il logging. Ciò garantirà che la nuova configurazione sia attiva ed in esecuzione nel tuo cluster Kubernetes.

I Pod multi-container in Kubernetes offrono diversi vantaggi, consentendo un deployment e una gestione delle applicazioni più flessibili ed efficienti.

Pattern di Pod Multi-Container

Pattern Sidecar

I container sidecar possono potenziare l’applicazione principale fornendo funzioni ausiliarie come il logging, la gestione delle configurazioni o il proxying. Questo pattern aiuta ad estendere la funzionalità senza modificare il container principale. Un container sidecar può gestire il logging raccogliendo e inoltrando i log dal container dell’applicazione principale.

Pattern Ambassador

I container ambassador fungono da proxy, gestendo la comunicazione tra l’applicazione principale e i servizi esterni. Ciò può semplificare l’integrazione e la configurazione. Un container ambassador potrebbe gestire la terminazione SSL o le funzioni di API gateway.

Il pattern Ambassador coinvolge l’utilizzo di un container sidecar per gestire la comunicazione tra l’applicazione principale e i servizi esterni. Questo pattern può gestire compiti come il proxying, il bilanciamento del carico o la gestione delle connessioni sicure, astrayendo la complessità dal container dell’applicazione principale.

  • Container Applicazione Principale: Un semplice server web che effettua richieste HTTP
  • Container Ambassador: Proxy Envoy configurato per gestire le richieste all’API esterna
YAML

 

apiVersion: v1
kind: Pod
metadata:
  name: app-with-ambassador
spec:
  containers:
  - name: web-app
    image: python:3.8-slim
    command: ["python", "-m", "http.server", "8000"]
    volumeMounts:
    - name: config-volume
      mountPath: /etc/envoy
  - name: envoy-proxy
    image: envoyproxy/envoy:v1.18.3
    args: ["-c", "/etc/envoy/envoy.yaml"]
    ports:
    - containerPort: 8080
    volumeMounts:
    - name: config-volume
      mountPath: /etc/envoy
  volumes:
  - name: config-volume
    configMap:
      name: envoy-config

---
apiVersion: v1
kind: ConfigMap
metadata:
  name: envoy-config
data:
  envoy.yaml: |
    static_resources:
      listeners:
      - name: listener_0
        address:
          socket_address:
            address: 0.0.0.0
            port_value: 8080
        filter_chains:
        - filters:
          - name: envoy.filters.network.http_connection_manager
            typed_config:
              "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
              stat_prefix: ingress_http
              route_config:
                name: local_route
                virtual_hosts:
                - name: local_service
                  domains: ["*"]
                  routes:
                  - match:
                      prefix: "/"
                    route:
                      cluster: external_service
              http_filters:
              - name: envoy.filters.http.router
      clusters:
      - name: external_service
        connect_timeout: 0.25s
        type: LOGICAL_DNS
        lb_policy: ROUND_ROBIN
        load_assignment:
          cluster_name: external_service
          endpoints:
          - lb_endpoints:
            - endpoint:
                address:
                  socket_address:
                    address: api.example.com
                    port_value: 80

Questo esempio dimostra il pattern Ambassador in Kubernetes, in cui un proxy Envoy funge da intermediario per gestire la comunicazione esterna per il container dell’applicazione principale. Questo pattern aiuta ad astrarre le complessità della comunicazione e migliora la modularità e la manutenibilità.

Pattern Adattatore

I container Adattatore possono modificare o trasformare i dati tra l’applicazione principale e i sistemi esterni, garantendo compatibilità e integrazione. Ad esempio, un container adattatore può riformattare i dati di log per soddisfare i requisiti di un servizio di logging esterno.

Il pattern Adattatore in Kubernetes coinvolge l’uso di un container sidecar per trasformare i dati tra il container dell’applicazione principale e i sistemi esterni. Questo può essere utile quando l’applicazione principale richiede dati in un formato specifico diverso da quello fornito o richiesto dai sistemi esterni.

Supponiamo di avere un container di applicazione principale che genera log in un formato personalizzato. È necessario inviare questi log a un servizio di logging esterno che richiede log in un formato standard specifico. Un container adattatore può essere utilizzato per trasformare i dati di log nel formato richiesto prima di inviarli al servizio esterno.

  • Container Applicazione Principale: Un’applicazione semplice che scrive log in un formato personalizzato.
  • Container Adattatore: Un container che legge i log personalizzati, li trasforma in formato JSON e li invia a un servizio di logging esterno.
YAML

 

apiVersion: v1
kind: Pod
metadata:
  name: app-with-adapter
spec:
  containers:
  - name: log-writer
    image: busybox
    command: ["sh", "-c", "while true; do echo \"$(date) - Custom log entry\" >> /var/log/custom/app.log; sleep 5; done"]
    volumeMounts:
    - name: shared-logs
      mountPath: /var/log/custom
  - name: log-adapter
    image: busybox
    command: ["sh", "-c", "tail -f /var/log/custom/app.log | while read line; do echo \"$(echo $line | sed 's/ - / - {\"timestamp\": \"/;s/$/\"}/')\" >> /var/log/json/app.json; done"]
    volumeMounts:
    - name: shared-logs
      mountPath: /var/log/custom
    - name: json-logs
      mountPath: /var/log/json
  - name: log-sender
    image: busybox
    command: ["sh", "-c", "while true; do cat /var/log/json/app.json | grep -v '^$' | while read line; do echo \"Sending log to external service: $line\"; done; sleep 10; done"]
    volumeMounts:
    - name: json-logs
      mountPath: /var/log/json
  volumes:
  - name: shared-logs
    emptyDir: {}
  - name: json-logs
    emptyDir: {}

Questo esempio illustra il pattern Adapter in Kubernetes, in cui un contenitore adattatore trasforma i dati dal contenitore dell’applicazione primaria nel formato richiesto prima di inviarli a un sistema esterno. Questo pattern aiuta ad integrare le applicazioni con i servizi esterni gestendo le trasformazioni dei formati dei dati all’interno del contenitore sidecar.

Riepilogo

In sintesi, i pods:

  • Sono le unità più piccole distribuibili in Kubernetes.
  • Rappresentano un’istanza singola di un processo in esecuzione.
  • Possono contenere uno o più contenitori.

I servizi:

  • Forniscono un indirizzo IP stabile e un nome DNS per accedere ai pods.
  • Abilitano il bilanciamento del carico tra un insieme di pods.
  • Agevolano la scoperta dei servizi all’interno del cluster.

Source:
https://dzone.com/articles/the-ultimate-guide-to-kubernetes-pods-and-services