Variables, configMaps y secrets

En Kubernetes, ConfigMaps, Variables de entorno, y Secrets son mecanismos que te permiten gestionar la configuración y los datos sensibles de las aplicaciones de manera segura y flexible. A continuación, te explico para qué sirve cada uno y cómo funcionan:

1. ConfigMaps

Los ConfigMaps se utilizan para almacenar y gestionar configuraciones no sensibles de las aplicaciones en formato clave-valor. Esto permite desacoplar la configuración de la imagen del contenedor, lo que hace que las aplicaciones sean más flexibles y fáciles de gestionar.

Usos de los ConfigMaps:

  • Almacenar configuraciones como archivos de configuración, parámetros de inicio o variables que las aplicaciones necesitan para su ejecución.
  • Proporcionar configuraciones de forma centralizada, lo que permite modificar el comportamiento de una aplicación sin necesidad de reconstruir la imagen del contenedor.

Ejemplo de un ConfigMap:

apiVersion: v1
kind: ConfigMap
metadata:
  name: my-config
data:
  APP_NAME: "Mi aplicación"
  LOG_LEVEL: "DEBUG"
  MAX_CONNECTIONS: "100"

Este ConfigMap tiene tres valores: APP_NAME, LOG_LEVEL y MAX_CONNECTIONS, que pueden ser consumidos por los contenedores de varias formas:

  • Como variables de entorno.
  • Montado como archivos en un volumen.
  • Utilizado directamente por aplicaciones que consulten los valores en ejecución.

Uso en un Pod:

apiVersion: v1
kind: Pod
metadata:
  name: app-pod
spec:
  containers:
  - name: my-app-container
    image: my-app-image
    env:
    - name: APP_NAME
      valueFrom:
        configMapKeyRef:
          name: my-config
          key: APP_NAME

2. Variables de entorno

Las variables de entorno son una manera sencilla de pasar información de configuración a los contenedores de una aplicación. Son ideales para configuraciones simples que pueden estar embebidas directamente en los manifiestos o provenir de otros recursos, como ConfigMaps o Secrets.

Usos de las variables de entorno:

  • Pasar parámetros directamente a los contenedores.
  • Acceder a configuraciones globales o específicas de la aplicación.
  • Se pueden configurar estáticamente en el archivo YAML o referenciarse desde ConfigMaps o Secrets.

Ejemplo de variables de entorno simples en un Pod:

apiVersion: v1
kind: Pod
metadata:
  name: my-app-pod
spec:
  containers:
  - name: my-app-container
    image: my-app-image
    env:
    - name: ENVIRONMENT
      value: "production"
    - name: DEBUG
      value: "false"

3. Secrets

Los Secrets se utilizan para almacenar datos sensibles, como contraseñas, tokens de autenticación, certificados, claves privadas, entre otros. Los Secrets son gestionados de forma segura, ya que están encriptados en la API de Kubernetes y tienen mecanismos de control de acceso más estrictos que los ConfigMaps.

Usos de los Secrets:

  • Almacenar credenciales de bases de datos, claves API, certificados SSL, y cualquier otro dato confidencial que las aplicaciones necesiten.
  • Evitar exponer datos sensibles directamente en los manifiestos o imágenes de contenedores.
  • Puede ser utilizado de forma similar a los ConfigMaps: como variables de entorno, montados en volúmenes o accedidos directamente por las aplicaciones.

Ejemplo de un Secret:

apiVersion: v1
kind: Secret
metadata:
  name: my-secret
type: Opaque
data:
  username: dXNlcm5hbWU=   # Valor codificado en Base64 (username)
  password: cGFzc3dvcmQ=   # Valor codificado en Base64 (password)

Aquí, los valores username y password están codificados en Base64. Cuando los consumas en tu aplicación, Kubernetes los descodificará y los proporcionará en su formato original.

Uso en un Pod:

apiVersion: v1
kind: Pod
metadata:
  name: my-app-pod
spec:
  containers:
  - name: my-app-container
    image: my-app-image
    env:
    - name: DB_USERNAME
      valueFrom:
        secretKeyRef:
          name: my-secret
          key: username
    - name: DB_PASSWORD
      valueFrom:
        secretKeyRef:
          name: my-secret
          key: password

En este ejemplo, el Pod accederá al usuario y contraseña almacenados en el Secret y los utilizará como variables de entorno.

Resumen:

  • ConfigMaps: Se utilizan para almacenar configuraciones no sensibles. Se pueden consumir como variables de entorno, montados como volúmenes o accedidos directamente.
  • Variables de entorno: Método simple para pasar configuraciones directamente a los contenedores. Se pueden referenciar desde ConfigMaps o Secrets.
  • Secrets: Se utilizan para almacenar información sensible de forma segura. Al igual que los ConfigMaps, pueden ser utilizados como variables de entorno o montados como volúmenes.

Estos mecanismos permiten gestionar de manera flexible y segura la configuración de tus aplicaciones en Kubernetes, asegurando que puedas mantener configuraciones separadas del código, lo que facilita el mantenimiento, despliegue y escalabilidad de los sistemas.

Ejemplo practico de variables de entorno

apiVersion: apps/v1
kind: Deployment
metadata:
  name: mysql-deploy
  labels:
    app: mysql
    type: db
spec:
  replicas: 1
  selector: 
    matchLabels:
      app: mysql
      type: db
  template:
    metadata:
      labels:
        app: mysql
        type: db
    spec:
      containers:
        - name: mysql57
          image: mysql:5.7
          ports:
            - containerPort: 3306
              name: db-port
          env:
            - name: MYSQL_ROOT_PASSWORD
              value: kubernetes
            - name: MYSQL_USER
              value: usudb
            - name: MYSQL_PASSWORD
              value: usupass
            - name: MYSQL_DATABASE
              value: kubernetes

ConfigMaps

Como vimos arriba tenemos los env estos solo fueron 4, pero puede crecer muy rapidamente, lo que provocaria que nuestro yaml sea muy grande y dificil de mantener. Para eso usamos configMaps.

Si tuvieramos un archivo con las claves y valores que nos interesan, como datos_mysql.properties con el siguiente contenido :

MYSQL_ROOT_PASSWORD=kubernetes
MYSQL_USER=usudb
MYSQL_PASSWORD=usupass
MYSQL_DATABASE=kubernetes

Podriamos crear un configMaps en base a ese fichero asi :

kubectl create configmap datos-mysql-env --from-env-file datos_mysql.properties

Ahora podemos hacer

❯ kubectl get cm
NAME               DATA   AGE
datos-mysql-env    4      5s
kube-root-ca.crt   1      22h

Vemos en DATA las 4 variables de entorno.

Revisamos con :

kubectl describe cm datos-mysql-env

Data
====
MYSQL_USER:
----
usudb

MYSQL_DATABASE:
----
kubernetes

MYSQL_PASSWORD:
----
usupass

MYSQL_ROOT_PASSWORD:
----
kubernetes


BinaryData
====

Ahora usamos esas env en pods o Deployment.

apiVersion: v1
kind: Pod
metadata:
  name: pod1
spec:
  containers:
    - name: test-container
      image: gcr.io/google-samples/node-hello:1.0
      envFrom:
      - configMapRef:
          name: datos-mysql-env
  restartPolicy: Never

Como vemos cambiamod env_ por envFrom.

Verificación:

Puedes asegurarte de que el ConfigMap está correctamente creado con este comando:

kubectl get configmap datos-mysql-env -o yaml

Para verificar si las variables de entorno fueron inyectadas correctamente en el Pod, una vez que esté en ejecución, puedes ejecutar:

kubectl exec pod1 -- env

Este comando mostrará todas las variables de entorno en el contenedor del Pod y te permitirá verificar que las variables del ConfigMap han sido aplicadas correctamente.

ConfigMap declarativo

datos-mysql-env.yml

apiVersion: v1
data:
  MYSQL_DATABASE: kubernetes
  MYSQL_PASSWORD: usupass
  MYSQL_ROOT_PASSWORD: kubernetes
  MYSQL_USER: usudb
kind: ConfigMap
metadata:
  name: datos-mysql-env
  namespace: default
kubectl apply -f datos-mysql-env

Usando el configmap en mysql.yml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: mysql-deploy
  labels:
    app: mysql
    type: db
spec:
  replicas: 1
  selector: 
    matchLabels:
      app: mysql
      type: db
  template:
    metadata:
      labels:
        app: mysql
        type: db
    spec:
      containers:
        - name: mysql57
          image: mysql:5.7
          ports:
            - containerPort: 3306
              name: db-port
          env:
            - name: MYSQL_ROOT_PASSWORD # nombre dentro del contenedor
              valueFrom:
                configMapKeyRef:
                  name: datos-mysql-env # configmap donde buscar los valores
                  key: MYSQL_ROOT_PASSWORD # key que contiene el valor deseado

            - name: MYSQL_USER
              valueFrom:
                configMapKeyRef:
                  name: datos-mysql-env
                  key: MYSQL_USER
            
            - name: MYSQL_DATABASE
              valueFrom:
                configMapKeyRef:
                  name: datos-mysql-env
                  key: MYSQL_DATABASE

            - name: MYSQL_PASSWORD
              valueFrom:
                configMapKeyRef:
                  name: datos-mysql-env
                  key: MYSQL_PASSWORD

Configmap y Volume

El uso de un ConfigMap en un volumen en Kubernetes es una manera poderosa de montar configuraciones como archivos en el sistema de archivos de un contenedor. Esto es útil cuando una aplicación necesita archivos de configuración específicos, como archivos de configuración en formato .properties, .json, o cualquier otro tipo que la aplicación pueda leer directamente del sistema de archivos en lugar de utilizar variables de entorno. i

¿Por qué usar ConfigMaps como volúmenes?

Archivos de configuración complejos: Algunas aplicaciones necesitan leer configuraciones desde archivos, en lugar de utilizar variables de entorno. Usar un ConfigMap como volumen te permite proporcionar esos archivos.

Facilidad de gestión: En lugar de tener que construir imágenes de contenedor que contengan archivos de configuración estáticos, puedes usar un ConfigMap para gestionar esos archivos de manera externa y cambiarlos sin necesidad de volver a crear la imagen.

Actualizaciones dinámicas: Los ConfigMaps montados como volúmenes pueden ser actualizados, y las aplicaciones que soporten la recarga de configuraciones podrán ver esos cambios automáticamente, dependiendo de cómo esté configurada la aplicación.

¿Cómo usar un ConfigMap en un volumen?

Crear un ConfigMap con datos: Primero, necesitas crear un ConfigMap que contenga los archivos o las claves-valor que deseas montar como archivos.

Aquí hay un ejemplo de un ConfigMap que contiene un archivo de configuración simulado:

apiVersion: v1
kind: ConfigMap
metadata:
  name: mi-config
data:
  config-file.properties: |
    app.name=MiAplicacion
    app.version=1.0.0
    app.debug=true

En este ConfigMap, estamos creando un archivo llamado config-file.properties con varias propiedades que podrían ser útiles para una aplicación Java, por ejemplo.

  1. Montar el ConfigMap en un volumen: Ahora puedes montar este ConfigMap en el sistema de archivos del contenedor mediante un volumen.

Aquí tienes un ejemplo de un Pod que monta el ConfigMap:

apiVersion: v1
kind: Pod
metadata:
  name: mi-app
spec:
  containers:
    - name: mi-app-container
      image: my-app-image
      volumeMounts:
        - name: config-volume
          mountPath: /etc/config
  volumes:
    - name: config-volume
      configMap:
        name: mi-config

Secrets

Que es un secret

Un Secret en Kubernetes es un recurso que se utiliza para almacenar y gestionar información sensible de manera segura, como contraseñas, claves API, tokens, certificados, entre otros. Los Secrets permiten que las aplicaciones accedan a estos datos sin necesidad de incluirlos directamente en el código o en los manifiestos de Kubernetes, lo cual es una buena práctica de seguridad.

A diferencia de los ConfigMaps, que almacenan datos no sensibles (como configuraciones), los Secrets están diseñados para manejar información confidencial y ofrecen una mayor seguridad.

¿Para qué sirve un Secret en Kubernetes?

  • Almacenar credenciales de bases de datos: Puedes almacenar el nombre de usuario y la contraseña de una base de datos en un Secret.
  • Guardar claves API: Si tu aplicación necesita autenticarse en servicios externos mediante claves API, estas pueden guardarse de manera segura en un Secret.
  • Administrar certificados TLS: Los certificados y claves privadas para conexiones seguras (TLS/SSL) se pueden almacenar en un Secret.
  • Proteger tokens de autenticación: Si la aplicación usa tokens para interactuar con otros servicios, estos tokens pueden almacenarse en un Secret.

Características principales de los Secrets:

  1. Almacenamiento en Base64: Los datos almacenados en un Secret están codificados en Base64 (no encriptados), lo cual es solo una forma de codificación, no una forma de protección fuerte. Sin embargo, Kubernetes ofrece opciones para encriptar los Secrets en reposo a nivel de clúster (con KMS, por ejemplo).

  2. Acceso restringido: Los Secrets tienen un control de acceso estricto mediante RBAC (Role-Based Access Control), lo que significa que puedes definir exactamente qué usuarios, roles o servicios pueden leer o modificar los Secrets.

  3. Seguridad en tránsito: Cuando los Secrets se transmiten entre el API de Kubernetes y los nodos, se protegen mediante TLS para evitar que sean interceptados.

  4. Acceso como variables de entorno o volúmenes: Los Secrets pueden ser consumidos por los Pods como:

    • Variables de entorno.
    • Archivos montados en el sistema de archivos del contenedor a través de volúmenes.

Ejemplo de creación y uso de un Secret:

  1. Creación de un Secret:

    Puedes crear un Secret con información sensible utilizando un archivo YAML o directamente desde la línea de comandos.

Opción 1: Crear un Secret desde un archivo YAML:

apiVersion: v1
kind: Secret
metadata:
  name: my-secret
type: Opaque
data:
  username: dXNlcg==     # "user" codificado en Base64
  password: cGFzc3dvcmQ= # "password" codificado en Base64
  • En este ejemplo, se define un Secret llamado my-secret con dos claves (username y password), donde los valores están codificados en Base64.
  • El tipo Opaque es el más común para almacenar datos arbitrarios. Existen otros tipos para datos específicos, como kubernetes.io/tls para certificados TLS.

Podemos codificar en base64 asi echo -n "a codificar" | base64

Opción 2: Crear un Secret desde la línea de comandos:

kubectl create secret generic my-secret --from-literal=username=user --from-literal=password=password

Este comando crea un Secret llamado my-secret con las claves username y password. Los valores no necesitan estar codificados en Base64, Kubernetes lo hará automáticamente.

  1. Consumo de un Secret:

    Una vez que el Secret está creado, puedes consumirlo desde un Pod como una variable de entorno o montándolo como un archivo.

Consumo del Secret como variables de entorno:

apiVersion: v1
kind: Pod
metadata:
  name: my-app
spec:
  containers:
    - name: my-container
      image: my-app-image
      env:
      - name: USERNAME
        valueFrom:
          secretKeyRef:
            name: my-secret
            key: username
      - name: PASSWORD
        valueFrom:
          secretKeyRef:
            name: my-secret
            key: password
  • En este ejemplo, el Pod my-app consume el Secret my-secret como variables de entorno (USERNAME y PASSWORD). Kubernetes tomará los valores del Secret y los inyectará como variables de entorno en el contenedor.

Consumo de secret com variable de entorno sin poner explicitament cada campo clave/valor

apiVersion: v1
kind: Pod
metadata:
  name: pod1
spec:
  containers:
    - name: test-container
      image: ubuntu
      command: [ "/bin/sh", "-c", "sleep 1000000" ]
      envFrom:
        - secretRef:
              name: secreto1
  restartPolicy: Never

Montar un Secret como un volumen:

apiVersion: v1
kind: Pod
metadata:
  name: my-app
spec:
  containers:
    - name: my-container
      image: my-app-image
      volumeMounts:
        - name: secret-volume
          mountPath: "/etc/secret"
  volumes:
    - name: secret-volume
      secret:
        secretName: my-secret
  • Aquí, el Secret se monta como un volumen en el directorio /etc/secret. Cada clave en el Secret (como username y password) se convierte en un archivo en ese directorio, con el contenido del archivo siendo el valor descodificado del Secret.

Tipos de Secrets:

  • Opaque (Genérico): El tipo más común, que permite almacenar datos arbitrarios codificados en Base64.
  • kubernetes.io/tls: Utilizado para almacenar certificados TLS y claves privadas.
  • kubernetes.io/dockerconfigjson: Utilizado para almacenar credenciales de autenticación de Docker (por ejemplo, para acceder a imágenes privadas de Docker Hub).
  • kubernetes.io/basic-auth: Secret para almacenar credenciales básicas de autenticación HTTP (usuario y contraseña).

Seguridad:

Aunque Kubernetes maneja los Secrets de manera segura (codificados en Base64 y transmitidos mediante TLS), es importante tener en cuenta las siguientes mejores prácticas de seguridad:

  1. Encriptación en reposo: Activa la encriptación en reposo de Secrets para mayor protección a nivel de clúster.
  2. Control de acceso (RBAC): Utiliza políticas de control de acceso para limitar quién o qué componentes del clúster pueden acceder a los Secrets.
  3. Monitoreo y auditoría: Monitorea el acceso y uso de los Secrets para asegurarte de que no se estén accediendo de manera inapropiada.

Resumen:

  • Un Secret en Kubernetes es un objeto diseñado para almacenar y gestionar información confidencial de manera segura.
  • Los Secrets pueden almacenar credenciales, claves API, tokens y otros datos sensibles.
  • Los Secrets se pueden inyectar en los Pods como variables de entorno o montarse como volúmenes.
  • Aunque los datos en los Secrets están codificados en Base64, es importante configurar encriptación en reposo para mayor seguridad.

El uso adecuado de Secrets en Kubernetes es crucial para proteger la información sensible de tu infraestructura y aplicaciones.

Secrets en imagenes privadas docker.

Para utilizar imágenes privadas de Docker en Kubernetes, primero necesitas configurar un Secret en Kubernetes para almacenar las credenciales de acceso al registro privado de Docker. Luego, puedes configurar un Pod para usar ese Secret y hacer un pull de la imagen desde el registro privado.

Pasos para configurar un Secret y usarlo con imagePullSecrets:

Paso 1: Crear el Secret para las credenciales de Docker

  1. Utiliza el siguiente comando para crear un Secret con tus credenciales de Docker. Aquí necesitas reemplazar los valores por tus credenciales:

    • <docker-username>: tu nombre de usuario en Docker (o el servicio del registro privado).
    • <docker-password>: tu contraseña de Docker.
    • <docker-email>: tu correo electrónico asociado a la cuenta de Docker.
    • <docker-server>: la URL del registro privado (por ejemplo, https://index.docker.io/v1/ para Docker Hub, o la URL de tu registro privado).
    kubectl create secret docker-registry my-docker-secret \
    --docker-username=<docker-username> \
    --docker-password=<docker-password> \
    --docker-email=<docker-email> \
    --docker-server=<docker-server>
    

    Este comando crea un Secret llamado my-docker-secret que almacenará las credenciales para acceder al registro de Docker.

Paso 2: Configurar el Pod para usar el Secret con imagePullSecrets

Después de crear el Secret, puedes configurarlo en el manifiesto del Pod para que Kubernetes utilice este Secret cuando intente hacer pull de las imágenes desde el registro privado.

Aquí tienes un ejemplo simple de cómo usar el Secret en un Pod:

apiVersion: v1
kind: Pod
metadata:
  name: my-private-pod
spec:
  containers:
  - name: my-private-container
    image: <docker-server>/<docker-username>/<image-name>:<tag>  # Tu imagen privada
  imagePullSecrets:
  - name: my-docker-secret  # El nombre del secret creado en el paso anterior

Explicación:

  1. image: Especifica la imagen que está almacenada en tu registro privado. Debes incluir el servidor del registro privado, tu nombre de usuario o el nombre del repositorio, el nombre de la imagen y la etiqueta (tag).

    • Ejemplo: docker.io/myusername/myimage:latest.
  2. imagePullSecrets: Este campo le dice a Kubernetes que debe usar el Secret my-docker-secret para obtener las credenciales al momento de hacer pull de la imagen.

Resumen de los pasos:

  1. Crear un Secret que contiene tus credenciales para el registro de Docker.
  2. Configurar el Pod para usar ese Secret con la propiedad imagePullSecrets, lo que permitirá a Kubernetes hacer pull de las imágenes del registro privado utilizando esas credenciales.

Este enfoque asegura que Kubernetes pueda obtener la imagen desde un registro privado de forma segura usando el Secret configurado para las credenciales de Docker.