Vamos construir um ambiente de desenvolvimento com Docker

Iniciante | 90 min leitura | 2025.12.02

O que voce vai aprender neste tutorial

  • Escrita de Dockerfile e significado de cada instrucao
  • Build de imagem e inicializacao de container
  • Gerenciamento de multiplos containers com docker-compose
  • Construcao de ambiente de desenvolvimento Node.js + PostgreSQL

Pre-requisitos: Docker Desktop instalado. Se docker --version exibir a versao, esta tudo certo.

O que e Docker? Por que foi criado?

Historia da tecnologia de containers

As raizes da tecnologia de containers remontam ao chroot do UNIX V7 em 1979. Depois disso, evoluiu da seguinte forma:

AnoTecnologiaDescricao
1979chrootIsolamento de sistema de arquivos
2000FreeBSD JailIsolamento de processos
2006cgroups (Google)Limitacao de recursos
2008LXC (Linux Containers)Containers leves para Linux
2013DockerPadronizacao e popularizacao de containers

O nascimento do Docker

Em 2013, Solomon Hykes (dotCloud) apresentou o Docker.

“Docker tornou possivel empacotar aplicacoes facilmente e executa-las da mesma forma em qualquer lugar” — Docker oficial

Por que o Docker foi revolucionario

  1. Compartilhar o “ambiente que funciona”: Resolveu o problema “funciona no meu ambiente”
  2. Leve: Diferente de VMs, compartilha o OS, iniciando em segundos
  3. Reprodutibilidade de imagem: Reproducao completa do ambiente via Dockerfile
  4. Ecossistema: Compartilhamento de imagens via Docker Hub

Diferenca entre VM e Container

flowchart TB
    subgraph VM["Maquina Virtual (VM)"]
        direction TB
        AppA1["App A"] & AppB1["App B"]
        GuestOS1["Guest OS"] & GuestOS2["Guest OS"]
        Hypervisor["Hypervisor"]
        HostOS1["Host OS"]
        HW1["Hardware"]
    end

    subgraph Container["Container"]
        direction TB
        AppA2["App A"] & AppB2["App B"]
        Runtime["Container Runtime<br/>(Docker)"]
        HostOS2["Host OS"]
        HW2["Hardware"]
    end
CaracteristicaVMContainer
Tempo de inicializacaoMinutosSegundos
Uso de memoriaGBMB
Nivel de isolamentoCompletoNivel de processo
Independencia de OSCompletaKernel compartilhado

Documentacao oficial: Docker overview

Conceitos basicos do Docker

Imagem e Container

  • Imagem (Image): “Planta” da aplicacao. Somente leitura
  • Container: “Instancia em execucao” criada a partir da imagem
flowchart LR
    subgraph Static["Estatico"]
        Dockerfile["Dockerfile"]
        Image["node:20<br/>(Imagem)"]
    end
    subgraph Dynamic["Dinamico"]
        Container["Em execucao<br/>(Container)"]
        Stdout["stdout<br/>(Logs)"]
    end
    Dockerfile -->|build| Image
    Image -->|run| Container
    Container -->|logs| Stdout

Estrutura de camadas da imagem

Imagens Docker sao compostas por camadas, e apenas as partes alteradas sao reconstruidas:

FROM node:20-alpine    # Camada base (~100MB)
WORKDIR /app           # Camada de configuracao (~0KB)
COPY package*.json ./  # Camada de dependencias (~1KB)
RUN npm install        # Camada node_modules (~50MB)
COPY . .               # Camada de codigo da app (~10KB)

Melhor pratica: Coloque itens que mudam menos frequentemente no topo e os mais frequentes embaixo para usar o cache de forma eficiente

Step 1: Criacao da estrutura do projeto

Primeiro, crie a estrutura de diretorios do projeto.

mkdir docker-tutorial
cd docker-tutorial
mkdir src
touch Dockerfile docker-compose.yml src/index.js package.json

Step 2: Criacao da aplicacao Node.js

Crie um servidor Express simples.

package.json

{
  "name": "docker-tutorial",
  "version": "1.0.0",
  "main": "src/index.js",
  "scripts": {
    "start": "node src/index.js",
    "dev": "nodemon src/index.js"
  },
  "dependencies": {
    "express": "^4.18.2"
  },
  "devDependencies": {
    "nodemon": "^3.0.1"
  }
}

src/index.js

const express = require('express');
const app = express();
const PORT = process.env.PORT || 3000;

app.get('/', (req, res) => {
    res.json({
        message: 'Hello from Docker!',
        timestamp: new Date().toISOString()
    });
});

app.get('/health', (req, res) => {
    res.json({ status: 'healthy' });
});

app.listen(PORT, () => {
    console.log(`Server running on port ${PORT}`);
});

Step 3: Criacao do Dockerfile

O Dockerfile e a planta para construir a imagem.

Dockerfile

# Especificar imagem base
FROM node:20-alpine

# Definir diretorio de trabalho
WORKDIR /app

# Copiar arquivos de dependencias (otimizacao de cache)
COPY package*.json ./

# Instalar dependencias
RUN npm install

# Copiar codigo da aplicacao
COPY . .

# Expor porta (para documentacao)
EXPOSE 3000

# Comando de inicializacao
CMD ["npm", "start"]

Explicacao detalhada das instrucoes do Dockerfile

InstrucaoDescricaoExemplo
FROMEspecifica imagem baseFROM node:20-alpine
WORKDIRDefine diretorio de trabalhoWORKDIR /app
COPYCopia arquivosCOPY . .
RUNExecuta comando no buildRUN npm install
CMDComando padrao ao iniciar containerCMD ["npm", "start"]
ENTRYPOINTPonto de entrada do containerENTRYPOINT ["node"]
ENVDefine variavel de ambienteENV NODE_ENV=production
EXPOSEExpoe porta (documentacao)EXPOSE 3000
ARGArgumento de buildARG VERSION=1.0

Diferenca entre CMD e ENTRYPOINT

# CMD: Comando padrao que pode ser sobrescrito
CMD ["npm", "start"]
# docker run myapp npm run dev  ← sera sobrescrito

# ENTRYPOINT: Comando sempre executado
ENTRYPOINT ["node"]
CMD ["index.js"]
# docker run myapp app.js  ← executa como node app.js

Documentacao oficial: Dockerfile reference

Step 4: Build e execucao da imagem

# Build da imagem
docker build -t my-node-app .

# Verificar imagem construida
docker images

# Iniciar container
docker run -p 3000:3000 my-node-app

# Iniciar em background
docker run -d -p 3000:3000 --name my-app my-node-app

# Acesse http://localhost:3000 no navegador

Opcoes comuns do docker run

docker run \
  -d                      # Modo detached (background)
  -p 3000:3000            # Mapeamento de porta (host:container)
  --name my-app           # Nome do container
  -e NODE_ENV=production  # Variavel de ambiente
  -v $(pwd):/app          # Montagem de volume
  --rm                    # Remocao automatica ao terminar
  my-node-app             # Nome da imagem

Step 5: Construcao de ambiente com docker-compose

Gerencie multiplos servicos (app + banco de dados) de uma vez.

docker-compose.yml

version: '3.8'

services:
  app:
    build: .
    ports:
      - "3000:3000"
    environment:
      - NODE_ENV=development
      - DATABASE_URL=postgres://user:password@db:5432/mydb
    volumes:
      - ./src:/app/src  # Para hot reload
    depends_on:
      - db
    restart: unless-stopped

  db:
    image: postgres:15-alpine
    environment:
      POSTGRES_USER: user
      POSTGRES_PASSWORD: password
      POSTGRES_DB: mydb
    volumes:
      - postgres_data:/var/lib/postgresql/data
    ports:
      - "5432:5432"

volumes:
  postgres_data:

Estrutura do docker-compose.yml

version: '3.8'          # Versao da especificacao Compose

services:               # Definicao de servicos (containers)
  service_name:
    image: xxx          # ou build: ./path
    ports:              # Mapeamento de portas
    environment:        # Variaveis de ambiente
    volumes:            # Volumes
    depends_on:         # Dependencias
    restart:            # Politica de reinicio

volumes:                # Volumes nomeados
networks:               # Redes personalizadas

Comandos docker-compose

# Iniciar todos os servicos
docker-compose up

# Iniciar em background
docker-compose up -d

# Rebuild e iniciar
docker-compose up --build

# Verificar logs
docker-compose logs -f app

# Iniciar apenas servico especifico
docker-compose up app

# Parar servicos
docker-compose down

# Remover incluindo volumes
docker-compose down -v

# Verificar status dos servicos
docker-compose ps

Hot reload: Ao montar o codigo fonte com volumes, as alteracoes nos arquivos sao refletidas imediatamente no container.

Documentacao oficial: Docker Compose overview

Melhores praticas do Dockerfile

1. Build multi-estagio

Crie imagens leves para producao:

# Estagio de build
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

# Estagio de producao
FROM node:20-alpine AS production
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
EXPOSE 3000
CMD ["node", "dist/index.js"]

2. Uso do .dockerignore

Exclua arquivos desnecessarios do contexto de build:

.dockerignore

node_modules
npm-debug.log
.git
.gitignore
.env
*.md
.DS_Store
coverage
.nyc_output

3. Executar como usuario nao-root

Por seguranca, evite o usuario root:

FROM node:20-alpine

# Criar usuario nao-root
RUN addgroup -g 1001 -S nodejs
RUN adduser -S nodejs -u 1001

WORKDIR /app
COPY --chown=nodejs:nodejs . .

# Mudar para usuario nao-root
USER nodejs

CMD ["node", "index.js"]

4. Adicionar health check

HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
  CMD curl -f http://localhost:3000/health || exit 1

5. Principios do 12 Factor App

The Twelve-Factor App sao principios de design para aplicacoes cloud native:

  • Configuracao: Gerenciar configuracao com variaveis de ambiente
  • Dependencias: Declarar explicitamente (package.json)
  • Porta: Expor servico via port binding
  • Processos: Executar como processos stateless
  • Logs: Emitir como streams para stdout

Comandos Docker mais usados

Operacoes de container

# Verificar containers em execucao
docker ps

# Verificar todos os containers (incluindo parados)
docker ps -a

# Executar comando dentro do container
docker exec -it container_name sh

# Verificar logs do container
docker logs container_name
docker logs -f container_name  # Follow

# Parar container
docker stop container_name

# Remover container
docker rm container_name

# Remover todos os containers parados
docker container prune

Operacoes de imagem

# Listar imagens
docker images

# Remover imagem
docker rmi image_name

# Remover imagens nao utilizadas
docker image prune

# Verificar historico da imagem
docker history image_name

Limpeza

# Remover recursos nao utilizados de uma vez
docker system prune

# Remover incluindo volumes (cuidado!)
docker system prune -a --volumes

# Verificar uso de disco
docker system df

Solucao de problemas

Container nao inicia

# Verificar logs
docker logs container_name

# Iniciar em modo interativo para debug
docker run -it my-node-app sh

Porta em uso

# Verificar porta em uso
lsof -i :3000

# Mapear para outra porta
docker run -p 3001:3000 my-node-app

Build lento

  1. Verificar .dockerignore
  2. Otimizar ordem das camadas
  3. Usar build multi-estagio

Imagem muito grande

# Verificar tamanho da imagem
docker images

# Usar imagem base leve
FROM node:20-alpine  # ~100MB (versao normal ~900MB)

# Remover arquivos desnecessarios
RUN npm ci --only=production && npm cache clean --force

Proximos passos

Apos dominar o basico do Docker, aprenda orquestracao:

Documentacao oficial

Melhores praticas

Ferramentas e recursos

  • Docker Desktop - Ferramenta de gerenciamento GUI
  • Dive - Ferramenta de analise de camadas de imagem Docker
  • Hadolint - Ferramenta de analise estatica de Dockerfile

Cheat sheets

← Voltar para a lista