Tutorial de instalação do Docker Engine (CE) a partir do repositório oficial da Docker Inc., executado nas três VMs do ambiente docker criado na parte 5 da série Terraform+KVM — cada uma com seu disco extra de 32 GiB dedicado ao /var/lib/docker. Ao final, as três distribuições rodam a mesma versão do Docker com rede dual-stack (IPv4/IPv6).
Pré-requisito: infraestrutura provisionada conforme Discos Extras nas VMs KVM/Libvirt com Terraform — quatro VMs (gateway + 3 hosts Docker) na rede isolada
10.16.1.0/24, acessíveis via ProxyJump pelo gateway.
Versão automatizada: todos os passos manuais deste tutorial estão automatizados com Ansible em Automatizando a Instalação do Docker com Ansible.
| VM | Distribuição | IP interno | Disco extra |
|---|---|---|---|
gateway |
Debian 13 | 10.16.0.230 (externa, DHCP) + 10.16.1.1 |
— |
docker-deb |
Debian 13 | 10.16.1.10 |
32 GiB → /var/lib/docker |
docker-ol |
Oracle Linux 9 | 10.16.1.11 |
32 GiB → /var/lib/docker |
docker-ub |
Ubuntu 24.04 | 10.16.1.12 |
32 GiB → /var/lib/docker |
A definição completa das VMs está no servers.auto.tfvars da parte 5. O acesso usa o ~/.ssh/config com Host 10.16.1.* + ProxyJump gateway; o IP externo do gateway é obtido com virsh domifaddr gateway.
As VMs internas saem para a internet através do gateway — sem este passo, nenhuma delas alcança os repositórios do Docker. Todo o bloco abaixo roda em gateway:
No Debian, o firewall vem desabilitado por padrão (nftables.service em inactive (dead)):
sudo systemctl enable --now nftables
sudo nft list ruleset # apenas a tabela inet filter padrão, policy accept
echo "net.ipv4.ip_forward=1" | sudo tee /etc/sysctl.d/99-forwarding.conf
echo "net.ipv6.conf.all.forwarding=1" | sudo tee -a /etc/sysctl.d/99-forwarding.conf
sudo sysctl -p /etc/sysctl.d/99-forwarding.conf
# Tabelas e chains de NAT (IPv4 e IPv6)
sudo nft add table ip nat
sudo nft add chain ip nat postrouting { type nat hook postrouting priority srcnat\; }
sudo nft add table ip6 nat
sudo nft add chain ip6 nat postrouting { type nat hook postrouting priority srcnat\; }
# Masquerade na interface externa (enp1s0 = rede "external")
sudo nft add rule ip nat postrouting oifname "enp1s0" masquerade
sudo nft add rule ip6 nat postrouting oifname "enp1s0" masquerade
sudo nft list ruleset | sudo tee /etc/nftables.conf
sudo systemctl restart nftables
sudo systemctl status nftables
Verificação final — o ruleset deve conter as duas tabelas nat com masquerade:
table ip nat {
chain postrouting {
type nat hook postrouting priority srcnat; policy accept;
oifname "enp1s0" masquerade
}
}
table ip6 nat {
chain postrouting {
type nat hook postrouting priority srcnat; policy accept;
oifname "enp1s0" masquerade
}
}
/var/lib/dockerO Terraform entregou o vdb cru (sem partição nem sistema de arquivos — o mount_hint era só documental). Antes de instalar o Docker, formatamos e montamos o disco no ponto onde o daemon guardará imagens, containers e volumes. Assim, o /var/lib/docker nasce no disco certo desde o primeiro docker pull.
Ordem importa: disco montado antes da instalação. Se o Docker já estiver instalado, o diretório existente precisa ser migrado — evite esse trabalho seguindo a sequência deste tutorial.
Em docker-deb (10.16.1.10) e docker-ub (10.16.1.12) o procedimento é idêntico. O lsblk mostra o disco extra esperando:
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS
vda 254:0 0 16G 0 disk
├─vda1 254:1 0 243M 0 part /boot/efi
└─vda2 254:2 0 15,8G 0 part /
vdb 254:16 0 32G 0 disk
MOUNT_POINT=/var/lib/docker
DISK_DEVICE=/dev/vdb
# Tabela GPT + partição primária ocupando o disco inteiro
sudo apt update && sudo apt install parted -y
sudo parted -s ${DISK_DEVICE} mklabel gpt
sudo parted -s ${DISK_DEVICE} mkpart primary ext4 0% 100%
# Formatar
sudo mkfs.ext4 ${DISK_DEVICE}1
# Montagem persistente via UUID no fstab (com backup do original)
UUID=$(sudo blkid -o export ${DISK_DEVICE}1 | grep '^UUID=')
sudo mkdir ${MOUNT_POINT}
sudo cp -p /etc/fstab{,.dist}
echo "${UUID} ${MOUNT_POINT} ext4 defaults 0 2" | sudo tee -a /etc/fstab
sudo systemctl daemon-reload
sudo mount ${MOUNT_POINT}
Confirmação:
suporte@docker-deb:~$ df -hT | grep docker
/dev/vdb1 ext4 32G 2,1M 30G 1% /var/lib/docker
Em docker-ol (10.16.1.11), aproveitamos para praticar o padrão da família RHEL: LVM sobre o disco inteiro, sistema de arquivos XFS:
sudo pvcreate /dev/vdb
sudo vgcreate VGdocker /dev/vdb
sudo lvcreate -l 100%FREE -n LVdocker VGdocker
sudo mkfs.xfs /dev/mapper/VGdocker-LVdocker
sudo mkdir /var/lib/docker
UUID=$(sudo blkid -o export /dev/mapper/VGdocker-LVdocker | grep UUID)
sudo cp -p /etc/fstab{,.dist}
echo "${UUID} /var/lib/docker xfs defaults 0 2" | sudo tee -a /etc/fstab
sudo systemctl daemon-reload
sudo mount /var/lib/docker
Confirmação:
[suporte@docker-ol ~]$ df -hT | grep docker
/dev/mapper/VGdocker-LVdocker xfs 32G 261M 32G 1% /var/lib/docker
Por que LVM em vez de partição direta? O volume lógico pode crescer (
lvextend+xfs_growfs) quando o disco for redimensionado — combina com osize_gbdo Terraform. O XFS, por sua vez, é o padrão do Oracle Linux/RHEL e exigido por alguns recursos do Docker (como quotas comoverlay2+pquota).
Instalação a partir do repositório oficial — nunca dos pacotes docker.io/podman-docker das distribuições, que ficam defasados e têm nomenclatura diferente.
# Dependências
sudo apt install apt-transport-https ca-certificates curl gnupg lsb-release
# Chave GPG do repositório
curl -fsSL https://download.docker.com/linux/debian/gpg | \
sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
# Repositório (codename da release resolvido dinamicamente)
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/debian $(lsb_release -cs) stable" | \
sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
# Instalação
sudo apt update
sudo apt install docker-ce docker-ce-cli containerd.io docker-compose-plugin
sudo dnf update -y
sudo dnf install dnf-utils -y
# Repositório — a Docker publica para RHEL; Oracle Linux é compatível
sudo dnf config-manager --add-repo https://download.docker.com/linux/rhel/docker-ce.repo
sudo dnf update
sudo dnf install docker-ce docker-ce-cli containerd.io docker-compose-plugin -y
# Na família RHEL o serviço não sobe automaticamente após a instalação
sudo systemctl start docker
sudo systemctl enable docker
Mesma receita do Debian, trocando a URL do repositório para /linux/ubuntu:
sudo apt install apt-transport-https ca-certificates curl gnupg lsb-release
curl -fsSL https://download.docker.com/linux/debian/gpg | \
sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | \
sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt update
sudo apt install docker-ce docker-ce-cli containerd.io docker-compose-plugin
A URL da chave é
linux/debian/gpgmesmo no Ubuntu — a Docker publica a mesma chave nos dois caminhos (linux/ubuntu/gpgexiste e é idêntica). Funciona, mas ao escrever playbooks padronizados prefira a URL da própria distro por clareza.
Nas três VMs, o resultado deve ser idêntico:
suporte@docker-deb:~$ docker --version
Docker version 29.7.2, build a7dcaa6
sudo usermod -aG docker $USER
newgrp docker # ativa o grupo na sessão atual (sem relogar)
O grupo
dockerequivale a root. Qualquer membro pode montar o/do host em um container. Em laboratório é cômodo; em produção, avalie o acesso a esse grupo com o mesmo rigor do sudo.
Por padrão o Docker usa apenas IPv4 (172.17.0.0/16). Como nosso laboratório é dual-stack, configuramos o daemon com prefixos ULA próprios — mantidos fora das faixas das redes do laboratório (10.16.0.0/16 e fd00:0:1::/48) para evitar sobreposição:
// /etc/docker/daemon.json
{
"ipv6": true,
"ip6tables": true,
"fixed-cidr": "10.10.0.0/24",
"fixed-cidr-v6": "fd00:dead:beef::/64",
"default-address-pools": [
{ "base": "10.20.0.0/16", "size": 24 },
{ "base": "fd00:cafe::/56", "size": 64 }
]
}
| Chave | Efeito |
|---|---|
ipv6 / ip6tables |
Habilita IPv6 na bridge padrão e deixa o Docker gerenciar as regras de firewall v6 |
fixed-cidr |
Sub-rede IPv4 da bridge padrão docker0 (substitui 172.17.0.0/16) |
fixed-cidr-v6 |
Sub-rede IPv6 (ULA) da docker0 |
default-address-pools |
Faixas de onde saem as sub-redes das redes criadas pelo usuário (docker network create) |
Aplicando:
sudo systemctl restart docker
ip -br a s docker0
docker0 DOWN 10.10.0.1/24 fd00:dead:beef::1/64
docker system info
Pontos a conferir na saída:
Server Version: 29.7.2 e Storage Driver: overlayfs — o filesystem dedicado está em uso;Docker Root Dir: /var/lib/docker — confirmando que os dados vivem no disco extra;Default Address Pools: 10.20.0.0/16, fd00:cafe::/56 — o dual-stack está ativo;buildx e compose presentes (instalados pelo pacote docker-compose-plugin).Teste final, valendo as três camadas (repositório oficial, disco dedicado e dual-stack):
docker run --rm hello-world