В компаниях с большим количеством проектов часто возникает ситуация, когда при разработке пайплайнов мы начинаем повторять себя, добавляя в разные сервисы одинаковые конструкции. Это противоречит основному принципу программирования DRY (Don’t Repeat Yourself), а ещё усложняет внесение изменений в код. Справиться с описанными проблемами помогает Jenkins Shared Library.
Мы пообщались с Кириллом Борисовым, Infrastructure Engineer технологического центра Deutsche Bank, и узнали, какие задачи решает Jenkins Shared Library и что её внедрение даёт компании. А ещё рассмотрели кейс разработки и тестирования с примерами кода.
Что такое Jenkins Shared Library
Jenkins Shared Library — это библиотека для многократного использования кода, которая позволяет описать методы один раз и затем применять их во всех пайплайнах. Она решает проблемы дублирования кода и унификации. Благодаря ней нам не нужно держать в каждом пайплайне огромное количество кода и постоянно повторять его. Мы можем написать код в одном месте, а потом инклюдить и вызывать его в рамках самого пайплайна.
Как это работает? Первым делом во всех пайплайнах мы выделяем общие части, которые можем объединить. Предположим, мы заметили, что везде используем нотификацию в Telegram, и везде она реализована по-разному. Мы создаем библиотеку, в ней делаем модуль нотификации и внедряем его во все проекты.
Затем смотрим, что ещё можем унифицировать. Например, обращение к внутренней системе, которое тоже везде реализовано по-разному. Мы заносим этот кусочек кода в библиотеку и далее распространяем на каждый микросервис. Так, выделяя общие части в каждом пайплайне, мы постепенно обогащаем нашу библиотеку и имплементируем её в наш пайплайн.
Польза Jenkins Shared Library для компании
Jenkins Shared Library уменьшает количество кода в пайплайне и повышает его читаемость. Для бизнеса это не столь важно, его больше заботит скорость доставки новых версий продукта до конечных пользователей. А здесь многое зависит от скорости разработки. Если каждая команда компании перестанет придумывать своё и начнёт использовать наработки других, вероятнее всего, разработка пайплайнов пойдёт быстрее. Соответственно, процесс доставки обновлений тоже ускорится.
С точки зрения самой команды, Jenkins Shared Library одновременно и усложняет, и упрощает работу. Например, проводить онбординг оказывается проще — вы показываете всё в одном месте. В то же время командам становится сложнее взаимодействовать. Сложность заключается в необходимости постоянной коммуникации. Когда решаем изменить что-то у себя в библиотеке, мы сначала должны синхронизироваться с другой командой, чтобы убедиться, что у неё ничего не сломается. Но обычно это решается регулярными синками, ревью кода и пул-реквестами.
Как команда понимает, что пора внедрять Jenkins Shared Library
На прошлом проекте у нас было 20 микросервисов, очень похожих между собой. Они были написаны по одному скелету на Java, а ещё в каждом из них лежал Jenkins file, где был описан пайплайн сборки проектов. Всякий раз, когда нужно было что-то поменять в пайплайне (а делать это приходилось часто, так как проект динамичный и быстро развивался), мы проходились по 20 репозиториям и поправляли всё вручную. Это довольно геморройный процесс, и мы подумали: «А почему бы нам не сделать что-то общее?».
Так мы перешли на Jenkins Shared Library. У нас появился базовый пайплайн в библиотеке, и, когда требовалось что-то изменить, мы работали с ним. Добавляли какие-то правки, и они автоматически имплементировались в каждый микросервис.
Ограничения Jenkins Shared Library
Любой инструмент призван решать какую-то проблему. Jenkins Shared Library решает проблему дублирования кода и унификации. Если вам эта унификация не нужна, смысла тащить библиотеку в проект нет.
Если, скажем, у вас небольшой проект, где всего 5 микросервисов, писать для него библиотеку с нуля не стоит. Единственный вариант — переиспользовать, если она уже написана. Jenkins Shared Library — всё-таки решение для более крупных проектов с большим количеством микросервисов.
Кейс: разработка и тестирование Jenkins Shared Library
Для разработки и тестирования Jenkins Shared Library нам необходимо установить на свой компьютер gradle. Вот инструкция по установке — https://gradle.org/install/.
Далее в рабочем каталоге мы выполняем команду инициализации «gradle init», говорим установщику, что хотим настроить base каталог с groovy-файлами, и получаем готовый проект.
Следующий шаг — создание каталогов для JSL:
-
var
-
src
-
resources
-
test
Директория src используется для Groovy классов, которые добавляются в classpath.
Директория resources содержит файлы, которые мы можем загружать в пайплайн.
Директория test содержит тесты для наших скриптом.
Директория vars используется в скриптах, которые определяют глобальные переменные, доступные из пайплайна.
Затем заполянем build.gradle, добавляя в него описание наших каталогов:
–-- sourceSets { main { groovy { srcDirs = ['src','vars'] } resources { srcDirs = ['resources'] } } test { groovy { srcDirs = ['test'] } } } ---
И добавляем зависимости, которые понадобятся нам в работе. Полный файл можно посмотреть в репозитории: ссылка на репозиторий.
Пишем простой первый класс, который увеличивает возраст на указанное значение 🙂
package com.example class SampleClass { String name Integer age def increaseAge(Integer years) { this.age += years } }
Не забываем про тесты — создаем в директории test файл с именем SampleClassTest.groovy и содержимым.
В секции Before описываем действия, которые необходимо выполнить перед тестом — в нашем случае это объявление класса. Далее в секции Test описываем сам тест. Объявляем age = 7 и выполняем функцию increaseAge с параметром 3. В случае правильного выполнения ожидаем получить 10.
class SampleClassTest { def sampleClass @Before void setUp() { sampleClass = new SampleClass() } @Test void testIncrease() { sampleClass.age = 7 def expect = 10 assertEquals expect, sampleClass.increaseAge(3) } }
Тест готов, запускаем командой gradle test. Результат будет такой:
Но это синтетические примеры, давайте рассмотрим реальный
Каждый раз для вызова GET или POST запроса в пайплайн нам нужно вызывать класс HttpsURLConnection, передавать в него правильные параметры и проверять валидность сертификата.
Последуем главному принципу программирования DRY и подготовим класс, который позволит нам вызывать get и post запросы в пайплайне.
Создаем в директории src/com/example HttpsRequest.groovy, в нём создаем два метода get и post. В параметрах методов передаём URL-запроса и в случае POST еще и body-запроса.
Корневым методом нашего класса будет метод httpInternal. В нём закладываем логику, добавляем параметры к HttpsURLConnection, отлавливаем ошибку и в результате возвращаем тело ответа и ошибку:
package com.example import groovy.json.JsonSlurper import javax.net.ssl.HttpsURLConnection import javax.net.ssl.SSLContext import javax.net.ssl.TrustManager import javax.net.ssl.X509TrustManager import java.security.cert.CertificateException import java.security.cert.X509Certificate class HttpsRequest { def get(String uri) { httpInternal(uri, null, false) } def post(String uri, String body) { httpInternal(uri, body, true) } def httpInternal(String uri, String body, boolean isPost) { def response = [:] def error try { def http = new URL(uri).openConnection() as HttpsURLConnection if (isPost) { http.setRequestMethod('POST') http.setDoOutput(true) if (body) { http.outputStream.write(body.getBytes("UTF-8")) } } http.setRequestProperty("Accept", 'application/json') http.setRequestProperty("Content-Type", 'application/json') http.connect() if (http.responseCode == 200) { response = new JsonSlurper().parseText(http.inputStream.getText('UTF-8')) } else { response = -1 } } catch (Exception e) { println(e) error = e } return [response, error] } }
Покрываем тестами и создаем файл в директории test/HttpsRequestTest.groovy.
Для примера делаем get запрос, получаем ответ и проверяем, что он соответствует нашим ожиданиям:
void testGet() { def expect = json.parseText('{"hello":"slurm"}') def (result, error) = http.get("https://run.mocky.io/v3/0bd64f74-1861-4833-ad9d-80110c9b5f25") if (error != null) { println(error) } assertEquals "result:", expect, result }
Запускаем тесты:
Осталось дело за малым — подключить библиотеку в Jenkins.
Для этого в Jenkins нужно перейти: Manage Jenkins → Configure System (Настроить Jenkins → Конфигурирование системы). В блоке Global Pipeline Libraries, добавить наш репозиторий:
Последний шаг — создаем наш пайплайн:
@Library('jenkins-shared-library') _ import com.example.HttpsRequest pipeline { agent any stages { stage('Demo') { steps { script { def http = new HttpsRequest() def (result, error) = http.get("https://run.mocky.io/v3/0bd64f74-1861-4833-ad9d-80110c9b5f25") if (error != null) { println(error) } else { println result } } } } } }
Выполняем 🙂
Весь код можно найти в репозитории: ссылка на репозиторий.
Вместо заключения: о чём нужно помнить при работе с Jenkins Shared Library
Jenkins Shared Library — больше инструмент разработки в том плане, что нужно программировать. Чаще всего когда вы первый раз работаете с ней, если вы не разработчик, а администратор или DevOps, вы делаете это по наитию с точки зрения администратора. Это рабочий подход, но у него есть свои недостатки. Например, расширять проект дальше сложнее.
Лучше сразу начинать мыслить как разработчик: выделять классы и методы, правильно их имплементировать в пайплайн и т.д. Если вы не из мира разработки, это осознание приходит с запозданием. И чем позже оно придёт, тем больше времени и сил вы потратите на то, чтобы переписать то, что уже сделали.
Для тех, кто хочет углубиться в тонкости работы с Jenkins Shared Library
6 сентября у нас стартует курс по Jenkins, автором которого выступил Кирилл Борисов, Infrastructure Engineer технологического центра Deutsche Bank. В курсе будет много кейсов и примеров из практики спикера.
Вы научитесь:
-
автоматизировать процесс интеграции и поставки;
-
ускорять цикл разработки и внедрять полезные инструменты;
-
настраивать плагины и создавать пайплайны Jenkins as a code;
-
работать с Jenkins Shared Library.
Ознакомиться с программой и записаться: https://slurm.club/3a6OEs8
ссылка на оригинал статьи https://habr.com/ru/company/southbridge/blog/674506/
Добавить комментарий