Bem-vindo à aula de introdução ao Docker! Docker é uma plataforma de código aberto que automatiza a implantação de aplicações dentro de containers de software. Ele permite que você empacote uma aplicação com todas as suas dependências em uma unidade padronizada, garantindo que ela rode de forma consistente em qualquer ambiente. Nesta aula, vamos explorar os conceitos fundamentais de imagens e containers, o comando docker run, o ciclo de vida de um container e os comandos básicos que você usará constantemente.

Se você está começando no mundo de DevOps, entender Docker é essencial, pois ele se tornou o padrão de fato para criação de ambientes isolados, facilitando desde o desenvolvimento local até a orquestração em larga escala com Kubernetes. Vamos mergulhar nos detalhes práticos, com exemplos que você pode executar em sua máquina.

Imagens e containers

Para entender Docker, é fundamental compreender a diferença entre imagens e containers. Uma imagem é um pacote executável que inclui tudo o que é necessário para rodar uma aplicação: código, runtime, bibliotecas, variáveis de ambiente e configurações. Ela é imutável, ou seja, uma vez criada, não pode ser alterada. As imagens são construídas a partir de um Dockerfile e podem ser armazenadas em registries, como o Docker Hub.

Um container é uma instância em execução de uma imagem. Quando você inicia um container, ele cria uma camada gravável por cima da imagem, permitindo que você faça alterações durante a execução (como criar arquivos ou instalar pacotes). Containers são leves, isolados e podem ser iniciados, parados, pausados e removidos. A analogia comum é: a imagem é uma classe (o molde) e o container é um objeto (a instância).

Vamos ver um exemplo prático. Primeiro, vamos baixar uma imagem do Nginx (um servidor web popular):

docker pull nginx:alpine

Esse comando baixa a imagem nginx:alpine do Docker Hub. A tag alpine indica uma versão leve baseada no Alpine Linux. Agora, para criar e iniciar um container a partir dessa imagem, usamos docker run:

docker run -d -p 8080:80 --name meu-nginx nginx:alpine

Aqui, -d roda o container em segundo plano (detached), -p 8080:80 mapeia a porta 8080 do host para a porta 80 do container, e --name dá um nome ao container. Agora você pode acessar http://localhost:8080 e ver a página padrão do Nginx.

docker run

O comando docker run é o principal comando para criar e iniciar containers. Ele combina a criação de um container a partir de uma imagem e a sua execução. A sintaxe básica é:

docker run [OPÇÕES] IMAGEM [COMANDO] [ARG...]

As opções mais comuns incluem:

  • -d: executa o container em segundo plano (detached).
  • -it: mantém o terminal interativo (geralmente para executar comandos como bash).
  • -p ou --publish: mapeia portas do host para o container.
  • -v ou --volume: monta um volume para persistir dados.
  • --name: atribui um nome ao container.
  • -e: define variáveis de ambiente.
  • --rm: remove o container automaticamente quando ele é parado.

Exemplo interativo: para executar um shell dentro de um container Ubuntu, você pode usar:

docker run -it ubuntu bash

Isso iniciará um container a partir da imagem ubuntu e abrirá um shell interativo. Para sair, digite exit. O container será encerrado (mas não removido, a menos que use --rm).

Outro exemplo: rodar um comando específico sem entrar no container. Por exemplo, para ver a versão do Node.js em uma imagem node:

docker run node:18 node --version

Isso executa o comando node --version dentro de um container criado a partir da imagem node:18 e sai imediatamente.

Ciclo de vida

O ciclo de vida de um container Docker pode ser dividido em vários estados: criado, em execução, pausado, parado e removido. Vamos entender cada um:

  • Criado: um container é criado quando você usa docker create ou quando docker run inicia o processo de criação. Nesse estado, ele não está em execução.
  • Em execução: o container está ativo, executando o processo principal. Você pode entrar nele, enviar comandos, etc.
  • Pausado: o container é suspenso temporariamente, congelando o processo. Isso é útil para liberar recursos sem encerrar o container.
  • Parado: o processo principal foi encerrado, mas o container ainda existe. Ele pode ser reiniciado com docker start.
  • Removido: o container é excluído permanentemente, juntamente com seus dados não persistidos.

Vamos ver como isso funciona na prática. Primeiro, crie um container sem iniciá-lo:

docker create --name meu-container nginx:alpine

Isso cria um container chamado meu-container, mas não o inicia. Para iniciá-lo, use docker start:

docker start meu-container

Para pausar e despausar:

docker pause meu-container
docker unpause meu-container

Para parar o container:

docker stop meu-container

E para removê-lo:

docker rm meu-container

Se você quiser parar e remover em um único comando, use docker rm -f (força a remoção mesmo se estiver em execução) ou docker container prune para remover todos os containers parados.

O comando docker run na verdade combina docker create e docker start. Por isso, quando você usa docker run, o container já nasce em estado de execução (ou criado, se você usar --detach=false).

Comandos básicos

Aqui estão os comandos essenciais que você usará frequentemente no dia a dia com Docker:

  • docker pull: baixa uma imagem de um registry.
  • docker images: lista as imagens locais.
  • docker ps: lista os containers em execução. Use docker ps -a para listar todos (inclusive parados).
  • docker logs: exibe os logs de um container.
  • docker exec: executa um comando dentro de um container em execução.
  • docker inspect: mostra informações detalhadas de um container ou imagem.
  • docker rm: remove um container.
  • docker rmi: remove uma imagem.
  • docker build: constrói uma imagem a partir de um Dockerfile.

Vamos ver alguns exemplos práticos. Para listar imagens baixadas:

docker images

Para listar containers em execução:

docker ps

Para ver todos os containers, incluindo os parados:

docker ps -a

Para ver os logs de um container chamado meu-nginx:

docker logs meu-nginx

Para executar um comando dentro de um container em execução, por exemplo, listar arquivos:

docker exec meu-nginx ls /usr/share/nginx/html

Para inspecionar um container e ver detalhes como IP, portas, etc.:

docker inspect meu-nginx

Para remover um container (após pará-lo):

docker rm meu-nginx

Para remover uma imagem (após remover os containers que a usam):

docker rmi nginx:alpine

Esses comandos formam a base para gerenciar containers e imagens. Com eles, você pode criar ambientes isolados para desenvolvimento, teste e produção.

Boas práticas e observações finais

Ao trabalhar com Docker, algumas boas práticas ajudam a evitar problemas:

  • Use imagens oficiais e com tags específicas (evite latest em produção, pois pode mudar).
  • Sempre defina limites de recursos (--memory, --cpus) para containers em produção.
  • Prefira usar volumes para dados persistentes, em vez de armazenar dados dentro do container.
  • Use --rm em containers temporários para não acumular containers parados.
  • Organize seus containers com redes personalizadas para facilitar a comunicação.
  • Mantenha o Docker atualizado e use docker system prune periodicamente para limpar recursos não utilizados.

Lembre-se de que Docker é uma ferramenta poderosa, mas com grande responsabilidade. Sempre entenda o que está rodando e por quê.

Exercícios

  1. Exercício 1: Baixe a imagem hello-world e execute um container a partir dela. O que acontece?
  2. ✓ Resposta: O comando docker run hello-world baixa a imagem (se não estiver local) e executa um container que imprime uma mensagem de boas-vindas explicando que o Docker está funcionando corretamente. O container sai imediatamente após a execução.
  3. Exercício 2: Crie um container interativo com a imagem ubuntu e execute o comando echo "Olá, Docker!" dentro dele. Depois, saia do container.
  4. ✓ Resposta: Use docker run -it ubuntu bash para entrar no shell do container. Depois, execute echo "Olá, Docker!" e veja a saída. Para sair, digite exit.
  5. Exercício 3: Inicie um container Nginx em segundo plano, mapeando a porta 8080 do host para a porta 80 do container. Depois, liste os containers em execução e pare o container.
  6. ✓ Resposta: Execute docker run -d -p 8080:80 --name meu-nginx nginx:alpine. Para listar, use docker ps. Para parar, use docker stop meu-nginx.
  7. Exercício 4: Crie um container temporário com a imagem alpine que execute o comando ls e depois seja removido automaticamente. Use --rm.
  8. ✓ Resposta: Execute docker run --rm alpine ls. O container executará ls e será removido automaticamente após a execução.
  9. Exercício 5: Liste todas as imagens locais e remova uma delas (por exemplo, hello-world).
  10. ✓ Resposta: Use docker images para listar. Depois, docker rmi hello-world para remover a imagem. Se houver containers usando a imagem, será necessário removê-los antes.

Referências