Ir al contenido

Docker Compose

Compose es la forma más directa de ejecutar el Agent en un solo host (una VM dedicada, por ejemplo). Para orquestadores, consulta Kubernetes o la configuración de producción.

services:
whystack-agent:
image: <address>/whystack-agent@sha256:<digest>
restart: unless-stopped
stop_grace_period: 150s
volumes:
- whystack-identity:/identity
environment:
RUNNER_TUNNEL_URL: wss://tunnel.whystack.ai:8443
RUNNER_ENROLL_URL: https://app.whystack.ai
RUNNER_ATTESTATION_MODE: bootstrap-token
RUNNER_BOOTSTRAP_TOKEN: ${RUNNER_BOOTSTRAP_TOKEN}
RUNNER_IDENTITY_DIR: /identity
RUNNER_INSTANCE_ID: agent-prod-1
RUNNER_CONNECTORS: real
RUNNER_SECRET_STORE_MODE: aws
NODE_ENV: production
volumes:
whystack-identity:

stop_grace_period mayor que RUNNER_DRAIN_TIMEOUT_MS (2 minutos por defecto), o el SIGKILL llega a mitad del drenaje.

La imagen declara un HEALTHCHECK de liveness (whystack-health live) sin ningún puerto: el Agent escribe la señal en un archivo y Docker la lee mediante exec. docker compose ps muestra healthy cuando el proceso está vivo, lo que no significa que esté conectado. Para saber si está sirviendo:

Ventana de terminal
docker compose exec whystack-agent whystack-health ready

Si ejecutas el contenedor con read_only: true, monta tmpfs: [/tmp]: ahí es donde se escribe la señal (RUNNER_HEALTH_DIR).

La identidad necesita un volumen con escritura

Sección titulada «La identidad necesita un volumen con escritura»

Es lo que hace que docker compose pull && up -d no cueste un bootstrap token nuevo. Sin él, la identidad muere con el contenedor y el token anterior ya está gastado: es de un solo uso.

Prefiere un volumen con nombre, como en el ejemplo de arriba. La imagen ya trae /identity con propietario uid 1000 (se ejecuta como no root), y un volumen con nombre hereda esa propiedad al crearse, así que funciona sin que tengas que hacer nada.

Un bind mount (./identity:/identity) no hereda nada: ahí la propiedad es la del directorio del host, y tiene que pertenecer al uid 1000. Nunca lo montes con :ro.

Si el directorio no tiene permiso de escritura, el boot no falla. El Agent avisa y sigue, y solo te enteras en el siguiente deploy:

could not persist identity — next boot will re-enroll from scratch

Los certificados, si usas un certificado aprovisionado

Sección titulada «Los certificados, si usas un certificado aprovisionado»
volumes:
- ./certs:/certs:ro

El directorio debe contener runner.crt, runner.key y ca.pem. El entrypoint los lee y los exporta como RUNNER_CERT_PEM, RUNNER_KEY_PEM y RUNNER_CA_PEM. El Agent lee el PEM inline, no como ruta, y montar el directorio evita la pesadilla de PEMs multilínea dentro de un env_file.

Ese camino ignora el enrollment y la identidad persistida: los dos bloques de arriba dejan de aplicar, y el certificado no se renueva. Consulta Identidad y enrollment.

Si el Agent corre en EC2 y usa la cadena de credenciales

Sección titulada «Si el Agent corre en EC2 y usa la cadena de credenciales»

Con RUNNER_AWS_CRED_MODE=chain, la cadena por defecto del SDK resuelve a través del instance profile sin ningún volumen extra. Si en cambio usas un perfil local basado en archivo, móntalo:

volumes:
- ${HOME}/.aws:/home/node/.aws:ro

Sin ese volumen el contenedor no ve el perfil, y la cadena falla sin decir por qué: el clásico “funciona en mi terminal, pero no en el contenedor”.