Skip to main content

2 - Instância do Docker no WSL

🐳 docker.wsl.lab.local

Este procedimento descreve a instalação do Debian 12 como uma instância isolada no Windows Subsystem for Linux (WSL), com o nome Docker, localizada em D:\WSL\Docker. A separação do laboratório em outro disco é uma decisão assertiva, pensada para diversas finalidades que serão exploradas ao longo dos trabalhos. O objetivo é criar um ambiente dedicado exclusivamente a testes e à execução de containers Docker, separado da instância principal utilizada no ambiente wsl.lab.local. A instalação é realizada por meio da importação manual da imagem oficial do Debian, garantindo maior controle sobre a estrutura do sistema e evitando interferências entre ambientes.

💡 Nota
Embora este guia utilize o Docker instalado diretamente no WSL para maior leveza e controle, também é possível rodar contêineres no Windows usando o Docker Desktop. Ele oferece interface gráfica, integração com WSL 2 e Kubernetes, mas consome mais recursos e exige licença paga para uso empresarial. Alternativas como Rancher Desktop, Podman Desktop, Colima (via WSL) ou a instalação manual do Docker Engine no WSL podem ser mais adequadas conforme o cenário.

Utilizaremos o PowerShell em modo Administrador para executar todas as nossas atividades no Windows. Toda a configuração desse lab.local é suportada pelo Windows Terminal. A construção desse ambiente é uma jornada de aprendizado Hibrido entre os sistemas operacionais Debian Linux e Windows.

Instalar Debian 12 no WSL com o nome Docker
  • Windows 11 com WSL2 habilitado
  • PowerShell 7.5.2
  • A pasta D:\WSL deve existir (ou será criada no processo)

Baixar a imagem oficial do Debian 12 (Bookworm)

Invoke-WebRequest `
  -Uri https://github.com/debuerreotype/docker-debian-artifacts/raw/dist-amd64/bookworm/oci/blobs/rootfs.tar.gz `
  -OutFile D:\WSL\debian-docker-rootfs.tar.gz

Criar o diretório da nova instância

New-Item -ItemType Directory `
  -Path D:\WSL\Docker -Force

Importar a imagem como instância Docker

wsl --import Docker D:\WSL\Docker `
  D:\WSL\debian-docker-rootfs.tar.gz --version 2

A imagem importada é extremamente minimalista. Comandos como ping, ip a e nano não estão disponíveis até que sejam instalados manualmente.

Criar a bridge no Hyper-V (wsl.lab.local)

No PowerShell (como administrador):

New-VMSwitch `
  -Name "wsl.lab.local" `
  -NetAdapterName (Get-NetAdapter |
    Where {$_.Status -eq "Up" -and $_.HardwareInterface} |
    Select-Object -First 1 -ExpandProperty Name) `
  -AllowManagementOS $true

Configurar o WSL para usar a bridge wsl.lab.local

@"
[wsl2]
networkingMode=bridged
vmSwitch=wsl.lab.local
"@ | Out-File "$env:USERPROFILE\.wslconfig" -Encoding ASCII -Force

Esse comando faz:
Cria (ou sobrescreve) o arquivo wslconfig
Insere as configurações necessárias para o WSL2 usar modo bridge
Usa o switch virtual wsl.lab.local que você criou com o New-VMSwitch

🖥️ Hosts no Windows no PowerShell (Admin):

$hosts="$env:WINDIR\System32\drivers\etc\hosts"
Add-Content -Path $hosts -Value "`r`n192.168.2.100 wsl.lab.local portainer.wsl.lab.local"

ou manualmente com:

notepad C:\Windows\System32\drivers\etc\hosts

Iniciar a Instância no WSL

wsl -d Docker

Ajustar o SystemD no WSL

tee -a /etc/wsl.conf > /dev/null <<EOF

[boot]
systemd=true
EOF

Editar /etc/wsl.conf

tee -a /etc/wsl.conf > /dev/null <<EOF

[network]
generateHosts = false
hostname = docker
EOF

Isso edita o /etc/wsl.conf para que a configuração fique persistente

Configurar o DNS na Instância Docker (Debian)
echo "nameserver 1.1.1.1" > /etc/resolv.conf
Preparar a instância Docker (Debian) com o necessário

📦 Instalar pacotes essenciais

apt update
apt install -y iproute2 iputils-ping net-tools dnsutils \
  curl wget nano procps systemd systemd-sysv dbus cron \
  sudo tree locales dialog
Criar o usuário e adicionar sudo

Adicionar usuário

adduser mol

Adicionar usuário do grupo de sudo

usermod -aG sudo mol
newgrp sudo

Configurar o usuário default para a instância e entrar sem senha

tee -a /etc/wsl.conf > /dev/null <<EOF
[user]
default=mol
EOF

Atualizar o /etc/hosts

tee /etc/hosts > /dev/null <<'EOF'
# hosts definidos
127.0.0.1       localhost
127.0.1.1       docker.wsl.lab.local    docker

# The following lines are desirable for IPv6 capable hosts
::1     ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
EOF

Isso substitui totalmente o conteúdo atual do /etc/hosts

Fixar IP na Instância Docker do WSL

O WSL não tem recurso para fixar o IP na instância, com isso alguns serviços dependem de IP Fixo para as configurações.

Criar o script /usr/local/bin/set-static-ip.sh

Rede 192.168.2.0/24

tee /usr/local/bin/set-static-ip.sh > /dev/null <<'EOF'
#!/bin/bash

# Limpa IPs anteriores
ip addr flush dev eth0

# Define IP fixo e gateway
ip addr add 192.168.2.100/24 dev eth0
ip route add default via 192.168.2.1

# Define DNS fixo
echo "nameserver 1.1.1.1" > /etc/resolv.conf
EOF

Criar a unidade systemd /etc/systemd/system/set-static-ip.service

tee /etc/systemd/system/set-static-ip.service > /dev/null <<EOF
[Unit]
Description=Set static IP on eth0 for WSL
After=network-online.target
Wants=network-online.target

[Service]
Type=oneshot
ExecStart=/usr/local/bin/set-static-ip.sh
RemainAfterExit=yes

[Install]
WantedBy=multi-user.target
EOF

Ativar e iniciar o serviço

chmod +x /usr/local/bin/set-static-ip.sh
systemctl daemon-reload
systemctl enable set-static-ip.service

Provavelmente vai dar um erro: "System has not been booted with systemd as init system (PID 1). Can't operate.
Failed to connect to bus: Host is down". Não precisa se preocupar com ele.

Sair da instância do WSL

exit

# Ou Crtl + D para sair da instância

No PowerShell desligar o WSL para aplicar as configurações

wsl --shutdown
wsl -d Docker
Conferir se funcionou

Iniciar a instância novamente e dentro dela verificar se a rede tá ok!

ip a

Você deverá ver eth0 com um IP da sua rede local real (ex: 192.168.x.x), não da sub-rede 172.x.

Instalar o Docker
curl -fsSL https://get.docker.com | sh

Observar que o script oficial dá um aviso de recomendação para que preferencialmente do WSL seja utilizado o Docker Desktop, mas esse não é o objetivo do wsl.lab.local. Aguardar 20 segundos que ele inicia o processo normalmente.

Ativar o Docker na inicialização da Instância com systemd.

sudo systemctl enable --now docker

Adicionar o usuário no grupo docker

sudo usermod -aG docker mol
newgrp docker

Testar o Docker Engine

docker run hello-world

Docker Engine está OK, o container Hello World subiu com usuário criado, não precisando mas do root para operar o Docker e a configuração foi validada.

Inicializar o Swarm na instância Docker (como nó manager)
docker swarm init --advertise-addr eth0

🔎 --advertise-addr eth0 faz com que o Docker use o IP da sua interface real (útil no WSL bridge)

Guarde esse token para adicionar workers no futuro.

Se você preferir fixar o IP diretamente:

sudo docker swarm init --advertise-addr 192.168.2.100

Verificar o status do cluster

docker node ls

Você verá algo como:

Construir o esqueleto de diretórios
sudo mkdir -p \
  "/srv/docker/.backup" \
  "/srv/docker/apps" \
  "/srv/docker/core/traefik/dynamic" \
  "/srv/docker/core/portainer" \
  "/srv/docker/shared/certs" \
  "/srv/docker/.envs" \
  "/srv/docker/volumes/portainer/data" \
  "/srv/docker/volumes/traefik"

Resultado do comando tree /srv/docker, que exibe de forma hierárquica a estrutura de diretórios criada para organizar os serviços Docker no ambiente local.

/srv/docker/
├── .backup/                          → Backups do lab (configs, envs, volumes)
├── apps/                              → Serviços adicionais via Portainer
│   ├── nome-app-1/
│   ├── nome-app-2/
│   └── ...
├── core/                             → Serviços essenciais do lab.local
│   ├── traefik/
│   │   └── dynamic/
│   └── portainer/
├── shared/
│   └── certs/
├── .envs/                            → Arquivos de variáveis de ambiente
├── volumes/
│   ├── portainer/data/
│   └── traefik/

Nesta estrutura, temos:

  • .backup/: Diretório reservado para armazenar backups do laboratório, incluindo arquivos de configuração, variáveis de ambiente, exports do Portainer e dumps de volumes importantes.
  • apps/: diretório reservado para os serviços que não são essenciais que serão ativados a partir do Portainer
  • core/: diretório reservado para os serviços essenciais. Aqui colocamos o:
    • portainer/: onde ficará o docker-compose.yml do Portainer.
    • traefik/: contendo a pasta dynamic/ com configurações dinâmicas do Traefik (como roteadores, middlewares, TLS, etc).
  • shared/certs/: onde serão armazenados os certificados TLS auto assinados ou importados manualmente. Isso permite o uso de HTTPS local mesmo sem domínio público.
  • volumes/: diretório dedicado a volumes persistentes de cada serviço:
    • volumes/portainer/data/: armazenará os dados de configuração da interface do Portainer.
    • volumes/traefik/: poderá conter arquivos como acme.json, se necessário, para persistência de certificados gerenciados pelo Traefik.

Essa organização facilita a separação de responsabilidades, torna o ambiente mais legível e permite escalar ou versionar os serviços de forma independente.

💡 A separação clara entre core, shared e volumes é especialmente útil quando lidamos com múltiplos serviços e stacks Docker, mesmo em ambientes locais como WSL.

Aplicar permissões nos diretórios

No Linux, cada arquivo ou diretório tem um usuário dono e um grupo dono (ownership). Definir corretamente quem é o dono evita acessos indevidos e permite que administradores trabalhem sem precisar usar sudo o tempo todo. Agora vamos aplicar permissões e ownership adequados para manter segurança e facilitar a colaboração entre administradores.

# Dono root, grupo docker
sudo chown -R root:docker /srv/docker

# Diretórios com setgid (2775) para herdar o grupo 'docker'
sudo find /srv/docker -type d -exec chmod 2775 {} \;

# Restringir pastas sensíveis (.envs e certs) para leitura apenas pelo dono e grupo
sudo chmod 2750 /srv/docker/.envs
sudo chmod 2750 /srv/docker/shared
sudo chmod 2750 /srv/docker/shared/certs

# Garantir que o arquivo acme.json do Traefik exista com permissão 600
sudo touch /srv/docker/volumes/traefik/acme.json
sudo chown root:docker /srv/docker/volumes/traefik/acme.json
sudo chmod 600 /srv/docker/volumes/traefik/acme.json

Explicação sobre os diretórios

Diretório

Função

/srv/docker/core/traefik/dynamic

Configurações dinâmicas do Traefik (middlewares, regras extras).

/srv/docker/core/portainer

Arquivos de configuração do Portainer.

/srv/docker/shared/certs

Certificados SSL/TLS locais.

/srv/docker/.envs

Arquivos .env com variáveis de ambiente (senhas, tokens, chaves).

/srv/docker/volumes/portainer/data

Volume persistente de dados do Portainer.

/srv/docker/volumes/traefik

Volume persistente do Traefik (inclui acme.json).

Por que configuramos ownership no laboratório?
Garantir que somente administradores (root e membros do grupo docker) possam modificar configs sensíveis.
Permitir que membros do grupo docker acessem e gerenciem os arquivos sem precisar ser root o tempo todo.
Evitar que “usuários comuns” do sistema (fora do grupo) leiam coisas como .env ou certificados.

Gerar o Certificado Auto Hospedado
sudo openssl req -x509 -newkey rsa:4096 -sha256 -days 825 -nodes \
  -keyout /srv/docker/shared/certs/wsl.lab.local.key \
  -out /srv/docker/shared/certs/wsl.lab.local.crt \
  -subj "/CN=wsl.lab.local" \
  -addext "subjectAltName=DNS:wsl.lab.local,DNS:painel.wsl.lab.local,DNS:*.wsl.lab.local" \
  -addext "basicConstraints=CA:FALSE" \
  -addext "keyUsage=digitalSignature,keyEncipherment" \
  -addext "extendedKeyUsage=serverAuth"

sudo chmod 600 /srv/docker/shared/certs/wsl.lab.local.key
sudo chmod 644 /srv/docker/shared/certs/wsl.lab.local.crt

Conferir o certificado pelo OpenSSL

echo | openssl s_client -servername wsl.lab.local -connect 192.168.2.100:443 2>/dev/null \
  | openssl x509 -noout -subject -dates -ext subjectAltName

🧩 Confiar o certificado no Windows

Import-Certificate -FilePath "\\wsl$\Docker\srv\docker\shared\certs\wsl.lab.local.crt" -CertStoreLocation Cert:\LocalMachine\Root
Stacks de Serviços Básicos

Abaixo tem os guias para instalar o serviços do core que são Traefik e Portainer

Comandos Úteis
# PowerShell - Excluir Instância do WSL
wsl --unregister Docker

# Forçar Updade do Traefik
docker service update --force traefik_traefik