Прим. перев.: это перевод статьи из инженерного блога компании Preply о том, как можно использовать конфигурацию как код для такого популярного CI/CD инструмента как Jenkins.
В нашей компании, мы стараемся следовать практикам «Все как код», это касается не только инфраструктурных ресурсов, но и мониторинга, Jenkins джоб и т.д. В статье я расскажу, о том, как мы используем эту практику при разворачивании и поддержке Jenkins. Причем это касается не только инфраструктуры для сервера и агентов, но и плагинов, доступов, джоб и множества других вещей.
Кроме того, в данной статье мы попробуем найти ответы на такие вопросы как:
- Стал ли наш Jenkins стабильнее?
- Можем ли мы делать частые изменения в конфигурацию сервера и джоб?
- Является ли обновление Jenkins для нас все еще болью?
- Можем ли мы контролировать все наши изменения?
- Можем ли мы быстро восстановить Jenkins в случае факапа?
![Разворачиваем Jenkins как код - 1 image](https://www.pvsm.ru/images/2020/03/03/razvorachivaem-Jenkins-kak-kod.png)
Введение
Обычно, первое что приходит в голову при упоминании словосочетания «инструменты DevOps» — это CI/CD система. К примеру, мы используем Jenkins, потому что мы запускаем сотни задач каждый день, а это десятки тысяч билдов. Некоторые возможности, которые мы используем в Jenkins либо отсутствуют в других CI/CD системах, либо имеют ограниченный функционал.
![Разворачиваем Jenkins как код - 2 Разворачиваем Jenkins как код - 2](https://www.pvsm.ru/images/2020/03/03/razvorachivaem-Jenkins-kak-kod-2.png)
Источник: Инженерный блог компании Altexsoft
Нам бы хотелось управлять Jenkins полностью из кода, включая инфраструктуру, конфигурации, джобы и плагины. Мы пробовали запускать Jenkins в Kubernetes, однако под наши нужды он не вписался, плюс непросто было масштабироваться из-за его архитектуры.
![Разворачиваем Jenkins как код - 3 image](https://www.pvsm.ru/images/2020/03/03/razvorachivaem-Jenkins-kak-kod-3.png)
Вот об этом пойдет речь
Инфраструктура для Jenkins
Мы используем AWS и конфигурируем всю инфраструктуру с помощью Terraform и других инструментов из хашистека, таких как Packer и Vault.
Как упоминалось ранее, мы пробовали использовать Jenkins в Kubernetes и столкнулись с некоторыми проблемами масштабирования PVC, ресурсов, а также не очень продуманной архитектуры.
Здесь же, мы используем обычные ресурсы AWS: EC2 инстансы, SSL-сертификаты, балансировщики, Cloudfront и т.д. Образ ОС (AMI) сконфигурирован с помощью Packer, который отлично интегрируется с Terraform и 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"]
}]
}
Пример того, как выглядит конфигурация образа ОС в Packer
В свою очередь, файл packer_bootstrap.sh
содержит в себе набор команд, с помощью которых устанавливается софт внутрь образа. К примеру, мы можем установить Docker, docker-compose и vaultenv или Datadog-агент для мониторинга. Касаемо инфраструктуры под этот образ, мы можем использовать Terraform, Cloudformation, Pulumi или даже Ansible.
![Разворачиваем Jenkins как код - 5 Разворачиваем Jenkins как код - 5](https://www.pvsm.ru/images/2020/03/03/razvorachivaem-Jenkins-kak-kod-5.png)
Вот пример возможной инфраструктуры на AWS для Jenkins
Пользователи заходят на Jenkins через внутренний балансер, а Github-хуки попадают на сервер через внешний. Мы используем интеграцию Jenkins с GitHub, поэтому некоторые ссылки сервера должны быть доступны из интернета. Здесь есть множество различных решений (к примеру белый список для IP-адресов, урлов или токенов, и т.д.), в нашем случае мы используем комбинацию разрешенных урлов и валидации токена.
Итак, после проделанных нами манипуляций, у нас уже есть готовая инфраструктура с собранным образом ОС, возможностью мониторинга и доступом к корпоративному хранилищу секретов.
Используем Docker для установки Jenkins и его плагинов
Следующее чем мы займемся, это установкой Jenkins и его плагинов. У нас постоянно возгникали проблемы с обновлением плагинов, поэтому основной целью было иметь четкий слепок установленных плагинов и их версий в коде.
И здесь нам поможет Docker, ведь мы можем взять уже готовый предустановленный Docker-образ и использовать его как базовый, для нашей конфигурации.
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
Dockerfile
Внутрь Docker-образа устанавливаются некоторые пакеты как Job Builder, о котором я расскажу позже, также регистрируются хранилища и устанавливаются плагины, указанные в файле plugins.txt
.
Jenkins.instance.pluginManager.plugins.each{
plugin ->
println ("${plugin.getShortName()}:${plugin.getVersion()}")
}
Получить список установленных плагинов в Jenkins можно по ссылке https://our-jenkins-url/script
и сохранив вывод в файл plugins.txt
И наконец, конфигурация для docker-compose, которая будет запускать Jenkins в 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
Мы также используем vaultenv для проброса секретов из Vault
Обратите внимание на некоторые параметры Java, которые помогли нам со сборкой мусора и ограничением ресурсов. Вот в этой статье очень классно расписано о тюнинге Jenkins.
Ну и конечно теперь мы можем локально развернуть копию Jenkins и экспериментировать с новыми версиями сервера и плагинов. Это очень удобно.
Теперь у нас есть чистая инсталляция Jenkins и плагинов, которая легко может запускаться в проде. Давайте добавим больше конфигурации для нее.
Настройка плагина Jenkins as a Code (JCaSC) для конфигурации сервера
В общем, есть такой плагин под названием Jenkins Configuration as Code (JCasC), который позволяет хранить конфигурацию сервера в человекочитаемом текстовом формате.
С помощью этого плагина можно описывать конфигурации безопасности, доступы, настройки плагинов, агентов, вкладок и многое другое.
Конфигурация представлена в формате YAML и разделена на 5 блоков:
- credentials (описание системных секретов)
- jenkins (настройки авторизации и облака, глобальные настройки, описание агентов, некоторые настройки безопасности и вкладки)
- security (глобальные настройки безопасности, такие как разрешенные скрипты)
- tool (конфигурация для внешних инструментов, таких как git, allure, и т.д.)
- unclassified (другие настройки, такие как интеграция со Slack)
Плагин поддерживаем импорт конфигурации из уже существующей инсталляции Jenkins
Помимо этого, плагин предоставляет поддержку различных провайдеров секретов, однако, в данном примере будем просто использовать переменные окружения.
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}
...
Вот так можно описывать секреты
Мы также используем плагин Amazon EC2 для поднятия агентов в AWS, и его конфигурация также может быть описана с помощью этого плагина. Матричная авторизация позволяет нам конфигурировать доступы пользователей с помощью кода.
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
...
Описание агентов и доступов
Плагин поддерживает и некоторые другие штуки, которые мы используем. С правильно организованным процессом локального тестирования Jenkins, можно эффективно находить и исправлять баги перед тем как они потенциально могут попасть в прод Jenkins.
Теперь у нас есть воспроизводимая конфигурация для сервера, осталось дело за малым, а именно — джобы.
Используем Job Builder для freestyle-проектов
Существует несколько способов создания freestyle-джоб в Jenkins:
- с помощью веб-интерфейса (самый простой способ, наклацал и пошел дальше)
- напрямую с помощью REST API
- с помощью плагинов таких как Job DSL или враппера JJB
Jenkins Job Builder (JJB) позволяет конфигурировать джобы с помощью YAML или JSON. И это довольно удобно, ведь можно конфигурировать все джобы и хранить их состояние в условном git. То есть, по факту, мы можем построить CI/CD процесс для нашего CI/CD инструмента с помощью JJB.
.
├── config.ini
├── jobs
│ ├── Job1.yaml
│ | ...
│ └── Job2.yaml
└── scripts
├── job1.sh
| ...
└── job2.sh
Вот так (упрощенно) выглядит структура конфигурации джобы на ФС
- 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
А так выглядит джоба в файле Job1.yaml
, шаги джобы в скрипте job1.sh
Конфигурационный файл JJB также выглядит просто.
$ 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/
Применение новых изменений легко может быть запущено с помощью команды jenkins-jobs update
Само собой, пользователь, для которого создавался токен, должен иметь соответствующие привилегии для создания и настройки джоб. Нам всего лишь необходимо применить одну инициализационную джобу (seed job), которая будет применять изменения с помощью JJB в Jenkins.
Стоит упомянуть, что JJB не является «серебрянной пулей», так как некоторые не очень популярные плагины не поддерживаются. Однако, это очень гибкий инструмент для хранения джоб в коде, в том числе с поддержкой макросов.
Итоги
Теперь, когда мы дошли до конца этой статьи, хотелось бы вернуться в начало и ответить на вопросы заданные в начале. На каждый из поставленных вопросов мы можем ответить — «да».
В этой статье мы не углублялись в тонкости настройки тех или иных технологий или в то, как правильно настраивать Jenkins, мы всего лишь делимся своим опытом, который, возможно, пригодится и вам.
P.S. В статье я часто использую слово «джоба» (с англ. «job», «задача»), для меня оно звучит привычнее чем «задача» в контексте CI/CD в целом или Jenkins.
Автор: Амет Умеров