Docker para implantação de ERP de produção: um guia completo de operações
Organizações que executam sistemas ERP em contêineres Docker relatam ciclos de implantação 73% mais rápidos e 45% menos incidentes relacionados ao ambiente em comparação com implantações bare-metal tradicionais. O Docker transforma a implantação de ERP de um processo de vários dias, sujeito a erros, em uma operação repetível e controlada por versão que qualquer membro da equipe pode executar.
Este guia cobre o ciclo de vida completo da execução de sistemas ERP corporativos --- incluindo Odoo, back-ends NestJS personalizados e front-ends Next.js --- em ambientes Docker de produção.
Principais conclusões
- As compilações do Docker em vários estágios reduzem o tamanho das imagens dos contêineres de ERP em 60-80%, melhorando a velocidade de implantação
- Docker Compose orquestra ERP, banco de dados, proxy reverso e serviços de cache como uma única unidade implantável
- Volumes nomeados e montagens vinculadas garantem a persistência dos dados em reinicializações e atualizações de contêineres
- Verificações de integridade e políticas de reinicialização fornecem recuperação automática de falhas transitórias
Arquitetura de uma pilha ERP Dockerizada
Uma implantação de ERP de produção normalmente envolve cinco ou mais serviços interconectados. O Docker Compose define esses serviços de forma declarativa, garantindo uma implantação consistente em todos os ambientes.
Topologia de serviço
A pilha ERP Dockerizada padrão:
- Servidor de aplicação: O tempo de execução do ERP (Odoo, NestJS ou similar)
- Banco de dados: PostgreSQL com armazenamento de volume persistente
- Proxy reverso: Nginx lida com terminação SSL, arquivos estáticos e roteamento de solicitação
- Camada de cache: Redis para armazenamento de sessões, filas de trabalhos e cache de aplicativos
- Trabalhadores em segundo plano: processadores de trabalho assíncronos para e-mails, relatórios e integrações
Os serviços opcionais incluem contêineres de backup (pg_dump no cron), monitoramento de sidecars (exportadores Prometheus) e transportadores de log (Fluent Bit).
Construções em vários estágios para aplicativos ERP
Construções em vários estágios são essenciais para imagens Docker de produção. Eles separam as dependências de tempo de construção do tempo de execução, produzindo imagens enxutas e seguras.
Construção de back-end NestJS
# Stage 1: Install dependencies and build
FROM node:20-alpine AS builder
WORKDIR /app
# Install pnpm
RUN corepack enable
# Copy workspace configuration
COPY pnpm-lock.yaml pnpm-workspace.yaml package.json ./
COPY packages/ ./packages/
COPY apps/api/package.json ./apps/api/
# Install dependencies
RUN pnpm install --frozen-lockfile
# Copy source and build
COPY apps/api/ ./apps/api/
RUN pnpm --filter @ecosire/db build
RUN pnpm --filter @ecosire/types build
RUN pnpm --filter @ecosire/validators build
RUN pnpm --filter @ecosire/api build
# Stage 2: Production runtime
FROM node:20-alpine AS runner
WORKDIR /app
RUN addgroup -g 1001 -S appgroup && \
adduser -S appuser -u 1001 -G appgroup
COPY --from=builder --chown=appuser:appgroup /app/apps/api/dist ./dist
COPY --from=builder --chown=appuser:appgroup /app/node_modules ./node_modules
COPY --from=builder --chown=appuser:appgroup /app/apps/api/package.json ./
USER appuser
EXPOSE 3001
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
CMD wget --no-verbose --tries=1 --spider http://localhost:3001/health || exit 1
CMD ["node", "dist/main.js"]
Construção de front-end Next.js
FROM node:20-alpine AS builder
WORKDIR /app
RUN corepack enable
COPY pnpm-lock.yaml pnpm-workspace.yaml package.json ./
COPY packages/ ./packages/
COPY apps/web/package.json ./apps/web/
RUN pnpm install --frozen-lockfile
COPY apps/web/ ./apps/web/
RUN pnpm --filter @ecosire/web build
FROM node:20-alpine AS runner
WORKDIR /app
RUN addgroup -g 1001 -S appgroup && \
adduser -S appuser -u 1001 -G appgroup
COPY --from=builder --chown=appuser:appgroup /app/apps/web/.next/standalone ./
COPY --from=builder --chown=appuser:appgroup /app/apps/web/.next/static ./.next/static
COPY --from=builder --chown=appuser:appgroup /app/apps/web/public ./public
USER appuser
EXPOSE 3000
ENV NODE_ENV=production
CMD ["node", "server.js"]
Comparação de tamanho de imagem
| Tipo de construção | Tamanho da imagem | Tempo de construção |
|---|---|---|
| Estágio único (imagem de nó completo) | 1,8 GB | 4 minutos |
| Monoestágio (Alpino) | 650 MB | 3,5 minutos |
| Multiestágio (Alpino) | 180 MB | 5 minutos |
| Deps multiestágio + podados | 120 MB | 5,5 minutos |
O tempo de construção de 5,5 minutos é aceitável porque acontece na CI, não nas máquinas dos desenvolvedores.
Docker Compose para produção
version: "3.8"
services:
api:
build:
context: .
dockerfile: apps/api/Dockerfile
environment:
- DATABASE_URL=postgresql://app:${DB_PASSWORD}@db:5432/ecosire
- REDIS_URL=redis://redis:6379
- NODE_ENV=production
depends_on:
db:
condition: service_healthy
redis:
condition: service_healthy
restart: unless-stopped
networks:
- backend
- frontend
web:
build:
context: .
dockerfile: apps/web/Dockerfile
environment:
- API_URL=http://api:3001
- NODE_ENV=production
depends_on:
- api
restart: unless-stopped
networks:
- frontend
db:
image: postgres:17-alpine
environment:
POSTGRES_USER: app
POSTGRES_PASSWORD: ${DB_PASSWORD}
POSTGRES_DB: ecosire
volumes:
- postgres-data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U app -d ecosire"]
interval: 10s
timeout: 5s
retries: 5
restart: unless-stopped
networks:
- backend
redis:
image: redis:7-alpine
command: redis-server --requirepass ${REDIS_PASSWORD} --maxmemory 256mb --maxmemory-policy allkeys-lru
volumes:
- redis-data:/data
healthcheck:
test: ["CMD", "redis-cli", "-a", "${REDIS_PASSWORD}", "ping"]
interval: 10s
timeout: 5s
retries: 5
restart: unless-stopped
networks:
- backend
nginx:
image: nginx:alpine
ports:
- "80:80"
- "443:443"
volumes:
- ./infrastructure/nginx/production.conf:/etc/nginx/conf.d/default.conf:ro
- ./certbot/conf:/etc/letsencrypt:ro
- ./certbot/www:/var/www/certbot:ro
depends_on:
- web
- api
restart: unless-stopped
networks:
- frontend
volumes:
postgres-data:
redis-data:
networks:
frontend:
backend:
Isolamento de rede
A configuração acima usa duas redes:
- frontend: Nginx, web e API (proxies Nginx para ambos)
- backend: API, banco de dados e Redis
O banco de dados e o Redis não são acessíveis no contêiner Nginx ou na rede externa. Essa segmentação de rede é uma prática crítica de segurança.
Gerenciamento de volume e persistência de dados
Os volumes são a parte mais crítica de uma implantação de ERP Dockerizado. Perca seus volumes e você perderá seus dados.
Tipos de volume
| Tipo | Caso de uso | Persistência | Desempenho |
|---|---|---|---|
| Volumes nomeados | Banco de dados, Redis | Sobrevive à remoção de contêineres | Velocidade nativa do sistema de arquivos |
| Vincular montagens | Arquivos de configuração, logs | Vinculado ao sistema de arquivos host | Velocidade nativa do sistema de arquivos |
| montagens tmpfs | Arquivos temporários, segredos | Somente memória, perdida na reinicialização | Velocidade da memória |
Estratégia de backup para volumes Docker
#!/bin/bash
# backup-volumes.sh - Run via cron every 6 hours
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
BACKUP_DIR="/opt/backups"
# Stop the application briefly for consistent backup
docker compose stop api web
# Backup PostgreSQL
docker compose exec -T db pg_dump -U app ecosire | gzip > "$BACKUP_DIR/db_$TIMESTAMP.sql.gz"
# Backup Redis
docker compose exec -T redis redis-cli -a "$REDIS_PASSWORD" BGSAVE
sleep 5
docker cp $(docker compose ps -q redis):/data/dump.rdb "$BACKUP_DIR/redis_$TIMESTAMP.rdb"
# Restart services
docker compose start api web
# Upload to S3
aws s3 sync "$BACKUP_DIR" "s3://company-backups/docker-volumes/" --exclude "*.tmp"
# Retain 30 days locally
find "$BACKUP_DIR" -name "*.gz" -mtime +30 -delete
find "$BACKUP_DIR" -name "*.rdb" -mtime +30 -delete
Verificações de integridade e políticas de reinicialização
Os contêineres de produção devem reportar sua integridade e se recuperar automaticamente de falhas.
Ponto final de verificação de integridade do aplicativo
// health.controller.ts
@Controller('health')
export class HealthController {
constructor(
private readonly db: DatabaseService,
private readonly redis: RedisService,
) {}
@Get()
@Public()
async check() {
const checks = {
database: await this.checkDatabase(),
redis: await this.checkRedis(),
uptime: process.uptime(),
memory: process.memoryUsage(),
};
const healthy = checks.database && checks.redis;
return { status: healthy ? 'ok' : 'degraded', checks };
}
private async checkDatabase(): Promise<boolean> {
try {
await this.db.execute('SELECT 1');
return true;
} catch {
return false;
}
}
private async checkRedis(): Promise<boolean> {
try {
await this.redis.ping();
return true;
} catch {
return false;
}
}
}
Reiniciar seleção de política
| Política | Comportamento | Caso de uso |
|---|---|---|
| CÓDIGO0 | Nunca reinicie | Desenvolvimento, tarefas pontuais |
| CÓDIGO0 | Reinicie apenas na saída diferente de zero | Trabalhadores, trabalhos em lote |
| CÓDIGO0 | Sempre reinicie (inclusive na reinicialização do docker daemon) | Serviços de produção |
| CÓDIGO0 | Gosta de always mas respeita paradas manuais | A maioria dos serviços de produção |
Use unless-stopped para serviços de produção. Isso garante a reinicialização dos contêineres após a reinicialização do servidor ou daemon do Docker, mas respeita os comandos manuais docker compose stop durante a manutenção.
Fluxo de trabalho de implantação
Atualizações contínuas com Docker Compose
#!/bin/bash
# deploy.sh - Zero-downtime deployment
set -e
echo "Pulling latest code..."
git pull origin main
echo "Building new images..."
docker compose build --no-cache api web
echo "Rolling update - API first..."
docker compose up -d --no-deps api
sleep 10
# Verify API health
if ! curl -sf http://localhost:3001/health > /dev/null; then
echo "API health check failed, rolling back..."
docker compose up -d --no-deps api
exit 1
fi
echo "Rolling update - Web..."
docker compose up -d --no-deps web
sleep 5
# Verify Web health
if ! curl -sf http://localhost:3000 > /dev/null; then
echo "Web health check failed, rolling back..."
docker compose up -d --no-deps web
exit 1
fi
echo "Deployment complete!"
docker compose ps
Segurança na migração de banco de dados
Nunca execute migrações dentro da inicialização do aplicativo. Em vez disso, execute-os como uma etapa separada:
# Run migrations before deploying new containers
docker compose run --rm api npx drizzle-kit push
# Then deploy the new version
docker compose up -d
Esse padrão garante que, se uma migração falhar, a versão antiga continuará em execução sem ser afetada.
Registro e depuração
Registro Centralizado
# Add to docker-compose.yml
services:
api:
logging:
driver: json-file
options:
max-size: "10m"
max-file: "5"
labels: "service"
labels:
service: "ecosire-api"
Comandos comuns de depuração
# View logs for a specific service
docker compose logs -f api --tail 100
# Execute a shell inside a running container
docker compose exec api sh
# View resource usage
docker stats --format "table {{.Name}}\t{{.CPUPerc}}\t{{.MemUsage}}\t{{.NetIO}}"
# Inspect container networking
docker compose exec api ping db
# View container environment variables
docker compose exec api env | sort
Perguntas frequentes
Como lidamos com migrações de banco de dados no Docker?
Execute as migrações como uma etapa separada antes de implantar novos contêineres de aplicativos. Use docker compose run --rm api npx drizzle-kit push (ou o comando de migração do seu ORM) como uma etapa de pré-implantação. Nunca incorpore a execução da migração no comando de inicialização do contêiner – uma migração com falha não deve impedir que a versão atual continue em execução.
Qual é a sobrecarga de desempenho do Docker?
No Linux, a sobrecarga de desempenho do Docker é insignificante – normalmente menos de 2% para cargas de trabalho vinculadas à CPU e nenhuma diferença mensurável para cargas de trabalho vinculadas a E/S. No macOS e no Windows, o Docker é executado dentro de uma máquina virtual, adicionando 5 a 15% de sobrecarga. Para produção (que deveria ser Linux), o Docker não é uma preocupação significativa de desempenho.
Como gerenciamos segredos no Docker?
Nunca coloque segredos em arquivos Dockerfiles ou docker-compose.yml. Use arquivos de variáveis de ambiente (.env) excluídos do controle de versão, segredos do Docker (para modo Swarm) ou gerenciadores de segredos externos (AWS Secrets Manager, HashiCorp Vault). Para Docker Compose, um arquivo .env na raiz do projeto é a abordagem mais simples.
Devemos usar Docker Swarm ou Kubernetes?
Para a maioria das implantações de ERP para pequenas e médias empresas, o Docker Compose é suficiente. Docker Swarm adiciona orquestração multi-host com sobrecarga mínima de complexidade. O Kubernetes é apropriado quando você precisa de escalonamento automático, políticas de rede complexas ou recursos de malha de serviço. Consulte nosso guia de escalonamento do Kubernetes e guia de arquitetura de microsserviços para estruturas de decisão.
Como lidamos com módulos personalizados Odoo no Docker?
Monte módulos personalizados como um volume de montagem vinculado apontando para seu diretório de complementos. No Dockerfile, certifique-se de que o caminho dos complementos esteja configurado em odoo.conf. Para CI/CD, crie uma imagem Docker personalizada que seja integrada em seus módulos, garantindo a consistência da versão. Consulte nosso guia de implantação do Docker Odoo existente para obter configurações específicas do Odoo.
O que vem a seguir
Docker é a base para a implantação moderna de ERP. Quando sua pilha conteinerizada estiver estável, explore estratégias de implantação com tempo de inatividade zero, monitoramento de produção e infraestrutura como código para criar um pipeline de operações totalmente automatizado.
Entre em contato com a ECOSIRE para obter consultoria de implantação do Docker ou explore nossos serviços de implementação Odoo para implantação de ERP em contêineres totalmente gerenciada.
Publicado pela ECOSIRE – ajudando as empresas a implantar software empresarial com confiança.
Escrito por
ECOSIRE Research and Development Team
Construindo produtos digitais de nível empresarial na ECOSIRE. Compartilhando insights sobre integrações Odoo, automação de e-commerce e soluções de negócios com IA.
Artigos Relacionados
Automação de contas a pagar: reduza os custos de processamento em 80 por cento
Implemente a automação de contas a pagar para reduzir os custos de processamento de faturas de US$ 15 para US$ 3 por fatura com OCR, correspondência de três vias e fluxos de trabalho de ERP.
IA em automação contábil e contábil: o guia de implementação do CFO
Automatize a contabilidade com IA para processamento de faturas, reconciliação bancária, gerenciamento de despesas e relatórios financeiros. Ciclos de fechamento 85% mais rápidos.
Padrões de gateway de API e práticas recomendadas para aplicativos modernos
Implemente padrões de gateway de API, incluindo limitação de taxa, autenticação, roteamento de solicitações, disjuntores e controle de versão de API para arquiteturas web escaláveis.