Contextos Docker: Gestiona varios hosts como un profesional

¿Te fue útil?

La forma más rápida de arruinarte la tarde es escribir docker compose up -d mientras tu shell está apuntando al demonio equivocado.
Crees que vas a reiniciar una pila de desarrollo; producción acepta silenciosamente y hace exactamente lo que pediste.

Los contextos Docker existen para evitar eso. Te proporcionan endpoints nombrados e inspeccionables con conmutación explícita, además de suficiente metadata para
convertir “¿dónde estoy desplegando?” en una pregunta que puedes responder antes de que algo se queme.

Por qué existen los contextos (y por qué DOCKER_HOST es una trampa)

Si llevas tiempo con Docker, probablemente hayas usado DOCKER_HOST o espolvoreado
docker -H tcp://... en scripts como condimento. Funciona. También crea un estado que no puedes ver, auditar
ni versionar fácilmente. Tu terminal se vuelve una responsabilidad.

Los contextos Docker arreglan esto al convertir la “selección del demonio de destino” en un concepto de primera clase: le pones un nombre, puedes listarlo, inspeccionarlo,
exportarlo/importarlo y obligar a tus herramientas a usarlo. Esa última parte importa en la respuesta a incidentes:
quieres menos globals implícitos, no más.

Los contextos además unifican más de un tipo de endpoint. En la práctica verás:

  • Local (por defecto) hacia Docker Desktop o el socket del demonio en Linux de tu estación de trabajo.
  • SSH a un host remoto sin exponer la API TCP de Docker a internet.
  • TCP/TLS para entornos legacy o redes controladas (ten cuidado, más sobre eso más adelante).
  • Metadata del orquestador (históricamente para integraciones con Swarm/Kubernetes; hoy, mayormente para llevar el control de endpoints).

Un contexto no es “solo conveniencia.” Es una barandilla de seguridad. También es una herramienta para la rendición de cuentas:
puedes construir flujos de trabajo donde “prod” sea algo que debes seleccionar explícitamente, no un accidente al que llegas por casualidad.

Broma #1: Lo único más permanente que una solución temporal es una CLI de Docker apuntando a producción.

Datos interesantes y breve historia

  • La API remota de Docker precede a los contextos: el control remoto temprano fue mayormente con DOCKER_HOST y la API TCP, lo que hacía comunes los despliegues por error.
  • Los contextos se volvieron mainstream con flujos de trabajo de la era v19.03 del CLI, cuando los equipos comenzaron a tratar múltiples entornos como algo rutinario, no excepcional.
  • Los contextos SSH se apoyan en el cliente OpenSSH: obtienes forwarding de agente, verificación de known_hosts y tu configuración SSH existente gratis (y sus aristas también).
  • Docker Desktop usa una abstracción tipo contexto internamente para enrutar comandos a su demonio basado en VM; los contextos hacen explícito ese concepto para ti.
  • Swarm fue un impulsor temprano de “múltiples endpoints, una CLI”, aunque muchas organizaciones migraron a Kubernetes o plataformas gestionadas después.
  • El demonio tiene poder parecido al root: un usuario que puede hablar con el socket de Docker normalmente puede volverse root en ese host. Los contextos no cambian eso; solo te ayudan a apuntar ese poder deliberadamente.
  • Los contextos se almacenan en el cliente (en tu directorio de configuración de Docker), por eso exportarlos/importarlos importa para portátiles, runners CI efímeros y jump boxes de emergencia.
  • Compose respeta los contextos: docker compose habla con el contexto configurado en el CLI, así que un “contexto equivocado” afecta todo tu flujo de trabajo.

Modelo mental: qué es realmente un contexto

Piensa en un contexto Docker como una tupla nombrada:
(endpoint, auth, TLS/SSH plumbing, metadata).
No es el motor. No es un clúster. No es una variable de entorno que olvidas.
Es un objeto de configuración respaldado por archivo que el CLI consulta antes de hacer cualquier cosa.

Qué contiene

Cuando inspeccionas un contexto, normalmente verás:

  • Endpoints: usualmente docker con un Host como unix:///var/run/docker.sock o ssh://user@host.
  • Material TLS (si usas TCP/TLS): certificados y flags de verificación.
  • Orquestador: a menudo campos como swarm o kubernetes según la época; en muchos entornos modernos está presente pero sin usar.
  • Descripción: que deberías completar, porque los humanos también necesitan barandillas.

Precedencia: ¿quién gana?

Esto importa porque los incidentes de producción adoran los bugs de precedencia. A grandes rasgos:

  • docker --context X ... gana para esa invocación.
  • DOCKER_CONTEXT puede anular el contexto “actual”.
  • docker context use X establece el contexto actual para comandos futuros.
  • DOCKER_HOST puede bypassear contextos por completo para algunas herramientas o scripts, dependiendo de cómo estén escritos.

Toma de posición: prohíbe DOCKER_HOST en shells interactivos. Permítelo solo en automatización estrictamente controlada,
y aun así, prefiere --context por claridad.

Una frase confiable de ops

“La esperanza no es una estrategia.” — Gene Kranz (idea parafraseada)

Los contextos son cómo dejas de “esperar” estar hablando con el demonio correcto.

Comandos principales que importan en producción

No necesitas memorizar 50 subcomandos. Necesitas un puñado que puedas ejecutar bajo presión,
y debes entender lo que implica su salida.

Regla de oro

Antes de cualquier comando destructivo (rm, down, system prune, volume rm, “vamos a redeployar”),
haz una comprobación del contexto. Hazlo un tic.

Nombres que deberías adoptar

  • dev, staging, prod (aburrido, efectivo)
  • prod-eu-west, prod-us-east si realmente operas en varias regiones
  • buildkit o builder si usas builders remotos

Evita nombres ingeniosos. Tu yo futuro en una rotación de pager no se impresionará.

Tareas prácticas: 12+ cosas que realmente harás

A continuación hay tareas reales de operador con comandos, salida de ejemplo, qué significa y la decisión que tomas a partir de ello.
Trata estas como ejercicios de memoria muscular.

Task 1: Listar contextos e identificar el actual

cr0x@server:~$ docker context ls
NAME        DESCRIPTION                               DOCKER ENDPOINT                             ERROR
default *   Current DOCKER_HOST based configuration   unix:///var/run/docker.sock
staging     Staging host via SSH                      ssh://deploy@stg-01
prod        Production host via SSH                   ssh://deploy@prod-01

Qué significa la salida: el asterisco marca el contexto activo. La columna “DOCKER ENDPOINT” indica cómo el CLI alcanzará el demonio.

Decisión: si vas a tocar producción, deberías ver prod * y nada más. Si no es así, detente.

Task 2: Inspeccionar un contexto antes de usarlo

cr0x@server:~$ docker context inspect prod
[
  {
    "Name": "prod",
    "Metadata": {},
    "Endpoints": {
      "docker": {
        "Host": "ssh://deploy@prod-01",
        "SkipTLSVerify": false
      }
    },
    "TLSMaterial": {},
    "Storage": {
      "MetadataPath": "/home/cr0x/.docker/contexts/meta/7c1a3c2b6f",
      "TLSPath": "/home/cr0x/.docker/contexts/tls/7c1a3c2b6f"
    }
  }
]

Qué significa la salida: este es un contexto SSH; el material TLS está vacío porque SSH provee la seguridad del transporte.

Decisión: confirma que el par usuario/host sea correcto y coincida con tus expectativas de configuración SSH.
Si dice root@prod-01, tienes un arma cargada en las manos.

Task 3: Cambiar contextos explícitamente

cr0x@server:~$ docker context use staging
staging
cr0x@server:~$ docker context ls
NAME        DESCRIPTION                               DOCKER ENDPOINT                             ERROR
default     Current DOCKER_HOST based configuration   unix:///var/run/docker.sock
staging *   Staging host via SSH                      ssh://deploy@stg-01
prod        Production host via SSH                   ssh://deploy@prod-01

Qué significa la salida: tu shell ahora está apuntando a staging para todos los comandos Docker futuros.

Decisión: en terminales compartidas (tmux, screen, jump hosts), anuncia tu contexto en el prompt o en la línea de estado. El silencio es cómo nacen los errores.

Task 4: Usar un contexto puntual sin cambiar la sesión

cr0x@server:~$ docker --context prod ps
CONTAINER ID   IMAGE                    COMMAND                  CREATED        STATUS        PORTS                  NAMES
a12b3c4d5e6f   nginx:1.25               "/docker-entrypoint.…"   2 days ago     Up 2 days     0.0.0.0:80->80/tcp     web

Qué significa la salida: solo este comando apuntó a prod; tu contexto actual permanece sin cambios.

Decisión: prefiere este patrón en automatización y en runbooks de copia/pega.
Es más difícil “olvidar dónde estás”.

Task 5: Crear un contexto vía SSH (el sane default)

cr0x@server:~$ docker context create prod --docker "host=ssh://deploy@prod-01"
prod
cr0x@server:~$ docker context ls
NAME        DESCRIPTION                               DOCKER ENDPOINT                             ERROR
default *   Current DOCKER_HOST based configuration   unix:///var/run/docker.sock
prod        Production host via SSH                   ssh://deploy@prod-01

Qué significa la salida: has definido un endpoint remoto sin exponer la API TCP de Docker.

Decisión: usa contextos SSH salvo que tengas una razón fuerte para no hacerlo. “Porque funcionó en 2017” no es una razón fuerte.

Task 6: Validar que hablas con el motor correcto (chequeo barato de identidad)

cr0x@server:~$ docker --context prod info --format 'Name={{.Name}} Server={{.ServerVersion}} RootDir={{.DockerRootDir}}'
Name=prod-01 Server=25.0.3 RootDir=/var/lib/docker

Qué significa la salida: obtuviste tres señales de identidad: nombre del demonio, versión y directorio raíz.

Decisión: incorpora esto en tus checks previos. Si el nombre es incorrecto, estás en el host equivocado. Si la versión es inesperada, planifica para cambios de comportamiento.

Task 7: Detectar “el contexto funciona pero SSH te está engañando” (mismatch de clave host/config)

cr0x@server:~$ docker --context prod ps
error during connect: Get "http://docker.example/v1.45/containers/json": command [ssh -o ConnectTimeout=30 -T -l deploy prod-01 docker system dial-stdio] exit status 255

Qué significa la salida: el CLI intentó ejecutar docker system dial-stdio por SSH y falló. Esto es transporte, no Docker.

Decisión: ejecuta un comando SSH simple a continuación para aislar el problema: si SSH falla, arregla SSH (claves, bastion, clave del host).
Si SSH funciona pero Docker falla, arregla permisos del demonio.

Task 8: Probar conectividad SSH básica (sin Docker en el medio)

cr0x@server:~$ ssh -o BatchMode=yes deploy@prod-01 'hostname; id; docker version --format "{{.Server.Version}}"'
prod-01
uid=1001(deploy) gid=1001(deploy) groups=1001(deploy),998(docker)
25.0.3

Qué significa la salida: el usuario puede ejecutar docker y pertenece al grupo docker. Eso es, efectivamente, poder root en esa máquina.

Decisión: trata la pertenencia al grupo docker como sudo. Concedela deliberadamente, audítala y quítala cuando no sea necesaria.

Task 9: Exportar un contexto para un portátil de emergencia o runner CI

cr0x@server:~$ docker context export prod -o prod.dockercontext
cr0x@server:~$ ls -l prod.dockercontext
-rw------- 1 cr0x cr0x 2840 Jan  3 10:12 prod.dockercontext

Qué significa la salida: generaste un bundle de contexto portable.

Decisión: almacénalo como un secreto. Si incluye material TLS, es literalmente acceso. Incluso con endpoints solo SSH, sigue siendo metadata operativa que no quieres por ahí suelta.

Task 10: Importar un contexto en otra máquina

cr0x@server:~$ docker context import prod -i prod.dockercontext
prod
cr0x@server:~$ docker context ls
NAME        DESCRIPTION                               DOCKER ENDPOINT                             ERROR
default *   Current DOCKER_HOST based configuration   unix:///var/run/docker.sock
prod        Production host via SSH                   ssh://deploy@prod-01

Qué significa la salida: tu CLI ahora sabe cómo alcanzar producción de la misma forma que la máquina origen.

Decisión: para CI, prefiere generar contextos en tiempo de ejecución (con credenciales efímeras) en lugar de importar contextos de larga duración.
Pero para respuesta a incidentes, importar un contexto conocido y bueno es oro.

Task 11: Eliminar un contexto que ya no confías

cr0x@server:~$ docker context rm staging
staging
cr0x@server:~$ docker context ls
NAME        DESCRIPTION                               DOCKER ENDPOINT                             ERROR
default *   Current DOCKER_HOST based configuration   unix:///var/run/docker.sock
prod        Production host via SSH                   ssh://deploy@prod-01

Qué significa la salida: la metadata del contexto desapareció del cliente.

Decisión: elimina contextos cuando los hosts se retiren, se muevan o se reconstruyan. Los contextos obsoletos son cómo terminas desplegando en un hostname reciclado.

Task 12: Comparar rápidamente dos entornos (imágenes, contenedores, disco)

cr0x@server:~$ docker --context staging system df
TYPE            TOTAL     ACTIVE    SIZE      RECLAIMABLE
Images          18        6         5.2GB     2.7GB (51%)
Containers      9         6         210MB     0B (0%)
Local Volumes   14        9         22.4GB    3.1GB (13%)
Build Cache     0         0         0B        0B

Qué significa la salida: obtienes una percepción rápida de la presión de almacenamiento y si vale la pena limpiar.

Decisión: si los volúmenes dominan, no ejecutes un system prune a lo loco. Probablemente tus datos estén en volúmenes. Planea una limpieza dirigida.

Task 13: Ejecutar la misma comprobación en prod sin cambiar contextos

cr0x@server:~$ docker --context prod system df
TYPE            TOTAL     ACTIVE    SIZE      RECLAIMABLE
Images          42        18        14.8GB    1.1GB (7%)
Containers      31        27        1.8GB     0B (0%)
Local Volumes   66        55        640.2GB   12.4GB (1%)
Build Cache     0         0         0B        0B

Qué significa la salida: la producción tiene mucho peso en volúmenes y recuperabilidad mínima. Eso es normal para servicios stateful, y también un recordatorio de que el disco es planificación de capacidad, no un botón de limpieza.

Decisión: si suenan alarmas de disco, eliminar cachés no te salvará. Necesitas disciplina en el ciclo de vida de volúmenes, políticas de retención o discos más grandes.

Task 14: Verificar que un despliegue de Compose apunta al host previsto

cr0x@server:~$ docker --context prod compose ps
NAME                IMAGE                 COMMAND                  SERVICE   CREATED        STATUS        PORTS
app-web-1           registry/app:web      "/entrypoint.sh"         web       3 hours ago    Up 3 hours    0.0.0.0:443->443/tcp
app-db-1            postgres:16           "docker-entrypoint.s…"   db        3 hours ago    Up 3 hours    5432/tcp

Qué significa la salida: Compose está hablando con prod. Los servicios en ejecución se listan desde ese demonio.

Decisión: cuando no estés seguro a dónde apunta Compose, no adivines—pregúntalo con compose ps usando --context.

Task 15: Diagnosticar “contexto equivocado” desde el prompt con un marcador explícito

cr0x@server:~$ docker context show
prod

Qué significa la salida: el CLI imprime el nombre del contexto activo, nada más.

Decisión: conéctalo a tu prompt de shell (PS1) o a la barra de estado de tmux. Quieres que el contexto sea visible como tu rama de git actual.

Task 16: Verificar el stream de eventos en el demonio remoto (ideal durante incidentes)

cr0x@server:~$ docker --context prod events --since 10m
2026-01-03T10:02:21.223456789Z container die a12b3c4d5e6f (exitCode=137, image=registry/app:web, name=app-web-1)
2026-01-03T10:02:24.019283746Z container start 9f8e7d6c5b4a (image=registry/app:web, name=app-web-1)

Qué significa la salida: estás viendo churn en el ciclo de vida de contenedores; exit 137 suele implicar SIGKILL, comúnmente OOM-kill o kill manual.

Decisión: si ves muertes/arranques repetidos, deja de redeployar. Revisa límites de memoria, logs de OOM del kernel o fallos de la aplicación.

Broma #2: “Es solo un reinicio rápido” es el equivalente ops de “solo tomaré una galleta”.

Seguridad y control de acceso: mantener al demonio con correa

Los contextos Docker no aseguran nada mágicamente. Facilitan el uso de transportes seguros y hacen más difícil cometer estupideces por accidente.
El demonio sigue siendo un plano de control de alto privilegio. Trátalo como SSH + sudo combinados.

Contextos SSH: el predeterminado que quieres

Con contextos ssh://, Docker ejecuta un comando auxiliar en el host remoto para establecer una conexión tunelizada.
Esto te da:

  • Autenticación basada en claves SSH y tus guardarraíles empresariales habituales (bastiones, envoltorios MFA, grabación de sesiones).
  • Verificación de la clave del host (si no la deshabilitaste como un monstruo).
  • No hace falta exponer el puerto de la API TCP de Docker.

Contextos TCP: usar solo con TLS y cuando esté justificado

Si tienes un entorno legacy que requiere tcp://host:2376 con certificados TLS, puedes representarlo como un contexto.
Pero recuerda: la API TCP de Docker sin autenticar es básicamente “root en la red”.
No uses tcp://0.0.0.0:2375 a menos que tu modelo de amenazas sea “nos gusta el caos”.

Menor privilegio (tanto como Docker lo permite)

En Docker Engine clásico, cualquiera que pueda acceder a la API de Docker normalmente puede montar el filesystem, ejecutar contenedores privilegiados
y escalar privilegios. Tus controles reales son:

  • Quién puede SSH al host (y desde dónde).
  • Qué cuenta de usuario se usa para contextos (identidades “deploy” separadas son más sanas que cuentas personales).
  • Auditabilidad de cambios (eventos, logs del sistema y pipelines de despliegue).
  • Endurecimiento del host: AppArmor/SELinux, modo rootless cuando sea factible y ajustes del demonio cuidadosamente elegidos.

Higiene operativa que compensa

  • Usa contextos separados para entornos separados. No reutilices “prod” para múltiples hosts.
  • Pon el nombre del entorno en la descripción del contexto y en el banner del host remoto (motd) si puedes.
  • Haz explícita la conmutación de contexto en runbooks y scripts: usa --context.
  • Registra “qué contexto se usó” en la salida de CI/CD. Es banal y ayuda durante arqueología sin culpa.

Tres mini-historias corporativas desde el frente

Incidente causado por una suposición errónea: “default” significaba “dev”

Un equipo tenía un jump host que “todo el mundo usaba” para operaciones con contenedores. Lo mantenía el grupo de plataforma y tenía Docker instalado.
La suposición—nunca verbalizada, siempre implícita—era que el Docker del jump host era solo una conveniencia para herramientas. La gente lo usaba para linters, tirar imágenes y quizá probar Compose.

Un ingeniero recibió un pager por un problema en staging. Se SSHeó al jump host, ejecutó docker ps, vio un montón de contenedores y concluyó que staging estaba sano.
Luego hizo una limpieza: docker system prune -af. El comando terminó rápido. Demasiado rápido.

El demonio de Docker del jump host no era “dev local”. Estaba configurado—mediante un antiguo DOCKER_HOST exportado en /etc/profile.d—para apuntar a un motor de producción compartido.
Los contenedores que vio eran contenedores de producción. El prune eliminó imágenes y cache de build en producción, lo que provocó una cascada de pulls y reinicios durante tráfico pico.

El postmortem no fue sobre “quién tecleó el comando”. Fue sobre estado invisible y suposiciones frágiles.
La solución fue simple y ligeramente humillante: eliminar DOCKER_HOST global, definir contextos y actualizar el prompt del jump host para mostrar docker context show.

También pusieron un pequeño wrapper: los comandos destructivos requerían --context explícito.
No evitó todos los errores, pero detuvo toda esta clase de fallos “creía que estaba en dev”.

Optimización que salió mal: endpoint TCP remoto “por velocidad”

Otra organización quería builds CI más rápidos. Alguien notó que los contextos SSH añaden overhead: establecer sesiones SSH, algo de latencia, rarezas con bastiones.
Así que “optimizaron” exponiendo el demonio de Docker por TCP dentro de la red interna. Estaba detrás de un firewall y prometieron añadir TLS más tarde.

Funcionó. Los builds fueron más rápidos. El equipo celebró aumentando jobs en paralelo, lo que incrementó la carga de la API Docker, lo que aumentó la CPU del demonio y ralentizó todo otra vez.
Mientras tanto, “TLS después” se transformó calladamente en “TLS nunca”, porque la pipeline ya dependía del endpoint inseguro.

El verdadero problema explotó meses después durante un proyecto de segmentación de red.
Cambiaron reglas de firewall y de repente un subconjunto de runners aún podía alcanzar la API de Docker mientras otros no. Los builds se volvieron inestables y los reintentos saturaron más al demonio.
Los ingenieros pasaron días depurando “Docker es poco fiable” cuando el problema era “construiste un demonio remoto central con una dependencia de red frágil”.

La solución eventual no fue glamorosa: volver a contextos SSH para acceso ad-hoc y mover CI a builders por runner o a un clúster de builds bien gestionado.
Los contextos siguieron usándose, pero la estrategia del endpoint cambió. La optimización había apuntado al cuello de botella equivocado.

La lección: ahorrar milisegundos en un canal de control rara vez es el problema real de rendimiento. Normalmente quieres localidad, caching y capacidad—no un puerto de demonio abierto a lo ancho.

Práctica aburrida pero correcta que salvó el día: contextos explícitos en todas partes

Un equipo más maduro tenía una regla: cada comando en el runbook incluía --context. Cada uno.
Era ligeramente molesto. La gente se quejaba. Luego dejaron de notarlo, como los cinturones de seguridad.

Una noche tuvieron un incidente en producción relacionado con un volumen de logs fuera de control que llenó el disco.
El ingeniero on-call tenía staging abierto en una pestaña de terminal y producción en otra. Bajo estrés, eso es receta para errores cross-entorno.

Siguieron el runbook. Cada comando estaba fijo: docker --context prod ....
Inspeccionaron uso de volúmenes, detuvieron el contenedor ruidoso correcto y lo reiniciaron con un límite de logs que ya habían validado en staging.

La mejor parte: durante la revisión post-incident tuvieron logs de terminal y de CI claros que mostraban exactamente qué contexto se usó para cada acción.
No hubo forense conjetural, ni “creo que estaba en prod”. El registro fue claro.

Las prácticas aburridas no dan charlas en conferencias. También mantienen tu empleo.

Guion de diagnóstico rápido (encuentra el cuello de botella rápido)

Cuando “Docker está lento” o “el contexto remoto es inestable”, no cambies ajustes al azar. Triagia como un adulto:
identifica si el cuello de botella es transporte, salud del demonio, registro/pull de imágenes,
almacenamiento o tu propia máquina cliente.

Primero: confirma que estás depurando el objetivo correcto

cr0x@server:~$ docker context show
prod

Decisión: si el contexto no es el entorno que crees, detente. Arregla eso primero. Todo lo demás es ruido.

Segundo: mide la latencia básica del plano de control

cr0x@server:~$ time docker --context prod version
Client: Docker Engine - Community
 Version:           25.0.3
 API version:       1.45
...

real    0m0.612s
user    0m0.078s
sys     0m0.021s

Qué significa: sub-segundo está bien. Varios segundos sugieren problemas con SSH/bastion/DNS o un demonio saturado.

Decisión: si esto está lento, no empieces por “tunear almacenamiento Docker”. Empieza por red/SSH y la carga del demonio.

Tercero: revisar la salud del demonio y la presión de recursos

cr0x@server:~$ docker --context prod info | sed -n '1,35p'
Client:
 Version:    25.0.3
 Context:    prod
 Debug Mode: false

Server:
 Containers: 31
  Running: 27
  Paused: 0
  Stopped: 4
 Images: 42
 Server Version: 25.0.3
 Storage Driver: overlay2
 Logging Driver: json-file
 Cgroup Driver: systemd
...

Decisión: confirma driver de almacenamiento, driver de logging y conteos generales de contenedores/imágenes. Si estos difieren entre entornos, el comportamiento también lo hará.

Cuarto: identifica si el almacenamiento es el verdadero culpable

cr0x@server:~$ docker --context prod system df -v | sed -n '1,40p'
Images space usage:

REPOSITORY        TAG       IMAGE ID       CREATED        SIZE      SHARED SIZE   UNIQUE SIZE   CONTAINERS
registry/app      web       2aa1b3c4d5e6   3 hours ago    412.5MB   0B           412.5MB       4
...

Decisión: si hay mucho churn de imágenes, enfócate en la velocidad de pull, rendimiento del registry y caching.
Si dominan los volúmenes, enfócate en ciclo de vida/retención y rendimiento del sistema de ficheros.

Quinto: observa churn y reinicios de contenedores

cr0x@server:~$ docker --context prod ps --format 'table {{.Names}}\t{{.Status}}\t{{.RunningFor}}'
NAMES        STATUS                     RUNNING FOR
app-web-1    Up 3 hours (healthy)       3 hours
app-db-1     Up 3 hours                 3 hours
worker-1     Restarting (1) 5 seconds   8 minutes

Decisión: los bucles de reinicio harán que todo parezca “lento” porque el demonio está constantemente creando/ destruyendo recursos.
Arregla el contenedor que falla antes de tocar otra cosa.

Sexto: si los contextos SSH fallan intermitentemente, aisla SSH

cr0x@server:~$ ssh -o ServerAliveInterval=5 -o ServerAliveCountMax=2 deploy@prod-01 'echo ok'
ok

Decisión: si esto se cae, tu problema es ruta de red, estabilidad del bastion o timeouts SSH—no contextos Docker.

Errores comunes: síntoma → causa raíz → solución

Mistake 1: “Desplegué en el host equivocado”

Síntoma: cambios en staging aparecen en prod, o los cambios en prod no aparecen donde esperas.

Causa raíz: contexto actual implícito, o DOCKER_HOST anulando lo que creías activo.

Solución: usa docker --context ... en runbooks/scripts; elimina DOCKER_HOST global; muestra el contexto en el prompt; exige selección explícita para acciones destructivas.

Mistake 2: “docker context use prod” funciona, pero los comandos se cuelgan

Síntoma: el CLI se queda en espera en ps, info, pull o compose up.

Causa raíz: problema en la ruta SSH (bastion, DNS, MTU), o demonio remoto con presión de CPU/memoria.

Solución: temporiza docker --context prod version; prueba SSH simple; verifica carga del host remoto; considera keepalives SSH.

Mistake 3: “Dice permiso denegado al demonio de Docker”

Síntoma: errores de contexto remoto como “permission denied while trying to connect to the Docker daemon socket.”

Causa raíz: el usuario remoto puede SSH pero no puede acceder a /var/run/docker.sock (no está en el grupo docker, desajuste con demonio rootless).

Solución: otorga la membresía de grupo correcta (con cuidado), o usa endpoints de Docker rootless intencionalmente; valida con ssh host 'docker ps'.

Mistake 4: “Mi contexto apunta a un host antiguo tras un rebuild”

Síntoma: el contexto conecta, pero ves un demonio vacío o nombre/versión inesperados.

Causa raíz: hostname reciclado, DNS actualizado o una nueva instancia tomó ese nombre.

Solución: valida identidad con docker info --format 'Name=...'; fija la configuración SSH a claves de host estables; actualiza contextos tras reconstrucciones.

Mistake 5: “Contexto exportado funciona en un portátil pero no en otro”

Síntoma: el contexto importado existe pero falla al conectar vía SSH.

Causa raíz: el agente/las claves SSH difieren, la configuración SSH difiere o hay mismatch en known_hosts.

Solución: asegúrate de que la máquina importadora tenga la identidad y configuración SSH correctas; prueba con SSH simple; evita depender de agent-forwarding en automatización.

Mistake 6: “Compose se comporta distinto que docker”

Síntoma: docker ps muestra un entorno, pero docker compose afecta a otro (o viceversa).

Causa raíz: uso mixto de DOCKER_HOST, DOCKER_CONTEXT y “contexto actual”, o ejecutar shells diferentes con vars de entorno distintas.

Solución: estandariza en --context; imprime vars de entorno en shells de depuración; evita exportar DOCKER_HOST en perfiles.

Mistake 7: “La lista de contextos muestra ERROR”

Síntoma: docker context ls muestra una columna de error para un contexto.

Causa raíz: endpoint inalcanzable o mal configurado; el CLI de Docker intentó validar.

Solución: inspecciona el contexto; prueba SSH; recrea el contexto si la cadena del endpoint es incorrecta; elimina contextos obsoletos.

Mistake 8: “Usamos TCP porque es interno; ahora seguridad está molesta”

Síntoma: hallazgo de auditoría: API de Docker expuesta sin TLS, reglas de firewall amplias, acceso al demonio misterioso.

Causa raíz: diseño de endpoint impulsado por conveniencia; API remota tratada como un servicio inofensivo.

Solución: pasa a contextos SSH o endpoints autenticados con TLS; restringe rutas de red; rota credenciales; trata el acceso al demonio como privilegiado.

Listas de verificación / plan paso a paso

Paso a paso: adoptar contextos sin romper a nadie

  1. Inventario de endpoints: lista dónde aterrizan actualmente los comandos Docker (portátiles de desarrolladores, jump hosts, runners CI).
  2. Eliminar globals ocultos: busca DOCKER_HOST en perfiles de shell e inyecciones de entorno en CI.
  3. Crear contextos nombrados: al menos dev, staging, prod. Usa endpoints SSH.
  4. Añadir descripciones: incluye entorno, región y responsabilidad. Los humanos se confunden; la metadata ayuda.
  5. Estandarizar runbooks: actualiza cada comando para incluir --context.
  6. Hacer visible el contexto: integración prompt/tmux para que siempre veas dónde estás.
  7. Definir procedimiento de emergencia: flujo de exportar/importar contextos, manejo de credenciales y expectativas de auditoría.
  8. Disciplina CI/CD: usa contextos explícitos o endpoints explícitos por job; evita depender del “contexto actual”.
  9. Rotar accesos: si históricamente usaste TCP sin TLS, considera eso comprometido y rota en consecuencia.
  10. Entrenar con simulacros: practica “cambiar contexto, verificar identidad, ejecutar comando” hasta que sea automático.

Checklist operacional: antes de ejecutar algo riesgoso

  • Ejecuta docker context show.
  • Ejecuta docker info --format 'Name={{.Name}}' contra ese contexto.
  • Confirma que estás en la pestaña/ventana de terminal correcta (suena tonto; evita outages).
  • Prefiere docker --context X ... para el comando riesgoso.
  • Para limpieza: inspecciona docker system df primero; no hagas prune a lo loco.

Checklist de hardening: hacer más difícil hacer lo incorrecto

  • Cuentas separadas para deploy vs admin; no uses claves SSH personales para automatización.
  • Usa contextos SSH; evita exponer la API TCP de Docker.
  • Forzar verificación de clave de host; gestionar known_hosts centralmente donde proceda.
  • Limitar quién puede alcanzar hosts Docker (ACLs de red, bastiones, proxies con identidad).
  • Auditar la membresía del grupo docker como auditas sudoers.

Preguntas frecuentes

1) ¿Un contexto Docker es lo mismo que un contexto de Kubernetes?

No. Los contextos de Kubernetes viven en kubeconfig y seleccionan clústeres/namespaces/usuarios para kubectl.
Los contextos Docker seleccionan endpoints del CLI de Docker (socket local, SSH, TCP/TLS). Idea similar, universo distinto.

2) ¿Debo usar docker context o DOCKER_HOST?

Usa contextos para humanos y la mayoría de la automatización. Reserva DOCKER_HOST para herramientas legacy que no puedes cambiar,
y mantenlo estrictamente por proceso, no exportado en perfiles.

3) ¿Cuál es la forma más segura de gestionar acceso a producción?

Usa contextos SSH con una cuenta deploy dedicada, rutas de red restringidas (bastion) y gestión de claves auditada.
Haz que cada comando prod sea explícito via --context prod.

4) ¿Por qué Docker sobre SSH a veces se siente más lento?

Porque hace más trabajo: establecimiento SSH, autenticación y una conexión stdio tunelizada. Si la latencia es dolorosa,
la solución real suele ser mejores rutas de red, control masters persistentes o mover cargas de build más cerca del demonio.

5) ¿Puedo almacenar contextos en git?

No comites contextos exportados. Pueden incluir material TLS sensible y endpoints operativos.
En su lugar, guarda las instrucciones para crear contextos y généralos en tiempo de ejecución.

6) ¿Cómo evito sorpresas de “contexto actual” en scripts?

Usa docker --context NAME en cada comando. Los scripts deben ser deterministas.
Si un script depende del estado interactivo de tu shell, no es un script; es una sugerencia.

7) ¿Los contextos Docker pueden ayudar con rollouts multi-región?

Sí. Crea contextos como prod-eu-west y prod-us-east, luego ejecuta los mismos comandos contra cada uno explícitamente.
Solo sé disciplinado con el nombrado y las comprobaciones de identidad.

8) ¿Por qué docker context ls muestra error en un contexto?

El endpoint es inalcanzable o está mal configurado, o Docker intentó y falló en una prueba de conexión.
Inspecciona el contexto, valida SSH manualmente y recréalo si la cadena del host es incorrecta.

9) ¿Los contextos se comparten entre usuarios en la misma máquina?

Usualmente no. Los contextos se almacenan en el directorio de configuración de Docker del usuario. Eso es bueno: evita acoplamientos sorpresa entre usuarios.
También significa que debes gestionarlos por cuenta en jump boxes compartidos.

10) ¿Usar contextos hace que Docker sea “seguro” para hosts multi-tenant?

Los contextos son un selector del lado cliente. La seguridad multi-tenant depende de la configuración del demonio, del aislamiento del kernel y del control de acceso.
Los contextos te ayudan a evitar errores de operador; no proveen aislamiento entre tenants.

Próximos pasos que deberías hacer esta semana

Si operas más de un host Docker, ya estás en la tierra de “múltiples destinos”. Compórtate en consecuencia.

  1. Crea contextos para cada entorno usando endpoints SSH. Nómbralos claramente.
  2. Haz visible el contexto en tu prompt de shell o en la barra de estado de tmux.
  3. Actualiza runbooks para que cada comando fije --context.
  4. Elimina DOCKER_HOST global de entornos interactivos y jump hosts compartidos.
  5. Añade un preflight de identidad: docker --context X info --format 'Name={{.Name}} Server={{.ServerVersion}}'.
  6. Audita accesos: quién puede alcanzar hosts prod, quién está en el grupo docker y cómo se gestionan las claves.

Los contextos no evitarán todos los fallos. Evitarán los más tontos. Esos son los que aparecen a las 2 a.m.

← Anterior
Cuellos de botella sin histeria: Cómo medir el límite real
Siguiente →
Servidores en armarios calientes: cómo la “sin ventilación” mata la disponibilidad

Deja un comentario