Hace unos seis meses, mi equipo tenía un problema clásico de startup: doce microservicios corriendo en EC2 con Docker Compose y un script de deploy bash que tres de los cuatro ingenieros teníamos miedo de tocar. No era que no funcionara — es que cuando fallaba, fallaba de formas que tardábamos dos horas en diagnosticar. Nadie tenía ganas de abrir ese script los lunes por la mañana.
Decidí hacer algo que no recomendaría a nadie con deadlines ajustados: evaluar tres herramientas de orquestación en paralelo. Dos semanas serias con cada una, un mes adicional con la finalista en staging, y después la migración real. Este post es lo que encontré — incluyendo la parte donde cambié de opinión a mitad de camino.
El problema con evaluar esto "objetivamente"
Antes de entrar en comparaciones, tengo que ser honesto sobre algo: estas herramientas son tan distintas en filosofía que comparar métricas directas es un poco trampa. Kubernetes 1.35 y Nomad 1.9 no compiten exactamente en el mismo espacio. Uno es un sistema de orquestación de contenedores con un modelo de recursos muy opinado; el otro es un scheduler de workloads que maneja contenedores como uno más entre varios tipos. Docker Swarm es, bueno, Docker Swarm.
Así que en lugar de hacer una tabla con checkboxes, voy a contar lo que realmente me importó: cuánto tiempo tardé en entender qué había fallado cuando algo rompía, qué tan fácil era onboardear a un ingeniero nuevo, y si podía dormir los viernes después de un deploy.
Kubernetes 1.35: el ecosistema que te consume antes de desplegar nada
Yo venía con experiencia básica en K8s — había usado EKS en un trabajo anterior, pero siempre con un equipo de DevOps dedicado que manejaba el plano de control. Esta vez íbamos a manejarlo nosotros mismos con kOps 1.29 en AWS.
El primer día con 1.35 es impresionante en el mal sentido. No porque la herramienta sea mala, sino porque la distancia entre "tengo un contenedor corriendo" y "tengo un sistema de producción razonable" es brutal. Necesitas entender Deployments, Services, Ingress, NetworkPolicies, RBAC, PersistentVolumeClaims... y ninguno de esos conceptos se explica solo con mirar el otro.
# Esto parece simple hasta que tienes que debuggear por qué el pod no arranca
apiVersion: apps/v1
kind: Deployment
metadata:
name: payment-service
namespace: production
annotations:
# Esta annotation la añadí después de un incidente un viernes a las 6pm
kubernetes.io/change-cause: "v2.4.1 - fix race condition en procesamiento de webhooks"
spec:
replicas: 3
selector:
matchLabels:
app: payment-service
template:
spec:
containers:
- name: payment-service
image: myrepo/payment-service:2.4.1
resources:
requests:
memory: "256Mi"
cpu: "100m"
limits:
memory: "512Mi"
# Sin este límite, un bug en prod te come el nodo entero. Aprendido por las malas.
cpu: "500m"
readinessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 10
periodSeconds: 5
Lo que no me esperaba fue quedarme atascado en el networking. Pasé tres días completos entendiendo la diferencia entre ClusterIP, NodePort, LoadBalancer e Ingress. No tres días porque la documentación sea mala (es excelente), sino porque son cuatro abstracciones para el mismo problema y ninguna es la respuesta correcta en todos los casos.
Y después está la pregunta del service mesh. Cilium, Istio, Linkerd. Fui al Slack de Kubernetes (#sig-network, si alguien quiere perder horas de su vida) y salí más confundido que antes. Al final usé Cilium porque era la opción recomendada en la documentación de kOps 1.29, pero no te voy a mentir: aún no tengo 100% claro qué hace Cilium que los NetworkPolicies nativos no hacen, más allá de la observabilidad. Pensé que lo entendía, pero resultó que lo que me importaba no era el service mesh — era el modelo de deployment y el ciclo de debugging.
Donde K8s sí te compensa el sufrimiento inicial es en observabilidad. Prometheus Operator más Grafana más Loki stack es lo mejor que he visto para diagnosticar problemas en producción. Puedes decir "el pod X se reinició tres veces entre las 2am y las 3am, aquí están los logs y aquí está la métrica de memoria que causó el OOM kill" — y eso vale muchísimo a las 3am.
Para equipos grandes, K8s es prácticamente inevitable. Para un equipo de cuatro con doce servicios, estás comprando un sistema para un problema que todavía no tienes.
Docker Swarm en 2026: no está muerto, pero huele raro
Fui a Swarm con prejuicios. Esperaba una herramienta desactualizada que me hiciera perder el tiempo.
Lo que encontré fue más complicado. Docker Swarm en Docker Engine 27.x funciona. El setup fue el más rápido de los tres — docker swarm init, añadir los nodos workers, y tenía un cluster operativo en menos de veinte minutos. La sintaxis de docker-compose.yml como base para los stack files es bastante cómoda si ya conoces Docker. Ahí está la trampa.
# docker-stack.yml — casi idéntico a tu compose de desarrollo. Eso es la trampa.
version: "3.9"
services:
api-gateway:
image: myrepo/api-gateway:1.8.3
ports:
- "443:443"
deploy:
replicas: 2
update_config:
parallelism: 1
delay: 10s
failure_action: rollback # Esto sí funciona bien
restart_policy:
condition: on-failure
max_attempts: 3
# Aquí empieza el problema: esto es prácticamente todo lo que tienes.
# Sin NetworkPolicies granulares. Sin RBAC real. Sin admission controllers.
El problema no es lo que Swarm hace. Es lo que no hace y lo que no va a hacer, porque el desarrollo prácticamente se detuvo. La última vez que revisé el repositorio de Moby en GitHub, los issues más votados sobre Swarm llevaban meses sin actividad del equipo. Hay una PR de mejoras al networking overlay que lleva más de un año abierta. Eso me dice algo.
La observabilidad también es floja. Puedes integrar Prometheus con las métricas del daemon de Docker, pero no hay equivalente al Prometheus Operator ni a los dashboards predefinidos de Grafana para Swarm. Terminas construyendo más infraestructura de monitoreo de la que quieres, para una herramienta que no va a crecer contigo.
¿Cuándo usaría Swarm hoy? Si ya tengo un cluster en producción que funciona y no tengo una razón concreta para migrarlo. En ningún otro caso empezaría algo nuevo con él.
Nomad 1.9: el que cambió mis planes a mitad de la evaluación
Nomad fue el que menos conocía. Lo había visto mencionado en algunos hilos de Hacker News, pero en mi mundo — startups de fintech en LATAM — nadie lo usaba, o al menos nadie hablaba de él en público.
Lo primero que te impacta es lo simple que es el modelo mental. Un servidor de Nomad tiene tres conceptos: Jobs, Task Groups y Tasks. Un Job describe qué quieres correr. Un Task Group agrupa tasks que deben ir en el mismo nodo. Una Task es la unidad de trabajo individual — puede ser un contenedor Docker, un proceso Java, un script bash, o incluso un plugin de terceros. Eso es todo. No hay doce tipos de objetos que aprender.
# Este job file fue el primero que escribí y funcionó casi sin modificaciones en staging
job "payment-service" {
datacenters = ["dc1"]
type = "service"
group "api" {
count = 3
network {
port "http" { to = 8080 }
}
# Consul maneja el service discovery automáticamente — no tienes que configurar nada extra
service {
name = "payment-service"
port = "http"
check {
type = "http"
path = "/health"
interval = "10s"
timeout = "2s"
}
}
task "server" {
driver = "docker"
config {
image = "myrepo/payment-service:2.4.1"
ports = ["http"]
}
resources {
cpu = 500 # MHz
memory = 256 # MB
}
}
}
}
La integración con Consul para service discovery me dejó impresionado. En K8s, el service discovery interno vía CoreDNS funciona bien, pero cuando algo falla en la resolución de nombres a veces no está claro si el problema es DNS, el Service object, o el Endpoint. Con Nomad y Consul, el registro es visible y directo — puedo ver exactamente qué está registrado, cuándo, y en qué estado.
Igual tengo que contarlo: el modelo de networking de Nomad no incluye un overlay network integrado por defecto. Tienes opciones — CNI plugins, Consul Connect para mTLS entre servicios — pero no hay una solución predeterminada como la que K8s ofrece. Tardé día y medio en entender que lo que yo asumía como una característica incluida era algo que tenía que configurar explícitamente. Me sentí un poco tonto, pero también... creo que prefiero que las cosas sean explícitas.
Sobre la licencia BSL de HashiCorp (ahora IBM, desde la adquisición en 2024): sigue siendo un punto de tensión real en la comunidad. Para un startup sin planes de competir con HashiCorp como proveedor de servicios gestionados, en la práctica no importa. Pero si vendes infraestructura, habla con un abogado antes de comprometerte.
El debugging a las 11pm: la métrica que falta en los benchmarks
Ningún benchmark te va a ayudar a entender qué tan fácil es diagnosticar un problema cuando ya son las once de la noche y tienes alertas en el teléfono.
Aprendí esto de la manera difícil. Empujé un cambio de configuración de Nomad un viernes por la tarde — un ajuste de resource limits que parecía completamente inocuo — y dos horas después empezaron las alertas de que el servicio de notificaciones no procesaba mensajes. El problema resultó ser que había bajado demasiado el límite de memoria y el proceso Java moría silenciosamente. En Nomad pude ver el allocation log directamente con nomad alloc logs <alloc-id>, sin navegar entre namespaces o cambiar contextos de kubectl.
Con K8s, el debugging es más potente pero más laberíntico. kubectl describe pod, kubectl logs --previous, eventos del namespace, el dashboard de Lens si lo tienes configurado... hay muchos lugares donde puede estar la información. Con Nomad todo estaba en un lugar, aunque ese lugar tuviera menos información total.
Esto no es objetivamente mejor o peor. Es una tradeoff real: K8s tiene más información disponible pero distribuida; Nomad tiene menos información pero más concentrada. Para cuatro personas, la concentración ganó.
Una advertencia que no puedo ignorar: no sé cómo se comporta Nomad con 500 nodos y cargas de trabajo de empresas muy grandes. Hay casos documentados (Cloudflare lo usó durante años antes de migrar a K8s), pero mi experiencia personal termina alrededor de 15 nodos. Tu situación puede ser completamente diferente.
Qué usaría hoy, sin rodeos
Para un equipo de cuatro personas con doce microservicios en AWS: Nomad 1.9 con Consul.
Llevamos tres meses en producción con esta combinación. El tiempo de onboarding para el cuarto ingeniero del equipo fue un día — comparado con la semana que yo mismo necesité para tener un criterio razonable sobre K8s. Los deploys son predecibles. El debugging nocturno (que no ha desaparecido, seamos realistas) es manejable sin necesitar abrir cinco pestañas distintas.
Esta recomendación viene con una condición importante, no opcional: si en los próximos doce meses el equipo crece a diez o más ingenieros, o si necesitamos networking L7 complejo — múltiples tenants, políticas de seguridad por namespace, service mesh a gran escala — vamos a migrar a K8s. No porque Nomad no pueda manejarlo técnicamente, sino porque el ecosistema de K8s para esos problemas específicos es mucho más maduro, y encontrar ingenieros con experiencia es significativamente más fácil.
Docker Swarm no entraría en mi consideración para nada nuevo. El ahorro de complejidad versus Nomad no es suficiente para compensar el riesgo de quedar atrapado en una herramienta sin desarrollo activo.
La versión corta: equipos pequeños con microservicios moderados → Nomad. Equipos medianos o grandes, o que crecen rápido → K8s y acepta el costo operacional como parte del trabajo. Swarm → solo si ya lo tienes, funciona, y no tienes una razón concreta para cambiar.
Top comments (0)