Dicas do Docker: limpe o lixo da sua máquina

Dicas do Docker: limpe o lixo da sua máquina

Olá, Habr! Apresento a sua atenção uma tradução do artigo "Dicas do Docker: limpe sua máquina local" autor Luc Juggery.

Hoje falaremos sobre como o Docker usa o espaço em disco da máquina host e também descobriremos como liberar esse espaço de restos de imagens e contêineres não utilizados.


Dicas do Docker: limpe o lixo da sua máquina

Consumo total

Docker é uma coisa legal, provavelmente poucas pessoas duvidam disso hoje. Há apenas alguns anos, este produto nos proporcionou uma maneira completamente nova de construir, entregar e executar qualquer ambiente, permitindo-nos economizar significativamente recursos de CPU e RAM. Além disso (e para alguns isso será o mais importante), o Docker nos permitiu simplificar e unificar incrivelmente o gerenciamento do ciclo de vida de nossos ambientes de produção.

No entanto, todas estas delícias da vida moderna têm um preço. Quando executamos contêineres, baixamos ou criamos nossas próprias imagens e implantamos ecossistemas complexos, temos que pagar. E pagamos, entre outras coisas, com espaço em disco.

Se você nunca pensou em quanto espaço o Docker realmente ocupa em sua máquina, você pode se surpreender desagradavelmente com a saída deste comando:

$ docker system df

Dicas do Docker: limpe o lixo da sua máquina

Isto mostra o uso do disco do Docker em diferentes contextos:

  • imagens – o tamanho total das imagens que foram baixadas de repositórios de imagens e construídas em seu sistema;
  • contêineres – a quantidade total de espaço em disco usado pela execução de contêineres (ou seja, o volume total de camadas de leitura e gravação de todos os contêineres);
  • volumes locais – o volume de armazenamento local montado em contêineres;
  • build cache – arquivos temporários gerados pelo processo de construção da imagem (usando a ferramenta BuildKit, disponível a partir do Docker versão 18.09).

Aposto que após essa simples transferência você está ansioso para limpar o lixo do seu disco e trazer preciosos gigabytes de volta à vida (nota: especialmente se você paga aluguel por esses gigabytes todos os meses).

Uso de disco por contêineres

Cada vez que você cria um contêiner na máquina host, vários arquivos e diretórios são criados no diretório /var/lib/docker, entre os quais vale destacar:

  • Diretório /var/lib/docker/containers/container_ID – ao usar o driver de log padrão, é aqui que os logs de eventos são salvos no formato JSON. Logs muito detalhados, bem como logs que ninguém lê ou processa, geralmente fazem com que os discos fiquem cheios.
  • O diretório /var/lib/docker/overlay2 contém as camadas de leitura e gravação do contêiner (overlay2 é o driver preferido na maioria das distribuições Linux). Se o contêiner armazenar dados em seu sistema de arquivos, é nesse diretório que eles serão colocados.

Vamos imaginar um sistema no qual um Docker original está instalado, que nunca esteve envolvido no lançamento de contêineres ou na construção de imagens. Seu relatório de uso de espaço em disco ficará assim:

$ docker system df
TYPE           TOTAL      ACTIVE     SIZE       RECLAIMABLE
Images         0          0          0B         0B
Containers     0          0          0B         0B
Local Volumes  0          0          0B         0B
Build Cache    0          0          0B         0B

Vamos lançar algum contêiner, por exemplo, NGINX:

$ docker container run --name www -d -p 8000:80 nginx:1.16

O que acontece com o disco:

  • as imagens ocupam 126 MB, este é o mesmo NGINX que lançamos no container;
  • os contêineres ocupam ridículos 2 bytes.

$ docker system df
TYPE           TOTAL      ACTIVE     SIZE       RECLAIMABLE
Images         1          1          126M       0B (0%)
Containers     1          1          2B         0B (0%)
Local Volumes  0          0          0B         0B
Build Cache    0          0          0B         0B

A julgar pela conclusão, ainda não temos espaço que possamos liberar. Como 2 bytes é completamente frívolo, vamos imaginar que nosso NGINX escreveu inesperadamente em algum lugar 100 megabytes de dados e criou um arquivo test.img exatamente desse tamanho dentro de si.

$ docker exec -ti www 
  dd if=/dev/zero of=test.img bs=1024 count=0 seek=$[1024*100]

Vamos examinar novamente o uso do espaço em disco no host. Veremos que o contêiner (contêineres) ocupa ali 100 Megabytes.

$ docker system df
TYPE           TOTAL      ACTIVE     SIZE       RECLAIMABLE
Images         1          1          126M       0B (0%)
Containers     1          1          104.9MB    0B (0%)
Local Volumes  0          0          0B         0B
Build Cache    0          0          0B         0B

Acho que seu cérebro curioso já está se perguntando onde nosso arquivo test.img está localizado. Vamos procurar:

$ find /var/lib/docker -type f -name test.img
/var/lib/docker/overlay2/83f177...630078/merged/test.img
/var/lib/docker/overlay2/83f177...630078/diff/test.img

Sem entrar em detalhes, podemos notar que o arquivo test.img está convenientemente localizado no nível de leitura e gravação, controlado pelo driver overlay2. Se pararmos nosso contêiner, o host nos dirá que esse espaço pode, em princípio, ser liberado:

# Stopping the www container
$ docker stop www

# Visualizing the impact on the disk usage
$ docker system df
TYPE           TOTAL      ACTIVE     SIZE       RECLAIMABLE
Images         1          1          126M       0B (0%)
Containers     1          0          104.9MB    104.9MB (100%)
Local Volumes  0          0          0B         0B
Build Cache    0          0          0B         0B

Como podemos fazer isso? Ao excluir o contêiner, o que implicará a limpeza do espaço correspondente no nível de leitura e gravação.

Com o comando a seguir, você pode remover todos os contêineres instalados de uma só vez e limpar seu disco de todos os arquivos de leitura e gravação criados por eles:

$ docker container prune
WARNING! This will remove all stopped containers.
Are you sure you want to continue? [y/N] y
Deleted Containers:
5e7f8e5097ace9ef5518ebf0c6fc2062ff024efb495f11ccc89df21ec9b4dcc2

Total reclaimed space: 104.9MB

Portanto, liberamos 104,9 Megabytes excluindo o contêiner. Mas como não utilizamos mais a imagem baixada anteriormente, ela também se torna candidata à exclusão e liberação de nossos recursos:

$ docker system df
TYPE           TOTAL      ACTIVE     SIZE       RECLAIMABLE
Images         1          0          126M       126M (100%)
Containers     0          0          0B         0B
Local Volumes  0          0          0B         0B
Build Cache    0          0          0B         0B

Nota: Enquanto a imagem estiver em uso por pelo menos um contêiner, você não poderá usar esse truque.

O subcomando prune que usamos acima só tem efeito em contêineres parados. Se quisermos excluir não apenas contêineres parados, mas também em execução, devemos usar um destes comandos:

# Historical command
$ docker rm -f $(docker ps –aq)

# More recent command
$ docker container rm -f $(docker container ls -aq)

Notas laterais: se você usar o parâmetro -rm ao iniciar um contêiner, quando ele parar, todo o espaço em disco que ocupava será liberado.

Usando imagens de disco

Há alguns anos, um tamanho de imagem de várias centenas de megabytes era completamente normal: uma imagem do Ubuntu pesava 600 megabytes e uma imagem do Microsoft .Net pesava vários gigabytes. Naqueles dias difíceis, baixar apenas uma imagem poderia prejudicar muito o espaço livre em disco, mesmo se você estivesse compartilhando níveis entre imagens. Hoje - louvados sejam os grandes - as imagens pesam bem menos, mas mesmo assim você pode preencher rapidamente os recursos disponíveis se não tomar alguns cuidados.

Existem vários tipos de imagens que não são diretamente visíveis para o usuário final:

  • imagens intermediárias, com base nas quais outras imagens são coletadas - elas não podem ser excluídas se você usar contêineres baseados nessas “outras” imagens;
  • imagens pendentes são imagens intermediárias que não são referenciadas por nenhum dos contêineres em execução - elas podem ser excluídas.
  • Com o seguinte comando você pode verificar imagens pendentes em seu sistema:

$ docker image ls -f dangling=true
REPOSITORY  TAG      IMAGE ID         CREATED             SIZE
none      none   21e658fe5351     12 minutes ago      71.3MB

Você pode removê-los da seguinte maneira:

$ docker image rm $(docker image ls -f dangling=true -q)

Também podemos usar o subcomando prune:

$ docker image prune
WARNING! This will remove all dangling images.
Are you sure you want to continue? [y/N] y
Deleted Images:
deleted: sha256:143407a3cb7efa6e95761b8cd6cea25e3f41455be6d5e7cda
deleted: sha256:738010bda9dd34896bac9bbc77b2d60addd7738ad1a95e5cc
deleted: sha256:fa4f0194a1eb829523ecf3bad04b4a7bdce089c8361e2c347
deleted: sha256:c5041938bcb46f78bf2f2a7f0a0df0eea74c4555097cc9197
deleted: sha256:5945bb6e12888cf320828e0fd00728947104da82e3eb4452f

Total reclaimed space: 12.9kB

Se de repente quisermos excluir todas as imagens completamente (e não apenas as penduradas) com um comando, podemos fazer o seguinte:

$ docker image rm $(docker image ls -q)

Uso de disco por volumes

Os volumes são usados ​​para armazenar dados fora do sistema de arquivos do contêiner. Por exemplo, se quisermos salvar os resultados de uma aplicação para utilizá-los de alguma outra forma. Um exemplo comum são os bancos de dados.

Vamos iniciar um contêiner MongoDB, montar um volume externo ao contêiner e restaurar um backup do banco de dados dele (temos disponível no arquivo bck.json):

# Running a mongo container
$ docker run --name db -v $PWD:/tmp -p 27017:27017 -d mongo:4.0

# Importing an existing backup (from a huge bck.json file)
$ docker exec -ti db mongoimport 
  --db 'test' 
  --collection 'demo' 
  --file /tmp/bck.json 
  --jsonArray

Os dados estarão localizados na máquina host no diretório /var/lib/docker/volumes. Mas por que não no nível de leitura e gravação do contêiner? Porque no Dockerfile da imagem MongoDB, o diretório /data/db (onde o MongoDB armazena seus dados por padrão) é definido como um volume.

Dicas do Docker: limpe o lixo da sua máquina

Nota lateral: muitas imagens que devem produzir dados usam volumes para armazenar esses dados.

Quando brincamos bastante com o MongoDB e paramos (ou talvez até excluímos) o contêiner, o volume não será excluído. Ele continuará ocupando nosso precioso espaço em disco até que o excluamos explicitamente com um comando como este:

$ docker volume rm $(docker volume ls -q)

Bem, ou podemos usar o subcomando prune que já nos é familiar:

$ docker volume prune
WARNING! This will remove all local volumes not used by at least one container.
Are you sure you want to continue? [y/N] y
Deleted Volumes:
d50b6402eb75d09ec17a5f57df4ed7b520c448429f70725fc5707334e5ded4d5
8f7a16e1cf117cdfddb6a38d1f4f02b18d21a485b49037e2670753fa34d115fc
599c3dd48d529b2e105eec38537cd16dac1ae6f899a123e2a62ffac6168b2f5f
...
732e610e435c24f6acae827cd340a60ce4132387cfc512452994bc0728dd66df
9a3f39cc8bd0f9ce54dea3421193f752bda4b8846841b6d36f8ee24358a85bae
045a9b534259ec6c0318cb162b7b4fca75b553d4e86fc93faafd0e7c77c79799
c6283fe9f8d2ca105d30ecaad31868410e809aba0909b3e60d68a26e92a094da

Total reclaimed space: 25.82GB
luc@saturn:~$

Usando disco para cache de construção de imagem

No Docker 18.09, o processo de criação de imagens sofreu algumas alterações graças à ferramenta BuildKit. Isso aumenta a velocidade do processo e otimiza o armazenamento de dados e o gerenciamento de segurança. Aqui não consideraremos todos os detalhes desta ferramenta maravilhosa; focaremos apenas em como ela aborda questões de uso de espaço em disco.

Digamos que temos um aplicativo Node.Js completamente simples:

  • o arquivo index.js inicia um servidor HTTP simples que responde com uma linha a cada solicitação recebida:
  • o arquivo package.json define as dependências, das quais apenas expressjs é usado para executar o servidor HTTP:

$ cat index.js
var express = require('express');
var util    = require('util');
var app = express();
app.get('/', function(req, res) {
  res.setHeader('Content-Type', 'text/plain');
  res.end(util.format("%s - %s", new Date(), 'Got Request'));
});
app.listen(process.env.PORT || 80);

$ cat package.json
    {
      "name": "testnode",
      "version": "0.0.1",
      "main": "index.js",
      "scripts": {
        "start": "node index.js"
      },
      "dependencies": {
        "express": "^4.14.0"
      }
    }

O Dockerfile para construir a imagem fica assim:

FROM node:13-alpine
COPY package.json /app/package.json
RUN cd /app && npm install
COPY . /app/
WORKDIR /app
EXPOSE 80
CMD ["npm", "start"]

Vamos construir a imagem da maneira usual, sem usar o BuildKit:

$ docker build -t app:1.0 .

Se verificarmos o uso do espaço em disco, podemos ver que apenas a imagem base (node:13-alpine) e a imagem de destino (app:1.0) estão ocupando espaço:

TYPE           TOTAL      ACTIVE     SIZE       RECLAIMABLE
Images         2          0          109.3MB    109.3MB (100%)
Containers     0          0          0B         0B
Local Volumes  0          0          0B         0B
Build Cache    0          0          0B         0B

Vamos construir a segunda versão da nossa aplicação, usando o BuildKit. Para fazer isso, precisamos apenas definir a variável DOCKER_BUILDKIT como 1:

$ DOCKER_BUILDKIT=1 docker build -t app:2.0 .

Se verificarmos agora o uso do disco, veremos que o cache de construção (buid-cache) agora está envolvido lá:

$ docker system df
TYPE           TOTAL      ACTIVE     SIZE       RECLAIMABLE
Images         2          0          109.3MB    109.3MB (100%)
Containers     0          0          0B         0B
Local Volumes  0          0          0B         0B
Build Cache    11         0          8.949kB    8.949kB

Para limpá-lo, use o seguinte comando:

$ docker builder prune
WARNING! This will remove all dangling build cache.
Are you sure you want to continue? [y/N] y
Deleted build cache objects:
rffq7b06h9t09xe584rn4f91e
ztexgsz949ci8mx8p5tzgdzhe
3z9jeoqbbmj3eftltawvkiayi

Total reclaimed space: 8.949kB

Limpar tudo!

Então, analisamos a limpeza do espaço em disco ocupado por contêineres, imagens e volumes. O subcomando prune nos ajuda com isso. Mas também pode ser usado no nível do sistema docker e limpará tudo o que puder:

$ docker system prune
WARNING! This will remove:
  - all stopped containers
  - all networks not used by at least one container
  - all dangling images
  - all dangling build cache

Are you sure you want to continue? [y/N]

Se por algum motivo você estiver economizando espaço em disco em uma máquina executando Docker, a execução periódica deste comando deve se tornar um hábito.

Fonte: habr.com

Adicionar um comentário