Introdução

As aplicações Web modernas raramente são executadas como um único processo monolítico. Em vez disso, são compostas por vários serviços colaborativos: uma interface, uma API, uma base de dados, uma 'cache', uma fila de mensagens e talvez uma execução de tarefas. A gestão manual do ciclo de vida de cada contentor, a sua rede e as suas dependências tornam- se rapidamente propensas a erros e a consumir tempo. A Docker Compose e o Nginx em conjunto fornecem uma solução amigável à produção e testada para definir e implantar estes sistemas multicontentores com um mínimo de atrito. A Docker Compose permite- lhe descrever toda a sua pilha de aplicações num único ficheiro YAML, enquanto o Nginx actua como uma procuração reversa fiável e um balanceador de carga, encaminhando o tráfego para o serviço apropriado. Neste guia expandido, iremos percorrer uma configuração realista, escavar os detalhes de configuração, cobrir as melhores práticas de segurança e mostrar- lhe- á como escalonar desde o desenvolvimento até à produção.

Pré-requisitos

Antes de mergulhar, certifique-se de ter o seguinte instalado no seu sistema:

  • Motor de Docker (versão 20.10 ou mais recente) – ] Docker de Instalar
  • Compose de Docker (V2 está agora integrado no CLI de Docker; verifique com ]) – Compose de Docker Install
  • Conhecimento básico com comandos terminais e sintaxe YAML.

Opcionalmente, tenha um nome de domínio apontando para o seu servidor se você planeja seguir a seção de configuração SSL.

Compreender a Composição do Docker na Profundidade

A Docker Compose não é apenas uma simples embalagem de "docker run". É uma ferramenta declarativa que permite definir serviços, redes, volumes, variáveis de ambiente, reiniciar políticas e verificações de saúde em um único arquivo chamado . A especificação Compose (agora parte do ] Especificação Compose) padroniza como aplicativos multi-contentores são descritos, tornando sua configuração portátil em diferentes pipelines CI/CD e provedores de nuvem.

Serviços

Cada imagem de container, suas portas, volumes, ambiente e dependências são definidos sob a chave . Os serviços podem se comunicar entre si através de uma rede de ponte dedicada que Compõe cria automaticamente. Isso significa que você nunca precisa abrir portas host para comunicação inter-service - somente o proxy reverso precisa expor portas para o mundo exterior.

Redes

Por padrão, a Compose cria uma única rede para todos os serviços, dando-lhes a descoberta de serviços baseada em DNS usando o nome do serviço como nome de host. Por exemplo, um serviço chamado pode ser alcançado por outros serviços simplesmente como . Você também pode definir redes personalizadas para isolamento — por exemplo, colocando apenas o proxy inverso em uma rede voltada para o público e mantendo os containers de banco de dados em uma rede interna.

networks:
 frontend:
 backend:
services:
 nginx:
 networks:
 - frontend
 app:
 networks:
 - frontend
 - backend
 db:
 networks:
 - backend

Volumes

Os volumes são o mecanismo preferido para persistir nos dados gerados pelos contentores do Docker. No Compose, você pode declarar os volumes nomeados no nível superior e montá- los em serviços. Isto é essencial para bases de dados, envios de ficheiros e qualquer serviço de estado.

volumes:
 db_data:
services:
 postgres:
 image: postgres:16
 volumes:
 - db_data:/var/lib/postgresql/data

Variáveis de Ambiente

Externalizar a configuração usando variáveis de ambiente. Você pode codificá- las no arquivo Compose para desenvolvimento, mas para produção você deve usar arquivos ou segredos do Docker. Componha automaticamente um arquivo localizado no mesmo diretório se você não especificar .

services:
 api:
 image: my-api:latest
 env_file:
 - ./api.env
 environment:
 - NODE_ENV=production

Configurando Nginx como um Proxy Reverso – Avançado

O Nginx brilha como um proxy reverso devido ao seu alto desempenho, baixa pegada de memória e rico conjunto de recursos. Em uma arquitetura multi-contentor, o Nginx fica na borda, aceita solicitações de clientes e as encaminha para o serviço apropriado com base no URI de solicitação, cabeçalhos ou até mesmo conteúdo do corpo. Ele também pode lidar com terminação SSL, cache, limitação de taxa e balanceamento de carga.

Proxy Reverso Básico com Vários Upstreams

Aqui está uma configuração mais completa que demonstra como dividir o tráfego entre duas aplicações diferentes, incluindo o manuseio de ativos estáticos e WebSockets:

upstream app1_upstream {
 server app1:8000;
}

upstream app2_upstream {
 server app2:8001;
}

server {
 listen 80;
 server_name example.com;
 return 301 https://$server_name$request_uri;
}

server {
 listen 443 ssl http2;
 server_name example.com;

 ssl_certificate /etc/nginx/certs/fullchain.pem;
 ssl_certificate_key /etc/nginx/certs/privkey.pem;

 # Security headers
 add_header X-Frame-Options "SAMEORIGIN" always;
 add_header X-Content-Type-Options "nosniff" always;
 add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;

 # App 1 – main web frontend
 location / {
 proxy_pass http://app1_upstream;
 proxy_set_header Host $host;
 proxy_set_header X-Real-IP $remote_addr;
 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
 proxy_set_header X-Forwarded-Proto $scheme;
 }

 # App 2 – admin dashboard
 location /admin/ {
 proxy_pass http://app2_upstream/;
 proxy_set_header Host $host;
 proxy_set_header X-Real-IP $remote_addr;
 }

 # WebSocket support (e.g., for live updates)
 location /ws/ {
 proxy_pass http://app1_upstream;
 proxy_http_version 1.1;
 proxy_set_header Upgrade $http_upgrade;
 proxy_set_header Connection "upgrade";
 }

 # Static assets – serve directly for better performance
 location /static/ {
 alias /var/www/static/;
 expires 30d;
 add_header Cache-Control "public, immutable";
 }
}

Observe o uso de blocos . Eles permitem que você defina um grupo de servidores para balanceamento de carga. Mesmo com um único servidor, usar um grupo upstream torna mais fácil adicionar réplicas mais tarde sem tocar no bloco do servidor.

SSL/TLS com a cifragem

Para a produção, o serviço HTTPS não é negociável. Você pode automatizar a renovação do certificado usando o Certbot ou [[FLT: 0]]acme.sh[[ FLT: 1]] dentro de um recipiente sidecar. Um padrão comum é montar os certificados como volumes no recipiente Nginx. Por exemplo:

services:
 nginx:
 image: nginx:alpine
 volumes:
 - ./nginx.conf:/etc/nginx/conf.d/default.conf
 - ./certs:/etc/nginx/certs:ro
 - ./static:/var/www/static:ro
 ports:
 - "80:80"
 - "443:443"
 depends_on:
 - app1
 - app2

Para renovação automática, você pode adicionar um recipiente companheiro como ou que executa um trabalho de cron e recarrega Nginx quando certificados são atualizados.

Equilíbrio de Carga e Checagens de Saúde

Quando você escala um serviço para várias réplicas, Nginx pode distribuir solicitações usando round-robin, menos conexões, ou hash IP. Combine isso com a opção do Docker Compose:

upstream api_servers {
 least_conn;
 server api:80 max_fails=3 fail_timeout=30s;
}

Para detectar recipientes não saudáveis, o Nginx pode ser configurado com (requer NGINX Plus ou usar o da versão de código aberto com uma pequena solução). Uma alternativa mais simples é confiar nos controles de saúde do Docker e apenas registrar os recipientes que passam.

Construindo o arquivo Docker Compose – Um exemplo do mundo real

Vamos combinar tudo em um arquivo Compose prático para uma aplicação web que consiste em uma API Node.js, um frontend React servido por Nginx, um banco de dados PostgreSQL e um cache Redis. O container Nginx servirá arquivos estáticos e solicitações de API proxy.

version: "3.9"

services:
 nginx:
 image: nginx:alpine
 container_name: reverse-proxy
 restart: unless-stopped
 volumes:
 - ./nginx/nginx.conf:/etc/nginx/conf.d/default.conf:ro
 - ./frontend/build:/var/www/frontend:ro
 - ./certs:/etc/nginx/certs:ro
 ports:
 - "80:80"
 - "443:443"
 depends_on:
 - api
 - frontend
 networks:
 - public

 frontend:
 image: my-frontend:latest
 # In production, frontend is built into static files and served by Nginx
 # This container may run a dev server, but Nginx will bypass it for static files
 container_name: frontend-dev
 ports:
 - "3000:3000"
 networks:
 - public
 # healthcheck: ...

 api:
 image: my-api:latest
 container_name: api-server
 restart: unless-stopped
 env_file:
 - ./api/.env.production
 depends_on:
 postgres:
 condition: service_healthy
 redis:
 condition: service_started
 networks:
 - public
 - internal
 healthcheck:
 test: ["CMD", "curl", "-f", "http://localhost:4000/health"]
 interval: 30s
 timeout: 10s
 retries: 3

 postgres:
 image: postgres:16-alpine
 container_name: database
 restart: unless-stopped
 environment:
 POSTGRES_USER: app_user
 POSTGRES_DB: app_db
 POSTGRES_PASSWORD_FILE: /run/secrets/db_password
 volumes:
 - pgdata:/var/lib/postgresql/data
 secrets:
 - db_password
 networks:
 - internal
 healthcheck:
 test: ["CMD-SHELL", "pg_isready -U app_user"]
 interval: 10s
 timeout: 5s
 retries: 5

 redis:
 image: redis:7-alpine
 container_name: cache
 restart: unless-stopped
 volumes:
 - redisdata:/data
 networks:
 - internal
 healthcheck:
 test: ["CMD", "redis-cli", "ping"]
 interval: 10s
 timeout: 3s

volumes:
 pgdata:
 redisdata:

networks:
 public:
 internal:
 internal: true

secrets:
 db_password:
 file: ./secrets/db_password.txt

Pontos-chave neste ficheiro:

  • Isolação de rede: A rede é definida como , o que significa que apenas os serviços ligados a ela podem comunicar. A base de dados e o cache não são acessíveis de fora da sobreposição do Docker.
  • Segredos: Dados sensíveis como senhas são armazenados como segredos do Docker, montados como arquivos dentro do recipiente. Isto evita vazamentos em variáveis de ambiente que podem ser expostas via .
  • Cheques de saúde: Os serviços declaram os controlos de saúde para que possam esperar pela condição . Nginx só começará depois que a API estiver saudável.
  • Ativos estáticos: A saída de compilação de frontend é montada diretamente no Nginx, permitindo que o Nginx sirva arquivos estáticos sem bater no servidor dev de frontend. Isso melhora o desempenho e separa preocupações.

Implantando a Aplicação – Passo a passo

Com o arquivo Compose e a configuração do Nginx prontos, a implantação envolve alguns comandos simples. Primeiro, certifique-se de que todos os arquivos de configuração estejam no lugar:

project/
├── docker-compose.yml
├── nginx/
│ └── nginx.conf
├── api/
│ └── .env.production
├── frontend/
│ └── build/
├── certs/
│ └── (fullchain.pem, privkey.pem)
└── secrets/
 └── db_password.txt

Então, corre.

docker compose pull # pull latest images
docker compose up -d # start all services in background
docker compose ps # verify services are running
docker compose logs -f # tail logs for all services

Uma vez que tudo esteja pronto, teste a aplicação visitando o seu domínio. Verifique os registros do Nginx para confirmar as solicitações estão sendo corretamente proxiados. Se você precisar atualizar as variáveis de configuração ou ambiente, edite os arquivos relevantes e execute:

docker compose up -d --no-deps --build nginx # rebuild only nginx if config changed

Para as implantações em tempo zero, considere a utilização de azul-verde ou atualizações de rolagem[ com a funcionalidade da Compose ] e de controlos de saúde a montante da Nginx.

Escala e ajuste de desempenho

A Docker Compose torna trivial a escala de serviços sem estado como a API:

docker compose up -d --scale api=3

Agora estão em execução três réplicas do recipiente API. O Nginx, configurado com o bloco upstream, distribuirá automaticamente pedidos entre eles. Para melhorar ainda mais o desempenho:

  • Ativar compressão Gzip em Nginx para respostas baseadas em texto.
  • Definir cabeçalhos de cache adequados para ativos estáticos (como mostrado no exemplo).
  • Use um CDN para a entrega global de arquivos estáticos.
  • [[FLT: 0]] Tune Nginx worker processs para corresponder aos núcleos da CPU: [[FLT: 31]]
  • Aumentar os limites de ligação :

Para bancos de dados, certifique-se de que você tem conexão em conjunto em sua API e considere usar PgBouncer como um recipiente sidecar.

Resolver Problemas Comuns

Mesmo com uma configuração bem estruturada, as coisas podem dar errado. Aqui estão armadilhas frequentes e como corrigi-los:

  • O Conteiner não pode alcançar outro serviço pelo nome: Verifique se ambos os serviços estão na mesma rede do Docker. Use para testar.
  • [[FLT: 0]]Nginx retorna 502 Bad Gateway: O recipiente de montante pode não estar em execução ou não está a ouvir na porta esperada. Verifique com [[FLT: 34]]. Também assegure que o URL do proxy pass inclui o protocolo e o porto corretos.
  • Erros de permissão com volumes: Os arquivos montados em recipientes herdam a propriedade da máquina. Use os espaços de nomes de usuário ou defina a diretiva em Nginx e o recipiente de serviço.
  • certificado SSL não renovando: Se usar o certificado autônomo, certifique-se de que as portas 80 e 443 não são bloqueadas pelo Nginx durante a renovação. Use um método webroot ou um certbot com docker para evitar conflitos.
  • Variáveis de ambiente não carregadas: Verifique se o arquivo está no local correto ou use a diretiva corretamente. Você pode imprimir env dentro do recipiente com .

Melhores Práticas de Segurança

Executar aplicações multi-contentores na produção requer vigilância:

  • Nunca execute recipientes como root a menos que seja necessário. Use a diretiva ou especifique um no arquivo Docker.
  • Mantenha as imagens atualizadas com varredura de vulnerabilidade regular. Use imagens base oficiais e versões pin.
  • Restrinja a exposição à rede: Exponha apenas as portas de proxy reversas à internet. Todos os outros serviços devem estar em redes internas.
  • Use o gerenciamento de segredos para senhas, chaves de API e tokens. Os segredos do Docker são um bom começo; para configurações maiores, considere HashiCorp Vault.
  • Ativar o conteúdo trust do Docker para verificar assinaturas de imagens.
  • Registrar e auditar regularmente atividade de container usando ou uma solução de registro centralizado como ELK stack.
  • Aplicar limites de recursos no seu ficheiro Compose para evitar que um único contentor passe fome a outros:
services:
 api:
 deploy:
 resources:
 limits:
 cpus: '0.50'
 memory: 256M
 reservations:
 cpus: '0.25'
 memory: 128M

Esses limites são respeitados quando se usa o enxame Docker; para a Compose simples, eles são forçados com (embora seja melhor usar enxame ou K8s para orquestração de produção).

Conclusão

Docker Compose e Nginx formam uma plataforma poderosa, escalável e mantenedora para implantar aplicações web de múltiplos contentores. Ao definir a sua pilha num único ficheiro YAML, você obtém a paridade do ambiente do desenvolvimento à produção. O Nginx, como um proxy inverso, fornece um ponto central para terminação de SSL, roteamento de tráfego, balanceamento de carga e cache. Cobrimos os componentes essenciais: definições de serviço, rede, volumes, variáveis de ambiente, configuração de Nginx com optimizações de segurança e desempenho, dicas de escala e solução de problemas. Com esta base, você está pronto para arquitetar, implantar e operar aplicações modernas de contentores com confiança. Para mais leitura, consulte a documentação [[FLT: 0]]Docker Compose [[ FLT: 1] e a documentação [[FLT: 2]] Nginx[FLT: 3] e explore exemplos de mundo real a partir de [FLT: 4]