Docker Build & Deploy — middag-io
Build multi-arch, push GHCR/ECR, deploy EC2, integração 1Password Connect. Referências: ADR-007, ADR-008, ADR-005.
Arquitetura
GitHub Actions
│
├── docker buildx build --platform linux/arm64
│ │
│ ▼
│ ghcr.io/middag-io/{repo}/{service}:{tag} ← GHCR (sempre)
│ │
│ ├── (se PUSH_TO_ECR=true)
│ │ crane copy → ECR ← ECR (opcional)
│ │
│ SCP: docker-compose.yml, .env.tpl, Makefile
│ │
│ SSH: docker compose pull
│ op inject .env.tpl → .env ← 1Password Connect
│ docker compose up -d
│
▼
EC2 (produção/staging)1. Estratégia de Registry
Veja ADR-007 — Estratégia de Registry Docker para a justificativa completa, padrões de nomenclatura e quando usar ECR.
Resumo: GHCR por padrão (ghcr.io/middag-io/{repo}/{service}:{tag}), ECR opcional por repo via feature flag PUSH_TO_ECR. GITHUB_TOKEN trata autenticação GHCR automaticamente.
Habilitar ECR em um repo
gh variable set PUSH_TO_ECR --body "true" --repo middag-io/docker-wp-my-project
gh variable set ECR_REPO --body "my-project" --repo middag-io/docker-wp-my-project2. Configuração de Build
Build multi-arch (ARM64)
Servidores de produção rodam ARM64 (AWS Graviton). Build com --platform linux/arm64:
- name: Build and push
run: |
docker buildx build \
--platform linux/arm64 \
-t ghcr.io/middag-io/${{ github.event.repository.name }}/wordpress:${{ github.sha }} \
-t ghcr.io/middag-io/${{ github.event.repository.name }}/wordpress:latest \
--push .Tagging de imagens
| Tag | Quando usado | Mutável? |
|---|---|---|
{sha} | Todo build (commit SHA) | Não |
latest | Todo build em main | Sim |
v{x.y.z} | Tag de release | Não |
Step de cópia ECR
- name: Copiar para ECR
if: vars.PUSH_TO_ECR == 'true'
env:
AWS_REGION: ${{ vars.AWS_REGION || 'us-east-1' }}
run: |
aws ecr get-login-password --region ${AWS_REGION} \
| crane auth login ${ECR_REGISTRY} -u AWS --password-stdin
crane copy \
ghcr.io/middag-io/${{ github.event.repository.name }}/wordpress:${{ github.sha }} \
${AWS_ACCOUNT_ID}.dkr.ecr.${AWS_REGION}.amazonaws.com/${{ vars.ECR_REPO }}:${{ github.sha }}
crane copy \
ghcr.io/middag-io/${{ github.event.repository.name }}/wordpress:latest \
${AWS_ACCOUNT_ID}.dkr.ecr.${AWS_REGION}.amazonaws.com/${{ vars.ECR_REPO }}:latestCredenciais AWS vêm do vault 1Password CI-AWS via op run.
3. Deploy para EC2
Fluxo de deploy
- SCP de arquivos de config para o servidor
- SSH: pull de imagens, injeção de secrets, restart de serviços
Arquivos deployados
| Arquivo | Propósito |
|---|---|
docker-compose.yml | Definições de serviços |
.env.production.tpl | Template de secrets com referências op:// |
Makefile | Comandos de deploy (make deploy, make logs) |
Injeção de secrets no servidor
Instâncias EC2 rodam 1Password Connect localmente. Secrets são injetados no momento do deploy:
# No servidor (via SSH do CI)
op inject -i .env.production.tpl -o .env
# Exemplo do template:
DB_HOST=op://CI-MYPROJECT/database-production/host
DB_PASSWORD=op://CI-MYPROJECT/database-production/password
REDIS_URL=op://CI-MYPROJECT/redis-production/urlConnect resolve referências op:// usando o servidor Connect local — secrets nunca aparecem em logs do CI ou no GitHub.
Script de deploy (CI)
- name: Deploy
env:
OP_SERVICE_ACCOUNT_TOKEN: ${{ secrets.OP_SERVICE_ACCOUNT_TOKEN }}
run: |
# Carregar chave SSH do 1Password
eval $(op ssh agent)
# SCP de arquivos
scp docker-compose.yml .env.production.tpl Makefile ${{ vars.DEPLOY_USER }}@${{ vars.DEPLOY_HOST }}:${{ vars.DEPLOY_PATH }}/
# SSH deploy
ssh ${{ vars.DEPLOY_USER }}@${{ vars.DEPLOY_HOST }} << 'EOF'
cd ${{ vars.DEPLOY_PATH }}
docker compose pull
op inject -i .env.production.tpl -o .env
docker compose up -d --remove-orphans
docker image prune -f
EOF4. Estrutura Docker Compose
Layout padrão
docker-wp-my-project/
├── docker-compose.yml
├── docker-compose.override.yml # overrides de dev local
├── .env.production.tpl # template op:// 1Password
├── .env.staging.tpl
├── .env.example # exemplo para devs
├── Makefile # comandos de deploy/ops
├── wordpress/
│ ├── Dockerfile
│ └── conf/
├── nginx/
│ ├── Dockerfile
│ └── conf/
└── .github/
└── workflows/
├── ci.yml
└── release.ymlTargets do Makefile
deploy:
docker compose pull
op inject -i .env.production.tpl -o .env
docker compose up -d --remove-orphans
logs:
docker compose logs -f --tail=100
restart:
docker compose restart
status:
docker compose ps5. Workflow de Operações
Workflow reutilizável para operações comuns no servidor: backup, flush de cache, WP-CLI.
Operações disponíveis
| Operação | Descrição | Target Make |
|---|---|---|
backup-db | Exportar banco, baixar como artifact | make backup-db |
backup-full | Banco + arquivo de uploads | make backup |
cache-flush | Flush Redis + WP object cache | make cache-flush |
wp-cli | Executar comando WP-CLI arbitrário | make wp CMD='...' |
Setup do workflow chamador
Cada repo docker-wp precisa de um operations.yml fino:
name: Operations
on:
workflow_dispatch:
inputs:
operation:
type: choice
options: [backup-db, backup-full, cache-flush, wp-cli]
wp-command:
type: string
default: "plugin list"
jobs:
ops:
uses: middag-io/.github-private/.github/workflows/docker-wp-operations.yml@workflows-v1
with:
operation: ${{ inputs.operation }}
wp-command: ${{ inputs.wp-command }}
op-item-ec2: CI-MYPROJECT/AWS-EC2-docker-wp-myproject
op-item-ssh-key: CI-MYPROJECT/SSH-docker-wp-myproject-dev-key
op-service-account-secret: OP_SA_MYPROJECT
secrets: inheritItens 1Password necessários
O workflow reutilizável lê dois itens 1Password:
| Input | Campos | Exemplo |
|---|---|---|
op-item-ec2 | EC2/host, EC2/user, EC2/folder | CI-MYPROJECT/AWS-EC2-docker-wp-myproject |
op-item-ssh-key | private_key | CI-MYPROJECT/SSH-docker-wp-myproject-dev-key |
Artifacts de backup
Backups são carregados como artifacts do GitHub Actions com retenção de 30 dias. Após download, arquivos de backup são limpos no servidor para evitar acúmulo de disco.
Download em: Actions → Operations → Run → Artifacts.
Validação de build
O step de validação do build-and-deploy.yml faz pull da imagem recém-enviada e executa scripts/validate-build.sh dentro do container via emulação QEMU:
- name: Validar build
run: |
docker run --rm --platform linux/arm64 \
${{ env.GHCR_REPO }}/wordpress:${{ env.IMAGE_TAG }} \
bash scripts/validate-build.shO script verifica: extensões PHP, WordPress core, autoloader Composer, plugins obrigatórios/premium/customizados, temas, mu-plugins, drop-in de object-cache e ferramentas do sistema.
6. Troubleshooting
| Problema | Causa | Solução |
|---|---|---|
| Push GHCR negado | Token sem write:packages | Adicionar permissions: packages: write ao job |
| Login ECR falhou | Credenciais AWS expiradas no 1Password | Atualizar credenciais no vault CI-AWS |
crane copy falha | Auth no registry de destino faltando | Login em ambos os registries antes da cópia |
op inject falha no EC2 | Servidor Connect não está rodando | docker compose up -d op-connect-api op-connect-sync |
| Build ARM64 falha | Sem builder buildx para ARM | docker buildx create --use --platform linux/arm64 |
| Imagem não encontrada no pull | Registry ou tag errado | Verificar se imagem GHCR existe: crane ls ghcr.io/middag-io/{repo} |