Lesson  in  Kubernetes 101

Configuración con ConfigMap y Secret

Separa la configuración del código: inyecta variables de entorno desde un ConfigMap y un Secret, y monta un archivo de configuración como volumen dentro del Pod.

Hasta ahora tus contenedores llevaban todo dentro de la imagen. Pero la misma imagen debe funcionar en desarrollo, en staging y en producción, y lo que cambia entre entornos (URLs, colores de la interfaz, contraseñas) no puede vivir cocido en ella. Kubernetes separa ese material en dos objetos: el ConfigMap para configuración normal y el Secret para datos sensibles.

Es el capítulo de configuración del libro, con una diferencia: aquí vas a comprobar tú mismo qué protege un Secret y qué no.

En esta lección crearás uno de cada y los inyectarás en un Pod de las dos formas posibles: como variables de entorno y como archivo montado. Trabaja desde la pestaña dev-machine.

Paso 1: El ConfigMap

Crea el archivo configmap.yaml. Fíjate en que mezcla dos estilos de clave: un valor simple y un archivo completo.

cat << 'EOF' > configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: web-config
data:
  API_URL: "http://api:8080"
  app.properties: |
    server.port=8080
    server.timeout=30s
EOF

El YAML, explicado con preguntas y respuestas

¿Dónde está el spec?

No lo hay. Los ConfigMaps no describen un estado deseado que reconciliar, son puro almacenamiento de datos, así que usan un campo data plano de pares clave-valor.

¿Por qué una clave se llama app.properties?

Porque las claves de un ConfigMap pueden ser nombres de archivo, y su valor el contenido completo (el | de YAML conserva los saltos de línea). Cuando montes el ConfigMap como volumen, cada clave se convertirá en un archivo con ese nombre.

¿Hay límite de tamaño?

Sí, 1 MiB por ConfigMap. Es configuración, no almacenamiento de datos; para eso está el módulo siguiente.

Aplícalo:

kubectl apply -f configmap.yaml
kubectl describe configmap web-config

Paso 2: El Secret

Crea secret.yaml:

cat << 'EOF' > secret.yaml
apiVersion: v1
kind: Secret
metadata:
  name: db-credentials
type: Opaque
stringData:
  username: tienda
  password: s3cr3t0
EOF

El YAML, explicado con preguntas y respuestas

¿Qué significa type: Opaque?

Es el tipo genérico para datos arbitrarios del usuario. Existen tipos especializados (para credenciales de registros de imágenes, certificados TLS...) que la API valida de forma específica.

¿Por qué stringData y no data?

En un Secret, el campo data exige valores codificados en base64. stringData es el atajo de escritura: aceptas texto plano y la API lo codifica por ti al guardarlo. Compruébalo tras aplicar: kubectl get secret db-credentials -o yaml te devolverá el valor ya en base64, dentro de data.

Entonces, ¿base64 protege la contraseña?

No, y es la pregunta más importante de la lección. Base64 es codificación, no cifrado: cualquiera con acceso de lectura al Secret recupera el valor con base64 -d. La protección real de los Secrets viene de otras capas: el control de acceso RBAC (módulo 5) y el cifrado en reposo en etcd. Un Secret es, sobre todo, una etiqueta que dice "esto es sensible, trátalo distinto".

Aplícalo:

kubectl apply -f secret.yaml
kubectl get secret db-credentials -o yaml

Paso 3: El Pod que lo consume todo

Ahora el plato fuerte: un Pod que recibe el ConfigMap y el Secret por las dos vías. Crea pod-web.yaml:

cat << 'EOF' > pod-web.yaml
apiVersion: v1
kind: Pod
metadata:
  name: web
spec:
  containers:
  - name: app
    image: ghcr.io/iximiuz/labs/nginx:alpine
    env:
    - name: API_URL
      valueFrom:
        configMapKeyRef:
          name: web-config
          key: API_URL
    - name: DB_PASSWORD
      valueFrom:
        secretKeyRef:
          name: db-credentials
          key: password
    volumeMounts:
    - name: config-vol
      mountPath: /etc/config
      readOnly: true
  volumes:
  - name: config-vol
    configMap:
      name: web-config
      items:
      - key: app.properties
        path: app.properties
EOF

El YAML, explicado con preguntas y respuestas

¿Qué hace valueFrom frente a un value normal?

Un value fija el dato en el manifiesto del Pod; valueFrom lo referencia desde otro objeto (configMapKeyRef o secretKeyRef). El manifiesto del Pod queda igual en todos los entornos y solo cambia el ConfigMap o Secret de cada uno.

¿Existe una forma de traerse todas las claves de golpe?

Sí, envFrom con configMapRef o secretRef inyecta cada clave como una variable. Es cómodo, pero valueFrom clave a clave documenta mejor qué consume realmente la aplicación.

¿Cómo se relacionan volumes y volumeMounts?

Es un patrón en dos mitades que reaparecerá en la próxima lección: volumes (a nivel de Pod) declara qué volumen existe y de dónde sale su contenido, y volumeMounts (a nivel de contenedor) declara dónde aparece dentro de ese contenedor. Aquí el volumen sale de un ConfigMap, y cada clave listada en items se materializa como un archivo bajo /etc/config.

¿Qué pasa si el ConfigMap referenciado no existiera?

El Pod se queda en CreateContainerConfigError hasta que exista. Las referencias de configuración son dependencias duras del arranque.

Si cambio el ConfigMap, ¿cambia el Pod en caliente?

Depende de la vía. Los archivos montados como volumen se actualizan solos al cabo de un rato; las variables de entorno no, se fijan al crear el contenedor y solo cambian recreando el Pod. Es uno de los motivos por los que muchas aplicaciones prefieren leer archivos de configuración.

Aplícalo y verifica desde dentro, que es donde cuenta:

kubectl apply -f pod-web.yaml
kubectl wait --for=condition=Ready pod/web --timeout=60s
kubectl exec web -- sh -c 'echo $API_URL'
kubectl exec web -- sh -c 'echo $DB_PASSWORD'
kubectl exec web -- cat /etc/config/app.properties

Resumen

  • El ConfigMap guarda configuración; el Secret, datos sensibles con tratamiento especial (que no es cifrado por sí solo).
  • Dos vías de consumo: variables de entorno (valueFrom/envFrom) y archivos montados (volumes + volumeMounts).
  • Los volúmenes de ConfigMap se refrescan en caliente; las variables de entorno, no.
  • CreateContainerConfigError casi siempre significa una referencia de configuración que no existe.
Previous lesson
CoreDNS y el DNS interno