Разворачиваем Jenkins как код

от автора

Прим. перев.: это перевод статьи из инженерного блога компании Preply о том, как можно использовать конфигурацию как код для такого популярного CI/CD инструмента как Jenkins.

В нашей компании, мы стараемся следовать практикам «Все как код», это касается не только инфраструктурных ресурсов, но и мониторинга, Jenkins джоб и т.д. В статье я расскажу, о том, как мы используем эту практику при разворачивании и поддержке Jenkins. Причем это касается не только инфраструктуры для сервера и агентов, но и плагинов, доступов, джоб и множества других вещей.

Кроме того, в данной статье мы попробуем найти ответы на такие вопросы как:

  • Стал ли наш Jenkins стабильнее?
  • Можем ли мы делать частые изменения в конфигурацию сервера и джоб?
  • Является ли обновление Jenkins для нас все еще болью?
  • Можем ли мы контролировать все наши изменения?
  • Можем ли мы быстро восстановить Jenkins в случае факапа?

image

КДПВ

Введение

Обычно, первое что приходит в голову при упоминании словосочетания «инструменты DevOps» — это CI/CD система. К примеру, мы используем Jenkins, потому что мы запускаем сотни задач каждый день, а это десятки тысяч билдов. Некоторые возможности, которые мы используем в Jenkins либо отсутствуют в других CI/CD системах, либо имеют ограниченный функционал.

Источник: Инженерный блог компании Altexsoft

Нам бы хотелось управлять Jenkins полностью из кода, включая инфраструктуру, конфигурации, джобы и плагины. Мы пробовали запускать Jenkins в Kubernetes, однако под наши нужды он не вписался, плюс непросто было масштабироваться из-за его архитектуры.

image

Вот об этом пойдет речь

Инфраструктура для Jenkins

imageМы используем 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.

Вот пример возможной инфраструктуры на AWS для Jenkins

Пользователи заходят на Jenkins через внутренний балансер, а Github-хуки попадают на сервер через внешний. Мы используем интеграцию Jenkins с GitHub, поэтому некоторые ссылки сервера должны быть доступны из интернета. Здесь есть множество различных решений (к примеру белый список для IP-адресов, урлов или токенов, и т.д.), в нашем случае мы используем комбинацию разрешенных урлов и валидации токена.

Итак, после проделанных нами манипуляций, у нас уже есть готовая инфраструктура с собранным образом ОС, возможностью мониторинга и доступом к корпоративному хранилищу секретов.

Используем Docker для установки Jenkins и его плагинов

imageСледующее чем мы займемся, это установкой 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) для конфигурации сервера

imageВ общем, есть такой плагин под названием Jenkins Configuration as Code (JCasC), который позволяет хранить конфигурацию сервера в человекочитаемом текстовом формате.

С помощью этого плагина можно описывать конфигурации безопасности, доступы, настройки плагинов, агентов, вкладок и многое другое.

Конфигурация представлена в формате YAML и разделена на 5 блоков:

  • credentials (описание системных секретов)
  • jenkins (настройки авторизации и облака, глобальные настройки, описание агентов, некоторые настройки безопасности и вкладки)
  • security (глобальные настройки безопасности, такие как разрешенные скрипты)
  • tool (конфигурация для внешних инструментов, таких как git, allure, и т.д.)
  • unclassified (другие настройки, такие как интеграция со Slack)

imageimageПлагин поддерживаем импорт конфигурации из уже существующей инсталляции 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-проектов

imageСуществует несколько способов создания 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.

ссылка на оригинал статьи https://habr.com/ru/post/490756/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *