Contexto de compilación
Los comandos docker build y docker buildx build compilan imágenes de Docker a partir de
un
Dockerfile y un contexto.
¿Qué es un contexto de compilación?
El contexto de compilación es el conjunto de archivos a los que puede acceder tu compilación. El argumento posicional que pasas al comando de compilación especifica el contexto que deseas utilizar para la compilación:
$ docker build [OPTIONS] PATH | URL | -
^^^^^^^^^^^^^^
Puedes pasar cualquiera de las siguientes entradas como contexto para una compilación:
- La ruta relativa o absoluta a un directorio local
- Una URL remota de un repositorio Git, archivo tar (tarball) o archivo de texto plano
- Un archivo de texto plano o archivo tar canalizado al comando
docker builda través de la entrada estándar (stdin)
Contextos de sistema de archivos
Cuando tu contexto de compilación es un directorio local, un repositorio Git remoto o un archivo tar,
se convierte en el conjunto de archivos a los que el constructor puede acceder durante la
compilación. Las instrucciones de compilación como COPY y ADD pueden hacer referencia a cualquiera de los
archivos y directorios del contexto.
Un contexto de compilación del sistema de archivos se procesa de forma recursiva:
- Cuando especificas un directorio local o un archivo tar, se incluyen todos los subdirectorios.
- Cuando especificas un repositorio Git remoto, se incluyen el repositorio y todos los submódulos.
Para obtener más información sobre los diferentes tipos de contextos de sistema de archivos que puedes usar con tus compilaciones, consulta:
Contextos de archivo de texto
Cuando tu contexto de compilación es un archivo de texto plano, el constructor interpreta el archivo como un Dockerfile. Con este enfoque, la compilación no utiliza un contexto de sistema de archivos.
Para obtener más información, consulta contexto de compilación vacío.
Contexto local
Para usar un contexto de compilación local, puedes especificar una ruta de archivo relativa o absoluta
para el comando docker build. El siguiente ejemplo muestra un comando de compilación que
utiliza el directorio actual (.) como contexto de compilación:
$ docker build .
...
#16 [internal] load build context
#16 sha256:23ca2f94460dcbaf5b3c3edbaaa933281a4e0ea3d92fe295193e4df44dc68f85
#16 transferring context: 13.16MB 2.2s done
...
Esto hace que los archivos y directorios en el directorio de trabajo actual estén disponibles para el constructor. El constructor carga los archivos que necesita desde el contexto de compilación cuando es necesario.
También puedes utilizar archivos tar locales como contexto de compilación, canalizando el contenido
del archivo tar al comando docker build. Consulta Archivos tar.
Directorios locales
Considera la siguiente estructura de directorios:
.
├── index.ts
├── src/
├── Dockerfile
├── package.json
└── package-lock.jsonLas instrucciones del Dockerfile pueden hacer referencia e incluir estos archivos en la compilación si pasas este directorio como contexto.
# syntax=docker/dockerfile:1
FROM node:latest
WORKDIR /src
COPY package.json package-lock.json .
RUN npm ci
COPY index.ts src .$ docker build .
Contexto local con Dockerfile desde stdin
Utiliza la siguiente sintaxis para compilar una imagen utilizando archivos de tu sistema de archivos local, mientras usas un Dockerfile desde stdin.
$ docker build -f- <PATH>
La sintaxis utiliza la opción -f (o --file) para especificar el Dockerfile a utilizar, y utiliza un guion (-) como nombre de archivo para indicarle a Docker que lea el Dockerfile desde stdin.
El siguiente ejemplo utiliza el directorio actual (.) como contexto de compilación y compila una imagen utilizando un Dockerfile pasado a través de stdin usando un here-document.
# crea un directorio para trabajar
mkdir example
cd example
# crea un archivo de ejemplo
touch somefile.txt
# compila una imagen usando el directorio actual como contexto
# y un Dockerfile pasado a través de stdin
docker build -t myimage:latest -f- . <<EOF
FROM busybox
COPY somefile.txt ./
RUN cat /somefile.txt
EOFArchivos tar locales
Cuando canalizas un archivo tar al comando de compilación, la compilación utiliza el contenido del archivo tar como un contexto de sistema de archivos.
Por ejemplo, dado el siguiente directorio de proyecto:
.
├── Dockerfile
├── Makefile
├── README.md
├── main.c
├── scripts
├── src
└── test.DockerfilePuedes crear un archivo tar del directorio y canalizarlo a la compilación para usarlo como contexto:
$ tar czf foo.tar.gz *
$ docker build - < foo.tar.gz
La compilación resuelve el Dockerfile a partir del contexto del archivo tar. Puedes usar la
bandera --file para especificar el nombre y la ubicación del Dockerfile en relación con
la raíz del archivo tar. El siguiente comando compila utilizando test.Dockerfile
en el archivo tar:
$ docker build --file test.Dockerfile - < foo.tar.gz
Contexto remoto
Puedes especificar la dirección de un repositorio Git remoto, un archivo tar o un archivo de texto plano como tu contexto de compilación.
- Para repositorios Git, el constructor clona automáticamente el repositorio. Consulta Repositorios Git.
- Para archivos tar, el constructor descarga y extrae el contenido del archivo tar. Consulta Archivos tar.
Si el archivo tar remoto es un archivo de texto, el constructor no recibe ningún contexto de sistema de archivos y, en su lugar, asume que el archivo remoto es un Dockerfile. Consulta Contexto de compilación vacío.
Repositorios Git
Cuando pasas una URL que apunta a la ubicación de un Git como argumento
a docker build, el constructor utiliza el repositorio como contexto de compilación.
El constructor realiza una clonación superficial (shallow clone) del repositorio, descargando solo la confirmación HEAD, no todo el historial.
El constructor clona de forma recursiva el repositorio y cualquier submódulo que contenga.
$ docker build https://github.com/user/myrepo.git
Por defecto, el constructor clona la última confirmación en la rama predeterminada del repositorio que especifiques.
Fragmentos de URL
Puedes añadir fragmentos de URL a la dirección del repositorio Git para que el constructor clone una rama, etiqueta y subdirectorio específicos de un repositorio.
El formato del fragmento de URL es #ref:dir, donde:
refes el nombre de la rama, etiqueta o hash de la confirmacióndires un subdirectorio dentro del repositorio
Por ejemplo, el siguiente comando utiliza la rama container y
el subdirectorio docker en esa rama como contexto de compilación:
$ docker build https://github.com/user/myrepo.git#container:docker
La siguiente tabla representa todos los sufijos válidos con sus contextos de compilación:
| Sufijo de sintaxis de compilación | Confirmación utilizada | Contexto de compilación utilizado |
|---|---|---|
myrepo.git | refs/heads/<rama predeterminada> | / |
myrepo.git#mytag | refs/tags/mytag | / |
myrepo.git#mybranch | refs/heads/mybranch | / |
myrepo.git#pull/42/head | refs/pull/42/head | / |
myrepo.git#:myfolder | refs/heads/<rama predeterminada> | /myfolder |
myrepo.git#master:myfolder | refs/heads/master | /myfolder |
myrepo.git#mytag:myfolder | refs/tags/mytag | /myfolder |
myrepo.git#mybranch:myfolder | refs/heads/mybranch | /myfolder |
Cuando utilices un hash de confirmación como ref en el fragmento de URL, utiliza el hash
completo de 40 caracteres SHA-1 de la confirmación. No se admite un hash corto, por ejemplo, un hash
truncado a 7 caracteres.
# ✅ Esto funciona:
docker build github.com/docker/buildx#d4f088e689b41353d74f1a0bfcd6d7c0b213aed2
# ❌ Esto no funciona porque el hash de confirmación está truncado:
docker build github.com/docker/buildx#d4f088eConsultas de URL
Las consultas de URL son más estructuradas y recomendadas sobre los fragmentos de URL:
$ docker buildx build 'https://github.com/user/myrepo.git?branch=container&subdir=docker'
| Sufijo de sintaxis de compilación | Confirmación utilizada | Contexto de compilación utilizado |
|---|---|---|
myrepo.git | refs/heads/<rama predeterminada> | / |
myrepo.git?tag=mytag | refs/tags/mytag | / |
myrepo.git?branch=mybranch | refs/heads/mybranch | / |
myrepo.git?ref=pull/42/head | refs/pull/42/head | / |
myrepo.git?subdir=myfolder | refs/heads/<rama predeterminada> | /myfolder |
myrepo.git?branch=master&subdir=myfolder | refs/heads/master | /myfolder |
myrepo.git?tag=mytag&subdir=myfolder | refs/tags/mytag | /myfolder |
myrepo.git?branch=mybranch&subdir=myfolder | refs/heads/mybranch | /myfolder |
Se puede especificar un hash de confirmación como una consulta checksum (alias commit), junto con
consultas de tag, branch o ref para verificar que la referencia se resuelva en la
confirmación esperada:
$ docker buildx build 'https://github.com/moby/buildkit.git?tag=v0.21.1&checksum=66735c67'
Si no coincide, la compilación falla:
$ docker buildx build 'https://github.com/user/myrepo.git?tag=v0.1.0&commit=deadbeef'
...
#3 [internal] load git source https://github.com/user/myrepo.git?tag=v0.1.0-rc1&commit=deadbeef
#3 0.484 bb41e835b6c3523c7c45b248cf4b45e7f862bc42 refs/tags/v0.1.0
#3 ERROR: expected checksum to match deadbeef, got bb41e835b6c3523c7c45b248cf4b45e7f862bc42
NoteEl hash de confirmación corto es compatible con la consulta
checksum(aliascommit), pero pararef, solo se admite el hash completo de la confirmación.
Mantener el directorio .git
Por defecto, BuildKit no mantiene el directorio .git cuando utiliza contextos de Git.
Puedes configurar BuildKit para que mantenga el directorio estableciendo el
argumento de compilación BUILDKIT_CONTEXT_KEEP_GIT_DIR.
Esto puede ser útil si deseas obtener información de Git durante tu compilación:
# syntax=docker/dockerfile:1
FROM alpine
WORKDIR /src
RUN --mount=target=. \
make REVISION=$(git rev-parse HEAD) build$ docker build \
--build-arg BUILDKIT_CONTEXT_KEEP_GIT_DIR=1 \
https://github.com/user/myrepo.git#main
Repositorios privados
Cuando especificas un contexto de Git que también es un repositorio privado, el constructor necesita que proporciones las credenciales de autenticación necesarias. Puedes usar autenticación basada en SSH o en tokens.
Buildx detecta y utiliza automáticamente las credenciales SSH si el contexto de Git que
especificas es una dirección SSH o Git. Por defecto, esto utiliza $SSH_AUTH_SOCK.
Puedes configurar las credenciales SSH a usar con la
bandera --ssh.
$ docker buildx build --ssh default [email protected]:user/private.git
Si deseas utilizar la autenticación basada en tokens en su lugar, puedes pasar el token
utilizando la
bandera --secret.
$ GIT_AUTH_TOKEN=<token> docker buildx build \
--secret id=GIT_AUTH_TOKEN \
https://github.com/user/private.git
NoteNo utilices
--build-argpara secretos.
Contexto remoto con Dockerfile desde stdin
Utiliza la siguiente sintaxis para compilar una imagen utilizando archivos de tu sistema de archivos local, mientras usas un Dockerfile desde stdin.
$ docker build -f- <URL>
La sintaxis utiliza la opción -f (o --file) para especificar el Dockerfile a utilizar, y utiliza un guion (-) como nombre de archivo para indicarle a Docker que lea el Dockerfile desde stdin.
Esto puede ser útil en situaciones en las que deseas compilar una imagen desde un repositorio que no contiene un Dockerfile. O si deseas compilar con un Dockerfile personalizado, sin mantener tu propia bifurcación (fork) del repositorio.
El siguiente ejemplo compila una imagen utilizando un Dockerfile desde stdin y añade
el archivo hello.c del repositorio hello-world
en GitHub.
docker build -t myimage:latest -f- https://github.com/docker-library/hello-world.git <<EOF
FROM busybox
COPY hello.c ./
EOFArchivos tar remotos
Si pasas la URL a un archivo tar remoto, la URL misma se envía al constructor.
$ docker build http://server/context.tar.gz
#1 [internal] load remote build context
#1 DONE 0.2s
#2 copy /context /
#2 DONE 0.1s
...
La operación de descarga se realizará en el host donde se está ejecutando el demonio BuildKit.
Ten en cuenta que si estás utilizando un contexto de Docker remoto o un constructor
remoto, esa no es necesariamente la misma máquina desde donde emites el comando de compilación.
BuildKit descarga el context.tar.gz y lo utiliza como el contexto de compilación.
Los contextos de archivos tar deben ser archivos tar que cumplan con el formato estándar tar
de Unix y pueden estar comprimidos con cualquiera de los formatos xz, bzip2, gzip o
identity (sin compresión).
Contexto vacío
Cuando utilizas un archivo de texto como contexto de compilación, el constructor interpreta el archivo como un Dockerfile. Usar un archivo de texto como contexto significa que la compilación no tiene contexto de sistema de archivos.
Puedes compilar con un contexto de compilación vacío cuando tu Dockerfile no depende de ningún archivo local.
Cómo compilar sin contexto
Puedes pasar el archivo de texto utilizando un flujo de entrada estándar o apuntando a la URL de un archivo de texto remoto.
$ docker build - < Dockerfile
Get-Content Dockerfile | docker build -docker build -t myimage:latest - <<EOF
FROM busybox
RUN echo "hello world"
EOF$ docker build https://raw.githubusercontent.com/dvdksn/clockbox/main/Dockerfile
Cuando compilas sin un contexto de sistema de archivos, las instrucciones del Dockerfile como
COPY no pueden hacer referencia a archivos locales:
$ ls
main.c
$ docker build -<<< $'FROM scratch\nCOPY main.c .'
[+] Building 0.0s (4/4) FINISHED
=> [internal] load build definition from Dockerfile 0.0s
=> => transferring dockerfile: 64B 0.0s
=> [internal] load .dockerignore 0.0s
=> => transferring context: 2B 0.0s
=> [internal] load build context 0.0s
=> => transferring context: 2B 0.0s
=> ERROR [1/1] COPY main.c . 0.0s
------
> [1/1] COPY main.c .:
------
Dockerfile:2
--------------------
1 | FROM scratch
2 | >>> COPY main.c .
3 |
--------------------
ERROR: failed to solve: failed to compute cache key: failed to calculate checksum of ref 7ab2bb61-0c28-432e-abf5-a4c3440bc6b6::4lgfpdf54n5uqxnv9v6ymg7ih: "/main.c": not found
Archivos .dockerignore
Puedes usar un archivo .dockerignore para excluir archivos o directorios del contexto de compilación.
# .dockerignore
node_modules
barEsto ayuda a evitar el envío de archivos y directorios no deseados al constructor, mejorando la velocidad de compilación, especialmente cuando se utiliza un constructor remoto.
Nombre de archivo y ubicación
Cuando ejecutas un comando de compilación, el cliente de compilación busca un archivo llamado .dockerignore en el directorio raíz del contexto. Si este archivo existe, los archivos y directorios que coinciden con los patrones en el archivo se eliminan del contexto de compilación antes de que se envíe al constructor.
Si utilizas múltiples Dockerfiles, puedes usar diferentes archivos de omisión para cada Dockerfile. Lo haces utilizando una convención de nomenclatura especial para los archivos de omisión. Coloca tu archivo de omisión en el mismo directorio que el Dockerfile y antepone el nombre del Dockerfile al archivo de omisión, como se muestra en el siguiente ejemplo:
.
├── index.ts
├── src/
├── docker
│ ├── build.Dockerfile
│ ├── build.Dockerfile.dockerignore
│ ├── lint.Dockerfile
│ ├── lint.Dockerfile.dockerignore
│ ├── test.Dockerfile
│ └── test.Dockerfile.dockerignore
├── package.json
└── package-lock.jsonUn archivo de omisión específico de Dockerfile tiene prioridad sobre el archivo .dockerignore en la raíz del contexto de compilación si ambos existen.
Sintaxis
El archivo .dockerignore es una lista separada por saltos de línea de patrones similares a los globs de archivos de los shells de Unix. Se ignoran las barras inclinadas iniciales y finales en los patrones de omisión. Los siguientes patrones excluyen un archivo o directorio llamado bar en el subdirectorio foo bajo la raíz del contexto de compilación:
/foo/bar//foo/barfoo/bar/foo/bar
Si una línea en el archivo .dockerignore comienza con # en la columna 1, esta línea se considera un comentario y se ignora antes de ser interpretada por la CLI.
#/esto/es/un/comentarioSi estás interesado en conocer los detalles precisos de la lógica de coincidencia de patrones de .dockerignore, consulta el repositorio moby/patternmatcher en GitHub, que contiene el código fuente.
Coincidencias
El siguiente fragmento de código muestra un ejemplo de archivo .dockerignore.
# comentario
*/temp*
*/*/temp*
temp?Este archivo provoca el siguiente comportamiento de compilación:
| Regla | Comportamiento |
|---|---|
# comentario | Ignorado. |
*/temp* | Excluye archivos y directorios cuyos nombres comiencen con temp en cualquier subdirectorio inmediato de la raíz. Por ejemplo, el archivo plano /somedir/temporary.txt está excluido, al igual que el directorio /somedir/temp. |
*/*/temp* | Excluye archivos y directorios que comiencen con temp de cualquier subdirectorio que esté dos niveles por debajo de la raíz. Por ejemplo, /somedir/subdir/temporary.txt está excluido. |
temp? | Excluye archivos y directorios en el directorio raíz cuyos nombres sean una extensión de un carácter de temp. Por ejemplo, /tempa y /tempb están excluidos. |
La coincidencia se realiza utilizando las reglas de la función filepath.Match de Go. Un paso de procesamiento previo utiliza la función filepath.Clean de Go para recortar los espacios en blanco y eliminar . y ... Las líneas que quedan en blanco después del procesamiento previo se ignoran.
NotePor razones históricas, se ignora el patrón
..
Más allá de las reglas de filepath.Match de Go, Docker también admite un comodín especial ** que coincide con cualquier número de directorios (incluido cero). Por ejemplo, **/*.go excluye todos los archivos que terminan en .go que se encuentren en cualquier lugar del contexto de compilación.
Puedes usar el archivo .dockerignore para excluir el Dockerfile y los archivos .dockerignore. Estos archivos se siguen enviando al constructor ya que son necesarios para ejecutar la compilación, pero no puedes copiar los archivos en la imagen usando ADD, COPY o montajes de unión (bind mounts).
Negar coincidencias
Puedes anteponer un signo de exclamación ! a las líneas para hacer excepciones a las exclusiones. El siguiente es un ejemplo de archivo .dockerignore que utiliza este mecanismo:
*.md
!README.mdTodos los archivos markdown directamente bajo el directorio del contexto, excepto README.md, se excluyen del contexto. Ten en cuenta que los archivos markdown bajo subdirectorios se siguen incluyendo.
La ubicación de las reglas de excepción ! influye en el comportamiento: la última línea del archivo .dockerignore que coincide con un archivo en particular determina si se incluye o se excluye. Considera el siguiente ejemplo:
*.md
!README*.md
README-secret.mdNo se incluye ningún archivo markdown en el contexto, excepto los archivos README que sean distintos de README-secret.md.
Ahora considera este ejemplo:
*.md
README-secret.md
!README*.mdSe incluyen todos los archivos README. La línea intermedia no tiene efecto porque !README*.md coincide con README-secret.md y está al final.
Contextos con nombre
Además del contexto de compilación predeterminado (el argumento posicional del comando docker build), también puedes pasar contextos con nombre adicionales a las compilaciones.
Los contextos con nombre se especifican utilizando la bandera --build-context, seguida de un par nombre-valor. Esto te permite incluir archivos y directorios de múltiples fuentes durante la compilación, manteniéndolos separados lógicamente.
$ docker build --build-context docs=./docs .
En este ejemplo:
- El contexto con nombre
docsapunta al directorio./docs. - El contexto predeterminado (
.) apunta al directorio de trabajo actual.
Usar contextos con nombre en un Dockerfile
Las instrucciones de Dockerfile pueden hacer referencia a contextos con nombre como si fueran etapas en una compilación multi-etapa.
Por ejemplo, el siguiente Dockerfile:
- Utiliza una instrucción
COPYpara copiar archivos desde el contexto predeterminado al escenario de compilación actual. - Realiza un montaje de unión (bind mount) de los archivos en un contexto con nombre para procesar los archivos como parte de la compilación.
# syntax=docker/dockerfile:1
FROM buildbase
WORKDIR /app
# Copia todos los archivos desde el contexto predeterminado en /app/src en el contenedor de compilación
COPY . /app/src
RUN make bin
# Monta los archivos del contexto con nombre "docs" para compilar la documentación
RUN --mount=from=docs,target=/app/docs \
make manpagesCasos de uso para contextos con nombre
El uso de contextos con nombre permite una mayor flexibilidad y eficiencia al compilar imágenes de Docker. Aquí tienes algunos escenarios en los que el uso de contextos con nombre puede ser útil:
Ejemplo: combinar fuentes locales y remotas
Puedes definir contextos con nombre separados para diferentes tipos de fuentes. Por ejemplo, considera un proyecto donde el código fuente de la aplicación es local, pero los scripts de despliegue se almacenan en un repositorio Git:
$ docker build --build-context scripts=https://github.com/user/deployment-scripts.git .
En el Dockerfile, puedes usar estos contextos de forma independiente:
# syntax=docker/dockerfile:1
FROM alpine:latest
# Copia el código de la aplicación desde el contexto principal
COPY . /opt/app
# Ejecuta los scripts de despliegue utilizando el contexto remoto "scripts"
RUN --mount=from=scripts,target=/scripts /scripts/main.shEjemplo: compilaciones dinámicas con dependencias personalizadas
En algunos escenarios, es posible que necesites inyectar dinámicamente archivos de configuración o dependencias en la compilación desde fuentes externas. Los contextos con nombre facilitan esto al permitirte montar diferentes configuraciones sin modificar el contexto de compilación predeterminado.
$ docker build --build-context config=./configs/prod .
Ejemplo de Dockerfile:
# syntax=docker/dockerfile:1
FROM nginx:alpine
# Utiliza el contexto "config" para configuraciones específicas del entorno
COPY --from=config nginx.conf /etc/nginx/nginx.confEjemplo: fijar o anular imágenes
Puedes hacer referencia a contextos con nombre en un Dockerfile de la misma manera que harías referencia a una imagen. Eso significa que puedes cambiar la referencia de una imagen en tu Dockerfile reemplazándola con un contexto con nombre. Por ejemplo, dado el siguiente Dockerfile:
FROM alpine:3.23Si deseas forzar que la referencia de la imagen se resuelva a una versión diferente, sin cambiar el Dockerfile, puedes pasar un contexto con el mismo nombre a la compilación. Por ejemplo:
docker buildx build --build-context alpine:3.23=docker-image://alpine:edge .
El prefijo docker-image:// marca el contexto como una referencia de imagen. La referencia puede ser una imagen local o una imagen en tu registro.
Contextos con nombre con Bake
Bake es una herramienta integrada en docker build que te permite administrar tu configuración de compilación con un archivo de configuración. Bake es totalmente compatible con los contextos con nombre.
Para definir contextos con nombre en un archivo Bake:
target "app" {
contexts = {
docs = "./docs"
}
}Esto es equivalente a la siguiente invocación de la CLI:
$ docker build --build-context docs=./docs .
Vincular targets con contextos con nombre
Además de hacer que las compilaciones complejas sean más manejables, Bake también proporciona características adicionales sobre lo que puedes hacer con docker build en la CLI. Puedes usar contextos con nombre para crear pipelines de compilación, donde un target depende y se compila sobre otro. Por ejemplo, considera una configuración de compilación de Docker donde tienes dos Dockerfiles:
base.Dockerfile: para compilar una imagen baseapp.Dockerfile: para compilar una imagen de aplicación
El app.Dockerfile utiliza la imagen producida por base.Dockerfile como su imagen base:
FROM mybaseimageNormalmente, tendrías que compilar la imagen base primero y luego cargarla en el almacén de imágenes local de Docker Engine o enviarla a un registro. Con Bake, puedes hacer referencia a otros targets directamente, creando una dependencia entre el target app y el target base.
target "base" {
dockerfile = "base.Dockerfile"
}
target "app" {
dockerfile = "app.Dockerfile"
contexts = {
# el prefijo target: indica que 'base' es un target de Bake
mybaseimage = "target:base"
}
}Con esta configuración, las referencias a mybaseimage en app.Dockerfile utilizan los resultados de la compilación del target base. La compilación del target app también activará una reconstrucción de mybaseimage si es necesario:
$ docker buildx bake app
Lectura adicional
Para obtener más información sobre cómo trabajar con contextos con nombre, consulta: