0%
Instalação e primeiros passos

Instalação

Um engine rodando e os comandos que realmente importam.

10-15 min

Instalação e primeiros passos

Instalar o Docker leva cinco minutos quando se segue o procedimento certo, e uma noite inteira quando se segue o errado. A armadilha mais comum: o pacote docker.io ou docker oferecido pelo gerenciador de pacotes da sua distribuição. Ele existe, instala e funciona mais ou menos — mas costuma estar atrasado em várias versões maiores, às vezes sem o plugin Compose, o que fará você passar vinte minutos tentando entender por que um comando da documentação oficial « não existe » na sua máquina.

A segunda armadilha está em outro lugar e atinge quase todo mundo uma vez: depois de uma instalação perfeitamente bem-sucedida, o primeiro comando responde permission denied while trying to connect to the Docker daemon socket. Isso não é uma falha de instalação. É uma consequência direta da arquitetura vista na lição anterior — o cliente fala com um daemon privilegiado por meio de um socket — e se resolve com uma linha, desde que você entenda qual e por quê.

Esta lição ajuda você a superar esses dois obstáculos de acordo com o seu sistema e depois se concentra no que realmente importa: uma dezena de comandos que cobrem 90 % do trabalho diário. Não uma lista exaustiva de opções — a documentação existe para isso — mas o pequeno conjunto que você digitará todos os dias, com o que acontece por trás de cada um.

No final, você saberá iniciar um contêiner, entrar nele, ler seus logs, entender por que ele parou e recuperar as dezenas de gigabytes que o Docker terá acumulado discretamente no seu disco.

Instalar o Docker Engine no Linux

No Linux, o Docker roda nativamente: sem VM intermediária, sem camada de tradução. É o ambiente mais simples e de melhor desempenho. O método recomendado passa pelo repositório oficial do Docker, o que garante atualizações e a presença dos plugins modernos.

No Debian e no Ubuntu:

# 1. Retirer les paquets anciens ou concurrents, s'ils sont presents
for p in docker.io docker-doc docker-compose podman-docker containerd runc; do
  sudo apt-get remove -y "$p"
done

# 2. Ajouter la cle GPG officielle de Docker
sudo apt-get update
sudo apt-get install -y ca-certificates curl
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg \
  -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc

# 3. Declarer le depot (le nom de code de la distribution est detecte)
echo \
  "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] \
  https://download.docker.com/linux/ubuntu \
  $(. /etc/os-release && echo "$VERSION_CODENAME") stable" \
  | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

# 4. Installer le moteur ET les plugins (buildx, compose)
sudo apt-get update
sudo apt-get install -y docker-ce docker-ce-cli containerd.io \
  docker-buildx-plugin docker-compose-plugin

Os dois últimos pacotes são os que costumam ser esquecidos, e são eles que fornecem docker buildx e docker compose. Para o Debian, substitua as duas ocorrências de ubuntu por debian nas URLs. Para Fedora ou RHEL, o procedimento equivalente usa dnf e o repositório docker-ce.repo; ele está detalhado em docs.docker.com/engine/install.

Resta garantir que o daemon inicie, agora e a cada reinicialização da máquina:

# --now demarre le service, enable l'active au demarrage
sudo systemctl enable --now docker

# Verifier son etat
systemctl status docker --no-pager

Existe uma alternativa para uma máquina descartável ou de teste: o script de instalação oficial.

curl -fsSL https://get.docker.com -o get-docker.sh
# Toujours lire un script avant de l'executer en root
less get-docker.sh
sudo sh get-docker.sh

Ele é prático, mas o próprio Docker o desaconselha explicitamente em produção: ele adivinha a sua distribuição e toma decisões por você, o que não se quer em um servidor que precisa poder ser reconstruído de forma idêntica.

« permission denied »: o grupo docker

Este é o erro que espera todo mundo no primeiro comando:

permission denied while trying to connect to the Docker daemon socket
at unix:///var/run/docker.sock

O daemon escuta em um socket UNIX, /var/run/docker.sock, cujo proprietário é root e o grupo é docker. Seu usuário só pertence a esse grupo se você o tiver adicionado.

# Le groupe existe deja apres l'installation ; la commande est idempotente
sudo groupadd -f docker

# Ajouter votre utilisateur (-a = append, indispensable :
# sans lui, usermod REMPLACE la liste des groupes)
sudo usermod -aG docker "$USER"

# L'appartenance a un groupe est lue a l'ouverture de session.
# Ouvrez une nouvelle session, ou forcez-la pour le shell courant :
newgrp docker

# Verifier
id -nG | tr ' ' '\n' | grep docker
docker run --rm hello-world

Esquecer o -a em usermod -aG é um erro caro: ele remove o usuário de todos os outros grupos, inclusive sudo.

É preciso saber o que essa manipulação realmente autoriza. O daemon roda como root, e a API que ele expõe permite, entre outras coisas, montar qualquer diretório do host em um contêiner. Pertencer ao grupo docker equivale, portanto, na prática, a ter acesso de superusuário à máquina. Isso é aceitável na sua estação de trabalho; em um servidor compartilhado, merece uma decisão consciente. Para os casos em que essa elevação é inaceitável, o Docker oferece um modo rootless que executa o daemon sob a sua própria conta.

macOS e Windows: o desvio por uma máquina virtual

Lembrete da lição 1: uma imagem Linux exige um kernel Linux. Nem o macOS nem o Windows fornecem um. Nesses sistemas, o Docker Desktop instala e gerencia para você uma pequena VM Linux; o cliente docker do seu terminal fala com o daemon que roda dentro dela. Tudo funciona de forma transparente, mas duas consequências se fazem lembrar regularmente: as operações de entrada e saída em arquivos compartilhados com o host são mais lentas, e a memória alocada à VM é um teto global.

No macOS, a instalação é feita pelo pacote .dmg baixado de docs.docker.com/desktop/install/mac-install, ou pelo Homebrew:

brew install --cask docker
# Puis lancer l'application Docker une premiere fois : c'est elle
# qui installe le daemon et demarre la VM.
docker version

Atenção à arquitetura: nos Macs Apple Silicon (M1 e posteriores), o processador é arm64. Uma imagem publicada apenas para amd64 será emulada, lentamente, ou se recusará a iniciar. Se necessário, força-se a arquitetura:

# Voir l'architecture du daemon
docker info --format '{{.Architecture}}'

# Forcer une plateforme, au prix de l'emulation
docker run --rm --platform linux/amd64 alpine:3.20 uname -m

No Windows, a configuração correta é o WSL2 (Windows Subsystem for Linux versão 2), que fornece um kernel Linux de verdade. O antigo modo « Hyper-V » do Docker Desktop deve ser evitado, e o modo « contêineres Windows » atende a uma necessidade diferente (imagens Windows Server).

# Dans PowerShell en administrateur : installe WSL2 et une Ubuntu par defaut
wsl --install

# S'assurer que la version 2 est celle utilisee par defaut
wsl --set-default-version 2

# Verifier : la colonne VERSION doit afficher 2
wsl -l -v

Em seguida, instale o Docker Desktop e ative, nas configurações, em Settings > Resources > WSL Integration, a distribuição que você usa. Assim você poderá digitar seus comandos docker diretamente a partir do shell do Ubuntu.

Uma dica de desempenho que muda tudo no Windows: trabalhe no sistema de arquivos Linux, ou seja, em /home/voce/projetos, e não em /mnt/c/Users/.... Os arquivos montados a partir do disco do Windows atravessam uma camada de tradução; um npm install pode ser dez vezes mais lento ali.

Verificar se a instalação está saudável

Três comandos bastam, e cada um responde a uma pergunta diferente.

# 1. Le client et le daemon se parlent-ils, et en quelle version ?
#    Si la section "Server" manque, le daemon est injoignable.
docker version

# 2. Etat detaille : pilote de stockage, nombre de conteneurs, memoire
docker info

# 3. Le chemin complet fonctionne-t-il : pull depuis un registre + execution
docker run --rm hello-world

O terceiro é o verdadeiro teste de ponta a ponta: ele valida a resolução de DNS, o acesso ao registry, a escrita no cache local e a criação de um contêiner. O --rm remove o contêiner assim que ele termina — um reflexo a adquirir para todo contêiner descartável, caso contrário eles se acumulam.

Anatomia do docker run

O docker run é o comando mais usado e o mais denso. Na verdade, ele encadeia três operações: baixar a imagem se ela estiver ausente, criar um contêiner e iniciá-lo.

docker run [OPTIONS] IMAGE [COMMANDE] [ARGUMENTS...]

A ordem não é negociável: o que precede o nome da imagem configura o Docker, o que vem depois é passado ao processo dentro do contêiner. Colocar -p 8080:80 depois do nome da imagem não produz um erro de sintaxe — passa -p 8080:80 como argumento ao programa iniciado, que estranha.

As opções que importam, com o que elas realmente fazem:

docker run \
  -d \                       # detache : rend le terminal, le conteneur tourne en fond
  --name api \               # nom stable, sinon Docker en invente un aleatoire
  -p 8080:80 \               # publie le port : 8080 sur l'hote -> 80 dans le conteneur
  -e LOG_LEVEL=debug \       # variable d'environnement
  -v mes-donnees:/data \     # volume nomme monte sur /data
  -w /app \                  # repertoire de travail initial
  --restart unless-stopped \ # relance apres un crash ou un reboot de l'hote
  --memory 512m \            # plafond memoire (cgroups)
  nginx:1.27-alpine

Dois modos se opõem e atendem a necessidades distintas. O -d desanexa: é o modo dos serviços. O -it combina -i (manter a entrada padrão aberta) e -t (alocar um pseudo-terminal): é o modo das sessões interativas.

# Une session shell jetable dans une Alpine, supprimee a la sortie
docker run --rm -it alpine:3.20 sh

# Un shell Python temporaire, sans installer Python sur l'hote
docker run --rm -it python:3.12-slim python

# Remplacer la commande par defaut de l'image
docker run --rm nginx:1.27-alpine nginx -v

Esta última linha ilustra uma ideia a reter: o comando declarado na imagem é apenas um valor padrão, que pode ser substituído na inicialização.

O ciclo de vida de um contêiner

Um contêiner passa por estados, e muita confusão vem de uma leitura apressada do docker ps.

                 docker create
    (image) ─────────────────────> created
                                     │ docker start
                                     v
      docker stop / le processus finit
  exited <───────────────────────  running  <──> paused
    │                                │        (pause/unpause)
    │ docker start                   │ docker restart
    │                                v
    └──> docker rm ──> (disparu)   running
# Ne montre QUE les conteneurs en cours d'execution
docker ps

# -a montre aussi les conteneurs arretes. C'est la commande a reflexe
# quand "mon conteneur a disparu" : il est la, en statut Exited.
docker ps -a

# Format personnalise, bien plus lisible que la sortie par defaut
docker ps -a --format 'table {{.Names}}\t{{.Status}}\t{{.Ports}}'

O ponto pouco intuitivo: um contêiner vive tanto quanto seu processo principal (PID 1). Ele não « contém » uma máquina ligada esperando. Se o processo termina, o contêiner passa para Exited, imediatamente.

# Ce conteneur s'arrete instantanement : echo finit son travail.
docker run --name test-court alpine:3.20 echo "bonjour"
docker ps -a --filter name=test-court   # Exited (0)

# Celui-la tourne 30 secondes puis sort.
docker run -d --name test-sleep alpine:3.20 sleep 30

Essa é a explicação para o clássico « meu contêiner para sozinho »: a imagem não inicia nenhum serviço duradouro, ou o serviço vai para segundo plano e devolve o controle — o que nunca se deve fazer em um contêiner.

Os comandos de controle:

docker stop api      # SIGTERM, puis SIGKILL apres 10 s de grace
docker stop -t 30 api # allonger le delai de grace a 30 s
docker kill api      # SIGKILL immediat, aucune chance de finir proprement
docker start api     # redemarre un conteneur existant (etat disque conserve)
docker restart api
docker rm api        # supprime le conteneur arrete, et sa couche d'ecriture
docker rm -f api     # arrete et supprime d'un coup

A diferença entre stop e kill não é cosmética. O stop envia SIGTERM, dando à aplicação tempo para fechar suas conexões e esvaziar seus buffers; o kill corta de imediato, com o risco de corromper dados em escrita. Uma aplicação conteinerizada corretamente deve interceptar o SIGTERM.

Entrar em um contêiner e observá-lo

Quando um serviço não responde, o instinto é olhar por dentro. Três ferramentas, nesta ordem.

Primeiro os logs, pois na maioria das vezes eles bastam. O Docker captura a saída padrão e a saída de erro do processo principal.

docker logs api                    # tout depuis le demarrage
docker logs -f api                 # suivre en direct (comme tail -f)
docker logs --tail 50 api          # les 50 dernieres lignes
docker logs --since 10m api        # les 10 dernieres minutes
docker logs --timestamps api       # avec horodatage

Corolário capital: uma aplicação conteinerizada deve escrever seus logs na saída padrão, e não em um arquivo interno. Caso contrário, o docker logs fica vazio e os logs morrem junto com o contêiner.

Depois, abrir um shell em um contêiner em execução:

# bash quand l'image en dispose (Debian, Ubuntu)
docker exec -it api bash

# sh sur les images Alpine et les images minimales : bash n'y est pas
docker exec -it api sh

# Executer une commande unique sans session interactive
docker exec api ls -la /etc/nginx
docker exec api env

Observe bem a distinção: o docker exec adiciona um processo em um contêiner já em execução, enquanto o docker run cria um novo. E tudo o que você modifica durante essa sessão vive na camada de escrita do contêiner: será perdido quando ele for removido. Esse modo serve para diagnosticar, nunca para configurar de forma duradoura.

Por fim, a inspeção e as medições:

# Toda a configuração do contêiner, em JSON
docker inspect api

# Obter um valor específico com um modelo Go
docker inspect --format '{{.State.Status}}' api
docker inspect --format '{{.State.ExitCode}}' api
docker inspect --format '{{range $p, $c := .NetworkSettings.Ports}}{{$p}} {{end}}' api

# Consumo em tempo real (CPU, memória, rede, E/S)
docker stats

# Processos em execução no contêiner
docker top api

# Arquivos modificados em relação à imagem (A adicionado, C alterado, D removido)
docker diff api

# Copiar um arquivo nos dois sentidos
docker cp api:/etc/nginx/nginx.conf ./nginx.conf
docker cp ./nginx.conf api:/etc/nginx/nginx.conf

O código de saída merece atenção: 137 quase sempre significa que o processo recebeu um SIGKILL (128 + 9), frequentemente porque ultrapassou o limite de memória e o kernel o encerrou. É uma informação que nenhuma mensagem de erro da aplicação vai lhe dar.

Limpar: o Docker enche o disco em silêncio

Cada imagem baixada, cada contêiner parado, cada camada intermediária de build permanece no disco. Em uma máquina de desenvolvimento ativa, isso representa muito rapidamente dezenas de gigabytes.

# O balanço: o que o Docker ocupa e o que é recuperável
docker system df

# Detalhe por objeto
docker system df -v

A limpeza é feita em graus crescentes de brutalidade.

# 1. Os contêineres parados
docker container prune

# 2. As imagens sem nome (as "dangling", <none>), produzidas pelos rebuilds
docker image prune

# 3. Os volumes que nenhum contêiner utiliza
#    ATENÇÃO: são os seus dados. Verifique antes.
docker volume ls
docker volume prune

# 4. O cache de build, muitas vezes o maior item
docker builder prune

# 5. Tudo de uma vez, incluindo imagens não utilizadas
docker system prune -a

docker system prune -a é o comando que você precisa conhecer e usar com cautela: ele remove todas as imagens que nenhum contêiner utiliza, inclusive aquelas que você vai baixar de novo amanhã. Sem -a, ele se limita aos objetos realmente órfãos.

Algumas combinações práticas, que convém entender antes de colar:

# Parar todos os contêineres em execução (-q retorna apenas os identificadores)
docker stop $(docker ps -q)

# Remover todos os contêineres, inclusive os que estão em execução
docker rm -f $(docker ps -aq)

# Remover as imagens de um repositório específico
docker images --filter reference='mon-app' -q | xargs -r docker rmi

Uma observação para macOS e Windows: o espaço ocupado pela VM do Docker Desktop nem sempre é devolvido ao sistema hospedeiro após um prune. O arquivo de disco da VM permanece dimensionado no seu pico. O Docker Desktop oferece uma ação de recuperação de espaço em suas configurações.

Pontos-chave

  • No Linux, instale a partir do repositório oficial do Docker, nunca a partir do pacote da distribuição, e não esqueça de docker-buildx-plugin e docker-compose-plugin: sem eles, docker compose não existe.
  • O erro permission denied em /var/run/docker.sock se resolve com sudo usermod -aG docker $USER, seguido de uma nova sessão. Saiba que isso equivale a um acesso root à máquina.
  • No macOS e no Windows, o Docker se apoia em uma VM Linux. No Windows, use o WSL2 e mantenha seus projetos no sistema de arquivos Linux, não em /mnt/c, sob pena de lentidão significativa.
  • Um contêiner vive exatamente o tempo de seu processo PID 1. “Ele para sozinho” quase sempre significa que esse processo terminou. docker ps -a mostra os contêineres parados, docker ps não.
  • O dia a dia cabe em oito comandos: run, ps -a, logs -f, exec -it, stop, rm, inspect, system df. Adquira o reflexo de usar --rm em todo contêiner descartável.
  • Monitore o docker system df regularmente: imagens, volumes e caches de build enchem um disco sem avisar.

Dica: execute docker run --rm -it alpine:3.20 sh e explore livremente. Um contêiner descartável é o ambiente de experimentação mais seguro que existe: exit, e não sobra nada.

Comentários

Os comentários são alimentados pelo GitHub Discussions

Conecte-se com o GitHub para participar da discussão

Link copiado!