Gerenciando Sistemas de Arquivos de Containers

Beginner

This tutorial is from open-source community. Access the source code

Introdução

Por padrão, todos os arquivos criados dentro de um container são armazenados em uma camada de container gravável. Isso significa que:

  • Se o container não existir mais, os dados são perdidos,
  • A camada gravável do container está intimamente ligada à máquina host, e
  • Para gerenciar o sistema de arquivos, você precisa de um driver de armazenamento que forneça um sistema de arquivos de união (union file system), usando o kernel Linux. Essa abstração extra reduz o desempenho em comparação com os volumes de dados (data volumes), que escrevem diretamente no sistema de arquivos.

O Docker oferece duas opções para armazenar arquivos na máquina host: volumes e bind mounts. Se você estiver executando o Docker no Linux, também pode usar um tmpfs mount, e com o Docker no Windows, você também pode usar um named pipe.

Tipos de Mounts
  • Volumes são armazenados no sistema de arquivos host que é gerenciado pelo Docker.
  • Bind mounts são armazenados em qualquer lugar no sistema host.
  • tmpfs mounts são armazenados apenas na memória host.

Originalmente, a flag --mount era usada para serviços Docker Swarm e a flag --volume era usada para containers standalone. A partir do Docker 17.06 e superior, você também pode usar --mount para containers standalone e, em geral, é mais explícito e verboso do que --volume.

Este é um Lab Guiado, que fornece instruções passo a passo para ajudá-lo a aprender e praticar. Siga as instruções cuidadosamente para completar cada etapa e ganhar experiência prática. Dados históricos mostram que este é um laboratório de nível iniciante com uma taxa de conclusão de 100%. Recebeu uma taxa de avaliações positivas de 100% dos estudantes.

Volumes

Um volume de dados (data volume) ou volume é um diretório que ignora o Sistema de Arquivos de União (Union File System) do Docker.

Existem três tipos de volumes:

  • volume anônimo,
  • volume nomeado, e
  • volume host.

Volume Anônimo

Vamos criar uma instância de um popular banco de dados NoSQL de código aberto chamado CouchDB e usar um volume anônimo para armazenar os arquivos de dados do banco de dados.

Para executar uma instância do CouchDB, use a imagem CouchDB do Docker Hub em https://hub.docker.com/_/couchdb. Os documentos dizem que o padrão para CouchDB é escrever os arquivos do banco de dados no disco no sistema host usando sua própria gestão de volume interna.

Execute o seguinte comando:

docker run -d -p 5984:5984 --name my-couchdb -e COUCHDB_USER=admin -e COUCHDB_PASSWORD=passw0rd1 couchdb:3.1

CouchDB criará um volume anônimo e gerará um nome com hash. Verifique os volumes no seu sistema host:

labex:~/ $ docker volume ls
DRIVER VOLUME NAME
local 1d292aca855adb9de9be7acea88f6d3f8e6a08eef5bfd986a81f073f1906b82f

Defina uma variável de ambiente VOLUME com o valor do nome gerado:

export VOLUME=<VOLUME NAME>

E inspecione o volume que foi criado, use o nome com hash que foi gerado para o volume:

$ docker volume inspect $VOLUME
[
{
  "CreatedAt": "2020-09-24T14:10:07Z",
  "Driver": "local",
  "Labels": null,
  "Mountpoint": "/var/lib/docker/volumes/f543c5319ebd96b7701dc1f2d915f21b095dfb35adbb8dc851630e098d526a50/_data",
  "Name": "f543c5319ebd96b7701dc1f2d915f21b095dfb35adbb8dc851630e098d526a50",
  "Options": null,
  "Scope": "local"
}
]

Você vê que o Docker criou e gerencia um volume no sistema de arquivos host do Docker em /var/lib/docker/volumes/$VOLUME_NAME/_data. Observe que este não é um caminho na máquina host, mas sim uma parte do sistema de arquivos gerenciado pelo Docker.

Crie um novo banco de dados mydb e insira um novo documento com uma mensagem hello world.

curl -X PUT -u admin:passw0rd1 http://127.0.0.1:5984/mydb
curl -X PUT -u admin:passw0rd1 http://127.0.0.1:5984/mydb/1 -d '{"msg": "hello world"}'

Pare o container e inicie o container novamente:

docker stop my-couchdb
docker start my-couchdb

Recupere o documento no banco de dados para testar se os dados foram persistidos.

curl -X GET -u admin:passw0rd1 http://127.0.0.1:5984/mydb/_all_docs
curl -X GET -u admin:passw0rd1 http://127.0.0.1:5984/mydb/1

Saída:

## $ curl -X GET -u admin:passw0rd1 http://127.0.0.1:5984/mydb/_all_docs
{"total_rows":1,"offset":0,"rows":[
{"id":"1","key":"1","value":{"rev":"1-c09289617e06b96bc747fb1201fea7f1"}}
]}

## $ curl -X GET -u admin:passw0rd1 http://127.0.0.1:5984/mydb/1
{"_id":"1","_rev":"1-c09289617e06b96bc747fb1201fea7f1","msg":"hello world"}

Compartilhando Volumes

Você pode compartilhar um volume anônimo com outro container usando a opção --volumes-from.

Crie um container busybox com um volume anônimo montado em um diretório /data no container e, usando comandos de shell, escreva uma mensagem em um arquivo de log.

$ docker run -it --name busybox1 -v /data busybox sh
/ ## echo "hello from busybox1" > /data/hi.log
/ ## ls /data
hi.log
/ ## exit

Certifique-se de que o container busybox1 esteja parado, mas não removido.

labex:~/ $ docker ps -a | grep busybox1
f4dbf9ee7513   busybox                               "sh"                     2 minutes ago   Exited (0) About a minute ago                                                                                                                                          busybox1

Em seguida, crie um segundo container busybox chamado busybox2 usando a opção --volumes-from para compartilhar o volume criado por busybox1:

$ docker run --rm -it --name busybox2 --volumes-from busybox1 busybox sh
/ ## ls -al /data
total 12
drwxr-xr-x 2 root root 4096 Jan 23 07:20 .
drwxr-xr-x 1 root root 4096 Jan 23 07:24 ..
-rw-r--r-- 1 root root 20 Jan 23 07:20 hi.log
/ ## cat /data/hi.log
hello from busybox1
/ ## exit

O Docker criou o volume anônimo que você pôde compartilhar usando a opção --volumes-from e criou um novo volume anônimo.

labex:~/ $ docker volume ls
DRIVER VOLUME NAME
local 0f971b2477d5fc0d0c2b31fc908ee59d6b577b4887e381964650ce6853890dc9
local 1d292aca855adb9de9be7acea88f6d3f8e6a08eef5bfd986a81f073f1906b82f

Limpe os volumes e o container existentes.

docker stop my-couchdb
docker rm my-couchdb
docker rm busybox1
docker volume rm $(docker volume ls -q)
docker system prune -a
clear

Volume Nomeado

Um volume nomeado e um volume anônimo são semelhantes no sentido de que o Docker gerencia onde eles estão localizados. No entanto, um volume nomeado pode ser referenciado por nome ao montá-lo em um diretório de container. Isso é útil se você deseja compartilhar um volume entre vários containers.

Primeiro, crie um volume nomeado:

docker volume create my-couchdb-data-volume

Verifique se o volume foi criado:

$ docker volume ls
DRIVER VOLUME NAME
local my-couchdb-data-volume

Agora, crie o container CouchDB chamado my-couchdb-name-vol usando o volume nomeado:

docker run -d -p 59840:5984 --name my-couchdb-name-vol -v my-couchdb-data-volume:/opt/couchdb/data -e COUCHDB_USER=admin -e COUCHDB_PASSWORD=passw0rd1 couchdb:3.1

Aguarde até que o container CouchDB esteja em execução e a instância esteja disponível.

Crie um novo banco de dados mydb e insira um novo documento com uma mensagem hello world.

curl -X PUT -u admin:passw0rd1 http://127.0.0.1:59840/mydb
curl -X PUT -u admin:passw0rd1 http://127.0.0.1:59840/mydb/1 -d '{"msg": "hello world"}'

Agora é fácil compartilhar o volume com outro container. Por exemplo, leia o conteúdo do volume usando a imagem busybox e compartilhe o volume my-couchdb-data-volume montando o volume em um diretório no container busybox.

labex:~/ $ docker run --rm -it --name busybox -v my-couchdb-data-volume:/myvolume busybox sh
/ #
/ ## ls -al /myvolume
total 40
drwxr-xr-x 4 5984 5984 4096 Jan 23 07:30 .
drwxr-xr-x 1 root root 4096 Jan 23 07:31 ..
drwxr-xr-x 2 5984 5984 4096 Jan 23 07:29 .delete
-rw-r--r-- 1 5984 5984 8388 Jan 23 07:30 _dbs.couch
-rw-r--r-- 1 5984 5984 8385 Jan 23 07:29 _nodes.couch
drwxr-xr-x 4 5984 5984 4096 Jan 23 07:30 shards
/ ## exit

Você pode verificar o sistema de arquivos gerenciado pelo Docker para volumes executando um container busybox com permissão privilegiada e definindo o ID do processo como host para inspecionar o sistema host e navegar pelos diretórios gerenciados pelo Docker.

docker run -it --privileged --pid=host busybox nsenter -t 1 -m -u -n -i sh
/ ## ls -l /var/lib/docker/volumes
total 28
-rw------- 1 root root 32768 Nov 10 15:54 metadata.db
drwxr-xr-x 3 root root 4096 Nov 10 15:54 my-couchdb-data-volume
/ ## exit

Limpeza:

docker stop my-couchdb
docker rm my-couchdb
docker volume rm my-couchdb-data-volume
docker system prune -a
docker volume prune
clear

Volume Host

Quando você deseja acessar o diretório do volume facilmente da máquina host diretamente, em vez de usar os diretórios gerenciados pelo Docker, você pode criar um volume host.

Vamos usar um diretório no diretório de trabalho atual (indicado com o comando pwd) chamado data, ou escolher seu próprio diretório de dados na máquina host, por exemplo, /home/couchdb/data. Deixamos o docker criar o diretório $(pwd)/data se ele ainda não existir. Montamos o volume host dentro do container CouchDB no diretório do container /opt/couchdb/data, que é o diretório de dados padrão para CouchDB.

Execute o seguinte comando:

cd /home/labex/project
docker run -d -p 5984:5984 --name my-couchdb -v $(pwd)/data:/opt/couchdb/data -e COUCHDB_USER=admin -e COUCHDB_PASSWORD=passw0rd1 couchdb:3.1

Verifique se um diretório data foi criado:

$ ls -al
total 20
drwxrwxr-x 3 labex labex 4096 Aug 29 14:14 .
drwxr-x--- 25 labex labex 4096 Aug 29 14:14 ..
drwxr-xr-x 3 5984 5984 4096 Aug 29 14:14 data

e que o CouchDB criou arquivos de dados aqui:

$ ls -al data
total 32
drwxr-xr-x 3 5984 5984 4096 Aug 29 14:14 .
drwxrwxr-x 3 labex labex 4096 Aug 29 14:14 ..
-rw-r--r-- 1 5984 5984 4257 Aug 29 14:14 _dbs.couch
drwxr-xr-x 2 5984 5984 4096 Aug 29 14:14 .delete
-rw-r--r-- 1 5984 5984 8385 Aug 29 14:14 _nodes.couch

Verifique também que, agora, nenhum volume gerenciado foi criado pelo docker, porque estamos usando um volume host.

docker volume ls

e

docker run -it --privileged --pid=host busybox nsenter -t 1 -m -u -n -i sh
sh-5.1## ls -l /var/lib/docker/volumes
total 28
brw------- 1 root root 252, 3 Jan 23 15:15 backingFsBlockDev
-rw------- 1 root root 32768 Jan 23 15:33 metadata.db
drwx-----x 3 root root 4096 Jan 23 15:26 my-couchdb-data-volume
sh-5.1## exit

Crie um novo banco de dados mydb e insira um novo documento com uma mensagem hello world.

curl -X PUT -u admin:passw0rd1 http://127.0.0.1:5984/mydb
curl -X PUT -u admin:passw0rd1 http://127.0.0.1:5984/mydb/1 -d '{"msg": "hello world"}'

Observe que o CouchDB criou uma pasta shards:

$ ls -al data
total 40
drwxr-xr-x 4 5984 5984 4096 Aug 29 14:15 .
drwxrwxr-x 3 labex labex 4096 Aug 29 14:14 ..
-rw-r--r-- 1 5984 5984 8388 Aug 29 14:15 _dbs.couch
drwxr-xr-x 2 5984 5984 4096 Aug 29 14:14 .delete
-rw-r--r-- 1 5984 5984 8385 Aug 29 14:14 _nodes.couch
drwxr-xr-x 4 5984 5984 4096 Aug 29 14:15 shards

Liste o conteúdo do diretório shards:

$ ls -al data/shards
total 16
drwxr-xr-x 4 5984 5984 4096 Aug 29 14:15 .
drwxr-xr-x 4 5984 5984 4096 Aug 29 14:15 ..
drwxr-xr-x 2 5984 5984 4096 Aug 29 14:15 00000000-7fffffff
drwxr-xr-x 2 5984 5984 4096 Aug 29 14:15 80000000-ffffffff

e o primeiro shard:

$ ls -al data/shards/00000000-7fffffff/
total 20
drwxr-xr-x 2 5984 5984 4096 Aug 29 14:15 .
drwxr-xr-x 4 5984 5984 4096 Aug 29 14:15 ..
-rw-r--r-- 1 5984 5984 8346 Aug 29 14:15 mydb.1693289721.couch

Um shard é uma partição horizontal de dados em um banco de dados. A partição de dados em shards e a distribuição de cópias de cada shard para diferentes nós em um cluster dão aos dados maior durabilidade contra perda de nós. O CouchDB divide automaticamente os bancos de dados e distribui os subconjuntos de documentos entre os nós.

Limpeza:

docker stop my-couchdb
docker rm my-couchdb
sudo rm -rf $(pwd)/data
docker system prune -a

Bind Mounts

A sintaxe mount é recomendada pelo Docker em vez da sintaxe volume. Bind mounts têm funcionalidade limitada em comparação com volumes. Um arquivo ou diretório é referenciado por seu caminho completo na máquina host quando montado em um container. Bind mounts dependem do sistema de arquivos da máquina host ter uma estrutura de diretórios específica disponível e você não pode usar a CLI do Docker para gerenciar bind mounts. Observe que bind mounts podem alterar o sistema de arquivos host por meio de processos em execução em um container.

Em vez de usar a sintaxe -v com três campos separados por dois pontos (:), a sintaxe mount é mais verbosa e usa vários pares chave-valor:

  • type: bind, volume ou tmpfs,
  • source: caminho para o arquivo ou diretório na máquina host,
  • destination: caminho no container,
  • readonly,
  • bind-propagation: rprivate, private, rshared, shared, rslave, slave,
  • consistency: consistent, delegated, cached,
  • mount.
cd /home/labex/project
mkdir data
docker run -it --name busybox --mount type=bind,source="$(pwd)"/data,target=/data busybox sh

Digite o comando no container:

echo "hello busybox" > /data/hi.txt
exit

Verifique se o arquivo foi criado na máquina host.

cat data/hi.txt

[Opcional] OverlayFS

OverlayFS é uma implementação de sistema de arquivos de montagem de união (union mount filesystem) para Linux. Para entender o que é um volume Docker, é útil entender como as camadas e o sistema de arquivos funcionam no Docker.

Para iniciar um container, o Docker pega a imagem somente leitura e cria uma nova camada de leitura-gravação por cima. Para visualizar as camadas como uma só, o Docker usa um Sistema de Arquivos de União ou OverlayFS (Overlay File System), especificamente o driver de armazenamento overlay2.

Para ver os arquivos gerenciados pelo host Docker, você precisa de acesso ao sistema de arquivos do processo Docker. Usando as flags --privileged e --pid=host, você pode acessar o namespace de ID do processo do host de dentro de um container como busybox. Você pode então navegar para o diretório /var/lib/docker/overlay2 do Docker para ver as camadas baixadas que são gerenciadas pelo Docker.

Para visualizar a lista atual de camadas no Docker:

$ docker run -it --privileged --pid=host busybox nsenter -t 1 -m -u -n -i sh

/ ## ls -l /var/lib/docker/overlay2
total 16
drwx------ 3 root root 4096 Sep 25 19:44 0e55ecaa4d17c353191e68022d9a17fde64fb5e9217b07b5c56eb4c74dad5b32
drwx------ 5 root root 4096 Sep 25 19:44 187854d05ccd18980642e820b0d2be6a127ba85d8ed96315bb5ae37eb1add36d
drwx------ 4 root root 4096 Sep 25 19:44 187854d05ccd18980642e820b0d2be6a127ba85d8ed96315bb5ae37eb1add36d-init
drwx------ 2 root root 4096 Sep 25 19:44 l

/ ## exit

Faça o pull da imagem ubuntu e verifique novamente:

docker pull ubuntu
docker run -it --privileged --pid=host busybox nsenter -t 1 -m -u -n -i sh

Digite o comando para ver a lista de camadas novamente:

ls -l /var/lib/docker/overlay2/ & exit

Você vê que, ao baixar a imagem ubuntu, implicitamente foram baixadas 4 novas camadas:

  • a611792b4cac502995fa88a888261dfba0b5d852e72f9db9e075050991423779
  • d181f1a41fc35a45c16e8bfcb8eee6f768f3b98f82210a43ea65f284a45fcd65
  • dac2f37f6280a076836d39b87b0ae5ebf5c0d386b6d8b991b103aadbcebaa7c6
  • f3e921b440c37c86d06cd9c9fb70df50edad553c36cc87f84d5eeba734aae709

O driver de armazenamento overlay2 essencialmente sobrepõe diferentes diretórios no host e os apresenta como um único diretório.

  • camada base ou lowerdir,
  • camada diff ou upperdir,
  • camada overlay (visão do usuário), e
  • diretório work.

OverlayFS se refere aos diretórios inferiores como lowerdir, que contém a imagem base e as camadas somente leitura (R/O) que são baixadas.

O diretório superior é chamado de upperdir e é a camada de container de leitura-gravação (R/W).

A visão unificada ou camada overlay é chamada de merged.

Finalmente, um workdir é necessário, que é um diretório vazio usado pelo overlay para uso interno.

O driver overlay2 suporta até 128 camadas OverlayFS inferiores. O diretório l contém identificadores de camada abreviados como links simbólicos.

Overlay2 Storage Driver

Limpeza:

docker system prune -a
clear

Resumo

Neste laboratório, você aprendeu como gerenciar dados em containers usando volumes e bind mounts. Você também aprendeu sobre o driver de armazenamento overlay2 e como ele usa o union file system para gerenciar camadas.