Durante un incidente, siempre alguien hace la misma pregunta: ¿qué versión se está ejecutando de verdad? La respuesta suele empezar con un tag, como api:1.8.2, seguido de una larga pausa mientras se averigua si ese tag se reconstruyó, de qué commit salió y si la corrección que se fusionó ayer está dentro.
No tiene por qué ser una pausa. Con unas pocas convenciones en la build, cualquier contenedor en ejecución puede rastrearse hasta el commit, la ejecución de build y la pull request que lo produjeron.
Las etiquetas describen la intención, no la realidad
Un tag es un puntero. Se puede volver a empujar, sobrescribir con un rebuild o compartir entre varias imágenes con el tiempo. Dos clústeres que ejecutan api:1.8.2 pueden estar ejecutando código distinto. Un digest, el hash sha256: del manifiesto de la imagen, nombra una imagen exacta y nada más.
Kubernetes registra el digest resuelto aunque el manifest usara un tag, así que siempre puedes ver qué se descargó de verdad:
kubectl get pods -n prod \
-o custom-columns=POD:.metadata.name,IMAGE:.status.containerStatuses[0].imageID
La traza, paso a paso
- PodLee el digest de la imagen en el estado del contenedor.
- ImagenLee las etiquetas OCI o la provenance asociadas a ese digest.
- CommitLa etiqueta de revisión indica el commit exacto en el repositorio de código.
- BuildEl commit lleva a la ejecución de CI que construyó y subió la imagen.
- Pull requestEl commit lleva al cambio revisado que lo mergeó.
Cada enlace es barato de añadir. La cadena solo se rompe cuando falta uno.
La misma traza funciona en sentido inverso, que a menudo es la pregunta más útil. Cuando se hace merge de una corrección, quieres saber qué entornos la están ejecutando ya. Partiendo del commit, encuentra el digest que produjo el build y busca en los clústeres pods que ejecuten ese digest. Si no hay ninguno, la corrección no se ha desplegado, digan lo que digan las notas de release. Los equipos que saben responder rápido en ambas direcciones suelen notar que las llamadas de incidente se acortan y que los rollbacks dan menos miedo.
Meter el código fuente en la imagen
La especificación de imágenes OCI define claves de anotación estándar justo para esto. Fíjalas en el build:
docker build \
--label org.opencontainers.image.source=https://github.com/acme/api \
--label org.opencontainers.image.revision=$(git rev-parse HEAD) \
--label org.opencontainers.image.created=$(date -u +%Y-%m-%dT%H:%M:%SZ) \
-t ghcr.io/acme/api:1.8.2 .
En GitHub Actions, la docker/metadata-action genera estas etiquetas por ti. Leerlas de vuelta desde cualquier digest lleva un solo comando:
crane config ghcr.io/acme/api@sha256:<digest> | jq '.config.Labels'
Las etiquetas son fáciles, pero cualquiera que pueda construir una imagen puede ponerlas. Trátalas como una pista útil, y usa provenance firmada cuando la respuesta tenga que resistir un escrutinio.
Haz que el rastro sea fiable
La procedencia del build (build provenance) es una declaración firmada de la plataforma de CI que dice qué repositorio, commit y workflow produjo un digest. A diferencia de una etiqueta, no la pueden escribir los propios pasos del build, así que se sostiene incluso si alguien intenta hacer pasar por propia una imagen construida en otro sitio.
gh attestation verify oci://ghcr.io/acme/api@sha256:<digest> --repo acme/api
# from the commit, find the build run and the pull request
gh run list --repo acme/api --commit <commit-sha>
gh api repos/acme/api/commits/<commit-sha>/pulls --jq '.[].html_url'
Qué rompe el rastro
- Imágenes construidas en portátiles. Sin ejecución de CI, sin provenance, a menudo sin etiquetas. Bloquea los push a registries de producción desde cualquier sitio que no sea CI.
- Retag entre entornos. Promocionar reconstruyendo crea un digest nuevo. Promociona el mismo digest.
- Squash merges sin enlaces. El commit en main debe seguir apuntando a su pull request. La mayoría de los hosts lo gestionan, pero revisa tu configuración de merge.
- Deriva de la imagen base. Tu commit no ha cambiado, pero la base sí. Fija las imágenes base por digest y deja que el bot de actualizaciones proponga el cambio.
Compruébalo en 10 minutos
- Elige un servicio en producción y lista los digests de sus imágenes en ejecución con el comando
kubectlde arriba. - Ejecuta
crane configsobre un digest. ¿Hay una etiqueta de revisión? - Sigue ese commit hasta una ejecución de build y una pull request. Mide cuánto tarda.
- Comprueba si tus manifiestos de deploy usan tags o digests.
- Comprueba si alguien fuera de CI puede hacer push al registry de producción.
Si el rastreo tarda más de unos minutos, o se queda en el segundo paso, sabes qué eslabón añadir primero.
Dónde encaja en la línea
En la torre (en desarrollo):