01 - Jenkins
video: https://youtu.be/... no frontmatter da nota.Jenkins
Jenkins é um servidor de automação self-hosted de código aberto que executa pipelines de build, teste e deploy, sendo a ferramenta mais longeva e difundida da categoria em ambientes corporativos. Sua proposta central é flexibilidade total: qualquer linguagem, qualquer repositório, qualquer nuvem ou servidor bare metal, controlados por uma configuração que vive no próprio ambiente do time. O comportamento do pipeline é descrito em um Jenkinsfile versionado junto com o código, escrito em Groovy, e a execução é distribuída entre um controller e agentes especializados. Em produção, o Jenkins continua sendo realidade dominante em organizações com infraestrutura própria, requisitos de isolamento de rede ou centenas de jobs legados, e dominar seu modelo operacional é requisito prático para manter esse parque funcionando com confiabilidade.
Conceito e arquitetura: controller e agents
A arquitetura separa a orquestração da execução. O controller, historicamente chamado de master, hospeda a interface web, armazena configurações, agenda os jobs e mantém o estado do sistema. Os agents são máquinas ou containers que executam os jobs de fato, e cada um é rotulado com capacidades específicas, como docker, node18, gpu ou acesso a uma rede protegida. Essa separação permite escalar capacidade horizontalmente sem tocar no controller e isolar cargas sensíveis em agentes dedicados.
- Controller: processo de controle e coordenação; idealmente não executa builds pesados, apenas agenda e guarda o estado, porque uma sobrecarga nele derruba a operação inteira.
- Agent estático: nó permanente registrado manualmente, comum em ambientes on-premises com máquinas dedicadas.
- Agent efêmero: provisionado por container ou por nuvem a cada execução e destruído no fim, garantindo ambiente limpo e capacidade que acompanha a demanda.
- Etiquetas de capacidade: o Jenkinsfile pede um agente por rótulo, como
agent { label 'docker && node18' }, e o controller escolhe um nó livre que atenda à exigência.
A regra operacional mais importante dessa arquitetura é tratar o controller como gado raro: com backup da configuração, upgrade planejado e carga de build mínima, porque ele é o único ponto sem redundância natural no desenho clássico da ferramenta.
O problema que resolve
O Jenkins substitui a automação fragmentada em scripts locais, cron e conhecimento tácito de um time de infraestrutura. Antes dele, cada time tinha seu procedimento de build, ninguém conseguia reproduzir o release completo e o histórico de execução vivia em terminais perdidos. O servidor centraliza execução, agenda, credenciais e histórico, expondo tudo por interface e por API, com permissões por projeto e rastro auditável de quem disparou o quê. O valor que sustentou sua adoção massiva permanece válido em produção: nenhum outro sistema oferece o mesmo grau de controle sobre o ambiente de execução, o que importa quando o pipeline precisa alcançar bases de dados internas, integrações legadas e redes sem saída para a internet.
Há também um efeito de governança que costuma ser subestimado. Quando a automação vive em um sistema central, o processo de release da organização se torna observável: é possível responder com precisão quantos deploys por dia cada time executa, quanto tempo dura a validação e onde estão os gargalos. Com automação dispersa, essas perguntas não têm resposta confiável, e sem resposta não existe melhoria sistemática do fluxo de entrega.
Jenkinsfile e pipeline como código
O Jenkinsfile descreve o pipeline em Groovy, com dois sabores: declarativo, estruturado e mais simples de governar, e scripted, flexível porém propenso a virar código ininteligível. O declarativo é o padrão recomendado para qualquer time que valorize manutenção. O exemplo abaixo cobre o desenho típico de um serviço de produção:
pipeline {
agent { label 'docker' } // exige agente com Docker disponível
options {
timestamps() // carimbo de tempo em todos os logs
timeout(time: 30, unit: 'MINUTES') // trava contra job travado
buildDiscarder(logRotator(numToKeepStr: '50')) // retenção de builds
}
environment {
APP_IMAGE = "app:${env.GIT_COMMIT.take(8)}" // tag pelo SHA curto
}
stages {
stage('Lint') { // falha rápida em segundos
steps { sh 'npm run lint' }
}
stage('Test') { // suíte unitária com cobertura
steps { sh 'npm test -- --coverage' }
}
stage('Build') { // constrói e publica a imagem imutável
steps {
sh "docker build -t $APP_IMAGE ."
sh "docker push registry.local/$APP_IMAGE"
}
}
stage('Deploy staging') { // implantação automática em staging
steps { sh "./deploy.sh staging $APP_IMAGE" }
}
stage('Deploy producao') { // gate humano antes de produção
input { message "Aprovar deploy em produção?" }
steps { sh "./deploy.sh prod $APP_IMAGE" }
}
}
post {
failure { slackSend channel: '#ci', message: "Falhou: ${env.JOB_NAME} #${env.BUILD_NUMBER}" }
always { cleanWs() } // workspace limpo na próxima execução
}
}
A biblioteca compartilhada, shared library, é o mecanismo que evita duplicação entre centenas de Jenkinsfiles: passos como deploy, notificação e scan ficam centralizados em um repositório versionado e importados pelos pipelines, o que transforma a governança da automação em manutenção de código de verdade.
Execução distribuída e agentes efêmeros
Parques saudáveis de Jenkins executam em agentes efêmeros por container: o controller pede um executor a cada job, o runner nasce com as ferramentas declaradas na imagem, executa e morre. O benefício é duplo; ambiente garantidamente limpo entre execuções e capacidade que acompanha o pico de pushes de segunda-feira pela manhã. Agentes estáticos ainda têm lugar para cargas com requisitos de rede ou hardware específicos, como executar testes contra um mainframe inacessível a partir de containers. O erro clássico é o oposto: builds rodando direto no controller, disputando CPU com o próprio agendador e corrompendo o workspace global.
Os rótulos de agente merecem desenho deliberado, porque se tornam a moeda de capacidade do parque. Um esquema funcional comum separa rótulos por runtime, por acesso de rede e por classe de carga, como docker, node20, rede-financeira e pesado. Pipelines pedem exatamente o que precisam, e a capacidade de cada classe é dimensionada e monitorada de forma independente, o que impede que um build de imagem de 20 GB ocupe o mesmo executor que as suítes rápidas de lint de todo o time.
Plugins: força e risco de manutenção
O ecossistema de plugins é a razão da flexibilidade do Jenkins e, ao mesmo tempo, seu maior passivo operacional. Plugins cobrem desde integração com Git e Docker até autenticação corporativa, e cada um carrega ciclo próprio de atualização e histórico de vulnerabilidades. Em produção, a disciplina necessária é parecida com a de dependências de aplicação:
- Inventário explícito: lista mantida de plugins essenciais, com dono e justificativa; plugin órfão é removido, não herdado.
- Atualização programada: janela mensal de upgrades, com ambiente de homologação do próprio Jenkins para testar compatibilidade antes do controller de produção.
- Fonte de credenciais única: credenciais gerenciadas pelo sistema nativo ou por integração com o cofre corporativo, nunca dispersas em jobs.
- Menos superfície é mais segurança: cada plugin instalado amplia o caminho de ataque de um sistema que, por definição, guarda segredos de deploy da organização inteira.
A regra prática adotada por times maduros é simples: para cada plugin candidato, comparar o custo de escrever um passo próprio com o custo de manter a dependência para sempre. A maioria das decisões muda de lado quando o horizonte é de cinco anos.
O gerenciamento de plugins por lista versionada transforma essa manutenção em processo determinístico, auditável em revisão de código:
# plugins.txt versionado no repositório de infraestrutura
# cada linha é um plugin obrigatório do controller
workflow-aggregator # pipeline declarativo e etapas base
git # integração com repositórios
docker-workflow # agentes efêmeros por container
configuration-as-code # controller configurado por YAML
# aplicação determinística da lista no controller
jenkins-plugin-cli --plugins plugins.txt
Aplicação prática: operação do parque
Operar Jenkins em escala é, antes de tudo, um exercício de disciplina de plataforma. O parque típico de uma organização média concentra centenas de jobs pertencentes a times distintos, com prioridades conflitantes e picos simultâneos de execução. Sem governança, o resultado previsível é fila infinita, agents com discos cheios e um controller instável que vira piada interna. As práticas que separam parque saudável de parque em sofrimento são conhecidas:
- Backup automático do JENKINS_HOME, com restauração testada periodicamente, porque o controller é a única cópia da configuração, credenciais e histórico.
- Configuração do controller como código, por meio do plugin de configuration as code, reduzindo a diferença entre ambiente de homologação e produção.
- Quotas por pasta e prioridades por time no agendamento, evitando que um job pesado e mal desenhado consuma a capacidade de todos.
- Monitoramento do próprio Jenkins, com alerta para profundidade de fila, executores ociosos, espaço em disco dos agentes e duração média por job.
- Política de retenção de builds e artefatos definida por pasta, porque console e artefatos acumulados por anos tornam o backup do controller inviável e a navegação, lenta.
Papel atual frente a plataformas SaaS
O Jenkins convive hoje com plataformas gerenciadas que assumiram a experiência de desenvolvedor, e a escolha entre eles é menos tecnológica e mais de responsabilidade operacional. Ele permanece vencedor onde a automação precisa alcançar o que está atrás do firewall, onde já existe um parque maduro de milhares de jobs e onde o custo de migração supera o de manutenção disciplinada. Times que começam do zero em nuvem pública quase sempre escolhem o CI nativo da plataforma, pela ausência de servidor para cuidar; times com requisitos de residência de dados, integrações legadas e controle total do runtime continuam, com razão, no Jenkins.
O contraponto também merece honestidade. Escolher Jenkins em um time sem capacidade de plataforma é assumir um sistema distribuído a mais para operar, com atualizações de segurança, disponibilidade e backups que ninguém no time pediu para cuidar. A pergunta decisiva não é qual ferramenta é melhor, mas quem responde às 3 da manhã quando o servidor de automação cai no meio do release da madrugada.
| Aspecto | Jenkins | GitLab CI | GitHub Actions |
|---|---|---|---|
| Modelo de hospedagem | Self-hosted obrigatório | SaaS ou self-hosted | SaaS ou self-hosted |
| Configuração | Jenkinsfile em Groovy | .gitlab-ci.yml em YAML | Workflow em YAML |
| Manutenção da plataforma | Responsabilidade do time | Compartilhada ou do time | Compartilhada ou do time |
| Flexibilidade de ambiente | Total, inclusive redes fechadas | Alta, limitada pela integração | Alta, centrada no ecossistema GitHub |
| Curva de administração | Alta, com patches e plugins | Baixa no SaaS | Baixa no SaaS |
| Ecossistema de extensão | Plugins maduros e numerosos | Templates nativos | Marketplace enorme e novo |
Migrações para o CI nativo da nuvem raramente partem do Jenkins maduro; partem de times novos, e o parque legado permanece por anos. Planejar a convivência, com autenticação unificada e notificações comuns, é mais realista do que qualquer plano de eliminação total.
Erros comuns e boas práticas
Os erros abaixo concentram a maior parte das dores relatadas em produção:
- Controller saturado com builds: execução pesada no próprio controller degrada a interface e o agendamento; separar orquestração de execução é a primeira correção.
- Pipelines scripted gigantes: Groovy imperativo em um único arquivo sem biblioteca compartilhada; migração para o declarativo com funções centralizadas reduz o custo de cada mudança.
- Workspace sujo entre execuções: estado residual produz falha intermitente; agentes efêmeros ou limpeza garantida no post eliminam a classe inteira de bugs.
- Plugins desatualizados por medo: atrasar patches acumula vulnerabilidades conhecidas em sistema com credenciais críticas; janela de atualização com homologação resolve o medo.
- Jobs configurados pela interface: configuração fora do versionamento impede review e reprodução; pipeline as code com seed job é pré-requisito de governança.
- Credenciais expostas nos logs: comandos com segredos embutidos imprimem o valor no console, que é lido por qualquer pessoa com acesso ao job; mascaramento e cofre eliminam o vazamento.
Entre as boas práticas que sustentam o Jenkins a longo prazo, três se destacam. Manter a configuração do controller como código, com backup automático do estado e do inventário de plugins. Padronizar a imagem dos agentes, com ferramentas versionadas e atualização centralizada, para que um upgrade de Node ou de JDK não se torne uma peregrinação por dezenas de nós. Tratar cada Jenkinsfile como código de produção, com revisão, testes de mudança e dono claro, porque é nele que vive a automação que coloca software no ar.