Há uma variedade de técnicas envolvidas, sem solução única. Você provavelmente desejará fazer vários dos seguintes procedimentos:
Primeiro, otimize suas camadas de imagem para reutilização. Coloque as etapas de alteração frequente posteriormente no Dockerfile para aumentar as chances de as camadas iniciais serem armazenadas em cache de compilações anteriores. Uma camada reutilizada aparecerá como mais espaço em disco em um docker image ls
arquivo, mas se você examinar o sistema de arquivos subjacente, apenas uma cópia de cada camada será armazenada no disco. Isso significa que 3 imagens de 2 GB cada, mas que possuem apenas 50 MB diferentes nas últimas camadas da compilação, ocuparão apenas 2,1 GB de espaço em disco, embora a listagem faça parecer que eles estão usando 6 GB desde que você esteja contando duas vezes cada uma das camadas reutilizadas.
A reutilização de camada é o motivo pelo qual as imagens com dependências de construção que mudam com pouca frequência instalam essas antes de copiar no código. Veja qualquer exemplo de python que possui um padrão como:
FROM python
WORKDIR /app
COPY requirements.txt .
RUN pip install -r requirements.txt
# note how the code is copied only after the pip install
# since code changes but requirements.txt doesn't
COPY . .
CMD ["gunicorn", "app:app"]
Escolha uma imagem base mínima. É por isso que você vê as pessoas passarem ubuntu
para debian:slim
(as variantes mais finas são menores, são enviadas com menos ferramentas) ou até mesmo alpine
. Isso reduz o tamanho do seu ponto de partida e é muito útil se você está constantemente obtendo novas versões da imagem base. No entanto, se sua imagem base raramente mudar, a reutilização da camada removerá grande parte da vantagem de uma imagem base mínima.
A menor imagem base que você pode escolher é scratch
, que não é nada, não possui shell ou bibliotecas e é útil apenas com binários compilados estaticamente. Caso contrário, escolha uma imagem de base que inclua as ferramentas necessárias sem muitas ferramentas desnecessárias.
Em seguida, qualquer etapa que altere ou exclua um arquivo deve ser combinada com as etapas anteriores que criam esse arquivo. Caso contrário, o sistema de arquivos em camadas, que usa a cópia na gravação, mesmo em alterações de permissão de arquivo, terá o arquivo original em uma camada anterior e o tamanho da imagem não diminuirá quando você remover os arquivos. É por isso que seus rm
comandos não têm efeito no espaço em disco resultante. Em vez disso, você pode encadear os comandos, como:
RUN apt-get update \
&& apt-get install -y \
a-package \
wget \
&& ... \
&& apt-get purge -y wget \
&& rm -r a-build-dir \
&& apt-get purge -y a-package
Observe que o uso excessivo do encadeamento de comandos pode diminuir a velocidade de suas compilações, pois você precisa reinstalar o mesmo conjunto de ferramentas sempre que um pré-requisito for alterado (por exemplo, o código sendo extraído com o wget). Veja o estágio múltiplo abaixo para uma alternativa melhor.
Qualquer arquivo criado que você não precise na imagem resultante deve ser excluído, na etapa que o cria. Isso inclui caches de pacotes, logs, páginas de manual etc. Para descobrir quais arquivos estão sendo criados em cada camada, você pode usar uma ferramenta como wagoodman / dive (que eu ainda não testei pessoalmente e expressaria cautela, pois é executado com acesso root completo) no host), ou você pode criar suas imagens do docker sem remover os contêineres intermediários e, em seguida, visualizar o diff com:
# first create and leave containers from any RUN step using options on build
docker image build --rm=false --no-cache -t image_name .
# review which layers use an unexpectedly large amount of space
docker image history image_name
# list all containers, particularly the exited ones from above
docker container ps -a
# examine any of those containers
docker container diff ${container_id}
# ... repeat the diff for other build steps
# then cleanup exited containers
docker container prune
Com cada um desses recipientes intermédios, o dif mostrará quais arquivos são adicionados, alterados, apagados ou em que o passo (estes são indicados com um A
, C
ou D
antes de cada ficheiro de imagem). O que o diff está mostrando é o sistema de arquivos de leitura / gravação específico do contêiner, que é qualquer arquivo alterado pelo contêiner do estado da imagem usando a cópia na gravação.
A melhor maneira de reduzir o tamanho da imagem é eliminar todos os componentes desnecessários, como compiladores, da imagem enviada. Para isso, as construções de vários estágios permitem compilar em um estágio e, em seguida, copiar apenas os artefatos resultantes do estágio de construção para uma imagem de tempo de execução que possui apenas o mínimo necessário para executar o aplicativo. Isso evita a necessidade de otimizar qualquer uma das etapas de compilação, pois elas não são fornecidas com a imagem resultante.
FROM debian:9 as build
# still chain update with install to prevent stale cache issues
RUN apt-get update \
&& apt-get install -y \
a-package \
wget \
RUN ... # perform any download/compile steps
FROM debian:9-slim as release
COPY --from=build /usr/local/bin/app /usr/local/bin/app
CMD [ "/usr/local/bin/app" ]
O estágio múltiplo é ideal com binários estaticamente compilados, que você pode executar com o scratch como sua imagem base ou com a transição de um ambiente de compilação como o JDK para um tempo de execução como o JRE. Essa é a maneira mais fácil de reduzir drasticamente o tamanho da sua imagem enquanto ainda possui compilações rápidas. Você ainda pode executar o encadeamento de etapas no estágio de lançamento, se houver etapas que alterem ou excluam arquivos criados nas etapas anteriores, mas, na maioria das vezes, o COPY
de outro estágio isola o estágio de lançamento de qualquer inchaço de camada experimentado nos estágios de compilação anteriores.
Observe que eu não recomendo esmagar imagens, pois isso reduz o tamanho de uma imagem à custa de eliminar a reutilização da camada. Isso significa que futuras compilações da mesma imagem exigirão mais tráfego de disco e de rede para enviar atualizações. Para voltar ao primeiro exemplo, o esmagamento pode reduzir sua imagem de 2 GB para 1 GB, mas não três imagens podem ocupar 3 GB em vez dos 2,1 GB.
2.37
vs1.47 GB