Volumes Docker
https://docs.docker.com/storage/
Quando falamos de volumes estamos falando em storage que é o armazenamento de dados.
- Os dados criados em um container são armazenados na camada read write que é considerado uma camada gradável, ou seja, não persistente. Quando o container morre os dados são perdidos.
- A camada gradavel é acoplada a máquina host e não pode ser movido facilmente para outro lugar.
- O uso de volumes grava diretamente no sistema de arquivos do host que o container esta sendo executado.
- A gravação de dados na camada mais acima necessita de uma abstração de sistema de arquivos que reduz o desempenho comparado ao uso de volumes.
Storage Drivers Suportados
https://docs.docker.com/storage/storagedriver/select-storage-driver/
Driver | Description |
---|---|
overlay2 | É o preferido e não requer nenhuma configuração estra. É a evolução do aufs. |
fuse-overlayfs | Somente usado quando o overlay2 não é suportado |
btrfs and zfs | Utiliza o sistema Copy on Write (CoW) que utiliza o mesmo arquivo quando faz leitura, mas quando necessita de escrita ele cria um arquivo novo. Permite opções mais avançadas, mas também mais configuração e manutenção. Possibilita combinar varios dispositivos fisicos para formar um único sistema de arquivos |
vfs | O desempenho desse driver é ruim e geralmente não é recomendado para uso em produção. Grava direto no disco e não tem suporte ao CoW |
aufs | Usado somente e versões muito antigas do kernel baseado em debian que não suportam overlay2. |
devicemapper | Desempenho muito ruim e somente usado em versões antigas do kernel baseado em RedHat |
overlay | Obsoleto e legado nem perca tempo usando |
Para saber qual o systema de arquivo esta sendo usado no momento
docker system info
# ou
vagrant@worker1:~$ docker system info | grep Storage
WARNING: No swap limit support
Storage Driver: overlay2
vagrant@worker1:~$
Modificar o Storage Drive requer uma mudança no daemon em /etc/docker/daemon.json
Vamos utilizar o json abaixo para fazer uma mudança de teste
{
"storage-driver": "devicemapper"
}
vagrant@worker1:~$ sudo tee /etc/docker/daemon.json << EOF
{
"storage-driver": "devicemapper"
}
EOF
vagrant@worker1:~$ sudo systemctl restart docker
vagrant@worker1:~$ sudo docker system info | grep Storage
WARNING: No swap limit support
WARNING: the devicemapper storage-driver is deprecated, and will be removed in a future release.
WARNING: devicemapper: usage of loopback devices is strongly discouraged for production use.
Use `--storage-opt dm.thinpooldev` to specify a custom block storage device.
# observe como mudou o driver do nosso storage
Storage Driver: devicemapper
vagrant@worker1:~$
volte para overlay2 para continuar o resto do nosso lab.
vagrant@worker1:~$ sudo tee /etc/docker/daemon.json << EOF
{
"storage-driver": "overlay2"
}
EOF
{
"storage-driver": "overlay2"
}
vagrant@worker1:~$ sudo systemctl restart docker
vagrant@worker1:~$ sudo docker system info | grep Storage
WARNING: No swap limit support
Storage Driver: overlay2
vagrant@worker1:~$
Volumes
O volume é basicamente um diretório que é designado para trabalhar em um ou vários containers. É uma forma de compartilhar os dados para que vc possa ter vários processos lendo e escrevendo do mesmo lugar. Se o container morrer, os dados ficam persistentes no volume, logo quando o container voltar a rodar os dados não foram perdidos. Quando um container morre ele não exclui o volume automaticamente. É um recurso separado e gerenciado.
3 tipos básicos:
-
bind mount (volume que reside dentro do mesmo host que está rodando o docker) é quando o container esta usando arquivos direto o filesystem do host. Uma diretório dentro do container reflete em um difetório no host.
-
volume (tipo nomeado ou anonimo) é um sistema de arquivo que o docker gerencia para disponibilizar dentro do container. uma virtual maquina não tem um arquivo que representa o seu hd? É a mesma coisa.
- É melhor do que utilizar o bind
- Melhor para fazer backup
- Pode ser gerenciado pelo cli do docker
- É compartilhado por multiplos containers de maneira mais segura
- Pode ser habilitado armazenamento externo em cloud por exemplo.
- volumes ficam dentro de /var/lib/docker
-
tmpfs mount é um volume do tipo temporário, ele reside na memória ram da máquina, se o host por exemplo crashar vc perdeu o conteúdo. É interessante para uso em ambiente de muita performance. Muito poucos casos de uso.
Commandos
o comando docker volume
vagrant@worker1:~$ docker volume --help
Commands:
create Create a volume
inspect Display detailed information on one or more volumes
ls List volumes
prune Remove all unused local volumes
rm Remove one or more volumes
no comando de manipulação de container docker container se tiver o parametro -v ou --volume ele esta passando o volume para o container. A opção --mount é uma opção mais explicita e mais verbosa.
-
-v : funciona para bind mount
-
--mount : funciona para o bind mount e para o volumes inclusive para servicos cloud, etc
Criando uma container com bind mount e confeindo
#host:container
docker container run -dit --name servidor -v /srv:/srv debian
# Executando no container
vagrant@worker1:~$ docker container exec servidor df -Th
Filesystem Type Size Used Avail Use% Mounted on
# veja o overlay aqui dentro do container
overlay overlay 39G 2.3G 37G 6% /
tmpfs tmpfs 64M 0 64M 0% /dev
tmpfs tmpfs 489M 0 489M 0% /sys/fs/cgroup
shm tmpfs 64M 0 64M 0% /dev/shm
/dev/sda1 ext4 39G 2.3G 37G 6% /srv
tmpfs tmpfs 489M 0 489M 0% /proc/acpi
tmpfs tmpfs 489M 0 489M 0% /proc/scsi
tmpfs tmpfs 489M 0 489M 0% /sys/firmware
# Executando agora no host
vagrant@worker1:~$ df -Th
Filesystem Type Size Used Avail Use% Mounted on
udev devtmpfs 472M 0 472M 0% /dev
tmpfs tmpfs 98M 1.1M 97M 2% /run
# Veja que o sda1 tem o mesmo tamanho que o overlay do container, ou seja, é o mesmo
/dev/sda1 ext4 39G 2.3G 37G 6% /
tmpfs tmpfs 489M 0 489M 0% /dev/shm
tmpfs tmpfs 5.0M 0 5.0M 0% /run/lock
tmpfs tmpfs 489M 0 489M 0% /sys/fs/cgroup
/dev/loop0 squashfs 62M 62M 0 100% /snap/core20/1518
/dev/loop1 squashfs 47M 47M 0 100% /snap/snapd/16010
/dev/loop2 squashfs 68M 68M 0 100% /snap/lxd/22753
vagrant vboxsf 440G 48G 392G 11% /vagrant
tmpfs tmpfs 98M 0 98M 0% /run/user/1000
Ele simplesmente montou o caminho /srv no host dentro de /srv no container. Se criamos qualquer arquivo lá dentro em qualquer um dos lados irá aparecer.
# Veja que apareceu o srv no /
vagrant@worker1:~$ ls /
bin boot dev etc home lib lib32 lib64 libx32 lost+found media mnt opt proc root run sbin snap srv sys tmp usr vagrant var
vagrant@worker1:~$ cd /srv/
vagrant@worker1:~$ cd /srv/
vagrant@worker1:/srv$ sudo touch arquivo1.txt
vagrant@worker1:/srv$ docker container exec servidor ls -lR /srv
/srv:
total 0
-rw-r--r-- 1 root root 0 Jun 23 17:11 arquivo1.txt
vagrant@worker1:/srv$ docker container exec servidor touch /srv/arquivo2.txt
# depois de criado dentro do container, ele mostra no host
vagrant@worker1:/srv$ ls
arquivo1.txt arquivo2.txt
##
vagrant@worker1:/srv$ docker container rm -f servidor
servidor
vagrant@worker1:/srv$ docker container ls -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
vagrant@worker1:/srv$
Criando um container com volume anonimo
Quando eu passo somente o volume sem apontar origem e destino ele cria um volume dentro de /var/lib/docker/volumes um volume com um hash de 64 chars para o volume.
vagrant@worker1:~$ docker container run -dit --name servidor -v /srv debian
vagrant@worker1:~$ docker volume ls
DRIVER VOLUME NAME
local 7f17a7c16d36d501b49da3cf2d7a7fda9c384ac145387377fa4cf099556ae5ec
vagrant@worker1:~$ docker container inspect servidor | grep volume
"Type": "volume",
"Source": "/var/lib/docker/volumes/7f17a7c16d36d501b49da3cf2d7a7fda9c384ac145387377fa4cf099556ae5ec/_data",
vagrant@worker1:~$
Observando o inspect veja que o hash do volume é o mesmo do volume que temos.
obs: Os volumes do docker ficam em /var/lib/docker/volumes
Um volume sempre tem o _data que é de onde fato estão os dados, mas no mesmo nível pode ter outros arquivos de metadata do volume, tipo permissões.
também é possível inspecionar o volume
vagrant@worker1:~$ docker volume inspect 7f17a7c16d36d501b49da3cf2d7a7fda9c384ac145387377fa4cf099556ae5ec
[
{
"CreatedAt": "2022-06-23T17:48:15Z",
"Driver": "local",
"Labels": null,
"Mountpoint": "/var/lib/docker/volumes/7f17a7c16d36d501b49da3cf2d7a7fda9c384ac145387377fa4cf099556ae5ec/_data",
"Name": "7f17a7c16d36d501b49da3cf2d7a7fda9c384ac145387377fa4cf099556ae5ec",
"Options": null,
"Scope": "local"
}
]
vagrant@worker1:~$ docker container exec servidor touch /srv/arquivoVolume.txt
vagrant@worker1:~$ docker container inspect servidor | grep volume
"Type": "volume",
"Source": "/var/lib/docker/volumes/7f17a7c16d36d501b49da3cf2d7a7fda9c384ac145387377fa4cf099556ae5ec/_data",
vagrant@worker1:~$ sudo ls /var/lib/docker/volumes/7f17a7c16d36d501b49da3cf2d7a7fda9c384ac145387377fa4cf099556ae5ec/_data
arquivoVolume.txt
Se for criado um volume passando o nome sem o / na frente, ele entenderá é só um nome não a origem
vagrant@worker1:~$ docker container run -dit --name servidor -v volumedebian:/srv debian
76540b187448053054b227f20ba5a1bdab43185857c35dabf2c248f2aa42968e
vagrant@worker1:~$ docker volume list
DRIVER VOLUME NAME
local 7f17a7c16d36d501b49da3cf2d7a7fda9c384ac145387377fa4cf099556ae5ec
local volumedebian
Criando um container usando o --mount
Como falado anteriomente o --mount requer mais parametros.
vagrant@worker1:~$ docker container run -dit --name servidor --mount source=volume2,target=/meuvolume debian
be42b33fb8b6f928ceac632ec791c3da632b1a24ab36b239bab5e1e2c6eed597
vagrant@worker1:~$ docker volume list
DRIVER VOLUME NAME
local 7f17a7c16d36d501b49da3cf2d7a7fda9c384ac145387377fa4cf099556ae5ec
local volume2
local volumedebian
vagrant@worker1:~$
vagrant@worker1:~$ docker container inspect servidor --format '{{json .Mounts}}' | jq
[
{
"Type": "volume",
"Name": "volume2",
"Source": "/var/lib/docker/volumes/volume2/_data",
"Destination": "/meuvolume",
"Driver": "local",
# esse mode z (minisculo) quer dizer que compartilha entre multiplus containers se fosse Z (maiusculo) significa que não compartilha
"Mode": "z",
"RW": true,
"Propagation": ""
}
]
Vamos remover os volumes
vagrant@worker1:~$ docker volume ls
DRIVER VOLUME NAME
local 7f17a7c16d36d501b49da3cf2d7a7fda9c384ac145387377fa4cf099556ae5ec
local volume2
local volumedebian
vagrant@worker1:~$ docker volume rm volume2 volumedebian 7f17a7c16d36d501b49da3cf2d7a7fda9c384ac145387377fa4cf099556ae5ec
volumedebian
7f17a7c16d36d501b49da3cf2d7a7fda9c384ac145387377fa4cf099556ae5ec
Error response from daemon: remove volume2: volume is in use - [be42b33fb8b6f928ceac632ec791c3da632b1a24ab36b239bab5e1e2c6eed597]
# observe que não é possível remover um volume em uso por um container
vagrant@worker1:~$ docker volume ls
DRIVER VOLUME NAME
local volume2
vagrant@worker1:~$ docker container rm -f servidor
servidor
# O comando prune remove todos os volumes que não estão em uso por nenhum container
vagrant@worker1:~$ 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:
volume2
Total reclaimed space: 0B
vagrant@worker1:~$ docker volume ls
DRIVER VOLUME NAME
vagrant@worker1:~$
tmpfs mounts
--tmppfs
Como falado anteriormente é utilizado em memória ram. Somente interessante para aumentar o input e output e aplicações necessárias. Também é util para dados sensíveis que vc não quer gravar no disco.
- Não é possível passar parametro como tamanho limite, etc
- Não tem origem, pois a origem é a memória, somente o destino.
- Esse parametro não funciona em cluster, nesse caso seria necessário usar o --mount type=tmpfs
- tpmfs em swap não é possível
vagrant@worker1:~$ docker container run -dit --name servidor --tmpfs /volumetpmfs debian
30ce1b88b693104ec78c0360ed87dad23da7da74e410d8784ae88db36244cf91
vagrant@worker1:~$ docker container run -dit --name servidor2 --mount type=tmpfs,destination=/volumetmpfs debian
6d85918d60734564df7a3ed8e5937c3f446a5a9d0158fa398a43a53643aa5998
vagrant@worker1:~$
# observe que não tem mount quando subimos com o --tmpfs
vagrant@worker1:~$ docker container inspect servidor -f '{{json .Mounts}}' | jq
[]
vagrant@worker1:~$ docker container inspect servidor -f '{{json .HostConfig.Tmpfs}}' | jq
{
"/volumetpmfs": ""
}
vagrant@worker1:~$ docker container inspect servidor2 -f '{{json .Mounts}}' | jq
[
{
"Type": "tmpfs",
"Source": "",
"Destination": "/volumetmpfs",
"Mode": "",
"RW": true,
"Propagation": ""
}
]
# Não tem no HostConfig.Tmpfs quando subimos com o --mount
vagrant@worker1:~$ docker container inspect servidor2 -f '{{json .HostConfig.Tmpfs}}' | jq
null
vagrant@worker1:~$ docker container exec servidor df -Th
Filesystem Type Size Used Avail Use% Mounted on
overlay overlay 39G 2.4G 37G 6% /
tmpfs tmpfs 64M 0 64M 0% /dev
# criou um tmpfs aqui com o tamanho de 500M que é o padrão dele
tmpfs tmpfs 489M 0 489M 0% /sys/fs/cgroup
shm tmpfs 64M 0 64M 0% /dev/shm
tmpfs tmpfs 489M 0 489M 0% /volumetpmfs
/dev/sda1 ext4 39G 2.4G 37G 6% /etc/hosts
tmpfs tmpfs 489M 0 489M 0% /proc/acpi
tmpfs tmpfs 489M 0 489M 0% /proc/scsi
tmpfs tmpfs 489M 0 489M 0% /sys/firmware
Para alterar o tamanho padrão ou permissão somente através do --mount
# removendo o containers e criar novamente com um tamanho fixo
vagrant@worker1:~$ docker container rm -f $(docker container ls -aq)
6d85918d6073
30ce1b88b693
vagrant@worker1:~$ docker container run -dit --name servidor --mount type=tmpfs,destination=/volumetmpfs,tmpfs-size=100M debian
vagrant@worker1:~$ docker container inspect servidor -f '{{json .HostConfig.Mounts}}' | jq
[
{
"Type": "tmpfs",
"Target": "/volumetmpfs",
"TmpfsOptions": {
"SizeBytes": 104857600
}
}
]
vagrant@worker1:~$ docker container exec servidor df -Th
Filesystem Type Size Used Avail Use% Mounted on
overlay overlay 39G 2.4G 37G 6% /
tmpfs tmpfs 64M 0 64M 0% /dev
tmpfs tmpfs 489M 0 489M 0% /sys/fs/cgroup
shm tmpfs 64M 0 64M 0% /dev/shm
# Mostrando aqui os 100M
tmpfs tmpfs 100M 0 100M 0% /volumetmpfs
/dev/sda1 ext4 39G 2.4G 37G 6% /etc/hosts
tmpfs tmpfs 489M 0 489M 0% /proc/acpi
tmpfs tmpfs 489M 0 489M 0% /proc/scsi
tmpfs tmpfs 489M 0 489M 0% /sys/firmware
tentei baixar uma imagem do ubuntu dentro do volume de 100M para ver o que acontecia
vagrant@worker1:~$ docker attach servidor
root@467701c637fc:/ apt-get update && apt-get install wget
....
root@467701c637fc:/ cd /volumetmpfs
root@467701c637fc:/volumetmpfs# wget https://releases.ubuntu.com/22.04/ubuntu-22.04-desktop-amd64.iso
.........
......... # Parte retirada
ubuntu-22.04-desktop-amd64.iso 2%[=> ] 100.00M 21.7MB/s in 5.4s
# veja que estourou o espaço
Cannot write to 'ubuntu-22.04-desktop-amd64.iso' (No space left on device).
root@467701c637fc:/volumetmpfs#
Outro cenário que testei foi criar um container com o tpmfs maior do que a memória do host e ele permitiu, porém quando fiz o wget da imagem do ubuntu, o mesmo procedimento acima.. quando atintiu perto de 620MB de download, a máquina se tornou extramamente lenta e pouco responsiva. Então use com sabedoria. Não chegou a crashar a máquina, mas pode afetar outros container o uso indevido do tmpfs.
vagrant@worker1:~$ free -h
total used free shared buff/cache available
Mem: 976Mi 207Mi 634Mi 0.0Ki 134Mi 627Mi
Swap: 0B 0B 0B
vagrant@worker1:~$ docker container run -dit --name servidor --mount type=tmpfs,destination=/volumetmpfs,tmpfs-size=2000M debian
7355006196d27418913d904dd7e5ea8c9bf3187c44e171b22bcb6ea13f36d463
vagrant@worker1:~$
Backup e restore
Vamos subir um container e colocar um arquivo dentro
vagrant@worker1:~$ docker container run -dit --name servidor -v /webdata debian
e8d3713e08bb7c35346f5b230fbd322e04e82497f58cc1513861563e067d4c3a
vagrant@worker1:~$ mkdir -p ~/dockerfiles/images/
vagrant@worker1:~$ echo "{FROM debian} "~/dockerfiles/images/Dockerfile
{FROM debian} ~/dockerfiles/images/Dockerfile
#origem #destino maquina:path
vagrant@worker1:~$ docker container cp ./dockerfiles/ servidor:/webdata
vagrant@worker1:~$ docker container exec servidor ls -lha /webdata
total 12K
drwxr-xr-x 3 root root 4.0K Jun 23 20:50 .
drwxr-xr-x 1 root root 4.0K Jun 23 20:50 ..
drwxrwxr-x 3 1000 1000 4.0K Jun 23 20:47 dockerfiles
vagrant@worker1:~$ docker volume ls
DRIVER VOLUME NAME
local 4806a0c128952f1fc2cc3c1101c8c465a5233fc0203cd87fa294fbad19fe13da
vagrant@worker1:~$ docker container inspect servidor | grep volume
"Type": "volume",
"Source": "/var/lib/docker/volumes/4806a0c128952f1fc2cc3c1101c8c465a5233fc0203cd87fa294fbad19fe13da/_data",
Agora vamos subir outro container apontando pro mesmo volume
vagrant@worker1:~$ docker container run -dit --name servidor2 -v 4806a0c128952f1fc2cc3c1101c8c465a5233fc0203cd87fa294fbad19fe13da:/webdata debian
e8ac0f51f6eaa42401da39ae6e2fec182970c5b04b8fa5456cdc216c9657e384
vagrant@worker1:~$ docker container exec servidor2 ls -lha /webdata
total 12K
drwxr-xr-x 3 root root 4.0K Jun 23 20:50 .
drwxr-xr-x 1 root root 4.0K Jun 23 20:56 ..
drwxrwxr-x 3 1000 1000 4.0K Jun 23 20:47 dockerfiles
Porém isso é meio inviável e existe uma flag chamada --volumes-from que melhora muito como fazer isso.
vagrant@worker1:~$ docker container run -dit --name servidor3 --volumes-from servidor debian
c60e0d3f1f1ddc935414d98b424ff5964cfbaf540e257b3797b78ed7f44013f1
# Verificando se o volume esta lá do jeito que estava no servidor 1
vagrant@worker1:~$ docker container exec servidor3 ls -lha /webdata
total 12K
drwxr-xr-x 3 root root 4.0K Jun 23 20:50 .
drwxr-xr-x 1 root root 4.0K Jun 23 20:57 ..
drwxrwxr-x 3 1000 1000 4.0K Jun 23 20:47 dockerfiles
Aproveitando o que aprendemos aqui vamos agora subir um container alpine para fazer um backup do volume
vagrant@worker1:~$ docker container ls
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
c60e0d3f1f1d debian "bash" 31 minutes ago Up 31 minutes servidor3
e8ac0f51f6ea debian "bash" 33 minutes ago Up 33 minutes servidor2
e8d3713e08bb debian "bash" 43 minutes ago Up 43 minutes servidor
vagrant@worker1:~$ mkdir backup
vagrant@worker1:~$ docker container run -dit --rm --volumes-from servidor -v ./backup:/backup alpine tar cvf /backup/backup.tar /webdata
c6ffeaa65384af74738c81cea51504795c4361fad0fca52055856b21186ae08b
vagrant@worker1:~$ ls backup/
backup.tar
vagrant@worker1:~$ tar tvf backup/backup.tar
drwxr-xr-x root/root 0 2022-06-23 20:50 webdata/
drwxrwxr-x 1000/1000 0 2022-06-23 20:47 webdata/dockerfiles/
drwxrwxr-x 1000/1000 0 2022-06-23 20:47 webdata/dockerfiles/images/
Agora vamos criar outro container com o arquivo backup.tar que criamos mas antes vamos apagar todos os containers e volumes.
# como vimos no ultimo comando acima existe a pasta webcata que contem o restante, o strip 1 retira esse nível
docker container run -dit --name servidor -v ~/backup:/webdata debian bash -c "tar -xf /webdata/backup.tar --strip 1 --directory /webdata && rm /webdata/backup.tar"
No caso eu apaguei o backup, mas se não quisesse é só subir uma máquina com um volume vazio e outra com o volumes from e fazer backup de um dir no outro.