Introducción

Cuando trabajamos con contenedores, podemos utilizar diferentes patrones para ejecutar nuestras aplicaciones.

Algunos de ellos son:

  • Sidecar: Contenedor que extiende funcionalidad (logging, monitoring).
  • Ambassador: Proxy para comunicaciones externas.
  • Adapter: Transforma datos para compatibilidad.
  • Init Container: Crea y configura parte de la aplicación antes de que se ejecute el contenedor del Pod.

En este post vamos a hablar del patrón sidecar container. El objetivo de este patrón es que uno o varios contenedores extiendan la funcionalidad principal de otro contenedor.

Imagina que necesitas extraer parte de la información generada por tu aplicación. En vez de incluir esta funcionalidad dentro de tu aplicación, haciéndola más pesada, delegamos este trabajo en otra aplicación que se despliega junto a la tuya, leyendo y extrayendo la información generada.

Desde la versión Kubernetes 1.28 Planternetes (alpha), se soporta la configuración de este patrón de manera nativa, y desde la versión 1.33 Octarine (stable) está disponible de forma estable.

A continuación, mostramos cómo podemos utilizarlo.

¿Cómo crear un sidecar container sin utilizar el soporte nativo de Kubernetes?

Antes de que Kubernetes incluyera el soporte nativo de sidecar containers, podíamos aplicar el patrón de la siguiente manera:

apiVersion: v1
kind: Pod
metadata:
  name: web-with-logger
spec:
  containers:
  - name: web-server
    image: nginx:alpine
    ports:
    - containerPort: 80
    volumeMounts:
    - name: shared-logs
      mountPath: /var/log/nginx

  - name: log-reader
    image: busybox
    command: ["sh", "-c"]
    args:
    - |
      while true; do
        if [ -f /logs/access.log ]; then
          tail -n 5 /logs/access.log
        fi
        sleep 10
      done
    volumeMounts:
    - name: shared-logs
      mountPath: /logs

  volumes:
  - name: shared-logs
    emptyDir: {}

Esta manera de utilizar sidecar containers en Kubernetes presenta algunos problemas en ciertos casos:

  • Jobs: el Pod no termina hasta que todos los contenedores finalizan.
  • Los contenedores que procesan logs y métricas necesitan un orden específico de ejecución para evitar la pérdida de datos.
  • Service mesh: necesitan arrancar y estar en funcionamiento antes que el resto de los contenedores para ejecutar tareas relacionadas con el enrutamiento o la seguridad.

¿Cómo crear un sidecar container de manera nativa en Kubernetes?

La implementación nativa de sidecar containers se ha realizado utilizando la funcionalidad initContainers. Para ello, debemos especificar el valor Always en la propiedad restartPolicy del contenedor (si utilizamos otro valor, Kubernetes no ejecuta los contenedores como sidecar containers).

De esta manera, un sidecar container puede iniciarse, pararse o reiniciarse sin afectar al resto de contenedores que componen el Pod.

apiVersion: v1
kind: Pod
metadata:
  name: web-with-logger
spec:
  initContainers:
  - name: log-reader
    image: busybox
    command: ["sh", "-c"]
    restartPolicy: Always
    args:
    - |
      while true; do
        if [ -f /logs/access.log ]; then
          tail -n 5 /logs/access.log
        fi
        sleep 10
      done
    volumeMounts:
    - name: shared-logs
      mountPath: /logs
  containers:
  - name: web-server
    image: nginx:alpine
    ports:
    - containerPort: 80
    volumeMounts:
    - name: shared-logs
      mountPath: /var/log/nginx

  volumes:
  - name: shared-logs
    emptyDir: {}

Definiendo el valor Always en la propiedad restartPolicy, el contenedor se ejecutará durante todo el ciclo de vida del Pod y nos garantizará el orden correcto de ejecución de cada uno de los contenedores, siendo los definidos en initContainers los primeros en ejecutarse.

Cuando se inicia el proceso de finalización del Pod, kubelet pospone la finalización de los contenedores definidos en initContainers hasta que los contenedores definidos en la propiedad containers hayan finalizado, garantizando así hasta el final del ciclo de vida del Pod el funcionamiento de los sidecar containers.

Conclusión

Aunque la implementación mediante initContainers y la propiedad restartPolicy puede resultar menos intuitiva para definir los sidecar containers de nuestro Pod, creo que contar con soporte nativo en Kubernetes simplifica significativamente la aplicación de este patrón, resolviendo los problemas de orquestación entre los contenedores y garantizando su correcto funcionamiento.

Referencias