Expandindo a resposta de Peter Grainger, eu pude usar a compilação de vários estágios disponível desde o Docker 17.05. Página oficial afirma:
Com compilações de vários estágios, você usa várias FROM
instruções no Dockerfile. Cada FROM
instrução pode usar uma base diferente e cada uma delas inicia uma nova etapa da construção. Você pode copiar artefatos seletivamente de um estágio para outro, deixando para trás tudo o que não deseja na imagem final.
Mantendo isso em mente aqui é o meu exemplo de Dockerfile
inclusão de três estágios de construção. Ele foi criado para criar uma imagem de produção do aplicativo Web cliente.
# Stage 1: get sources from npm and git over ssh
FROM node:carbon AS sources
ARG SSH_KEY
ARG SSH_KEY_PASSPHRASE
RUN mkdir -p /root/.ssh && \
chmod 0700 /root/.ssh && \
ssh-keyscan bitbucket.org > /root/.ssh/known_hosts && \
echo "${SSH_KEY}" > /root/.ssh/id_rsa && \
chmod 600 /root/.ssh/id_rsa
WORKDIR /app/
COPY package*.json yarn.lock /app/
RUN eval `ssh-agent -s` && \
printf "${SSH_KEY_PASSPHRASE}\n" | ssh-add $HOME/.ssh/id_rsa && \
yarn --pure-lockfile --mutex file --network-concurrency 1 && \
rm -rf /root/.ssh/
# Stage 2: build minified production code
FROM node:carbon AS production
WORKDIR /app/
COPY --from=sources /app/ /app/
COPY . /app/
RUN yarn build:prod
# Stage 3: include only built production files and host them with Node Express server
FROM node:carbon
WORKDIR /app/
RUN yarn add express
COPY --from=production /app/dist/ /app/dist/
COPY server.js /app/
EXPOSE 33330
CMD ["node", "server.js"]
.dockerignore
repete o conteúdo do .gitignore
arquivo (impede node_modules
que os dist
diretórios resultantes do projeto sejam copiados):
.idea
dist
node_modules
*.log
Exemplo de comando para criar uma imagem:
$ docker build -t ezze/geoport:0.6.0 \
--build-arg SSH_KEY="$(cat ~/.ssh/id_rsa)" \
--build-arg SSH_KEY_PASSPHRASE="my_super_secret" \
./
Se sua chave SSH privada não tiver uma senha, basta especificar o SSH_KEY_PASSPHRASE
argumento vazio .
É assim que funciona:
1) Somente no primeiro estágio package.json
, os yarn.lock
arquivos e a chave SSH privada são copiados para a primeira imagem intermediária denominada sources
. Para evitar mais solicitações de senha de chave SSH, ela é automaticamente adicionada ssh-agent
. Finalmente, o yarn
comando instala todas as dependências necessárias do NPM e clona os repositórios git privados do Bitbucket sobre SSH.
2) O segundo estágio cria e reduz o código fonte do aplicativo Web e o coloca no dist
diretório da próxima imagem intermediária denominada production
. Observe que o código fonte do instalado node_modules
é copiado da imagem nomeada sources
produzida no primeiro estágio por esta linha:
COPY --from=sources /app/ /app/
Provavelmente também poderia ser a seguinte linha:
COPY --from=sources /app/node_modules/ /app/node_modules/
Temos apenas o node_modules
diretório da primeira imagem intermediária aqui, não SSH_KEY
e SSH_KEY_PASSPHRASE
mais argumentos. Todo o restante necessário para a construção é copiado do diretório do projeto.
3) No terceiro estágio, reduzimos o tamanho da imagem final que será marcada, ezze/geoport:0.6.0
incluindo apenas o dist
diretório da segunda imagem intermediária denominada production
e instalando o Node Express para iniciar um servidor da web.
A listagem de imagens fornece uma saída como esta:
REPOSITORY TAG IMAGE ID CREATED SIZE
ezze/geoport 0.6.0 8e8809c4e996 3 hours ago 717MB
<none> <none> 1f6518644324 3 hours ago 1.1GB
<none> <none> fa00f1182917 4 hours ago 1.63GB
node carbon b87c2ad8344d 4 weeks ago 676MB
onde imagens sem etiqueta correspondem aos primeiro e segundo estágios de construção intermediários.
Se você correr
$ docker history ezze/geoport:0.6.0 --no-trunc
você não verá nenhuma menção de SSH_KEY
e SSH_KEY_PASSPHRASE
na imagem final.