Como criar sempre a menor imagem de janela de encaixe de trabalho?

19

Objetivo: criar sempre as menores imagens de janela de encaixe de trabalho

Atual

REPOSITORY          TAG       IMAGE ID            CREATED             SIZE
a-docker-image      latest    x                   42 minutes ago       1.92 GB

Tentativa

Adicionando uma etapa de limpeza no final do Dockerfile:

#clean
RUN apt-get purge -y wget
RUN rm -r a-build-dir
RUN apt-get purge -y a-package

reduziu um pouco o tamanho da imagem:

REPOSITORY          TAG       IMAGE ID            CREATED             SIZE
a-docker-image      latest    y                   2 minutes ago       1.86 GB

Discussão

Eu construí várias imagens de janela de encaixe. Sempre que tento diminuir o tamanho da imagem criada, sempre sinto que ela é muito grande. Estou procurando um script que já tenha sido criado por alguém no github que remova todos os pacotes supérfluos da imagem, para que o tamanho da imagem criada seja o menor possível.

Como eu disse, sempre tento reduzir o tamanho da imagem, mas quero aplicar isso de maneira consistente, para que todas as imagens criadas a partir de agora sejam as menores possíveis.

Questão

Como criar sempre a menor imagem de janela de encaixe de trabalho?

030
fonte

Respostas:

1

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 lsarquivo, 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 ubuntupara 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 rmcomandos 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, Cou Dantes 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 COPYde 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.

BMitch
fonte
25

A Dockerfilecria uma nova camada para cada um dos comandos no arquivo. Como as camadas estão bem, colocadas umas sobre as outras - você não pode remover arquivos adicionados por uma camada anterior. É por isso que quando você instala pacotes, baixa arquivos ou cria compilações em um comando separado - eles ainda estão na imagem, mesmo que em uma camada futura os tenha removido.

Então, se você apenas mudar isso:

RUN apt-get update -y
RUN apt-get install -y wget a-package
# ...
RUN apt-get purge -y wget
RUN rm -r a-build-dir
RUN apt-get purge -y a-package

Para isso:

RUN apt-get update -y \
    && apt-get install -y wget a-package \
    && mkdir a-build-dir \
    && wget http://some-site/very-big-source-code.tar.gz \
    && tar xzvf very-big-source-code.tar.gz \
    && do-some-compilation \
    && apt-get purge -y wget \
    && cd .. \
    && rm -rf a-build-dir \
    && apt-get purge -y a-package

Você obterá uma imagem muito menor.


Outra opção é esmagar a imagem depois que você a construiu. P: Como funciona o novo docker --squash?


Outra opção é escolher uma imagem de base fina. Por exemplo, imagens que usam o Alpine Linux como base em vez do Debian, ocupam apenas 10-15mb em vez de 180-250mb. E isso é antes de adicionar seu próprio aplicativo e dados. Muitas imagens de base oficiais no Docker Hub têm uma versão alpina.

Evgeny
fonte
3
2.37vs1.47 GB
030
4

Provavelmente não é exatamente uma resposta, mas vale a pena dar as alternativas.

O habitat do Chef foi criado com isso em mente, criando um pacote com todas as dependências necessárias sem a carga de imagem de distribuição / base estranha que você não deseja.

Extrai o que importa aqui, o tamanho do contêiner desta postagem no blog com um aplicativo nodejs simples:

michael@ricardo-2:plans_pkg_part_2$ docker images
REPOSITORY           TAG                 IMAGE ID            CREATED             SIZE
mfdii/node-example   latest              36c6568c606b        40 minutes ago      655.9 MB
node                 latest              04c0ca2a8dad        16 hours ago        654.6 MB
mfdii/mytutorialapp  latest              534afd80d74d        2 minutes ago       182.1 MB

mdfii/node-exampleé uma imagem do docker de um arquivo docker clássico, enquanto mfdii/mytutorialappé a imagem do docker produzida com habitat.

Se o tamanho é sua principal preocupação e você está disposto a seguir a curva de aprendizado dos planos Habitat, isso pode ser uma solução para você.

Tensibai
fonte
0

Pode-se também usar mergulho

docker run --rm -it \
    -v /var/run/docker.sock:/var/run/docker.sock \
    wagoodman/dive:latest <dive arguments...>

para obter um relatório sobre quais resíduos podem ser removidos de uma imagem do Docker para reduzir o tamanho.

030
fonte
0

Se você deseja ter camadas de desenvolvimento reutilizáveis, mas reduz o uso do disco para entrega, é possível produzir uma "camada de entrega" mesclada como esta:

  1. Verifique se você possui um contêiner que usa sua imagem (se você não tiver uma, talvez use algo como docker run IMAGE echo, se o comando echo estiver disponível)
  2. Encontre o ID do contêiner (talvez usando docker container ls -l)
  3. Canal docker exportpara docker importcriar a camada mesclada (algo como docker export 20f192c6530a | docker import - project:merged)

Isso manterá suas camadas de desenvolvimento, mas fornecerá uma imagem menor e mesclada que você pode fornecer.

Bill Burdick
fonte
0

Construções de vários estágios. Use a imagem que possui todos os seus componentes de construção para criar seu aplicativo e uma imagem de tempo de execução mais clara. Copie apenas o artefato de construção na imagem de tempo de execução. Não há necessidade de excluir nada.

https://docs.docker.com/develop/develop-images/multistage-build/

frankd
fonte
0

simples .. docker ps verifique as imagens em execução atuais .. para obter um exemplo simples de arquivo abaixo ..

DE ubuntu16

MANUTENÇÃO sreeni (email / domínio)

EXECUTAR a atualização apt-get

Execute o apt-get install -y nginx

ENTRYPOINT ["/ usr / sbin / nginx", "- g", "daemon off;"]

EXPOSIÇÃO 80 (porta)

arquivo docker simples ...

use o comando docker abaixo

docker execute -d -p 80:80 --name servidor web ubuntu16 (nome da imagem) depois verifique o host local ou o endereço IP: 80 (abra o navegador e verifique)

sreeniwl
fonte
1
corrija a formatação da sua resposta ...
Pierre.Vriens