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.
-
Comentarios
Online: 0
Por favor inicie sesión para poder escribir comentarios.