Nota perev.: esta é uma tradução de um artigo do blog de engenharia da Preply sobre como você pode usar a configuração como código para uma ferramenta de CI / CD tão popular como a Jenkins.Em nossa empresa, tentamos seguir a prática de "Tudo é como código", isso se aplica não apenas aos recursos de infraestrutura, mas também ao monitoramento, ao trabalho de Jenkins, etc. Neste artigo, falarei sobre como usamos essa prática para implantar e dar suporte ao Jenkins. E isso se aplica não apenas à infraestrutura do servidor e agentes, mas também a plug-ins, acessos, trabalho e muitas outras coisas.Além disso, neste artigo, tentaremos encontrar respostas para perguntas como:- Nosso Jenkins se tornou mais estável?
- Podemos fazer alterações freqüentes no servidor e na configuração do trabalho?
- A atualização do Jenkins ainda é uma dor para nós?
- Podemos controlar todas as nossas mudanças?
- Podemos restaurar rapidamente o Jenkins em caso de fakap?
Introdução
Geralmente, a primeira coisa que vem à mente ao mencionar a frase "Ferramentas de DevOps" é o sistema de CI / CD. Por exemplo, usamos o Jenkins porque executamos centenas de tarefas todos os dias, e são dezenas de milhares de compilações. Alguns recursos que usamos no Jenkins não estão disponíveis em outros sistemas de CI / CD ou têm funcionalidade limitada.Gostaríamos de controlar o Jenkins completamente a partir do código, incluindo infraestrutura, configurações, tarefas e plug-ins. Tentamos executar o Jenkins no Kubernetes, mas ele não atendia às nossas necessidades, além de não ser fácil de dimensionar devido à sua arquitetura .Isso será discutidoInfraestrutura para Jenkins
Usamos a AWS e configuramos toda a infraestrutura usando o Terraform e outras ferramentas de hash, como o Packer e o Vault .Como mencionado anteriormente, tentamos usar o Jenkins no Kubernetes e enfrentamos alguns problemas com o dimensionamento de PVC , recursos e uma arquitetura não tão bem projetada.Aqui, usamos os recursos habituais da AWS: instâncias EC2, certificados SSL, balanceadores, Cloudfront etc. A imagem do SO ( AMI ) é configurada usando o Packer, que se integra perfeitamente ao Terraform e ao Vault.{
"variables": {
"aws_access_key": "{{vault `packer/aws_access_key_id` `key`}}",
"aws_secret_key": "{{vault `packer/aws_secret_access_key` `key`}}",
"aws_region": "{{vault `packer/aws_region` `key`}}",
"vault_token": "{{env `VAULT_TOKEN`}}"
},
"builders": [{
"access_key": "{{ user `aws_access_key` }}",
"secret_key": "{{ user `aws_secret_key` }}",
"region": "{{ user `aws_region` }}",
"type": "amazon-ebs",
"communicator": "ssh",
"ssh_username": "ubuntu",
"instance_type": "c5.xlarge",
"security_group_id": "sg-12345",
"iam_instance_profile": "packer-role-profile",
"ami_name": "packer-jenkins-master-{{timestamp}}",
"ami_description": "Jenkins master image",
"launch_block_device_mappings": [{
"device_name": "/dev/sda1",
"volume_size": 50,
"volume_type": "gp2",
"delete_on_termination": true
}],
"source_ami_filter": {
"filters": {
"virtualization-type": "hvm",
"name": "ubuntu/images/*ubuntu-bionic-18.04-amd64-server-*",
"root-device-type": "ebs"
},
"owners": ["099720109477"],
"most_recent": true
}
}],
"provisioners": [{
"type": "shell",
"environment_vars": ["VAULT_TOKEN={{ user `vault_token` }}"],
"scripts": ["packer_bootstrap.sh"]
}]
}
Um exemplo de como é a configuração de uma imagem do SO no Packer.Porsua vez, o arquivo packer_bootstrap.sh
contém um conjunto de comandos com os quais o software é instalado dentro da imagem. Por exemplo, podemos instalar o Docker, docker-compose e vaultenv ou Datadog-agent para monitoramento. Em relação à infraestrutura para esta imagem, podemos usar Terraform, Cloudformation, Pulumi ou até Ansible.Aqui está um exemplo de uma possível infraestrutura da AWS para o Jenkins: osusuários efetuam login no Jenkins através de um balanceador interno, e os ganchos do Github acessam o servidor através de um externo. Usamos a integração do Jenkins com o GitHub, portanto, alguns links do servidor devem estar acessíveis na Internet. Existem muitas soluções diferentes aqui (por exemplo, uma lista branca de endereços IP , URLs ou tokens etc.). No nosso caso, usamos uma combinação de URLs permitidos e validação de token.Assim, após as manipulações que fizemos, já temos uma infraestrutura pronta com a imagem do SO montada, a capacidade de monitorar e acessar o armazenamento secreto corporativo.
Usamos o Docker para instalar o Jenkins e seus plugins
A próxima coisa que faremos é instalar o Jenkins e seus plugins. Como constantemente tínhamos problemas para atualizar os plug-ins, o objetivo principal era ter uma base clara dos plug-ins instalados e de suas versões no código.E aqui o Docker nos ajudará, porque podemos pegar uma imagem pré - instalada do Docker e usá-la como base para nossa configuração.FROM jenkins/jenkins:2.215
ENV CASC_JENKINS_CONFIG /jenkins_configs
USER root
RUN apt update && \
apt install -y python3 python3-pip && \
pip3 install awscli jenkins-job-builder jjb-reactive-choice-param --no-cache-dir
USER jenkins
VOLUME /jenkins_configs
VOLUME /var/jenkins_home
COPY plugins.txt /usr/share/jenkins/ref/
RUN /usr/local/bin/install-plugins.sh < /usr/share/jenkins/ref/plugins.txt
DockerfileDentro da imagem do Docker, alguns pacotes são instalados como o Job Builder, que discutirei mais adiante, os repositórios também são registrados e os plug-ins especificados no arquivo plugins.txt
.Jenkins.instance.pluginManager.plugins.each{
plugin ->
println ("${plugin.getShortName()}:${plugin.getVersion()}")
}
Você pode obter uma lista dos plug-ins instalados no Jenkins clicando no link https://our-jenkins-url/script
e salvando a saída em um arquivo.Finalmenteplugins.txt
, a configuração para docker-compose, que executará o Jenkins no Docker.version: "3"
services:
jenkins:
build: .
container_name: jenkins
restart: always
ports:
- "50000:50000"
- "8080:8080"
volumes:
- ./configs/:/jenkins_configs/:ro
- ./jenkins_home/:/var/jenkins_home/:rw
environment:
- VAULT_TOKEN
- GITHUB_TOKEN
- AWS_ACCESS_KEY_ID
- AWS_SECRET_ACCESS_KEY
- JAVA_OPTS=-Xms4G -Xmx8G -Xloggc:/var/jenkins_home/gc-%t.log -XX:NumberOfGCLogFiles=5 -XX:+UseGCLogFileRotation -XX:GCLogFileSize=20m -XX:+PrintGC -XX:+PrintGCDateStamps -XX:+PrintGCDetails -XX:+PrintHeapAtGC -XX:+PrintGCCause -XX:+PrintTenuringDistribution -XX:+PrintReferenceGC -XX:+PrintAdaptiveSizePolicy -XX:+UseG1GC -XX:+ExplicitGCInvokesConcurrent -XX:+ParallelRefProcEnabled -XX:+UseStringDeduplication -XX:+UnlockExperimentalVMOptions -XX:G1NewSizePercent=20 -XX:+UnlockDiagnosticVMOptions -XX:G1SummarizeRSetStatsPeriod=1
volumes:
configs:
driver: local
jenkins_home:
driver: local
Também usamos o vaultenv para forjar segredos do Vault.Observe algumas opções de Java que nos ajudaram na coleta de lixo e nos limites de recursos. Este artigo é muito interessante sobre o ajuste do Jenkins.E, é claro, agora podemos implantar localmente uma cópia do Jenkins e experimentar novas versões do servidor e plugins. É muito confortável.Agora temos uma instalação limpa de Jenkins e plugins, que podem ser facilmente lançados no produto. Vamos adicionar mais configurações para ela.
Configurando o plugin Jenkins como um código (JCaSC) para configuração do servidor
Em geral, existe um plug-in chamado Jenkins Configuration as Code ( JCasC ), que permite armazenar a configuração do servidor em formato de texto legível por humanos.Usando este plug-in, você pode descrever configurações de segurança, acessos, configurações de plug-ins, agentes, guias e muito mais.A configuração é apresentada no formato YAML e é dividida em 5 blocos:- credenciais (descrição dos segredos do sistema)
- jenkins (configurações de autorização e nuvem, configurações globais, descrições de agentes, algumas configurações e guias de segurança)
- segurança (configurações globais de segurança, como scripts permitidos)
- ferramenta (configuração para ferramentas externas como git, allure etc.)
- não classificado (outras configurações, como integração com o Slack)

O plug-in suporta a importação de configurações de uma instalação existente do Jenkins, além de fornecer suporte a vários provedores secretos , no entanto, neste exemplo, usaremos apenas variáveis de ambiente.credentials:
system:
domainCredentials:
- credentials:
- usernamePassword:
description: "AWS credentials"
id: "aws-creds"
password: ${AWS_SECRET_ACCESS_KEY}
scope: GLOBAL
username: ${AWS_ACCESS_KEY_ID}
- string:
description: "Vault token"
id: "vault-token"
scope: GLOBAL
secret: ${VAULT_TOKEN}
...
É assim que os segredos podem ser descritos: também usamos o plug - in Amazon EC2 para aumentar agentes na AWS, e sua configuração também pode ser descrita usando esse plug-in. A autorização da matriz nos permite configurar o acesso do usuário usando o código.jenkins:
authorizationStrategy:
projectMatrix:
permissions:
- "Overall/Administer:ivan.a@example.org"
- "Credentials/View:petr.d@example.org"
...
clouds:
- amazonEC2:
cloudName: "AWS"
privateKey: ${EC2_PRIVATE_KEY}
region: "${AWS_REGION}"
templates:
- ami: "ami-12345678"
amiType:
unixData:
sshPort: "22"
connectionStrategy: PRIVATE_IP
deleteRootOnTermination: true
description: "jenkins_agent"
idleTerminationMinutes: "20"
instanceCapStr: "100"
minimumNumberOfInstances: 0
mode: EXCLUSIVE
numExecutors: 1
remoteAdmin: "jenkins"
remoteFS: "/home/jenkins"
securityGroups: "sg-12345678"
subnetId: "subnet-12345678"
type: C52xlarge
...
Descrição de agentes e acessosO plug-in suporta outras coisas que usamos. Com um processo de teste local organizado da Jenkins, você pode encontrar e corrigir bugs efetivamente antes que eles possam entrar nas vendas da Jenkins.Agora, temos uma configuração reproduzível para o servidor, e continua sendo o caso de pequenos, a saber - o trabalho.
Usamos o Job Builder para projetos de estilo livre
Existem várias maneiras de criar trabalhos de estilo livre no Jenkins:- usando a interface da web (a maneira mais fácil, pulou e continuou)
- diretamente usando a API REST
- usando Plug-ins como Job DSL ou Wrapper JJB
O Jenkins Job Builder (JJB) permite configurar tarefas usando YAML ou JSON. E é bastante conveniente, porque você pode configurar todos os trabalhos e armazenar seu estado no git condicional. Na verdade, podemos construir um processo de CI / CD para nossa ferramenta de CI / CD usando JJB..
├── config.ini
├── jobs
│ ├── Job1.yaml
│ | ...
│ └── Job2.yaml
└── scripts
├── job1.sh
| ...
└── job2.sh
É assim que (simplificada) a estrutura da configuração do trabalho no FS parece- job:
name: Job1
project-type: freestyle
auth-token: mytoken
disabled: false
concurrent: false
node: jenkins_agent
triggers:
- timed: '0 3 * * *'
builders:
- shell:
!include-raw: ../scripts/job1.sh
E é assim que o trabalho no arquivo se parece Job1.yaml
, as etapas dojob1.sh
script.Oarquivo de configuração JJB também parece simples.$ cat config.ini
[job_builder]
ignore_cache=True
exclude=jobs/Job2
[jenkins]
url=https://jenkins.example.org
user=some_user
password=some_password
$ jenkins-jobs --conf config.ini test -r jobs/
$ jenkins-jobs --conf config.ini update -r jobs/
O aplicativo de novas alterações pode ser facilmente iniciado usando o comando Porjenkins-jobs update
si só, o usuário para quem o token foi criado deve ter os privilégios adequados para criar e configurar o trabalho. Só precisamos aplicar um trabalho de inicialização (trabalho inicial), que aplicará as alterações usando JJB no Jenkins.Vale ressaltar que o JJB não é uma “bala de prata”, pois alguns plugins não muito populares não são suportados. No entanto, é uma ferramenta muito flexível para armazenar trabalhos em código, incluindo suporte a macro .Sumário
Agora que chegamos ao final deste artigo, gostaria de voltar ao início e responder às perguntas feitas no início. Podemos responder "sim" a cada uma das perguntas colocadas.Neste artigo, não nos aprofundamos nas sutilezas de configurar determinadas tecnologias ou em como configurar o Jenkins corretamente , apenas compartilhamos nossa experiência, que também pode ser útil para você.PS No artigo, costumo usar a palavra "trabalho" (do inglês. "Trabalho", "tarefa"), para mim parece mais familiar do que a "tarefa" no contexto de CI / CD em geral ou Jenkins.