Como montar secretos y configs en Kubernetes como archivos o variables de entorno


Introducción

Este será un artículo corto explorando las diferentes formas en las que podés montar secretos en Kubernetes y también entender cuándo utilizar cada método. Montar significa cargar o exponer, básicamente, dentro de tu Pod, ya sea como una variable de entorno (env var), como un archivo o incluso como múltiples archivos. La decisión depende de vos y de cómo tu aplicación utiliza o consume esos secretos.

Primero exploraremos cómo crear y exponer secretos como variables de entorno, probablemente la forma más utilizada, ya que el formato es fácil de seguir y mantener. Veamos un ejemplo:

 kubectl create secret generic supersecret -n example --from-literal=MY_SUPER_ENVVAR=a_secret --from-literal=ANOTHER_ENVVAR=just_another_secret
secret/supersecret created

Veamos el contenido del secreto, mantiene una forma similar a como lo definimos. Asegurate de revisar la ayuda de kubectl, ya que contiene muchos ejemplos de cómo pasar datos dentro del secreto real.

 kubectl get secret supersecret -o yaml
apiVersion: v1
data:
  ANOTHER_ENVVAR: anVzdF9hbm90aGVyX3NlY3JldA==
  MY_SUPER_ENVVAR: YV9zZWNyZXQ=
kind: Secret
metadata:
  name: supersecret
  namespace: example
type: Opaque

Todo bien hasta ahora, pero ¿cómo usamos realmente ese secreto? Vamos a crear una especificación para ello y luego exploraremos algunas opciones…


Primer escenario: clave única desde un secreto

En este ejemplo, tenemos un StatefulSet que monta un secreto usando una clave específica como una variable de entorno. Esto es bastante común y útil cuando no necesitas que todas las claves se monten en todos los pods, por ejemplo, o cuando usas configuraciones compartidas. Presta especial atención a la sección valueFrom, donde el nombre indica el nombre del secreto y la clave cómo está almacenada dentro del bloque data del mismo. Esto también funciona con ConfigMaps, como probablemente hayas adivinado; en lugar de secretKeyRef, usa configMapKeyRef.

apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: example
  name: example
  namespace: example
spec:
  replicas: 1
  selector:
    matchLabels:
      app: example
  template:
    metadata:
      labels:
        app: example
    spec:
      containers:
      - name: example
        image: busybox:1.36
        command: ["sh", "-c", "/bin/sleep 3600"]
        env:
          - name: MY_SUPER_ENVVAR
            valueFrom: 
              secretKeyRef:
                name: supersecret
                key: MY_SUPER_ENVVAR

Y el resultado seria:

 kubectl -n example exec -ti example-64f956f9c9-fxn28 -- env | grep ENV
MY_SUPER_ENVVAR=a_secret

Segundo escenario: variables de entorno desde un secreto

En este segundo ejemplo, vamos a montar todos los valores del secreto como variables de entorno, por lo que es importante tener el formato correcto en el secreto. Para esto, hay una palabra clave súper útil: envFrom, y también funciona con ConfigMaps. En lugar de usar secretRef, tendrías que especificar: configMapRef.

apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: example
  name: example
  namespace: example
spec:
  replicas: 1
  selector:
    matchLabels:
      app: example
  template:
    metadata:
      labels:
        app: example
    spec:
      containers:
      - name: example
        image: busybox:1.36
        command: ["sh", "-c", "/bin/sleep 3600"]
        envFrom:
          - secretRef:
              name: supersecret

El resultado seria:

 kubectl -n example exec -ti example-584757d47f-gbxgq -- env | grep ENV
ANOTHER_ENVVAR=just_another_secret
MY_SUPER_ENVVAR=a_secret

Tercer escenario: un secreto como archivos

En algunos casos, tu aplicación podría necesitar un archivo de configuración montado en el pod. En esos casos, puedes montar todo el Secret o ConfigMap, o claves específicas como archivos específicos (esto lo veremos en el cuarto escenario).

Para este ejemplo, usaremos un nuevo secreto que contiene un archivo JSON almacenado bajo la clave test.json, que será el nombre real del archivo. Esto es bastante útil, ya que podrías tener múltiples archivos almacenados allí y podrías montarlos todos de una vez, o solo algunos:

 cat test.json
{
  "key1": "value1",
  "key2": "value2"
}

 kubectl create secret generic supersecret2 -n example --from-file=test.json
secret/supersecret2 created

 kubectl get secret supersecret2 -o yaml
apiVersion: v1
data:
  test.json: ewogICJrZXkxIjogInZhbHVlMSIsCiAgImtleTIiOiAidmFsdWUyIgp9Cg==
kind: Secret
metadata:
  name: supersecret2
  namespace: example
type: Opaque

Veamos el secreto:

apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: example
  name: example
  namespace: example
spec:
  replicas: 1
  selector:
    matchLabels:
      app: example
  template:
    metadata:
      labels:
        app: example
    spec:
      containers:
      - name: example
        image: busybox:1.36
        command: ["sh", "-c", "/bin/sleep 3600"]
        volumeMounts:
          - mountPath: "/var/mysecrets"
            name: test.json
            readOnly: true
      volumes:
        - name: test.json
          secret:
            secretName: supersecret2

Y el resultado seria:

 kubectl -n example exec -ti example-5b9c58b7f9-zv9nr -- cat /var/mysecrets/test.json
{
  "key1": "value1",
  "key2": "value2"
}

Cuarto escenario: una clave de un secreto como un archivo individual

A veces solo necesitas una configuración específica como archivo en una ruta particular. Imagina un archivo de configuración JSON o una cadena que debe estar presente en un archivo para que tu aplicación lo lea.

apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: example
  name: example
  namespace: example
spec:
  replicas: 1
  selector:
    matchLabels:
      app: example
  template:
    metadata:
      labels:
        app: example
    spec:
      containers:
      - name: example
        image: busybox:1.36
        command: ["sh", "-c", "/bin/sleep 3600"]
        volumeMounts:
          - mountPath: "/var/myapp/server.config"
            name: config
            readOnly: true
            subPath: test.json
      volumes:
        - name: config
          secret:
            secretName: supersecret2
            items:
              - key: test.json
                path: test.json

Y el resultado seria:

 kubectl -n example exec -ti example-59f565fbbf-cgk5c -- cat /var/myapp/server.config
{
  "key1": "value1",
  "key2": "value2"
}

Lo increíblemente útil de este enfoque es que podés montar el archivo como lo necesites, siempre y cuando lo tengas almacenado correctamente como un Secret o ConfigMap. Cuando estés depurando problemas con tus contenedores y secretos, asegurate de usar kubectl describe pod, ya que es una gran herramienta para entender la especificación que nuestro pod o carga de trabajo debe cumplir y te indicará cualquier posible error o equivocación.


Ejemplo con el nombre del secreto incorrecto (he limpiado un poco el output para que sea más legible):

 kubectl -n example describe pod example-58bbc5464f-2mcv7
Name:             example-58bbc5464f-2mcv7
Namespace:        example
Priority:         0
Service Account:  default
Containers:
  example:
    Image:         busybox:1.36
    Image ID:
    Port:          <none>
    Host Port:     <none>
      sh
      -c
      /bin/sleep 3600
    State:          Waiting
      Reason:       ContainerCreating
    Ready:          False
    Restart Count:  0
    Mounts:
      /var/mysecrets from config (ro)
Conditions:
  Type                        Status
  PodReadyToStartContainers   False
  Initialized                 True
  Ready                       False
  ContainersReady             False
  PodScheduled                True
Volumes:
  config:
    Type:        Secret (a volume populated by a Secret)
    SecretName:  mysupersecret2
    Optional:    false
Events:
  Type     Reason       Age                 From               Message
  ----     ------       ----                ----               -------
  Warning  FailedMount  6s (x9 over 2m14s)  kubelet            MountVolume.SetUp failed for volume "config" : secret "mysupersecret2" not found

Con esto, apenas hemos arañado la superficie de lo que es posible hacer con Kubernetes, pero espero que te haya sido útil. ¿Tenés alguna pregunta? Dejá un comentario :point_down:


Erratas

Si encontrás algún error o tenés alguna sugerencia, mandame un mensaje para que lo corrija.



No tienes cuenta? Regístrate aqui

Ya registrado? Iniciar sesión a tu cuenta ahora.

Iniciar session con GitHub
Iniciar sesion con Google
  • Comentarios

    Online: 0

Por favor inicie sesión para poder escribir comentarios.

by Gabriel Garrido