Deploy no CloudHub 2.0 a partir do Azure Pipelines: build único, aprovação e verificação
Referência:
templates/jobs/deploy.yml·deployment/· ADRs 5 e 6
A última parte do fluxo pega uma versão que já está no Anypoint Exchange e a coloca na frente dos usuários: test a partir do develop, depois uat e produção a cada release. Três ideias sustentam isso: construir uma vez, aprovar onde o ambiente é definido e verificar cada deploy.
Construir uma vez, fazer deploy a partir do Exchange
O CloudHub 2.0 faz deploy de aplicações a partir do Exchange. Isso torna o “build único” natural: o SNAPSHOT do develop e o release da main são publicados uma vez, e cada deploy se refere a eles pela versão.
O job de deploy nunca reconstrói. Ele usa o goal de deploy do Mule Maven plugin com um artefato fictício:
mvn $(MAVEN_ARGS) -q versions:set -DnewVersion="${APP_VERSION}" -DgenerateBackupPoms=false
mvn $(MAVEN_ARGS) mule:deploy -Dmule.artifact=dummy.jar -DmuleDeploy \
-Ddeploy.environment="${ANYPOINT_ENV}" \
-Ddeploy.applicationName="$(y .applicationName)" \
-Ddeploy.target="$(y .target)" \
-Ddeploy.runtimeVersion="$(y .runtimeVersion)" \
-Ddeploy.replicas="$(y .replicas)" \
-Ddeploy.vCores="$(y .vCores)" \
-Ddeploy.clientId="${ANYPOINT_CLIENT_ID}" \
-Ddeploy.clientSecret="${ANYPOINT_CLIENT_SECRET}"
O versions:set fixa o POM na versão que vai para o ambiente, e o -Dmule.artifact=dummy.jar impede o plugin de empacotar: o CloudHub 2.0 busca essa versão no Exchange. O log confirma — dois minutos, nenhuma compilação, Artifact mulesoft-orders-api-uat deployed. uat e produção recebem exatamente o artefato que o release produziu.
O bloco cloudhub2Deployment do POM lê propriedades deploy.*, e os valores de cada ambiente ficam no repositório da aplicação:
# deployment/prod.yaml
applicationName: mulesoft-orders-api-prod
target: Cloudhub-US-East-2
runtimeVersion: 4.9.0
replicas: 1
vCores: 0.1
healthUrl: https://mulesoft-orders-api-prod-aslzjy.5sc6y6-2.usa-e2.cloudhub.io/api/health
Dimensionar produção vira uma mudança revisada num pull request, e não um clique no Runtime Manager. A app também recebe o mule.env, que escolhe o config-<env>.yaml.
Promoção: test, uat, prod
Cada deploy é um job deployment apontando para um environment do Azure DevOps:
- deployment: deploy
environment: ${{ parameters.environment }}
strategy:
runOnce:
deploy:
steps: ...
Na main, os stages se encadeiam: release, depois uat, depois prod. A versão vem da variável de saída do stage de release, então prod não consegue receber nada diferente do que uat recebeu:
- stage: deploy_prod
dependsOn: [release, deploy_uat]
variables:
appVersion: $[ stageDependencies.release.release.outputs['release.appVersion'] ]
Aprovação onde o ambiente vive
O environment prod no Azure DevOps tem uma checagem de Approvals: aprovadores definidos, instruções (“confira o deploy em uat antes de aprovar”) e prazo. Quando o pipeline chega a prod, ele para. O aprovador recebe um e-mail e aprova no portal; a execução registra quem aprovou e quando.
Colocar a checagem no environment, e não no YAML, faz com que nenhuma aplicação consiga pular a aprovação, e a regra é configurada uma vez para todos os pipelines que fazem deploy em prod.
A aprovação também pode ser dada pelo Slack ou pelo Microsoft Teams, com os apps do Azure Pipelines, que publicam a aprovação pendente num canal com os botões Approve e Reject. Os dois apps exigem uma organização do Azure DevOps ligada ao Microsoft Entra ID — eles recusam contas pessoais da Microsoft, e por isso o projeto de referência aprova por e-mail e pelo portal. A maioria das organizações de empresa usa Entra ID, então na prática isso raramente é um bloqueio, mas vale saber antes de prometer ao time.
Verificar cada deploy
“Deployed” no log do Runtime Manager significa que a plataforma aceitou a aplicação, não que ela responde corretamente. Cada deploy termina com um smoke test contra o endpoint de health da app:
url=$(yq -r '.healthUrl // ""' "deployment/${ANYPOINT_ENV}.yaml")
for attempt in $(seq 1 20); do
body=$(curl -fsS -m 10 "$url" || true)
status=$(echo "$body" | jq -r '.status // empty' 2>/dev/null || true)
env=$(echo "$body" | jq -r '.environment // empty' 2>/dev/null || true)
if [[ "$status" == "UP" && "$env" == "${ANYPOINT_ENV}" ]]; then
echo "Healthy: $body"; exit 0
fi
sleep 15
done
exit 1
Verificar o ambiente informado, e não só o status, pega um erro silencioso: um deploy que “funcionou”, mas está usando a configuração do ambiente errado.
Como foi um release
O primeiro release da aplicação de referência, na linha do tempo do pipeline:
| Stage | Resultado |
|---|---|
| Build | sucesso |
| Release | sucesso — tag v1.0.0, main em 1.1.0-SNAPSHOT, dois commits [skip ci] |
| Deploy uat | sucesso |
| Aprovação | aprovado |
| Deploy prod | sucesso |
Chegar lá exigiu um hotfix — o filtro por caminho e o problema de push dos artigos anteriores —, e essa é a parte mais realista do exercício.
Checklist
- Todos os ambientes recebem o mesmo artefato publicado?
- O dimensionamento de cada ambiente está no repositório e é revisado em pull request?
- As aprovações estão configuradas no environment, de modo que nenhum pipeline consiga pulá-las?
- Cada deploy prova que a aplicação responde, a partir do ambiente certo?