Problema
En entornos de desarrollo y producción basados en Kubernetes es frecuente que los equipos necesiten separar claramente los componentes de una aplicación: un frontend (React, Angular, etc.), un backend (API en Node, Go, Python…) y la base de datos. La dificultad surge al decidir dónde y cómo almacenar la configuración (URLs, puertos, credenciales) y cómo exponer la base de datos sin acoplarla al pod del backend. Muchos usuarios intentan colocar variables en archivos .env dentro del contenedor o montar la base de datos como un contenedor adicional dentro del mismo pod, lo que genera problemas de escalabilidad, seguridad y mantenimiento.
Causa
-
Uso de archivos
.envdentro del contenedor
Copiar un.enval contenedor rompe la idea de inmutabilidad; cualquier cambio requiere reconstruir la imagen o redeployar el pod. -
Variables de entorno hardcodeadas en el manifiesto
DefinirDB_HOST,DB_PORTdirectamente en elDeploymentobliga a mantener varios manifiestos para cada entorno (dev, staging, prod). -
Base de datos como contenedor “side‑car”
Ejecutar PostgreSQL o MySQL dentro del mismo pod que el backend funciona en pruebas locales, pero en producción impide escalar el backend de forma independiente y complica la persistencia de datos. -
Falta de separación entre datos sensibles y no sensibles
Guardar contraseñas en ConfigMaps o en el mismo archivo de configuración expone secretos a usuarios que solo deberían ver la configuración pública.
Solución
Adoptar el modelo nativo de Kubernetes:
- ConfigMap para la configuración no sensible (host, puerto, nombres de colecciones).
- Secret para credenciales y cualquier dato que deba estar cifrado o al menos codificado en base64.
- PersistentVolumeClaim (PVC) para la base de datos, de forma que el pod del backend solo consuma el servicio, mientras que el PVC se asocia a un StatefulSet o a un Deployment dedicado a la base de datos.
- Service para exponer la base de datos dentro del clúster y permitir que el backend la descubra mediante DNS (
postgres-service.default.svc.cluster.local).
Paso a paso
1. ConfigMap con parámetros de conexión
kubectl apply -f - <<EOF
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config
data:
DB_HOST: postgres-service
DB_PORT: "5432"
DB_NAME: myapp
EOF
2. Secret con credenciales
kubectl create secret generic db-credentials \
--from-literal=username=appuser \
--from-literal=password=$(openssl rand -base64 12)
3. PVC para PostgreSQL
kubectl apply -f - <<EOF
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: pg-data
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 10Gi
EOF
4. Deployment del backend que consume ConfigMap y Secret
kubectl apply -f - <<EOF
apiVersion: apps/v1
kind: Deployment
metadata:
name: backend
spec:
replicas: 2
selector:
matchLabels:
app: backend
template:
metadata:
labels:
app: backend
spec:
containers:
- name: api
image: myregistry/backend:latest
env:
- name: DB_HOST
valueFrom:
configMapKeyRef:
name: app-config
key: DB_HOST
- name: DB_PORT
valueFrom:
configMapKeyRef:
name: app-config
key: DB_PORT
- name: DB_NAME
valueFrom:
configMapKeyRef:
name: app-config
key: DB_NAME
- name: DB_USER
valueFrom:
secretKeyRef:
name: db-credentials
key: username
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: db-credentials
key: password
ports:
- containerPort: 8080
EOF
5. Deployment (o StatefulSet) de PostgreSQL usando el PVC
kubectl apply -f - <<EOF
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: postgres
spec:
serviceName: "postgres-service"
replicas: 1
selector:
matchLabels:
app: postgres
template:
metadata:
labels:
app: postgres
spec:
containers:
- name: postgres
image: postgres:15
env:
- name: POSTGRES_DB
valueFrom:
configMapKeyRef:
name: app-config
key: DB_NAME
- name: POSTGRES_USER
valueFrom:
secretKeyRef:
name: db-credentials
key: username
- name: POSTGRES_PASSWORD
valueFrom:
secretKeyRef:
name: db-credentials
key: password
ports:
- containerPort: 5432
volumeMounts:
- name: pg-storage
mountPath: /var/lib/postgresql/data
volumeClaimTemplates:
- metadata:
name: pg-storage
spec:
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 10Gi
EOF
6. Service para la base de datos
kubectl apply -f - <<EOF
apiVersion: v1
kind: Service
metadata:
name: postgres-service
spec:
selector:
app: postgres
ports:
- protocol: TCP
port: 5432
targetPort: 5432
EOF
Con este esquema:
- Los pods del backend pueden escalar sin tocar la base de datos.
- La base de datos persiste en un PVC independiente, lo que permite migraciones o backups sin afectar al backend.
- Los cambios de entorno (dev → prod) solo requieren actualizar los ConfigMaps/Secrets o crear nuevos objetos con nombres diferentes, sin tocar los despliegues.
Cuándo aplicar esta solución
- Entornos multi‑ambiente (dev, staging, prod) donde la configuración varía pero la imagen del contenedor es la misma.
- Aplicaciones con requisitos de alta disponibilidad que necesitan escalar el backend sin reiniciar la base de datos.
- Equipos que siguen la filosofía GitOps: los manifiestos declarativos pueden versionarse y aplicar cambios mediante CI/CD.
- Escenarios donde la seguridad es crítica: separar secretos en objetos
Secretpermite habilitar encriptación en reposo (KMS) y limitar su exposición mediante RBAC.
No aplicar
- Proyectos extremadamente pequeños o pruebas rápidas donde la sobrecarga de crear ConfigMaps/Secrets no justifica el beneficio.
- Bases de datos externas gestionadas (Cloud SQL, RDS) donde el almacenamiento persiste fuera del clúster; en ese caso solo se necesita un
Secretcon la cadena de conexión.
Verificación
-
Comprobar que el backend recibe variables
kubectl exec -it $(kubectl get pod -l app=backend -o jsonpath="{.items[0].metadata.name}") -- env | grep DB_Deberían aparecer
DB_HOST,DB_PORT,DB_NAME,DB_USERyDB_PASSWORD. -
Validar la conectividad
kubectl exec -it $(kubectl get pod -l app=backend -o jsonpath="{.items[0].metadata.name}") -- curl -s http://postgres-service:5432La respuesta debe ser el banner de PostgreSQL o un código 200 si el backend expone un endpoint de healthcheck.
-
Revisar la persistencia
- Inserta un registro mediante el backend.
- Elimina el pod del backend (
kubectl delete pod -l app=backend). - Vuelve a crear el pod y verifica que el registro sigue presente.
-
Auditar secretos
kubectl get secret db-credentials -o yamlAsegúrate de que los valores aparecen codificados en base64 y que el RBAC restringe el acceso a los equipos que realmente lo necesiten.
Notas adicionales
- Encriptación de Secrets: habilita la encriptación a nivel de etcd o usa un proveedor KMS (AWS KMS, GCP CMEK) para cumplir con requisitos de compliance.
- Versionado de ConfigMaps: Kubernetes no versiona objetos; si necesitas historial, guarda los archivos YAML en Git y aplica cambios mediante
kubectl apply. - Backup de PVC: para bases de datos críticas, programa snapshots del PVC con la herramienta del proveedor de almacenamiento (Velero, CSI snapshots).
- Health checks: define
livenessProbeyreadinessProbeen el Deployment del backend para que Kubernetes retire pods que no puedan conectar a la base de datos. - Escalado horizontal: una vez que la base de datos está desacoplada, puedes usar
HorizontalPodAutoscalerpara el backend sin preocuparte por la carga en la base de datos.
Con estos pasos tienes una arquitectura modular, segura y preparada para escalar, aplicable tanto a proyectos personales como a entornos empresariales que eventualmente migrarán a servicios gestionados en la nube.