Buildx 0.38.0 ordena los builds de Laravel con Bake
Docker Buildx añade control de concurrencia y fallos en Bake para publicar imágenes de Laravel con más criterio.
Cuatro imágenes no deberían comportarse como cuatro scripts aislados
Una aplicación Laravel autoalojada suele acabar separando PHP-FPM, workers de cola, scheduler y frontend en imágenes distintas. Tiene sentido: cada proceso tiene dependencias, ciclos de despliegue y necesidades de ejecución diferentes. El problema aparece cuando construirlas y publicarlas depende de varios comandos sueltos, con concurrencia sin controlar y resultados difíciles de interpretar cuando algo falla.
Bake permite describir esos objetivos en un único archivo y ejecutarlos como un conjunto. Hasta ahora, lanzar varios targets implicaba aceptar la ejecución paralela sin demasiados matices desde la línea de comandos. Buildx 0.38.0 incorpora --jobs —también disponible como -j— para decidir cuántos targets de Bake pueden correr a la vez y cómo se gestiona un fallo. (github.com)
Un Bake para la aplicación y sus procesos
En mi caso, partiría de un docker-bake.hcl que represente los artefactos que realmente voy a desplegar. No hace falta duplicar toda la definición si PHP-FPM, workers y scheduler nacen del mismo Dockerfile: pueden compartir contexto, etiquetas y argumentos, cambiando solo el target del Dockerfile cuando sea necesario. El frontend puede tener su propia receta, pero sigue formando parte del mismo grupo de publicación.
group "app" {
targets = ["php", "worker", "scheduler", "frontend"]
}
target "php" {
context = "."
dockerfile = "Dockerfile"
target = "php-fpm"
tags = ["registry.example.com/mi-laravel/php:latest"]
output = ["type=registry"]
}
target "worker" {
context = "."
dockerfile = "Dockerfile"
target = "worker"
tags = ["registry.example.com/mi-laravel/worker:latest"]
output = ["type=registry"]
}
target "scheduler" {
context = "."
dockerfile = "Dockerfile"
target = "scheduler"
tags = ["registry.example.com/mi-laravel/scheduler:latest"]
output = ["type=registry"]
}
target "frontend" {
context = "."
dockerfile = "Dockerfile.frontend"
tags = ["registry.example.com/mi-laravel/frontend:latest"]
output = ["type=registry"]
}La ventaja no es solo tener un archivo más ordenado. El grupo app expresa que las cuatro imágenes pertenecen al mismo despliegue, aunque internamente sus builds sean independientes. Eso facilita ejecutar una comprobación local, integrar el mismo comando en CI y evitar que el pipeline tenga reglas repartidas entre scripts de shell.
Limitar la presión sobre el builder
La forma más sencilla de usar la nueva opción es indicar un número. Con -j=2, Bake programa como máximo dos targets simultáneos; es equivalente a --jobs=parallel=2. Esto limita la concurrencia entre targets, no el paralelismo interno con el que BuildKit resuelve los pasos de cada build. (docs.docker.com)
docker buildx bake app -j=2En un homelab o en un runner pequeño, este límite es útil cuando las cuatro imágenes compiten por CPU, memoria, caché de capas o acceso al registro. No convierte el equipo en más rápido, pero evita que un build de frontend y tres imágenes PHP saturen el builder a la vez. El valor correcto depende del hardware y de lo pesados que sean los Dockerfiles, así que conviene elegirlo observando el consumo real, no copiando un número arbitrario.
Elegir qué ocurre si algo falla
El modo predeterminado es fail-fast. Si un target falla, Bake detiene el conjunto y cancela los targets que sigan en marcha; sin embargo, las salidas que ya hayan sido escritas por targets correctos no se eliminan. Es una opción razonable para detectar pronto un error, pero no garantiza que el registro quede sin imágenes nuevas de una ejecución fallida. (docs.docker.com)
defer-error hace lo contrario en los targets independientes: deja que terminen aunque otro haya fallado y devuelve el error al final. Puede ser práctico en CI cuando quiero conocer todos los fallos posibles de una tanda, o aprovechar builds que no dependían del target roto. Sus resultados correctos pueden exportarse, así que no lo usaría como mecanismo para mantener una publicación estrictamente coherente.
docker buildx bake app --jobs=defer-error,parallel=2Para publicar las cuatro imágenes como una unidad lógica, el modo interesante es defer-output. Bake espera a que todos los targets participantes hayan evaluado correctamente sus resultados antes de que cualquiera empiece a exportar su salida. Si falla la construcción de worker, por ejemplo, se retiene la exportación de PHP-FPM, scheduler y frontend. (docs.docker.com)
docker buildx bake app --jobs=defer-outputLa letra pequeña de defer-output
defer-output no es una transacción de registro ni un mecanismo de rollback. Si un export falla después de que otros ya hayan terminado, Buildx no deshace las salidas completadas y todavía puede haber artefactos parciales. También hay una frontera importante en builds multinodo: la sincronización cubre la resolución y exportación de cada target, pero no todas las operaciones posteriores que Buildx pueda realizar al combinar manifiestos o publicar en un registro. (docs.docker.com)
Tampoco sirve para reducir la concurrencia a cualquier valor. Con defer-output, todos los targets participantes deben poder llegar juntos a la frontera de salida, por lo que Bake rechaza un límite de trabajos distinto de cero que sea menor que el número de targets participantes. La misma restricción puede afectar a targets enlazados y a targets implícitos usados mediante contextos target:. (docs.docker.com)
Esto no sustituye etiquetas inmutables, pruebas, escaneo de imágenes ni una promoción entre entornos. Para un despliegue serio, seguiría etiquetando cada imagen con el commit o la versión publicada y usaría latest solo como referencia adicional si la necesito. Lo que aporta --jobs es una política clara para la construcción y la exportación conjunta, no una solución completa de release management.
Un cambio pequeño que mejora el contrato del pipeline
La combinación que usaría para una aplicación Laravel con imágenes relacionadas es sencilla: un grupo Bake, tags coherentes y --jobs=defer-output en el paso que publica. Si el builder necesita proteger recursos, primero ajustaría el diseño de los targets o el entorno de CI, porque limitar trabajos por debajo del número de targets no encaja con ese modo. Para ejecuciones de diagnóstico donde interesa recoger todos los errores, cambiaría deliberadamente a defer-error.
No es una función que vaya a simplificar un Dockerfile mal planteado. Sí evita que la política de concurrencia y de fallos quede implícita en el comportamiento por defecto o escondida en un script. Para proyectos autoalojados, donde un registro privado y un builder comparten recursos limitados, dejar esa decisión escrita en el comando de Bake es una mejora concreta.
Fuente: Buildx v0.38.0 · Documentación oficial: docker buildx bake (github.com)