Agentes efêmeros
e deploy sem susto.
Domine a engenharia de distribuição e entrega contínua em profundidade: orquestre agentes via protocolo Remoting (SSH, JNLP e WebSocket) e agentes efêmeros sob demanda (Docker Cloud, Kubernetes Pods multi-contêiner e EC2 Spot), estruture os cinco ambientes de CD com Selenium Headless, Cucumber BDD, OWASP ZAP, SoapUI e Apache JMeter, e codifique deploys Normal, Rolling, Blue/Green e Canary no Jenkins.
Trilha de formação: Domínio do Jenkins da arquitetura ao deploy automatizado
Nesta Etapa 3 (Final), dissecamos os mecanismos de distribuição de carga em agentes estáticos e efêmeros (conectados ao nosso curso Docker em Produção com Critério) e implementamos, linha por linha, toda a esteira de testes automatizados de Entrega Contínua e as quatro estratégias de deploy em produção dos Capítulos 7 e 8.
Percurso da Etapa 3 (Agentes Efêmeros e Deploy Contínuo)
Explore cada oficina na sequência da esteira de Entrega Contínua. Todos os `Jenkinsfiles`, manifestos `docker-compose.yml`, payloads do Route53 e scripts de validação abaixo reproduzem e expandem os exemplos reais dos Capítulos 3.9, 7 e 8 do livro.
Domine como o `remoting.jar` / `agent.jar` serializa comandos entre Controller e Agentes via SSH, JNLP TCP 50000, WebSocket e Windows Services.
OFICINA 02Agentes Dinâmicos e Efêmeros ↗Elimine máquinas ociosas provisionando agentes sob demanda em contêineres Docker, Pods Kubernetes (`podTemplate` multi-contêiner) e instâncias Cloud Spot.
OFICINA 03Engenharia dos 5 Ambientes CD ↗Estruture a promoção do mesmo artefato imutável por QA, Segurança (DAST), Performance (efêmero), UAT (Alpha/Beta) e Produção.
OFICINA 04Selenium Headless, Cucumber e ZAP ↗Automatize regressão cross-browser headless com Selenium, especificações Gherkin (Given-When-Then) no Cucumber e DAST BlackBox com OWASP ZAP.
OFICINA 05SoapUI (API), JMeter e Simulador ↗Valide contratos JSON/XML, headers CORS e SQLi em APIs com SoapUI, execute carga em modo CLI non-GUI no Apache JMeter e simule o ROI do CD.
OFICINA 06Traefik, Normal (QA) e Rolling ↗Configure Service Discovery dinâmico com Traefik e implemente Normal/Recreate em QA e Rolling Update (`Max Surge`/`Max Unavailable`) com Health Check HTTP 200.
OFICINA 07Blue/Green com Input e Route53 ↗Codifique o deploy Blue/Green no Jenkinsfile com validação pré-cutover, troca de DNS no AWS Route53 e rollback instantâneo em um clique.
OFICINA 08Canary Ponderado (90/10) em Produção ↗Divida o tráfego ao vivo (90% Baseline / 10% Canary) via labels de peso no Traefik, compare telemetria real e promova para 100% ou faça rollback imediato.
“Com agentes dinâmicos, o nó nasce sob demanda para executar o job e é encerrado logo após o término — custo zero quando ocioso.”
Nos Capítulos 3.9, 7 e 8 de CI/CD Pipeline with Docker and Jenkins, Sandeep Rawat fecha o ciclo completo unindo infraestrutura elástica de agentes às quatro estratégias clássicas de deploy em produção.Arquitetura de Agentes Estáticos Distribuídos: Protocolo Remoting, SSH (`ssh-slaves`) e JNLP (`agent.jar`)
Por que simplesmente adicionar mais memória RAM e vCPUs ao servidor principal do Jenkins é uma armadilha que leva à corrupção do sistema operacional e à indisponibilidade global — e como o protocolo Remoting coordena agentes Linux, Windows e macOS?
Os 3 Limites Físicos da Escala Vertical no Controller e o Papel do `remoting.jar` (Seção 3.9)
Na Seção 3.9 do Capítulo 3 (páginas 70–75), Sandeep Rawat retoma a analogia do mordomo Alfred (de Batman) para responder a uma pergunta que todo arquiteto ouve: “Se podemos escalar verticalmente o hardware do servidor Jenkins principal (Master/Controller), por que precisamos do conceito de nós agentes (Slaves)?”. O autor demonstra que tentar concentrar tudo em uma única máquina esbarra em três barreiras intransponíveis:
- Indisponibilidade durante upgrades e picos (Downtime): redimensionar disco, CPU ou memória de um servidor monolítico exige paradas programadas, e qualquer job descontrolado que faça um loop infinito ou encha o disco de
/tmpderruba a interface web do Jenkins para toda a empresa; - Limitação a um Único Sistema Operacional (Single OS Support): se a sua empresa desenvolve um backend em Go/Java no Linux, uma aplicação desktop `.NET` no Windows e aplicativos móveis para Android e iOS (que exigem Xcode no macOS e emuladores pesados de hardware), um único servidor Linux jamais conseguirá compilar e testar nativamente todas essas plataformas;
- Corrupção do Sistema Operacional e Conflito de Pacotes (OS Corruption): quando dezenas de pipelines instalam pacotes via
apt,pip,npmou alteram variáveis globais na mesma máquina do Controller, um único pacote defeituoso pode corromper a biblioteca C ou a JVM do sistema, obrigando a reconstruir o servidor inteiro do zero.
Para resolver isso, o Jenkins utiliza sua camada de comunicação distribuída chamada Jenkins Remoting (distribuída no pacote binário remoting.jar, hoje renomeado para agent.jar). Quando o Controller se conecta a um agente, ele não envia apenas textos de shell soltos: o agent.jar estabelece um canal binário bidirecional sobre o qual o Controller envia closures Java/Groovy serializadas, variáveis de ambiente mascaradas, fluxos de saída padrão (stdout/stderr em tempo real para o Console Output) e transferências de arquivos para o workspace remoto.
Agentes SSH (Outbound) vs. Agentes JNLP / WebSocket (Inbound) e Registro por Labels
Nas páginas 71–74 (Figuras 3.13, 3.14 e 3.38 a 3.42), o livro detalha os dois protocolos de conexão para agentes permanentes ou semi-permanentes configurados em Manage Jenkins > Nodes:
| Característica Técnica | SSH Agents (Plugin `ssh-slaves`) | JNLP / Inbound Agents (`agent.jar` / `windows-slaves`) |
|---|---|---|
| Direção da Conexão de Rede | Controller → Agente (Outbound na porta TCP 22): o Controller toma a iniciativa de abrir uma sessão SSH até o IP do agente. | Agente → Controller (Inbound na porta TCP 50000 ou HTTPS WebSocket): o agente toma a iniciativa de conectar-se ao Controller. |
| Como o `agent.jar` é iniciado? | Totalmente automático: após autenticar via chave privada SSH, o Controller copia o remoting.jar/agent.jar via SFTP e inicia o processo Java remotamente. | Iniciado no próprio agente (via script de boot, systemd ou Serviço do Windows) passando a URL do Controller e o token secreto (-secret) daquele nó. |
| Comportamento atrás de NAT e Firewalls | Exige que o Controller tenha rota de rede direta para a porta 22 do agente. | Atravessa firewalls e NAT com facilidade: ideal quando o agente está em outra VPC, em uma rede de laboratório móvel macOS ou em máquinas Windows. |
| Estratégia de Verificação de Host Key (SSH) | Configure sempre Known Hosts file Verification Strategy (ou Manually provided key Verification Strategy) em produção — nunca use Non verifying em redes não confiáveis para evitar ataques Man-in-the-Middle! | Autenticado por chave HMAC criptográfica exclusiva por nó gerada pelo Controller (e TLS quando usado via -webSocket). |
Ao cadastrar qualquer nó em Manage Jenkins > Nodes > New Node, quatro parâmetros determinam seu comportamento na fila de execução:
- Number of executors: quantos builds paralelos aquele agente pode rodar simultaneamente (regra prática: alinhe ao número de núcleos de vCPU disponíveis na máquina para tarefas intensivas de compilação);
- Remote root directory: pasta dedicada no agente (ex.:
/var/jenkins_agentno Linux ouC:\jenkins_agentno Windows) onde o Jenkins criará as subpastasworkspace/,remoting/(logs e cache JAR) etools/(instaladores automáticos do Global Tool Configuration); - Labels (Etiquetas separadas por espaço): identificadores lógicos das capacidades daquele nó (ex.:
linux jdk17 docker-hostoumacos xcode16 ios-simulator). Nunca amarre seuJenkinsfileao nome físico de um servidor (comoagent { label 'servidor-ip-10-0-1-55' }); amarre sempre à capacidade funcional (agent { label 'linux && jdk17' }), permitindo adicionar ou substituir máquinas no pool sem tocar em uma linha de código dos pipelines! - Usage: escolha “Only build jobs with label expressions matching this node” para agentes especializados caros (como uma máquina com GPU ou emulador mobile), impedindo que jobs genéricos com
agent anyocupem aquele recurso.
# =========================================================================
# 1. PREPARAÇÃO DE UM AGENTE LINUX PARA CONEXÃO SSH (No servidor Agente)
# =========================================================================
sudo useradd -m -d /var/jenkins_agent -s /bin/bash jenkins_agent
sudo apt-get update && sudo apt-get install -y openjdk-17-jre-headless git
sudo mkdir -p /var/jenkins_agent/.ssh
# Adicione a chave pública do Controller em /var/jenkins_agent/.ssh/authorized_keys
sudo chmod 700 /var/jenkins_agent/.ssh && sudo chmod 600 /var/jenkins_agent/.ssh/authorized_keys
sudo chown -R jenkins_agent:jenkins_agent /var/jenkins_agent
# =========================================================================
# 2. EXECUÇÃO DE UM AGENTE INBOUND (JNLP / WebSocket) — Seção 3.9.1
# =========================================================================
curl -sO http://jenkins.empresa.com.br:8080/jnlpJars/agent.jar
java -jar agent.jar \
-url https://jenkins.empresa.com.br/ \
-webSocket \
-secret @/etc/jenkins/agent-secret.txt \
-name "agent-mobile-android-01" \
-workDir "/var/jenkins_agent"
# =========================================================================
# 3. JENKINSFILE ORQUESTRANDO MÚLTIPLOS AGENTES POR EXPRESSÃO DE LABELS
# =========================================================================
pipeline {
agent none // Nenhum agente global: cada stage aloca apenas o nó necessário!
stages {
stage('1. Compilação Backend Linux') {
agent { label 'linux && jdk17' }
steps {
sh 'mvn clean package -DskipTests'
// Stash guarda os artefatos compilados no Controller para usar em outro agente!
stash includes: 'target/*.war', name: 'pacote-compilado'
}
}
stage('2. Validação Cross-Platform Windows & Mobile') {
parallel {
stage('Teste Cliente Windows') {
agent { label 'windows && dotnet8' }
steps {
unstash 'pacote-compilado'
bat 'dir target\\'
}
}
stage('Teste Emulador Mobile') {
agent { label 'macos && ios-emulator' }
steps {
sh 'xcrun simctl list devices'
}
}
}
}
}
}Laboratório avançado · Como os comandos `stash` e `unstash` transferem arquivos entre agentes diferentes no mesmo pipeline?
Quando o Estágio 1 roda no Agente Linux A (label 'linux') e o Estágio 2 roda no Agente Windows B (label 'windows'), cada máquina possui seu próprio disco local e seu próprio diretório workspace/ — o arquivo .war gerado no Agente A não existe automaticamente no disco do Agente B! Em vez de subir o arquivo para um servidor externo apenas para trocá-lo de estágio, o comando stash name: 'meu-pacote', includes: 'target/*.war' comprime e envia os arquivos selecionados pelo canal Remoting para a memória/disco temporário do Controller, e o comando unstash 'meu-pacote' os extrai instantaneamente dentro do workspace do segundo agente!
Entrega da etapa
Projete a malha de agentes estáticos e especializados do seu servidor Jenkins: defina quais nós usarão SSH vs. JNLP/WebSocket, estabeleça as expressões booleanas de labels (sistema operacional, arquitetura, versão de runtime) e documente onde o par stash / unstash será usado para transferir artefatos entre nós distintos.
Conexão com o livro · Capítulo 3 (Seções 3.5.3: Administration Plugins — `ssh-slaves`, `windows-slaves` e 3.9: Master/Slave Architecture — JNLP Slaves, SSH Slaves, `agent.jar`/`remoting.jar` e cenários multi-SO e mobile).
Agentes Dinâmicos e Efêmeros sob Demanda: Docker Cloud, Kubernetes Pods (`podTemplate`) e EC2 Spot
Como eliminar 100% do custo de servidores agentes ociosos fora do horário de pico e garantir que cada build da empresa rode em um ambiente recém-criado, imutável e destruído logo após o término?
A Filosofia dos Dynamic Slaves: Elasticidade e Imutabilidade (Seção 3.9.3 + Capítulo 6)
Na Seção 3.9.3 (páginas 74–75, Figura 3.43), Sandeep Rawat analisa o problema financeiro e operacional dos agentes estáticos: se uma equipe executa baterias pesadas de build e testes diariamente às 15h00, manter servidores potentes ligados 24 horas por dia significa pagar por 23 horas de ociosidade diária — além do risco de um build sujar o diretório temporário ou alterar bibliotecas globais que quebram o build seguinte. Com os Dynamic Slaves (Agentes Dinâmicos / Efêmeros), configurados em Manage Jenkins > Nodes > Clouds > Add a new cloud, o Jenkins provisiona o agente sob demanda quando um job entra na fila e o destrói automaticamente ao concluir.
No nosso curso Docker em Produção com Critério, aprendemos a construir imagens enxutas multi-stage, isolar processos por namespaces/cgroups e configurar volumes. Aqui no Jenkins, damos o passo seguinte da orquestração: usamos essas imagens como Agentes Efêmeros de CI/CD em três arquiteturas progressivas (Contêiner por Estágio, Docker Cloud Template e Pods Multi-Contêiner no Kubernetes)!
As 3 Arquiteturas de Agentes Efêmeros no Jenkins
Cada organização escolhe um (ou combina dois) dos três modelos dinâmicos suportados pelos mais de 20 plugins de Cloud do Jenkins:
| Arquitetura Efêmera | Como Funciona nos Bastidores | Vantagens e Configuração de Cache |
|---|---|---|
| 1. Contêiner Efêmero por Estágio ( agent { docker { image '...' } } — Cap. 6) | Dentro de um agente com Docker Engine instalado, o Jenkins executa docker run na imagem especificada para aquele stage, monta o workspace do job como volume e faz docker rm ao sair do estágio. | Permite que o Estágio A rode em maven:3.9-jdk17 e o Estágio B rode em node:20-alpine no mesmo `Jenkinsfile`! Use args '-v $HOME/.m2:/root/.m2' (Código 6.5) para persistir o cache de pacotes entre builds. |
| 2. Docker Cloud / Kubernetes `podTemplate` (Plugin `kubernetes` ou `docker-plugin`) | O Controller chama a API do cluster Kubernetes (ou Docker Host remoto) para criar um Pod efêmero completo contendo o contêiner jnlp (que conecta de volta ao Controller) + contêineres especialistas lado a lado. | Escala elástica de nível enterprise: dezenas de pipelines rodam em paralelo sem disputar portas nem arquivos; todos os contêineres do mesmo Pod compartilham o volume /home/jenkins/agent. |
| 3. Instâncias de Nuvem sob Demanda (Plugins `ec2`, `google-compute-engine`, `azure-vm-agents`) | Monitora a fila do Jenkins; se faltar capacidade para uma label (ex.: gpu-ml-test), provisiona VMs (incluindo instâncias Spot / Preemptible até 70%–80% mais baratas!) a partir de uma AMI pré-aquecida e desliga após Idle Termination Time. | Ideal para builds pesados que exigem kernel completo, hipervisor aninhado (emuladores Android KVM) ou placas GPU dedicadas. |
// Pipeline de produção usando Agente Efêmero Kubernetes com 4 contêineres especialistas no mesmo Pod:
// 1. maven (compilação e testes com volume de cache persistente .m2)
// 2. hadolint (auditoria do Dockerfile)
// 3. kaniko (build e push da imagem Docker SEM precisar expor /var/run/docker.sock ou root!)
// 4. trivy (scan de vulnerabilidades da imagem antes do deploy)
pipeline {
agent {
kubernetes {
cloud 'k8s-prod-cluster'
yaml '''
apiVersion: v1
kind: Pod
metadata:
labels:
app: jenkins-ephemeral-agent
spec:
ServiceAccountName: jenkins-agent-sa
containers:
- name: maven
image: maven:3.9.6-eclipse-temurin-17
command: ['cat']
tty: true
volumeMounts:
- name: m2-cache
mountPath: /root/.m2
resources:
requests: { cpu: "500m", memory: "1Gi" }
limits: { cpu: "2", memory: "3Gi" }
- name: hadolint
image: hadolint/hadolint:latest-debian
command: ['cat']
tty: true
- name: kaniko
image: gcr.io/kaniko-project/executor:debug
command: ['/busybox/cat']
tty: true
- name: trivy
image: aquasec/trivy:latest
command: ['cat']
tty: true
volumes:
- name: m2-cache
persistentVolumeClaim:
claimName: jenkins-maven-cache-pvc
'''
}
}
stages {
stage('1. Compile & Unit Tests (Container Maven)') {
steps {
container('maven') {
checkout scm
sh 'mvn clean package'
}
}
}
stage('2. Dockerfile Lint (Container Hadolint)') {
steps {
container('hadolint') {
sh 'hadolint Dockerfile --no-fail -f json > hadolint.json'
}
}
}
stage('3. Build & Push OCI Image Rootless (Container Kaniko)') {
steps {
container(name: 'kaniko', shell: '/busybox/sh') {
sh """
/kaniko/executor \\
--context=\$(pwd) \\
--dockerfile=Dockerfile \\
--destination=registry.interno:5000/opstree/spring3hibernate:${env.BUILD_ID} \\
--cache=true
"""
}
}
}
stage('4. Container CVE Scan (Container Trivy)') {
steps {
container('trivy') {
sh "trivy image --exit-code 1 --severity CRITICAL registry.interno:5000/opstree/spring3hibernate:${env.BUILD_ID}"
}
}
}
}
}Laboratório avançado · Como funciona o compartilhamento do diretório de trabalho (`/home/jenkins/agent/workspace`) entre múltiplos contêineres do mesmo Pod?
Quando o plugin kubernetes do Jenkins cria um Pod a partir do seu podTemplate, ele injeta automaticamente um contêiner invisível chamado jnlp (que mantém a conexão Remoting com o Controller) e monta um volume compartilhado do tipo emptyDir no caminho /home/jenkins/agent dentro de todos os contêineres declarados no Pod! É por isso que, quando o contêiner maven faz o checkout scm e gera o arquivo target/Spring3HibernateApp.war no Estágio 1, o contêiner kaniko no Estágio 3 enxerga imediatamente o Dockerfile e o arquivo .war na mesma pasta local sem precisar de stash/unstash!
Entrega da etapa
Projete a configuração de Agentes Dinâmicos Efêmeros para a sua infraestrutura: defina quais imagens de contêiner comporão o seu template de agente (compilador, linter, construtor de imagem, scanner de segurança), como o cache de dependências (.m2, .npm, .cache/pip) será montado para acelerar builds efêmeros e quais limites de CPU/RAM evitarão contenção no cluster.
Conexão com o livro · Capítulos 3 (Seção 3.9.3: Dynamic Slaves em Docker, Kubernetes e Cloud) e 6 (Seção 6.2: execução de estágios CI em contêineres efêmeros com cache `-v ${HOME}/.m2:/root/.m2`).
Engenharia dos 5 Ambientes de Entrega Contínua: QA, Segurança, Performance, UAT e Produção
Por que rodar varreduras de segurança DAST ou testes de estresse do JMeter dentro do mesmo ambiente onde a equipe de QA está validando funcionalidades causa falsos alarmes, corrupção de banco e atrasos na sprint?
Por que 5 Ambientes Distintos após o Dev? A Arquitetura da Figura 8.1
No Capítulo 7 (Seção 7.1, páginas 136–141) e na abertura do Capítulo 8 (Seção 8.1, páginas 166–169, Figura 8.1), Sandeep Rawat relata o debate entre o Scrum Master Sajal, o Tech Lead Adeel e o engenheiro DevOps Abhishek. Quando Abhishek propõe cinco ambientes dedicados após o ambiente de Desenvolvimento (QA, Security Testing, Performance Testing, UAT e Production), Sajal inicialmente se espanta com o número — até compreender que misturar testes de naturezas incompatíveis no mesmo ambiente custa muito mais caro em retrabalho e quedas:
- 1. QA Environment (Ambiente de Garantia de Qualidade — Seção 7.1.1): é a rede de segurança funcional controlada pelos engenheiros de QA. Aqui rodam testes automatizados de regressão, fluxos de aceitação em BDD, validação de contratos de API e testes exploratórios sob a ótica do usuário. Precisa estar estável durante todo o horário de trabalho da equipe de QA;
- 2. Security Testing Environment (Ambiente de Testes de Segurança / DAST — Seção 7.1.2): enquanto no pipeline de CI fazemos análise estática do código parado (SAST com SonarQube e OWASP Dependency-Check), após o deploy precisamos simular ataques reais de hackers contra a aplicação em execução (DAST — Dynamic Application Security Testing, com ferramentas como OWASP ZAP, Nikto, Acunetix e AppScan). Por que um ambiente exclusivo? Porque um scanner DAST ativo injeta milhares de payloads maliciosos, tenta estourar sessões, cria registros sujos no banco e pode derrubar serviços; se rodar no ambiente de QA, inviabiliza o trabalho de toda a equipe de testes!
- 3. Performance Testing Environment (Ambiente Dinâmico de Performance — Seção 7.1.3): projetado para avaliar estabilidade, tempo de resposta e escalabilidade da aplicação e da infraestrutura sob carga pesada (usando Apache JMeter, Locust, LoadRunner ou LoadNinja). Detalhe arquitetural decisivo (página 139): manter um ambiente com capacidade equivalente à produção ligado 24 horas por dia apenas para testes de carga seria um desperdício financeiro enorme. Por isso, o ambiente de Performance é 100% dinâmico (efêmero): o pipeline o provisiona sob demanda antes do teste de carga, executa a bateria do JMeter e desliga/destrói o ambiente imediatamente após coletar as métricas!
- 4. Business / UAT Environment (Ambiente de Aceitação de Negócio — Seção 7.1.4): onde o software é validado pelos stakeholders de negócio e por consumidores reais no mundo real antes do lançamento geral, dividindo-se nas fases de Alpha Testing (validação controlada interna/parceiros próximos) e Beta Testing (amostra real de usuários finais). A infraestrutura do UAT deve ser um espelho fiel da Produção para garantir zero surpresas;
- 5. Production Environment (Ambiente de Produção — Seção 8.6): onde a aplicação atende toda a base de clientes e gera receita para a empresa, exigindo promoção do exato artefato/imagem já certificado nos quatro ambientes anteriores, sem jamais recompilar código em produção.
| Ambiente | Dono da Validação (Sign-off) | Ciclo de Vida da Infraestrutura | Estratégia de Deploy Associada (Cap. 8) |
|---|---|---|---|
| 1. QA Environment | Equipe de QA (Sonia) | Permanente no horário comercial | Normal / Recreate Deployment (aceita segundos de downtime na troca). |
| 2. Security Environment | Equipe de AppSec / DevSecOps | Permanente / Dedicado a scans longos | Rolling Deployment (zero downtime para não interromper scans DAST longos). |
| 3. Performance Environment | Engenharia de Performance / DevOps | Dinâmico / Efêmero (sobe para o teste e desliga ao terminar) | Blue/Green Deployment (cut-over rápido para comparar versões sob carga e rollback imediato). |
| 4. UAT Environment | Time de Negócios / Usuários Alpha & Beta | Permanente (espelho de Produção) | Canary Deployment (90/10) (valida experiência real de usuários piloto). |
| 5. Production Environment | Product Owner / SRE / Operações | Permanente (Alta Disponibilidade 24/7) | Canary Deployment (90/10 → 100%) (virada progressiva orientada a métricas). |
// Pipeline Orquestrador de Promoção CD (Figura 8.1 do livro):
// Garante que a mesma imagem imutável avance por QA -> Security -> Performance (Efêmero) -> UAT -> Prod
pipeline {
agent { label 'cd-orchestrator' }
parameters {
string(name: 'RELEASE_VERSION', defaultValue: 'v1.2.0', description: 'Versão imutável já aprovada no CI')
}
stages {
stage('1. Promoção para QA & Sign-off Funcional') {
steps {
build job: 'CD/01-deploy-qa-normal', parameters: [string(name: 'VERSION', value: params.RELEASE_VERSION)]
build job: 'CD/01b-run-selenium-cucumber-qa'
}
}
stage('2. Promoção para Security & Scan DAST') {
steps {
build job: 'CD/02-deploy-security-rolling', parameters: [string(name: 'NEW_VERSION', value: params.RELEASE_VERSION)]
build job: 'CD/02b-run-owasp-zap-dast'
}
}
stage('3. Ambiente Efêmero de Performance (Provision -> Test -> Teardown)') {
steps {
script {
try {
echo "Provisionando infraestrutura dinâmica de Performance para ${params.RELEASE_VERSION}..."
sh 'ansible-playbook -i hosts/perf_inventory.ini playbooks/provision_perf_env.yml -e state=present'
build job: 'CD/03-deploy-perf-bluegreen', parameters: [string(name: 'GREEN_VERSION', value: params.RELEASE_VERSION)]
build job: 'CD/03b-run-jmeter-loadtest'
} finally {
echo 'Desligando ambiente dinâmico de Performance para zerar custos ociosos (Seção 7.1.3)...'
sh 'ansible-playbook -i hosts/perf_inventory.ini playbooks/provision_perf_env.yml -e state=absent'
}
}
}
}
stage('4. Promoção para UAT (Alpha/Beta Canary)') {
steps {
build job: 'CD/04-deploy-uat-canary', parameters: [string(name: 'CANARY_VERSION', value: params.RELEASE_VERSION)]
}
}
stage('5. Portão de Aprovação e Release em Produção') {
steps {
timeout(time: 24, unit: 'HOURS') {
input message: "UAT aprovado pelo Negócio. Autorizar Canary de ${params.RELEASE_VERSION} em Produção?",
submitter: 'sajal-po,adeel-lead'
}
build job: 'CD/05-deploy-prod-canary', parameters: [string(name: 'CANARY_VERSION', value: params.RELEASE_VERSION)]
}
}
}
}Laboratório avançado · Por que usar o bloco `try { ... } finally { ... }` ao provisionar o Ambiente Dinâmico de Performance?
Se o teste de carga do Apache JMeter reprovar a versão porque a latência ultrapassou o limite aceitável, o passo build job: 'CD/03b-run-jmeter-loadtest' lançará uma exceção e interromperá o estágio. Se o comando que destrói as máquinas virtuais do ambiente de Performance estivesse em uma linha comum abaixo do teste, ele nunca seria executado em caso de falha nos testes — deixando servidores caros ligados na nuvem! Ao colocar o comando de teardown (state=absent / terraform destroy) dentro do bloco finally (ou no bloco declarativo post { always { ... } } do estágio), o Jenkins garante que a infraestrutura temporária seja sempre desligada, quer o teste de performance passe, quer falhe.
Entrega da etapa
Desenhe o fluxo de promoção entre os 5 ambientes de Entrega Contínua do seu projeto: especifique os critérios objetivos de passagem (Quality Gates) de um ambiente para o próximo, quem tem permissão no atributo submitter para aprovar a entrada em Produção e como o seu ambiente de Performance será ligado e desligado automaticamente.
Conexão com o livro · Capítulos 7 (Seção 7.1: Different Kinds of Environments — QA, Security, Performance e Business/UAT Alpha & Beta) e 8 (Seção 8.1: Deployment Strategy Discussion e Figura 8.1).
Automação de Testes CD (Parte 1): Regressão (Selenium Headless), BDD (Cucumber) e DAST (OWASP ZAP)
Depois que o contêiner da aplicação sobe no ambiente de QA e no ambiente de Segurança, como o Jenkins pilota navegadores invisíveis para testar fluxos completos, valida cenários de negócio escritos em português/inglês simples e simula ataques reais de invasão?
1. Regression Testing com Selenium WebDriver em Modo Headless (Seção 7.2.1, Figuras 7.2 e 7.3)
Na Seção 7.2.1 (páginas 141–143), Sandeep Rawat explica por que os testes funcionais de uma nova feature não substituem o Teste de Regressão: sempre que introduzimos uma funcionalidade nova, corrigimos um bug, fazemos uma otimização de performance ou alteramos uma variável de configuração, corremos o risco de quebrar funcionalidades antigas que já estavam funcionando. Após comparar opções como AppSurify, WebKing e TestDrive, a equipe escolhe o Selenium pela compatibilidade multi-linguagem (Java, Python, JS, C#) e suporte cross-browser.
Para rodar dentro de agentes Jenkins (que são servidores Linux ou contêineres sem placa de vídeo nem monitor conectado), é fundamental configurar o WebDriver em Modo Headless (--headless=new, --no-sandbox, --disable-dev-shm-usage): o motor de renderização real do Chrome ou Firefox executa todo o DOM, JavaScript e layout em memória sem abrir janelas gráficas, permitindo rodar dezenas de testes em paralelo com velocidade máxima.
2. Behavior Driven Development (BDD) com Cucumber e Gherkin (Seção 7.2.2, Figuras 7.4 a 7.6)
Na Seção 7.2.2 (páginas 143–146), o livro apresenta o BDD (Behavior Driven Development) com Cucumber. Enquanto um teste técnico em Java ou Python só é compreendido por programadores, o Cucumber separa a especificação em três peças arquiteturais (Figura 7.6):
- Feature File (`.feature` em linguagem Gherkin): arquivo de texto legível por qualquer pessoa do negócio (Product Owner, QA, cliente) estruturado nas cláusulas
Given(Dado que — contexto inicial),When(Quando — ação executada) eThen(Então — resultado esperado); - Step Definitions (Código de Cola / Glue Code): métodos anotados com
@Given,@Whene@Thenque traduzem cada frase do arquivo Gherkin em chamadas reais ao Selenium WebDriver ou à API; - Test Runner: classe executora acionada pelo Maven/Gradle/Pytest no Jenkins que cruza os arquivos
.featurecom as Step Definitions e gera o relatório visual de aceitação.
3. Dynamic Application Security Testing (DAST) com OWASP ZAP (Seção 7.2.3, Figuras 7.7 e 7.8)
Na Seção 7.2.3 (páginas 146–148), Rawat contrasta SAST (análise de código-fonte em repouso, feita no CI) com DAST (análise dinâmica BlackBox contra a aplicação em execução, simulando um invasor externo sem conhecimento prévio do código-fonte). A ferramenta escolhida é o OWASP Zed Attack Proxy (ZAP), que intercepta e explora todas as rotas da aplicação em dois modos:
- Passive Scan (Varredura Passiva): inspeciona requisições e respostas do spider/crawler procurando cabeçalhos de segurança ausentes (CSP, HSTS, X-Frame-Options), cookies inseguros e vazamento de stack traces sem alterar dados no servidor;
- Active Scan (Varredura Ativa): dispara ataques reais automatizados (SQL Injection, XSS refletido/armazenado, CSRF, Command Injection) contra cada parâmetro descoberto no Ambiente de Segurança, gerando relatórios HTML/XML detalhados por nível de risco (Alto, Médio, Baixo e Informativo).
| Ferramenta | O que Valida no Ambiente | Arquivos de Entrada | Relatório Publicado no Jenkins |
|---|---|---|---|
| Selenium WebDriver (Headless) | Compatibilidade cross-browser (Chrome/Firefox) e fluxos regressivos de UI de ponta a ponta. | Classes de teste Page Object + flags --headless=new --disable-gpu. | XML JUnit (`surefire-reports`) + capturas de tela em caso de falha. |
| Cucumber (BDD) | Comportamento de negócio sob a perspectiva do usuário final (linguagem Gherkin). | Arquivos src/test/resources/features/*.feature + Step Definitions. | Relatório interativo Cucumber HTML Report (`cucumber-reports.html`). |
| OWASP ZAP (DAST) | Vulnerabilidades exploráveis em tempo de execução (BlackBox Active & Passive Scan). | URL alvo do Ambiente de Segurança (ex.: http://sec-app:8080). | Relatório DAST HTML/JSON (`zap-report.html`) via `publishHTML`. |
// =========================================================================
// 1. ARQUIVO GHERKIN: src/test/resources/features/teleconsulta.feature (Seção 7.2.2)
// =========================================================================
Feature: Agendamento e Triagem na Plataforma de Telemedicina
Scenario: Paciente agenda consulta com especialista disponível
Given que o paciente acessa o portal de teleconsulta autenticado
When ele filtra pela especialidade "Cardiologia" e escolhe o horário "14:00"
Then o agendamento é confirmado com status "CONFIRMADO" e link da sala de vídeo
// =========================================================================
// 2. CONFIGURAÇÃO SELENIUM HEADLESS EM CONTAINER LINUX (Sem erro de memória /dev/shm)
// =========================================================================
// ChromeOptions options = new ChromeOptions();
// options.addArguments("--headless=new", "--no-sandbox", "--disable-dev-shm-usage", "--window-size=1920,1080");
// WebDriver driver = new ChromeDriver(options);
// =========================================================================
// 3. ESTÁGIOS NO JENKINSFILE: REGRESSÃO + BDD EM QA E OWASP ZAP EM SEGURANÇA
// =========================================================================
stage('Post-Deployment Validation | QA (Selenium + Cucumber) & Security (OWASP ZAP)') {
parallel {
stage('QA: Regression & Cucumber BDD (Headless)') {
agent {
docker {
image 'markhobson/maven-chrome:jdk-17'
args '--shm-size=2g -v $HOME/.m2:/root/.m2'
}
}
steps {
sh '''
mvn test-compile failsafe:integration-test \
-Dbrowser=chrome-headless \
-Dapp.base.url=http://qa-spring.opstree.com \
-Dcucumber.plugin="json:target/cucumber.json,html:target/cucumber-report.html"
'''
}
post {
always {
junit 'target/failsafe-reports/*.xml'
publishHTML([
allowMissing: false,
alwaysLinkToLastBuild: true,
keepAll: true,
reportDir: 'target',
reportFiles: 'cucumber-report.html',
reportName: 'Cucumber BDD Acceptance Report'
])
}
}
}
stage('Security Env: OWASP ZAP Active & Passive DAST') {
agent {
docker {
image 'ghcr.io/zaproxy/zaproxy:stable'
args '-u root'
}
}
steps {
sh '''
mkdir -p zap-output
# Executa spider + varredura passiva e ativa contra o Ambiente de Segurança:
zap-full-scan.py \
-t http://sec-spring.opstree.com \
-m 5 \
-r zap-output/owasp-zap-dast.html \
-J zap-output/owasp-zap-dast.json \
-I
'''
}
post {
always {
publishHTML([
allowMissing: false,
alwaysLinkToLastBuild: true,
keepAll: true,
reportDir: 'zap-output',
reportFiles: 'owasp-zap-dast.html',
reportName: 'OWASP ZAP DAST Security Report'
])
}
}
}
}
}Laboratório avançado · Por que contêineres rodando Chrome Headless (Selenium) travam misteriosamente sem `--shm-size=2g` ou `--disable-dev-shm-usage`?
Por padrão, o Docker monta a partição de memória compartilhada /dev/shm dentro de qualquer contêiner com apenas 64 MB de tamanho. O navegador Google Chrome utiliza intensamente /dev/shm para renderizar abas e páginas modernas; quando a página passa de 64 MB, a aba do Chrome trava com o erro clássico session deleted because of page crash no meio do teste do Selenium! Passar args '--shm-size=2g' no agente Docker do Jenkins (ou a flag --disable-dev-shm-usage no ChromeOptions, que redireciona os arquivos temporários para /tmp) elimina 100% dessas quedas aleatórias em pipelines de CI/CD.
Entrega da etapa
Estruture a bateria de testes funcionais, comportamentais e de segurança dinâmica do seu pipeline CD: escreva os cenários principais de negócio em Gherkin (`Given/When/Then`), defina as flags de execução Headless do Selenium e configure o estágio de OWASP ZAP apontando exclusivamente para o seu Ambiente de Segurança.
Conexão com o livro · Capítulo 7 (Seções 7.2.1: Regression Testing & Selenium Architecture; 7.2.2: Behavior Driven Development & Cucumber Architecture; e 7.2.3: SAST vs. DAST & OWASP ZAP Architecture).
Automação de Testes CD (Parte 2): API Testing (SoapUI), Engenharia de Performance (Apache JMeter) e Simulador
Como garantir que nenhuma alteração no backend quebre o contrato JSON esperado pelo frontend ou degrade o tempo de resposta sob pico de usuários — validando APIs e executando baterias de carga do Apache JMeter diretamente pelo Jenkins?
4. API Testing com SoapUI em Modo Headless (Seção 7.2.4, Figuras 7.9 e 7.10)
Na Seção 7.2.4 (páginas 149–151), Sandeep Rawat retoma o incidente real vivido pela equipe: o cadastro de usuários parou de funcionar e os desenvolvedores perderam horas depurando o frontend em ReactJS, quando na verdade a API backend estava devolvendo um payload vazio onde o frontend exigia um objeto JSON estruturado. O teste de API no pipeline de CD atua diretamente na camada de mensagens (JSON, XML, texto puro, REST e SOAP), cobrindo cinco verificações obrigatórias listadas no livro:
- Validação de Estrutura de Payload (JSON/XML Schema): garante que todos os campos obrigatórios, tipos de dados e objetos aninhados estejam presentes na resposta;
- Validação de Cabeçalhos HTTP e Políticas CORS (Header & CORS Validation): confirma que
Content-Type, headers de segurança e origens permitidas pelo CORS estão corretos; - Validação de Métodos e Status Codes HTTP: verifica se
GET,POST,PUTeDELETEretornam exatamente os códigos esperados (200 OK,201 Created,400 Bad Requestquando o usuário envia dados inválidos); - Validação de Autenticação e Autorização: garante que requisições sem token ou com perfil inferior recebam
401 Unauthorized/403 Forbidden; - Tempo Médio de Resposta e Prevenção de SQL Injection na API: mede a latência individual de cada endpoint e testa se inputs maliciosos nos parâmetros da requisição são devidamente sanitizados.
Para automatizar essas cinco checagens no Jenkins, o livro adota o SoapUI executando seu binário de linha de comando (testrunner.sh) com a flag -j para emitir relatórios compatíveis com JUnit XML.
5. Performance & Load Testing com Apache JMeter (Seção 7.2.5, Figuras 7.11 a 7.13)
Na Seção 7.2.5 (páginas 151–154), o livro aprofunda a Engenharia de Performance (Perf Testing). Antes de qualquer lançamento importante, precisamos validar não apenas o código da aplicação, mas também a capacidade de resposta, estabilidade e auto-scaling da infraestrutura provisionada sob carga real. O Apache JMeter (escrito em Java) simula grupos de usuários simultâneos (Thread Groups) contra aplicações Web HTTP/HTTPS, bancos de dados via JDBC, serviços SOAP/REST e conexões TCP.
Em um pipeline Jenkins profissional, você cria e calibra o plano de testes (arquivo XML .jmx) na interface gráfica do JMeter na sua máquina local, comita o arquivo .jmx no repositório Git e o executa no agente Jenkins exclusivamente em modo CLI não-gráfico (`-n`) parametrizado (passando número de usuários virtuais, tempo de rampa ramp-up e URL do ambiente dinâmico de Performance via propriedades -J), barrando o build se a taxa de erro ou o percentil 95 de latência violarem o SLA!
| Ferramenta e Comando CLI | Flags Críticas para CI/CD | Critério de Aprovação Automatizado |
|---|---|---|
| SoapUI (`testrunner.sh`) | -s "SuiteName" (suíte alvo), -r (resumo no console), -j (exporta JUnit XML), -f target/soapui (pasta de saída), -E env_qa (ambiente alvo). | Zero falhas de asserção de schema JSON, CORS, autenticação e tempo limite por endpoint. |
| Apache JMeter (`jmeter`) | -n (modo Non-GUI obrigatório), -t plano.jmx (arquivo de teste), -l result.jtl (log de amostras), -e -o dashboard/ (gera painel HTML), -Jthreads=200. | Validação automática do arquivo .jtl: taxa de erro HTTP < 0.5% e tempo de resposta p95 dentro da meta (ex.: < 300 ms). |
// Estágio completo de validação de API (SoapUI) e Carga (Apache JMeter) com portão automático de SLA:
stage('CD API & Performance Validation') {
steps {
// 1. Execução Headless dos testes funcionais e de segurança de API no SoapUI:
sh '''
/opt/soapui/bin/testrunner.sh \
-s"Telemedicina_API_Suite" \
-r -j -f target/soapui-reports \
-PhostUrl=http://green-pt-spring.opstree.com \
tests/api/Telemedicina-soapui-project.xml
'''
// 2. Execução do Teste de Carga no Apache JMeter em modo Non-GUI (-n) com parâmetros dinâmicos (-J):
sh '''
rm -rf target/jmeter-results.jtl target/jmeter-dashboard
jmeter -n \
-t tests/performance/teleconsulta_load_test.jmx \
-JtargetHost=green-pt-spring.opstree.com \
-JtargetPort=80 \
-JvirtualUsers=150 \
-JrampUpSeconds=30 \
-JdurationSeconds=180 \
-l target/jmeter-results.jtl \
-e -o target/jmeter-dashboard
'''
// 3. Portão Automatizado em Python que analisa o arquivo .jtl do JMeter e reprova o pipeline se violar o SLA:
sh '''
python3 - <<'PY'
import csv, sys
tempos, total, erros = [], 0, 0
with open("target/jmeter-results.jtl", "r", encoding="utf-8") as f:
for row in csv.DictReader(f):
total += 1
tempos.append(int(row["elapsed"]))
if row["success"].lower() != "true":
erros += 1
tempos.sort()
p95 = tempos[int(len(tempos) * 0.95) - 1] if tempos else 0
taxa_erro = (erros / total * 100) if total else 0
print(f"[JMeter Gate] Amostras={total} | Latência p95={p95}ms (SLA <=350ms) | Taxa de Erro={taxa_erro:.2f}% (SLA <=0.50%)")
if p95 > 350 or taxa_erro > 0.50:
sys.exit("REPROVADO NO GATE DE PERFORMANCE: Latência ou taxa de erro acima do limite!")
PY
'''
}
post {
always {
junit 'target/soapui-reports/*.xml'
publishHTML([
allowMissing: false,
alwaysLinkToLastBuild: true,
keepAll: true,
reportDir: 'target/jmeter-dashboard',
reportFiles: 'index.html',
reportName: 'JMeter Load Test Dashboard'
])
}
}
}Simulador de Economia com Agentes Efêmeros, Ambiente Dinâmico de Performance e Deploys sem Downtime
Calcule o retorno operacional mensal ao substituir servidores ociosos por Agentes Efêmeros e Ambiente de Performance sob demanda, somado à eliminação de janelas de indisponibilidade em produção.
Laboratório avançado · Como parametrizar um arquivo `.jmx` do Apache JMeter para que o mesmo arquivo funcione em qualquer ambiente do Jenkins?
Quando você grava um teste no JMeter GUI, é comum que o endereço do servidor e o número de threads fiquem fixos (hardcoded) dentro do XML do arquivo .jmx. Para torná-lo dinâmico para o Jenkins, substitua no JMeter os valores fixos pela função ${__P(nomePropriedade, valorPadrao)}: por exemplo, no campo Server Name use ${__P(targetHost, localhost)} e no campo Number of Threads use ${__P(virtualUsers, 50)}. Assim, o seu `Jenkinsfile` pode injetar qualquer host ou carga via linha de comando (-JtargetHost=green-pt-spring.opstree.com -JvirtualUsers=200) sem nunca precisar editar o arquivo `.jmx`!
Entrega da etapa
Especifique a validação de API e de Performance do seu pipeline CD: liste as asserções obrigatórias nos seus testes de API (SoapUI / Postman / RestAssured), defina os parâmetros dinâmicos (-JvirtualUsers, -JrampUpSeconds) e os limites de corte (p95 e taxa de erro) do Apache JMeter, e registre o saldo do simulador acima.
Conexão com o livro · Capítulo 7 (Seções 7.2.4: API Testing & SoapUI Architecture e 7.2.5: Performance Testing & Apache JMeter Architecture).
Estratégias de Deploy no Jenkins (Parte 1): Service Discovery com Traefik, Deploy Normal (QA) e Rolling Deployment (Segurança)
Como o proxy reverso Traefik descobre novos contêineres dinamicamente sem precisar reiniciar nem editar arquivos de configuração — e como codificar no `Jenkinsfile` tanto o deploy Normal/Recreate para QA quanto o Rolling Deployment com Health Check HTTP 200 para o Ambiente de Segurança?
Service Discovery Dinâmico com Traefik e Docker Labels (Seção 8.2, Códigos 8.1 e 8.2)
Nos Capítulos 7 (Seções 7.3.1 e 7.3.2) e 8 (Seções 8.2 e 8.3, páginas 155–159 e 169–175), Sandeep Rawat constrói toda a camada de roteamento de tráfego dos ambientes usando o Traefik (`traefik:v2.8`). Diferente de balanceadores legados em que você precisava editar um arquivo estático de IPs e recarregar o serviço a cada deploy, o Traefik monta o socket /var/run/docker.sock:ro (somente leitura!) e escuta eventos de contêineres em tempo real:
- Com a flag
--providers.docker.exposedbydefault=false(Código 8.1), nenhum contêiner do servidor fica exposto por acidente; - Para expor um contêiner no balanceador, o `Jenkinsfile` passa labels Docker no comando
docker run:--label traefik.enable=true, a regra de roteamento por domínio--label 'traefik.http.routers.spring3hibernate.rule=Host(`qa-spring.opstree.com`)'e a porta interna da aplicação--label "traefik.port=8080"!
1. Normal / Recreate Deployment no Ambiente QA (Código 8.3) vs. 2. Rolling Deployment no Ambiente de Segurança (Código 8.4)
Com o Traefik no ar, o livro contrasta as duas primeiras estratégias de deploy em código:
| Dimensão Técnica | Normal / Recreate Deployment (QA — Código 8.3) | Rolling / Ramped Deployment (Segurança — Código 8.4) |
|---|---|---|
| Sequência Exata de Operações no `Jenkinsfile` | 1. Recebe o parâmetro VERSION;2. Verifica com docker inspect se o contêiner existe;3. Se existir, derruba o antigo ( docker rm -f) e sobe o novo (docker run) com as labels do Traefik. | 1. Recebe OLDER_VERSION e NEW_VERSION;2. Sobe o contêiner novo sem expor no Traefik; 3. Inspeciona o IP interno e valida Health Check HTTP 200 via curl;4. Se retornar 200, anexa as labels do Traefik na versão nova e só então remove a versão antiga! |
| Parâmetros de Capacidade (`Max Surge` e `Max Unavailable`) | Capacidade cai a zero durante o desligamento da v1 e o boot da v2 (gera downtime proporcional ao tempo de inicialização da aplicação). | `Max Surge` define quantas instâncias novas sobem acima da capacidade atual; `Max Unavailable` define quantas antigas podem sair do ar (com Max Unavailable = 0, há zero downtime!). |
| Comportamento em Caso de Falha na Inicialização da `v2` | Se a v2 falhar ao subir, o ambiente de QA fica fora do ar até que um novo build corrija o problema. | O estágio Validating the NEW_VERSION detecta que response != 200, executa exit 1 e aborta o pipeline antes de tocar na versão antiga, preservando 100% da disponibilidade! |
# =========================================================================
# 1. INFRAESTRUTURA DO LOAD BALANCER TRAEFIK: docker-compose.yml (Código 8.1)
# =========================================================================
version: "3.3"
services:
traefik:
image: "traefik:v2.8"
container_name: "traefik"
command:
- "--api.insecure=true"
- "--providers.docker=true"
- "--providers.docker.exposedbydefault=false"
- "--entrypoints.web.address=:80"
ports:
- "80:80"
- "8080:8080"
volumes:
- "/var/run/docker.sock:/var/run/docker.sock:ro"
network_mode: bridge
# Subir com: docker compose up -d (Código 8.2)
// =========================================================================
// 2. JENKINSFILE — NORMAL / RECREATE DEPLOYMENT EM QA (Código 8.3)
// =========================================================================
node("deployment") {
properties([parameters([string(defaultValue: 'latest', name: 'VERSION', trim: true)])])
stage("Cloning codebase for QA Deployment") {
checkout scm
}
stage("Deploying the ${VERSION} on QA Environment") {
sh """
if ! sudo docker inspect spring3hibernate > /dev/null 2>&1
then
sudo docker run -itd --name spring3hibernate --label traefik.enable=true \\
--label 'traefik.http.routers.spring3hibernate.rule=Host(`qa-spring.opstree.com`)' \\
--label "traefik.port=8080" opstree/spring3hibernate:${VERSION}
else
sudo docker rm -f spring3hibernate
sudo docker run -itd --name spring3hibernate --label traefik.enable=true \\
--label 'traefik.http.routers.spring3hibernate.rule=Host(`qa-spring.opstree.com`)' \\
--label "traefik.port=8080" opstree/spring3hibernate:${VERSION}
fi
"""
}
}
// =========================================================================
// 3. JENKINSFILE — ROLLING DEPLOYMENT COM HEALTH CHECK EM SEGURANÇA (Código 8.4)
// =========================================================================
node("deployment") {
properties([parameters([
string(defaultValue: 'v1.0', name: 'OLDER_VERSION', trim: true),
string(defaultValue: 'v1.1', name: 'NEW_VERSION', trim: true)
])])
stage("Cloning codebase for Security Environment Deployment") {
checkout scm
}
stage("Deploying the ${NEW_VERSION} on Security Environment") {
sh """
sudo docker run -itd --name spring3hibernate-${NEW_VERSION} opstree/spring3hibernate:${NEW_VERSION}
"""
}
stage("Validating the ${NEW_VERSION}") {
sh """
sleep 10s
NEW_IP=\$(sudo docker inspect -f '{{range.NetworkSettings.Networks}}{{.IPAddress}}{{end}}' spring3hibernate-${NEW_VERSION})
response=\$(curl --write-out '%{http_code}' --silent --output /dev/null http://\${NEW_IP}:8080)
if [ "\${response}" != "200" ]; then
echo "Application is not working fine (HTTP \${response})"
sudo docker rm -f spring3hibernate-${NEW_VERSION}
exit 1
fi
"""
}
stage("Add the new version to load balancer") {
sh """
sudo docker rm -f spring3hibernate-${NEW_VERSION}
sudo docker run -itd --name spring3hibernate-${NEW_VERSION} --label traefik.enable=true \\
--label 'traefik.http.routers.spring3hibernate.rule=Host(`sec-spring.opstree.com`)' \\
--label "traefik.port=8080" opstree/spring3hibernate:${NEW_VERSION}
"""
}
stage("Removing old version") {
sh """
sudo docker rm -f spring3hibernate-${OLDER_VERSION} || true
"""
}
}Laboratório avançado · Como eliminar a recriação intermediária do contêiner ao adicioná-lo ao Traefik no Rolling Deployment?
No Código 8.4 do livro, o contêiner spring3hibernate-${NEW_VERSION} é iniciado primeiro sem labels para testar o IP interno e depois recriado com --label traefik.enable=true porque o Docker não permite alterar labels de um contêiner já em execução. Uma alternativa ainda mais fluida usando o próprio Traefik + Docker é iniciar o contêiner novo já com as labels do Traefik E com uma label de Health Check nativo do Traefik (--label traefik.http.services.sec.loadbalancer.healthcheck.path=/health e interval=5s): assim, o próprio Traefik só começa a enviar requisições para o contêiner novo depois que o endpoint /health responder 200 OK, sem precisar recriar o contêiner!
Entrega da etapa
Estruture a camada de Service Discovery e os deploys de QA e Segurança do seu projeto: defina a configuração do Traefik (com socket somente leitura e exposedbydefault=false), as regras de host de cada ambiente e o estágio de validação de saúde HTTP (Readiness / Health Check) do seu Rolling Deployment.
Conexão com o livro · Capítulos 7 (Seções 7.3.1: Normal Deployment e 7.3.2: Rolling/Ramped Deployment — `Max Surge` e `Max Unavailable`) e 8 (Seções 8.2 e 8.3 — Códigos 8.1, 8.2, 8.3 e 8.4).
Estratégias de Deploy no Jenkins (Parte 2): Blue/Green Deployment com Bloco `input` e AWS Route53
Como executar um cut-over de tráfego instantâneo entre duas infraestruturas completas usando o Jenkins e o AWS Route53 — mantendo um botão de Rollback vivo no pipeline capaz de reverter toda a operação em segundos caso o teste pós-virada aponte anomalia?
A Mecânica Completa do Blue/Green Deployment (Seção 7.3.3 e Seção 8.4, Código 8.5)
Nas páginas 159–162 (Figuras 7.20 e 7.21) e nas páginas 175–178 (Figuras 8.9 a 8.12 e Código 8.5), Sandeep Rawat disseca o Blue/Green Deployment (também conhecido como Red/Black Deployment). Ao contrário do Rolling Update — em que instâncias antigas e novas convivem misturadas atrás do mesmo balanceador e um rollback exige fazer todo o processo gradual ao contrário —, o Blue/Green opera com dois ambientes idênticos e independentes:
- Estado Inicial: o ambiente Blue (`BLUE_VERSION`) está servindo 100% do tráfego oficial (ex.:
pt-spring.opstree.comapontando parablue-pt-spring.opstree.com); - Stage 1 — Deploy Paralelo do Ambiente Green: o Jenkins sobe a nova versão (`GREEN_VERSION`) no ambiente Green atendendo pelo host isolado
green-pt-spring.opstree.com, sem tocar no tráfego do ambiente Blue; - Stage 2 — Pausa Interativa pré-Cutover (`input`): o pipeline pausa no estágio “Pause pipeline for Green version validation” aguardando que o engenheiro ou a suíte automatizada valide se o endpoint privado `green-pt-spring.opstree.com` está 100% saudável;
- Stage 3 — Cut-Over de Tráfego via DNS / Load Balancer: assim que aprovado, o Jenkins executa a chamada da AWS CLI
aws route53 change-resource-record-sets --hosted-zone-id ... --change-batch file://blue-green-performance/green-update.json, alterando o registro CNAME/Alias público para apontar para o ambiente Green; - Stage 4 — Portão de Decisão Pós-Virada (`Proceed` vs. `Rollback`): com o tráfego já no Green, o Jenkins abre um segundo prompt interativo
inputcom uma caixa de seleção (choice(choices: ['Rollback', 'Proceed'], name: 'action')) e armazena a resposta na variávelreturnValue:- Se
returnValue == "Rollback"(Figura 8.11): o estágio “Rollback in case of failure” executa imediatamenteaws route53 change-resource-record-sets ... file://blue-green-performance/blue-update.json, devolvendo todo o tráfego para o ambiente Blue que permaneceu ligado e intacto! - Se
returnValue == "Proceed"(Figura 8.12): o estágio “Live the traffic” consolida a nova versão como oficial e recicla os recursos excedentes.
- Se
| Componente do Pipeline | Código / Configuração Utilizada | Papel na Segurança da Mudança |
|---|---|---|
| Parâmetros do Job | GREEN_VERSION (versão nova candidata) e BLUE_VERSION (versão estável atual). | Explicita exatamente qual tag está ativa e qual tag está sendo promovida na execução. |
| Payloads Route53 (`green-update.json` e `blue-update.json`) | Arquivos JSON com a ação UPSERT no registro DNS (com TTL curto) apontando ora para o host Green, ora para o host Blue. | Permite chavear o tráfego de entrada em nível de rede/DNS por linha de comando auditável no Git. |
| Bloco Condicional `input` + `choice` | returnValue = input message: '...', parameters: [choice(choices: ['Rollback', 'Proceed'], name: 'action')] | Transforma o rollback em uma decisão de um único clique na interface Stage View / Blue Ocean do Jenkins. |
// =========================================================================
// 1. ARQUIVO: blue-green-performance/green-update.json (Cut-Over para Green)
// =========================================================================
{
"Comment": "Cut-over de trafego para a nova versao Green",
"Changes": [{
"Action": "UPSERT",
"ResourceRecordSet": {
"Name": "pt-spring.opstree.com.",
"Type": "CNAME",
"TTL": 15,
"ResourceRecords": [{ "Value": "green-pt-spring.opstree.com" }]
}
}]
}
// =========================================================================
// 2. ARQUIVO: blue-green-performance/blue-update.json (Rollback para Blue)
// =========================================================================
{
"Comment": "Rollback imediato de trafego para a versao estavel Blue",
"Changes": [{
"Action": "UPSERT",
"ResourceRecordSet": {
"Name": "pt-spring.opstree.com.",
"Type": "CNAME",
"TTL": 15,
"ResourceRecords": [{ "Value": "blue-pt-spring.opstree.com" }]
}
}]
}
// =========================================================================
// 3. JENKINSFILE — BLUE/GREEN DEPLOYMENT COMPLETO (Código 8.5 do Capítulo 8)
// =========================================================================
node("deployment") {
properties([parameters([
string(defaultValue: 'v1.1', name: 'GREEN_VERSION', trim: true, description: 'Green is newer version'),
string(defaultValue: 'v1.0', name: 'BLUE_VERSION', trim: true, description: 'Blue is older version')
])])
stage("Cloning codebase for Performance Environment Deployment") {
checkout scm
}
stage("Deploying the Green version on PT Environment") {
sh """
sudo docker rm -f spring3hibernate-green || true
sudo docker run -itd --name spring3hibernate-green --label traefik.enable=true \\
--label 'traefik.http.routers.spring3hibernate-green.rule=Host(`green-pt-spring.opstree.com`)' \\
--label "traefik.port=8080" opstree/spring3hibernate:${GREEN_VERSION}
"""
}
stage("Pause pipeline for Green version validation") {
input 'Do you want to proceed further?'
}
stage("Cut-over of traffic on Green version from Blue version") {
sh """
aws route53 change-resource-record-sets --hosted-zone-id Z10166071X5VNARP9PUCV \\
--change-batch file://blue-green-performance/green-update.json
"""
}
stage("Validation of Performance Testing Environment") {
returnValue = input message: 'Do you want to proceed further?',
parameters: [choice(choices: ['Rollback', 'Proceed'], name: 'action')]
}
if ("${returnValue}" == "Rollback") {
stage("Rollback in case of failure") {
sh """
aws route53 change-resource-record-sets --hosted-zone-id Z10166071X5VNARP9PUCV \\
--change-batch file://blue-green-performance/blue-update.json
"""
}
} else if ("${returnValue}" == "Proceed") {
stage("Live the traffic") {
sh """
sudo docker rm -f spring3hibernate-green
sudo docker rm -f spring3hibernate-blue || true
sudo docker run -itd --name spring3hibernate-blue --label traefik.enable=true \\
--label 'traefik.http.routers.spring3hibernate-blue.rule=Host(`blue-pt-spring.opstree.com`)' \\
--label "traefik.port=8080" opstree/spring3hibernate:${GREEN_VERSION}
"""
}
}
}Laboratório avançado · No estágio final “Live the traffic” do Código 8.5, por que o autor atualiza o contêiner `blue` com a imagem `${GREEN_VERSION}` após o Proceed?
Observe a engenharia inteligente no final do Código 8.5: durante o teste, o ambiente oficial permanente é o blue-pt-spring.opstree.com e o ambiente temporário de validação é o green-pt-spring.opstree.com. Quando você clica em Proceed confirmando que a versão ${GREEN_VERSION} foi aprovada nos testes de performance, o script remove o contêiner temporário spring3hibernate-green e sobe a versão aprovada ${GREEN_VERSION} assumindo o posto oficial do spring3hibernate-blue! Assim, você não precisa pagar por duas infraestruturas rodando 24 horas por dia após o encerramento da janela de deploy.
Entrega da etapa
Estruture o pipeline de Blue/Green Deployment para o seu projeto: escreva os manifestos de troca de rota (via DNS `Route53`, `Traefik` ou `Kubernetes Service Selector`), defina os testes que rodarão durante a pausa de validação do ambiente Green e documente o procedimento exato de Rollback.
Conexão com o livro · Capítulos 7 (Seção 7.3.3: Blue Green Deployment — Figuras 7.20 e 7.21) e 8 (Seção 8.4: Continuous Deployment for Performance Environment — Figuras 8.9 a 8.12 e Código 8.5).
Estratégias de Deploy no Jenkins (Parte 3): Canary Deployment Ponderado em UAT e Produção
Como expor uma nova versão em UAT e Produção para apenas 10% dos usuários reais enquanto 90% continuam na versão estável, comparar telemetria ao vivo e promover para 100% (ou abortar para 0%) diretamente pela interface do Jenkins?
A Arquitetura do Canary Deployment por Pesos (`90% Baseline / 10% Canary`) (Capítulos 7.3.4, 8.5 e 8.6)
Nas Seções 7.3.4 (páginas 162–164, Figuras 7.22 e 7.23) e nas Seções 8.5 e 8.6 (páginas 178–183, Figuras 8.13 a 8.17 e Código 8.6), Sandeep Rawat apresenta o ápice da esteira de Entrega Contínua para os ambientes de UAT e Produção: o Canary Deployment. Por que escolher Canary em vez de Blue/Green para Produção? Porque mesmo que uma versão tenha passado por todos os testes sintéticos em QA, Segurança e Performance, nada substitui observar como ela se comporta diante do tráfego real e imprevisível dos usuários finais — mas sem arriscar derrubar 100% dos clientes de uma só vez!
No Código 8.6 do livro, o Jenkins orquestra a divisão ponderada de tráfego (*Weighted Round Robin*) usando as labels de peso do Traefik (--label "traefik.weight=10" e --label "traefik.weight=90") associadas ao mesmo backend lógico (--label "traefik.backend=app_weighted") e ao mesmo domínio público (uat-spring.opstree.com ou prod-spring.opstree.com):
- Fase 1 — Lançamento Canário (10% Canary / 90% Baseline): o Jenkins sobe o contêiner
spring3hibernate-canaryrodando a${CANARY_VERSION}com peso 10 e o contêinerspring3hibernate-baselinerodando a${BASELINE_VERSION}com peso 90. De cada 10 requisições que chegam ao domínio, 9 vão para a versão antiga comprovada e 1 vai para a versão nova; - Fase 2 — Janela de Observação e Comparação de Métricas: enquanto o pipeline aguarda no estágio “Validation for Application” (
input), a equipe compara os gráficos de erro, latência, consumo de memória e conversão de negócios entre o contêiner Canary e o contêiner Baseline; - Fase 3A — Caminho de Rollback (Figura 8.16): se
returnValue == "Rollback", o estágio “Rollback complete traffic to Baseline” reconfigura o contêiner Baseline paratraefik.weight=100e destrói imediatamente o contêiner Canary (docker rm -f spring3hibernate-canary), restaurando a estabilidade total; - Fase 3B — Caminho de Promoção Completa (Figura 8.15): se
returnValue == "Proceed", o estágio “Shifting all traffic to canary” remove o contêiner Baseline antigo e promove a${CANARY_VERSION}paratraefik.weight=100, concluindo o release em Produção com zero downtime!
| Critério de Decisão | Normal / Recreate | Rolling / Ramped | Blue / Green | Canary Deployment |
|---|---|---|---|---|
| Impacto de Downtime | Causado a cada deploy (desliga v1 antes da v2). | Zero Downtime (substituição gradual). | Zero Downtime (virada de chave no roteador/DNS). | Zero Downtime (divisão percentual de tráfego). |
| Teste com Tráfego Real em Produção | Tudo ou nada (100% dos usuários expostos de imediato). | Gradual por instância, sem controle fino de percentual. | Testa em isolamento antes da virada; no cut-over vira 100% de uma vez. | Controle cirúrgico por peso (ex.: 10% → 50% → 100%). |
| Velocidade e Facilidade de Rollback | Lento (exige redeploy da versão antiga). | Lento (exige rolling update reverso). | Instantâneo (volta o ponteiro para o Blue). | Muito rápido (volta peso do Baseline para 100% e apaga Canary). |
| Custo Adicional de Infraestrutura | Zero (1x capacidade). | Mínimo (apenas as instâncias de Max Surge). | Alto (2x capacidade completa em paralelo). | Baixo (apenas as réplicas proporcionais ao peso do canário). |
// Jenkinsfile completo reproduzindo o Código 8.6 (Capítulos 8.5 e 8.6) para UAT e Produção:
node("deployment") {
properties([parameters([
string(defaultValue: 'v1.0', name: 'BASELINE_VERSION', trim: true, description: 'Older stable version (90% traffic)'),
string(defaultValue: 'v1.1', name: 'CANARY_VERSION', trim: true, description: 'Newer canary version (10% traffic)')
])])
stage("Cloning codebase for UAT/Prod Environment Deployment") {
checkout scm
}
stage("Deploy the Canary version (10%) alongside Baseline (90%)") {
sh """
sudo docker rm -f spring3hibernate-canary || true
sudo docker run -itd --name spring3hibernate-canary --label traefik.enable=true \\
--label 'traefik.http.routers.spring3hibernate-canary.rule=Host(`uat-spring.opstree.com`)' \\
--label "traefik.port=8080" --label "traefik.weight=10" --label "traefik.backend=app_weighted" \\
opstree/spring3hibernate:${CANARY_VERSION}
sudo docker rm -f spring3hibernate-baseline || true
sudo docker run -itd --name spring3hibernate-baseline --label traefik.enable=true \\
--label 'traefik.http.routers.spring3hibernate-baseline.rule=Host(`uat-spring.opstree.com`)' \\
--label "traefik.port=8080" --label "traefik.weight=90" --label "traefik.backend=app_weighted" \\
opstree/spring3hibernate:${BASELINE_VERSION}
"""
}
stage("Validation for Application (Telemetry & User Experience)") {
returnValue = input message: 'Canary receiving 10% live traffic. Do you want to proceed further?',
parameters: [choice(choices: ['Rollback', 'Proceed'], name: 'action')]
}
if ("${returnValue}" == "Rollback") {
stage("Rollback complete traffic to Baseline (100%)") {
sh """
sudo docker rm -f spring3hibernate-baseline
sudo docker run -itd --name spring3hibernate-baseline --label traefik.enable=true \\
--label 'traefik.http.routers.spring3hibernate-baseline.rule=Host(`uat-spring.opstree.com`)' \\
--label "traefik.port=8080" --label "traefik.weight=100" --label "traefik.backend=app_weighted" \\
opstree/spring3hibernate:${BASELINE_VERSION}
sudo docker rm -f spring3hibernate-canary
"""
}
} else if ("${returnValue}" == "Proceed") {
stage("Shifting all traffic to Canary (100% Live)") {
sh """
sudo docker rm -f spring3hibernate-baseline
sudo docker rm -f spring3hibernate-canary
sudo docker run -itd --name spring3hibernate-canary --label traefik.enable=true \\
--label 'traefik.http.routers.spring3hibernate-baseline.rule=Host(`uat-spring.opstree.com`)' \\
--label "traefik.port=8080" --label "traefik.weight=100" --label "traefik.backend=app_weighted" \\
opstree/spring3hibernate:${CANARY_VERSION}
"""
}
}
}Laboratório avançado · Como usar Sessões Aderentes (Sticky Sessions) no Canary Deployment para que um mesmo usuário não alterne entre v1 e v2 a cada clique?
Imagine que um usuário faz login na aplicação e sua primeira requisição cai nos 10% da versão Canary (v1.1), mas ao clicar na próxima página a segunda requisição cai nos 90% da versão Baseline (v1.0): se a estrutura da tela ou do estado de sessão mudou entre as versões, o usuário verá uma interface inconsistente! Para evitar isso no Traefik (ou no Nginx/ALB/Istio), habilite Sticky Sessions baseadas em Cookie no serviço ponderado (traefik.http.services.app_weighted.weighted.sticky.cookie=true): assim, uma vez que um usuário é sorteado para os 10% do Canary, todas as requisições subsequentes daquele navegador continuam indo para o Canary durante o teste.
Entrega da etapa
Consolide a estratégia final de Canary Deployment em UAT e Produção do seu projeto: defina os pesos iniciais (90/10), a configuração de Sticky Sessions se houver estado de interface, as métricas comparativas monitoradas durante a pausa de validação e o comando exato de promoção para 100% ou Rollback.
Conexão com o livro · Capítulos 7 (Seção 7.3.4: Canary Deployment e Figura 7.23) e 8 (Seções 8.5, 8.6 e 8.7: UAT Canary, Production Canary, Código 8.6 e Reflection da equipe).
Projeto prático e caderno da Etapa 3 (Conclusão da Trilha)
Consolide sua arquitetura distribuída de Agentes Efêmeros e Entrega Contínua em Produção (Capítulos 3.9, 6, 7 e 8 de Sandeep Rawat), valide suas decisões nos cenários abaixo e exporte seu caderno completo.
Suas anotações nas 8 oficinas
Especificação Completa de Agentes Efêmeros e Esteira de Deploy Contínuo (Etapa 3)
Feche o documento de engenharia da sua esteira de produção em quatro blocos:
Checklist de domínio da Etapa 3 e da Formação Completa
Marque os itens que você consolidou ao concluir as 3 etapas de Jenkins e CI/CD:
Referências bibliográficas e conexões com o acervo (Capítulos 3.9, 6, 7 e 8)
RAWAT, Sandeep. CI/CD Pipeline with Docker and Jenkins: Learn How to Build and Manage Your CI/CD Pipelines Effectively. London: BPB Publications, 2023. ISBN 978-93-55513-502 (Capítulo 3.9: Master/Slave Architecture e Dynamic Slaves; Capítulo 6: CI with Jenkins and Docker; Capítulo 7: Continuous Deployment — Environments, Selenium, Cucumber BDD, OWASP ZAP, SoapUI, JMeter e Deployment Strategies; Capítulo 8: Continuous Deployment Using Jenkins — Códigos 8.1 a 8.6 com Traefik, AWS Route53, Rolling, Blue/Green e Canary). Cursos integrados no acervo Ateliê da Tecnologia: Docker em Produção com Critério e Trilha Git com Critério.