Problema
Equipos pequeños que migran de Docker Compose a un clúster Kubernetes (k3s, EKS, GKE, etc.) suelen perder la agilidad que tenían al desarrollar localmente. En un flujo tradicional con Compose, cada ingeniero monta su código en un contenedor, reinicia al instante y ve los logs en la terminal. Cuando la aplicación pasa a Helm y ArgoCD, el proceso se vuelve más rígido: los pipelines generan imágenes versionadas, los charts se despliegan en entornos de staging y prod, y los desarrolladores ya no tienen una forma sencilla de lanzar una instancia aislada para pruebas rápidas.
El patrón que se repite es la falta de un “dev namespace” o de una estrategia que permita a cada ingeniero crear su propio pod, actualizar el código sin reconstruir la imagen y observar logs sin depender del pipeline completo. Sin una solución, el equipo termina bloqueado, esperando a que el equipo de infra abra namespaces o que el CI vuelva a compilar una nueva versión para cada pequeño cambio.
Causa
-
Modelo de despliegue basado en imágenes inmutables
Helm y ArgoCD están diseñados para trabajar con imágenes versionadas. Cuando el flujo de trabajo depende de rebuild‑push‑deploy, cualquier cambio de código requiere una nueva imagen, lo que rompe la rapidez del desarrollo local. -
Ausencia de namespaces de desarrollo
Los clústers productivos suelen limitar la creación de namespaces a los equipos de infra. Sin un namespace aislado, los pods de cada ingeniero compiten por recursos y pueden interferir con los entornos de staging. -
Falta de configuración de volúmenes en Helm
Los charts típicos no incluyen una opción para montar el código fuente desde el host del desarrollador. En Docker Compose esto se hace convolumes: - .:/app, pero en Helm se necesita parametrizar elhostPatho usaremptyDirconkubectl cp. -
CI/CD rígido
ArgoCD sincroniza el estado del clúster con el repositorio Git. Si el repositorio solo contiene charts de producción, no hay un camino para que un ingeniero “haga commit” de cambios locales y los vea inmediatamente.
Solución
Diseñar un flujo de trabajo dev‑first que combine Helm, ArgoCD y un namespace dedicado para cada ingeniero. Los pasos clave son:
-
Crear un namespace de desarrollo genérico
Un namespace llamadodevodev‑<username>se crea una sola vez por el equipo de infra. Dentro de él, los recursos pueden ser creados y destruidos libremente. -
Parametrizar el chart para modo desarrollo
Añadir una secciónvalues-dev.yamlque habilite:- Un
hostPathque apunte a una ruta compartida (por ejemplo, un PVC NFS o un directorio montado en el nodo). - Un
imagePullPolicy: IfNotPresentpara que el pod use la última imagen local sin necesidad de re‑taggear. - Un
restartPolicy: Alwaysycommand: ["sleep", "infinity"]para mantener el contenedor vivo mientras el código se sincroniza.
- Un
-
Utilizar
kubectl cporsyncpara sincronizar código
En lugar de reconstruir la imagen, copiar los archivos al pod en tiempo real. Conkubectl cpse pueden actualizar archivos sin reiniciar el contenedor. -
ArgoCD en modo “auto‑sync” solo para producción
Mantener ArgoCD deshabilitado en el namespace de desarrollo o usar la anotaciónargocd.argoproj.io/sync-options: Skippara evitar que sobrescriba los cambios locales. -
Makefile o script local para lanzar el pod
Un objetivomake devque:- Instala/actualiza el chart con
helm upgrade --installusandovalues-dev.yaml. - Copia el código al pod.
- Ejecuta
kubectl port-forwardpara exponer puertos al host.
- Instala/actualiza el chart con
-
Logs y debugging
Conkubectl logs -f <pod>ykubectl exec -it <pod> -- /bin/shel ingeniero tiene acceso inmediato al contenedor, igual que condocker compose up.
Ejemplo de values‑dev.yaml
image:
repository: myregistry/app
tag: latest
pullPolicy: IfNotPresent
volumeMounts:
- name: source-code
mountPath: /app
volumes:
- name: source-code
hostPath:
path: /mnt/dev-share/<username>
type: Directory
command: ["sleep", "infinity"]
Script Makefile simplificado
DEV_NS=dev-$(USER)
CHART_PATH=charts/app
VALUES_DEV=values-dev.yaml
dev:
@kubectl create namespace $(DEV_NS) --dry-run=client -o yaml | kubectl apply -f -
helm upgrade --install app-dev $(CHART_PATH) \
--namespace $(DEV_NS) \
-f $(VALUES_DEV) \
--set image.tag=latest \
--set volumeMounts[0].name=source-code \
--set volumes[0].hostPath.path=/mnt/dev-share/$(USER)
@kubectl cp . $(DEV_NS)/app-dev-0:/app -c app
@kubectl port-forward svc/app-dev 8080:80 -n $(DEV_NS) &
Cuándo aplicar esta solución
- Equipos pequeños (1‑5 ingenieros) que necesitan iterar rápidamente sin esperar a que el CI genere una nueva imagen.
- Entornos on‑prem o k3s donde se controla el nodo y se puede montar un directorio compartido (NFS, hostPath).
- Proyectos en fase de prototipo donde la estabilidad del pipeline no es crítica y la velocidad de desarrollo es prioridad.
No es adecuada cuando:
- El clúster está estrictamente aislado y no permite
hostPathni volúmenes compartidos. - La política de seguridad prohíbe el acceso directo a nodos o la ejecución de
kubectl cp. - Se requiere trazabilidad completa de versiones de imagen en producción; en ese caso, el flujo de CI/CD debe seguir usando imágenes versionadas.
Código
# Crear namespace de desarrollo (solo una vez)
kubectl create namespace dev-jdoe
# Instalar chart en modo dev
helm upgrade --install myapp-dev ./charts/myapp \
--namespace dev-jdoe \
-f values-dev.yaml \
--set image.tag=latest \
--set volumeMounts[0].name=src \
--set volumes[0].hostPath.path=/mnt/dev-share/jdoe
# Copiar código al pod (asume que el pod se llama myapp-dev-0)
kubectl cp . dev-jdoe/myapp-dev-0:/app -c myapp
# Exponer puerto localmente
kubectl port-forward svc/myapp-dev 8080:80 -n dev-jdoe &
Verificación
-
Estado del pod
kubectl get pods -n dev-jdoe -l app=myappEl pod debe estar en
Runningy el contenedor consleep infinity. -
Acceso al código
kubectl exec -n dev-jdoe myapp-dev-0 -- ls /appDebería listar los archivos que acabas de copiar.
-
Logs en tiempo real
kubectl logs -f -n dev-jdoe myapp-dev-0Verifica que la aplicación escribe en stdout como lo hacía en Docker Compose.
-
Endpoint accesible
Abrehttp://localhost:8080en el navegador; la respuesta debe ser la misma que obtenías en el contenedor local.
Notas adicionales
- Sincronización de código:
kubectl cpcopia todo el árbol de archivos cada vez. Para cambios frecuentes, considera montar un PVC NFS y usarrsync -avzdentro del pod; reduce el tiempo de copia y mantiene permisos. - Persistencia de datos: Si la aplicación escribe en disco, usa un
emptyDiro un PVC dedicado al pod de desarrollo; de lo contrario, los datos se perderán al reiniciar el pod. - Política de recursos: Limita CPU y memoria en el chart de desarrollo (
resources.limits) para evitar que un pod de prueba consuma todo el nodo. - ArgoCD: Añade la anotación
argocd.argoproj.io/sync-options: Skipalmetadata.annotationsdel Deployment envalues-dev.yamlpara que ArgoCD no intente sobrescribir los cambios locales. - Limpieza: Incluye un objetivo
make dev-cleanque elimina el namespace y los recursos asociados; evita que los nodos queden saturados con pods huérfanos.
Con este enfoque, cada ingeniero recupera la velocidad de los entornos Docker Compose mientras mantiene la consistencia de los charts y la visibilidad de ArgoCD para los entornos de staging y producción. La clave está en separar claramente los namespaces de desarrollo y producción y parametrizar el chart para que el mismo artefacto pueda servir a ambos mundos.