Plantillas avanzadas de compilación de varias etapas

imagen


La función de compilación de múltiples etapas en los archivos Dockerfile le permite crear pequeñas imágenes de contenedor con un mayor nivel de almacenamiento en caché y menos protección. En este artículo, le mostraré algunas plantillas avanzadas, algo más que copiar archivos entre compilación y ejecución. Le permiten lograr la máxima eficiencia de la función. Sin embargo, si usted es un principiante en el campo del ensamblaje de etapas múltiples, primero, probablemente, no será un error leer el manual del usuario.


Compatibilidad de versiones


El soporte de compilación multietapa se agregó a Docker en la versión v17.05. Todas las plantillas funcionan con cualquier versión posterior, pero algunas son mucho más eficientes, gracias a los enlazadores que utilizan el lado del servidor BuildKit . Digamos que BuildKit omite efectivamente las etapas no utilizadas y, si es posible, crea etapas al mismo tiempo (destaqué estos ejemplos por separado). BuildKit se está agregando actualmente a Moby como un backend experimental para la compilación y debería estar disponible en Docker CE v18.06. También se puede usar de forma autónoma o como parte del proyecto img .




Herencia escénica


La construcción de etapas múltiples agrega varios conceptos nuevos de sintaxis. En primer lugar, puede AS stagename la etapa que comience con el comando FROM el nombre AS stagename y usar la --from=stagename en el COPY para copiar archivos de esta etapa. De hecho, el comando FROM y la etiqueta --from tienen mucho más en común, no en vano, tienen el mismo nombre. Ambos toman el mismo argumento, lo reconocen y comienzan una nueva etapa desde este punto, o lo usan como fuente para copiar el archivo. Es decir, para usar la etapa anterior en la calidad de imagen original para la etapa actual, puede tomar no solo --from=stagename , sino también el nombre de etapa FROM stagename . Es útil si usa las mismas partes comunes en varios comandos en el Dockerfile: reduce el código común y simplifica su mantenimiento, manteniendo separados los pasos secundarios. Por lo tanto, la reconstrucción de una etapa no afecta la caché de ensamblaje para otras. En consecuencia, cada etapa se puede ensamblar individualmente utilizando la etiqueta --target cuando se llama a --target .


 FROM ubuntu AS base RUN apt-get update && apt-get install git FROM base AS src1 RUN git clone … FROM base as src2 RUN git clone … 

En este ejemplo, las fases segunda y tercera en BuildKit se crean al mismo tiempo.


Uso directo de imágenes.


En lugar de usar nombres de etapas de ensamblaje en comandos FROM que anteriormente solo admitían referencias de imágenes, puede usar imágenes directamente usando la etiqueta --from . Resulta copiar archivos directamente de estas imágenes. Por ejemplo, linuxkit/ca-certificatesimage en el siguiente código copia directamente las raíces de TLS CA en el paso actual.


 FROM alpine COPY --from=linuxkit/ca-certificates / / 

Alias ​​de imagen común


La fase de construcción no incluye necesariamente ningún comando; Puede consistir en una sola línea FROM . Si usa la imagen en varios lugares, esto simplificará la lectura y hará que si necesita actualizar la imagen general, solo necesita cambiar una línea.


 FROM alpine:3.6 AS alpine FROM alpine RUN … FROM alpine RUN … 

En este ejemplo, cada lugar que usa la imagen alpina está realmente comprometido con alpine:3.6 , no alpine:latest . Cuando llegue el momento de actualizar a alpine:3.7 , deberá cambiar una sola línea, y no hay duda: ahora todos los elementos del ensamblaje usan una versión actualizada.


Esto es aún más importante cuando el argumento de compilación se usa en el alias. El siguiente ejemplo es similar al anterior, pero permite al usuario redefinir todas las instancias del ensamblaje en el que está involucrada la imagen alpina configurando la --build-arg ALPINE_VERSION=value . Recuerde: cualquier argumento utilizado en los comandos FROM debe determinarse antes de la primera fase de compilación .


 ARG ALPINE_VERSION=3.6 FROM alpine:${ALPINE_VERSION} AS alpine FROM alpine RUN … 

Usar argumentos de compilación en "- desde"


El valor especificado en la etiqueta --from del COPY no debe contener argumentos de ensamblaje. Por ejemplo, el siguiente ejemplo no es válido.


 // THIS EXAMPLE IS INTENTIONALLY INVALID FROM alpine AS build-stage0 RUN … FROM alpine ARG src=stage0 COPY --from=build-${src} . . 

Esto se debe al hecho de que las dependencias entre las etapas deben determinarse antes de que comience el ensamblaje. Entonces no se requiere una evaluación constante de todos los equipos. Por ejemplo, una variable de entorno definida en una imagen alpine puede afectar la evaluación del valor --from . La razón por la que podemos evaluar los argumentos del comando FROM es porque estos argumentos se definen globalmente antes de que comience cualquier etapa. Afortunadamente, como descubrimos anteriormente, es suficiente definir la etapa del alias usando un comando FROM y consultarlo.


 ARG src=stage0 FROM alpine AS build-stage0 RUN … FROM build-${src} AS copy-src FROM alpine COPY --from=copy-src . . 

Ahora, si anula el argumento de ensamblaje src , el paso inicial para el elemento COPY final cambiará. Tenga en cuenta: si algunos pasos ya no se utilizan, solo los vinculadores basados ​​en BuildKit pueden omitirlos de manera efectiva.


Condiciones que utilizan argumentos de compilación


Se nos solicitó agregar soporte para las condiciones de estilo IF/ELSE al Dockerfile. Todavía no sabemos si agregaremos algo similar, pero en el futuro intentaremos usar el soporte al cliente en BuildKit. Mientras tanto, para lograr un comportamiento similar, puede usar los conceptos actuales de varias etapas (con algo de planificación).


 // THIS EXAMPLE IS INTENTIONALLY INVALID FROM alpine RUN … ARG BUILD_VERSION=1 IF $BUILD_VERSION==1 RUN touch version1 ELSE IF $BUILD_VERSION==2 RUN touch version2 DONE RUN … 

El ejemplo anterior muestra pseudocódigo para grabar condiciones usando IF/ELSE . Para lograr un comportamiento similar con las compilaciones de etapas múltiples actuales, es posible que deba definir varias condiciones de ramificación como pasos separados y usar un argumento para seleccionar la ruta de dependencia correcta.


 ARG BUILD_VERSION=1 FROM alpine AS base RUN … FROM base AS branch-version-1 RUN touch version1 FROM base AS branch-version-2 RUN touch version2 FROM branch-version-${BUILD_VERSION} AS after-condition FROM after-condition RUN … 

El último paso en el Dockerfile se basa en la etapa after-condition , que es el alias de la imagen (reconocido en función del BUILD_VERSION compilación BUILD_VERSION ). Dependiendo del valor de BUILD_VERSION , se selecciona esta o aquella etapa de la sección central.


Tenga en cuenta que solo los vinculadores basados ​​en BuildKit pueden omitir ramas no utilizadas. En las versiones anteriores de los enlazadores, se construirían todas las etapas, pero antes de crear la imagen final, sus resultados se descartarían.


Asistente de desarrollo / pruebas para la fase de producción mínima


En conclusión, veamos un ejemplo de combinación de plantillas anteriores para demostrar cómo crear un Dockerfile que crea una imagen de producción mínima y luego puede usar su contenido para probar y crear una imagen de desarrollo. Comencemos con el ejemplo básico de Dockerfile:


 FROM golang:alpine AS stage0 … FROM golang:alpine AS stage1 … FROM scratch COPY --from=stage0 /binary0 /bin COPY --from=stage1 /binary1 /bin 

Cuando se crea una imagen de producción mínima, esta es una opción bastante común. Pero, ¿qué sucede si también necesita obtener una imagen de desarrollador alternativa o ejecutar pruebas con estos binarios en la etapa final? Lo primero que viene a la mente es simplemente copiar binarios similares en las etapas de prueba y desarrollo. El problema es este: no hay garantía de que probará todos los binarios de producción en la misma combinación. En la etapa final, algo puede cambiar, pero olvidará realizar cambios similares en otras etapas o cometer un error al copiar archivos binarios. Al final, no estamos probando un archivo binario separado, sino la imagen final.


Una alternativa es determinar la fase de desarrollo y prueba después de la fase de producción y copiar todo el contenido de la fase de producción. Luego use un comando FROM para el paso de producción para hacer que el paso de producción predeterminado sea el último paso nuevamente.


 FROM golang:alpine AS stage0 … FROM scratch AS release COPY --from=stage0 /binary0 /bin COPY --from=stage1 /binary1 /bin FROM golang:alpine AS dev-env COPY --from=release / / ENTRYPOINT ["ash"] FROM golang:alpine AS test COPY --from=release / / RUN go test … FROM release 

De manera predeterminada, este Dockerfile continuará creando la imagen predeterminada mínima, mientras que, por ejemplo, un ensamblaje con la opción --target=dev-env creará una imagen con un shell que contiene todos los archivos binarios de la versión final.




Espero que esto haya sido útil y sugirió cómo crear archivos Docker de varias etapas más eficientes. Si participa en DockerCon2018 y desea obtener más información sobre compilaciones de varias etapas, Dockerfiles, BuildKit o cualquier otro tema relacionado, regístrese en el enlazador de pistas del pasillo o siga las reuniones internas de la plataforma Docker en las pistas de Contribute y Collaborate o Black Belt .

Source: https://habr.com/ru/post/es433790/


All Articles