
A função de construção de vários estágios no Dockerfiles permite criar pequenas imagens de contêiner com um nível mais alto de armazenamento em cache e menos proteção. Neste artigo, mostrarei alguns modelos avançados - algo mais do que copiar arquivos entre compilar e executar. Eles permitem que você alcance a máxima eficiência da função. No entanto, se você é iniciante no campo de montagem em vários estágios, primeiro, provavelmente, não será errado ler o manual do usuário.
Compatibilidade de versão
O suporte à compilação de vários estágios foi adicionado ao Docker na versão v17.05. Todos os modelos funcionam com qualquer versão subsequente, mas alguns são muito mais eficientes, graças aos vinculadores que usam o servidor BuildKit . Digamos que o BuildKit efetivamente pule estágios não utilizados e, se possível, crie estágios ao mesmo tempo (destaquei esses exemplos separadamente). No momento, o BuildKit está sendo adicionado ao Moby como um back-end experimental para a compilação e deve estar disponível no Docker CE v18.06. Também pode ser usado de forma autônoma ou como parte do projeto img .
Herança de Estágio
A compilação de vários estágios inclui vários novos conceitos de sintaxe. Primeiro, você pode dar ao estágio que começa com o comando FROM
o nome AS stagename
e usar a --from=stagename
no COPY
para copiar arquivos desse estágio. De fato, o comando FROM
e o rótulo --from
têm muito mais em comum, não à toa que eles tenham o mesmo nome. Ambos adotam o mesmo argumento, reconhecem-no e iniciam um novo estágio a partir deste ponto ou usam-no como fonte para copiar o arquivo. Ou seja, para usar o estágio anterior na qualidade da imagem original para o estágio atual, você pode usar não apenas --from=stagename
, mas também o nome do estágio FROM stagename
. É útil se você usar as mesmas partes comuns em vários comandos no Dockerfile: reduz o código comum e simplifica sua manutenção, mantendo as etapas filho separadas. Portanto, a reconstrução de um estágio não afeta o cache do assembly para outros. Consequentemente, cada estágio pode ser montado individualmente usando o rótulo --target
ao chamar a docker build
.
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 …
Neste exemplo, a segunda e a terceira fases no BuildKit são criadas ao mesmo tempo.
Uso direto de imagens
Em vez de usar nomes de estágio de montagem em comandos FROM
que anteriormente apenas suportavam referências de imagem, você pode usar imagens diretamente usando o rótulo --from
. Acontece que copia arquivos diretamente dessas imagens. Por exemplo, linuxkit/ca-certificatesimage
no código a seguir copia diretamente as raízes da CA TLS para a etapa atual.
FROM alpine COPY --from=linuxkit/ca-certificates / /
Alias de imagem comum
A fase de construção não inclui necessariamente nenhum comando; pode consistir em uma única linha FROM
. Se você usar a imagem em vários lugares, isso simplificará a leitura e fará com que, se você precisar atualizar a imagem geral, precise alterar apenas uma linha.
FROM alpine:3.6 AS alpine FROM alpine RUN … FROM alpine RUN …
Neste exemplo, todos os locais que usam a imagem alpina estão comprometidos com alpine:3.6
, e não alpine:latest
. Quando chegar a hora de atualizar para alpine:3.7
, você precisará alterar uma única linha e não há dúvida: agora todos os elementos da montagem usam uma versão atualizada.
Isso é ainda mais importante quando o argumento de construção é usado no alias. O exemplo a seguir é semelhante ao anterior, mas permite ao usuário redefinir todas as instâncias da montagem em que a imagem alpina está envolvida, definindo a --build-arg ALPINE_VERSION=value
. Lembre-se: qualquer argumento usado nos comandos FROM
deve ser determinado antes da primeira fase de construção .
ARG ALPINE_VERSION=3.6 FROM alpine:${ALPINE_VERSION} AS alpine FROM alpine RUN …
Usando argumentos de construção em "- from"
O valor especificado no rótulo --from
do COPY
não deve conter argumentos de montagem. Por exemplo, o exemplo a seguir é inválido.
// THIS EXAMPLE IS INTENTIONALLY INVALID FROM alpine AS build-stage0 RUN … FROM alpine ARG src=stage0 COPY --from=build-${src} . .
Isso se deve ao fato de que as dependências entre os estágios devem ser determinadas antes do início da montagem. Portanto, não é necessária uma avaliação constante de todas as equipes. Por exemplo, uma variável de ambiente definida em uma imagem alpine
pode afetar a avaliação do valor --from
. A razão pela qual podemos avaliar os argumentos do comando FROM
é porque esses argumentos são definidos globalmente antes de qualquer estágio começar. Felizmente, como descobrimos anteriormente, basta definir o estágio do alias usando um comando FROM
e fazer referência a ele.
ARG src=stage0 FROM alpine AS build-stage0 RUN … FROM build-${src} AS copy-src FROM alpine COPY --from=copy-src . .
Agora, se você substituir o argumento src
da montagem, a etapa inicial do elemento COPY
final será alternada. Observação: se algumas etapas não forem mais usadas, apenas os vinculadores baseados no BuildKit poderão ignorá-las com eficiência.
Condições usando argumentos de compilação
Foi-nos pedido que adicionássemos suporte às condições de estilo IF/ELSE
ao Dockerfile. Ainda não sabemos se adicionaremos algo semelhante, mas no futuro tentaremos usar o suporte ao cliente no BuildKit. Enquanto isso, para obter um comportamento semelhante, você pode usar os conceitos atuais de vários estágios (com algum planejamento).
// 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 …
O exemplo anterior mostra o pseudocódigo para condições de gravação usando IF/ELSE
. Para obter um comportamento semelhante às compilações de vários estágios atuais, pode ser necessário definir várias condições de ramificação como etapas separadas e usar um argumento para selecionar o caminho de dependência correto.
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 …
A etapa final no Dockerfile é baseada na etapa de after-condition
, que é o alias da imagem (reconhecido com base no BUILD_VERSION
construção BUILD_VERSION
). Dependendo do valor de BUILD_VERSION
, este ou aquele estágio da seção do meio é selecionado.
Observe: somente os vinculadores baseados no BuildKit podem ignorar ramificações não utilizadas. Nas versões anteriores dos vinculadores, todos os estágios seriam criados, mas antes de criar a imagem final, seus resultados seriam descartados.
Assistente de Desenvolvimento / Teste para a Fase Mínima de Produção
Concluindo, vejamos um exemplo de combinação de modelos anteriores para demonstrar como criar um Dockerfile que cria uma imagem mínima de produção e, em seguida, pode usar seu conteúdo para testar e criar uma imagem de desenvolvimento. Vamos começar com o exemplo básico do Dockerfile:
FROM golang:alpine AS stage0 … FROM golang:alpine AS stage1 … FROM scratch COPY --from=stage0 /binary0 /bin COPY --from=stage1 /binary1 /bin
Quando uma imagem mínima de produção é criada, essa é uma opção bastante comum. Mas e se você também precisar obter uma imagem alternativa do desenvolvedor ou executar testes com esses binários na fase final? A primeira coisa que vem à mente é simplesmente copiar binários semelhantes nos estágios de teste e desenvolvimento. O problema é este: não há garantia de que você testará todos os binários de produção na mesma combinação. No estágio final, algo pode mudar, mas você esquecerá de fazer alterações semelhantes em outros estágios ou cometer um erro na maneira de copiar arquivos binários. No final, não estamos testando um arquivo binário separado, mas a imagem final.
Uma alternativa é determinar a fase de desenvolvimento e teste após a fase de produção e copiar todo o conteúdo da fase de produção. Em seguida, use um comando FROM
para a etapa de produção para tornar a etapa padrão da produção a última etapa novamente.
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
Por padrão, esse Dockerfile continuará a criar uma imagem padrão mínima, enquanto, por exemplo, um assembly com a opção --target=dev-env
criará uma imagem com um shell contendo todos os binários da versão final.
Espero que isso tenha sido útil e sugerido como criar Dockerfiles em vários estágios mais eficientes. Se você participa do DockerCon2018 e deseja saber mais sobre compilações de vários estágios, Dockerfiles, BuildKit ou outros tópicos relacionados, inscreva-se no linkway trackway do corredor ou siga as reuniões internas da plataforma Docker nas faixas Contribute e Collaborate ou Black Belt .