Quais são as camadas do Docker?

Quais são as camadas do Docker?

Antes de entender as camadas propriamente ditas, precisamos entender como um dos principais requisitos para essa coisa acontecer, o Copy-On-Write (ou COW para os íntimos), funciona. Nas palavras do próprio Jérome Petazzoni:

It's a little bit like having a book. You can make notes in that book if you want, but each time you approach the pen to the page, suddenly someone shows up and takes the page and makes a xerox copy and hand it back to you, that's exactly how copy on write works.

Em tradução livre, seria como se você tivesse um livro e que fosse permitido fazer anotações nele caso quisesse, porém, cada vez que você estivesse prestes a tocar a página com a caneta, de repente alguém aparecesse, tirasse uma xerox dessa página e entregasse a cópia para você. É exatamente assim que o Copy-On-Write funciona.

Basicamente, significa que um novo recurso, seja ele um bloco no disco ou uma área em memória, só é alocado quando for modificado.

Tá, mas o que isso tudo tem a ver com o Docker? Bom, como você sabe, o Docker usa um esquema de camadas, ou layers, e para montar essas camadas são usadas técnicas de Copy-On-Write. Um container é basicamente uma pilha de camadas compostas por N camadas read-only e uma, a superior, read-write.

Entendendo o Storage drivers

Apesar de um container possuir uma camada de escrita, na maior parte do tempo você não quer escrever dados diretamente nele, por vários motivos, dentre eles a sua natureza volátil. Em situações onde sua aplicação gera dados, você vai preferir usar volumes "atachados" ao container e escrever neles (veremos mais à frente como fazer isso). Porém, em algumas situações é, sim, necessária a escrita local no container, e é aí que o storage driver entra na história. Storage driver é o mecanismo utilizado pela engine do Docker para ditar a forma como esses dados serão manipulados no filesystem do container. A seguir, os principais storage drivers e suas peculiaridades.

O que é o AUFS (Another Union File System)

O primeiro filesystem disponível para o Docker foi o AUFS, um dos mais antigos Copy-On-Write filesystems, e inicialmente teve que passar por algumas modificações a fim de melhorar a estabilidade.

O AUFS funciona no nível de arquivos (não em bloco), e a ideia é ter múltiplos diretórios (camadas) que ele apresenta para o SO como um ponto único de montagem.

Quando você tenta ler um arquivo, a busca é iniciada pela camada superior, até achar o arquivo ou concluir que ele não existe. Para escrever em um arquivo, este precisa primeiro ser copiado para a camada superior (writable) -- e, sim, você adivinhou: escrever em arquivos grandes pode causar certa degradação da performance, já que o arquivo precisaria ser copiado completamente para a primeira camada, mesmo quando uma parte bem pequena vai sofrer alteração.

Já que estamos falando de coisa chata, outra coisa que pode degradar a sua performance usando AUFS é o fato de que ele procura cada diretório de um path em cada camada do filesystem toda vez que você tentar executar um comando. Por exemplo, se você tem um path com cinco camadas, serão realizadas 25 buscas (stat(), uma system call). Isso pode ser bem complicado em aplicações que fazem load dinâmico, como os apps Python que importam os .py da vida.

Outra particularidade é quando algum arquivo é deletado. Quando isso acontece é criado um whiteout para esse arquivo. Em outras palavras, ele é renomeado para ".wh.arquivo" e fica indisponível para o container, já que, né, não dá para apagar de verdade, pois as outras camadas são read-only.

Vamos aprender Device Mapper

Device Mapper é um kernel-based framework da Red Hat usado para algumas abstrações, como, por exemplo, o mapeamento de "blocos físicos" em "blocos lógicos", permitindo técnicas como LVM e RAID. No contexto do Docker, porém, ele se resume ao "thin provisioning target" ou ao storage driver "devicemapper". Assim que essa coisa de Docker começou a andar, o pessoal da Red Hat (e toda a galera que usava alguma distro relacionada com Red Hat) se interessou bastante, só que havia um problema: eles não queriam usar AUFS. Para resolver isso, eles reuniram uma equipe de engenheiros muito habilidosos que adicionaram suporte ao Device Mapper no Docker.

Em se tratando de Docker, o Device Mapper e o AUFS são bem similares: a grande diferença entre eles é que, no Device Mapper, quando você precisa escrever no arquivo, a cópia é feita em nível de blocos, que era um problema lá no AUFS, e com isso você ganha uma granularidade bem maior. Em teoria, o problema que você tinha quando escrevia um arquivo grande desaparece. Por padrão, Device Mapper escreve em arquivos de loopback, o que deixa as coisas mais lentas, mas agora na versão 1.17+ você já pode configurá-lo em modo direct-lvm, que escreve em blocos e, em teoria, resolveria esse problema. É um pouco mais chatinho de configurar, mas é uma solução mais elegante para ambientes em produção.

Além de AUFS e Device Mapper, você também pode usar BRTFS e OverlayFS como storage driver. Por serem tecnologias relativamente jovens, aprecie com moderação.

OverlayFS e OverlayFS2

A bola da vez. Uma versão melhorada do AUFS, o OverlayFS e sua versão seguinte e oficialmente recomendada pelo Docker, o Overlay2, são ambos other union filesystems, mas dessa vez muito mais eficientes, rápidos e com uma implementação muito mais simples.

Por serem union file systems, também compartilham da ideia de juntar vários diretórios em um único ponto de montagem como nosso amigo AUFS, porém, no caso do OverlayFS, apenas dois diretórios são suportados, o que não acontece no Overlay2, que tem suporte multi-layer. Ambos suportam page caching sharing, ou seja, múltiplos containers acessando o mesmo arquivo dividem a mesma entrada no arquivo de paginação, o que é um uso mais eficiente de memória.

Aquele problema antigo do AUFS de ter de copiar todo o arquivo para a camada de cima para escrever nele ainda persiste, porém no OverlayFS ele só é copiado uma vez e fica lá para que as outras escritas no mesmo arquivo possam acontecer mais rápido, então tem uma pequena vantagem.

Nota-se um consumo excessivo de inodes quando se usa OverlayFS. Esse é um problema resolvido no Overlay2, então sempre que possível busque usá-lo -- até porque, no geral, tem uma performance superior. Lembrando que kernel 4.0+ é pré-requisito para usar OverlayFS2.

BTRFS

BTRFS é a geração seguinte de union filesystem. Ele é muito mais space-efficient, suporta muitas tecnologias avançadas de storage e já está incluso no mainline do kernel. O BTRFS, diferentemente do AUFS, realiza operações a nível de bloco e usa um esquema de thin provision parecido com o do Device Mapper e suporta copy-on-write snapshots. Você pode inclusive combinar vários devices físicos em um único BTRFS filesystem, algo como um LVM.

O BTRFS é suportado atualmente na versão CE apenas em distribuições debian-like e na versão EE apenas em SLES (Suse Linux Enterprise Server).

IMPORTANTE: alterar o storage drive fará com que qualquer container já criado se torne inacessível ao sistema local. Cuidado!

 

Sobre o autor:

Olá, eu sou o Jeferson :)
Sou engenheiro de produção com mais de 15 anos de experiência profissional em ambientes de grande escala e de ambientes críticos usando Linux e Unix, incluindo design e instalação de sistemas e serviços, configuração, dimensionamento, automação, planejamento de capacidade, containers, docker, kubernetes, networking, segurança e integração em ambientes corporativos heterogêneos.

Voltar para o blog

Deixe um comentário