- PVSM.RU - https://www.pvsm.ru -

Как собирать проекты в Jenkins, если нужно много разных окружений

На Хабре много статей о Jenkins, но мало где описывается пример работы Jenkins и докер агентов. Все популярные инструменты сборки проектов типа Drone.io [1], Bitbucket Pipeline [2], GitLab [3], GitHub actions [4] и другие, могут собирать все в контейнерах. Но как же Jenkins?

На сегодняшний день есть решение проблемы: Jenkins 2 замечательно умеет работать с Docker агентами [5]. В статье я хочу поделиться опытом и показать, как вы можете это сделать сами.

Почему я занялся решением этой проблемы?

Так как мы в компании используем множество различных технологий, то на сборочной машине приходится держать разные версии Node.JS, Gradle, Ruby, JDK и прочих. Но зачастую конфликтов версий не избежать. Да, вы будете правы если скажете, что есть различные менеджеры версий типа nvm, rvm, но не всё так гладкос ними и у этих решений есть проблемы:

  • большой объем рантаймов, который разработчики забывают чистить;
  • есть конфликты между разными версиями одних рантаймов;
  • каждому разработчику нужен разный набор компонентов.

Есть и другие проблемы, но давайте я лучше расскажу про решение.

Jenkins в Docker

Так как сейчас Docker уже хорошо укоренился в сфере разработки, то почти все можно запустить при помощи Docker. Мое же решение в том, чтобы Jenkins был в Docker и мог запускать другие Docker контейнеры. Этим вопросом стали задаваться еще в 2013 году в статье "Docker can now run within Docker [6]".

Если вкратце просто необходимо в рабочий контейнер установить сам Docker и примонтировать файл /var/run/docker.sock.

Вот пример Dockerfile, который получился для Jenkins.

FROM jenkins/jenkins:lts

USER root

RUN apt-get update && 

apt-get -y install apt-transport-https 
     ca-certificates 
     curl 
     gnupg2 
     git 
     software-properties-common && 
curl -fsSL https://download.docker.com/linux/$(. /etc/os-release; echo "$ID")/gpg > /tmp/dkey; apt-key add /tmp/dkey && 
add-apt-repository 
   "deb [arch=amd64] https://download.docker.com/linux/$(. /etc/os-release; echo "$ID") 
   $(lsb_release -cs) 
   stable" && 
apt-get update && 
apt-get -y install docker-ce && 
usermod -aG docker jenkins

RUN curl -L https://github.com/docker/compose/releases/download/1.25.0/docker-compose-`uname -s`-`uname -m` -o /usr/local/bin/docker-compose && chmod +x /usr/local/bin/docker-compose 

RUN apt-get clean autoclean && apt-get autoremove —yes && rm -rf /var/lib/{apt,dpkg,cache,log}/

USER jenkins

Таким образом мы получили Docker контейнер который может выполнять Docker команды на хостовой машине.

Настройка сборки

Не так давно Jenkins получил возможность описывать свои правила при помощи Pipeline [7] синтаксиса, что позволяет достаточно просто менять скрипт сборки и хранить его в репозитории.

Так давайте же мы поместим в сам репозиторий специальный Dockerfile, который будет содержать в себе все необходимые для сборки библиотеки. Таким образом сам разработчик может подготовить повторяемую среду и не нужно будет OPS просить поставить на хост определенную версию Node.JS.

FROM node:12.10.0-alpine

RUN npm install yarn -g

Такой сборочный образ подходит для большинства Node.JS приложений. А если вам, например, нужен образ для JVM проекта со включенным внутрь Sonar сканером? Вы сами вольны выбирать нужные для сборки компоненты.

FROM adoptopenjdk/openjdk12:latest

RUN apt update 
    && apt install -y 
        bash unzip wget

RUN mkdir -p /usr/local/sonarscanner 
    && cd /usr/local/sonarscanner 
    && wget https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-3.3.0.1492-linux.zip 
    && unzip sonar-scanner-cli-3.3.0.1492-linux.zip 
    && mv sonar-scanner-3.3.0.1492-linux/* ./ 
    && rm sonar-scanner-cli-3.3.0.1492-linux.zip 
    && rm -rf sonar-scanner-3.3.0.1492-linux 
    && ln -s /usr/local/sonarscanner/bin/sonar-scanner /usr/local/bin/sonar-scanner

ENV PATH $PATH:/usr/local/sonarscanner/bin/
ENV SONAR_RUNNER_HOME /usr/local/sonarscanner/bin/

Мы описали сборочное окружение, но при чем тут Jenkins? А Jenkins агенты умеют работать с такими Docker образами и проводить сборку внутри.

stage("Build project") {
    agent {
        docker {
            image "project-build:${DOCKER_IMAGE_BRANCH}"
            args "-v ${PWD}:/usr/src/app -w /usr/src/app"
            reuseNode true
            label "build-image"
        }
    }
    steps {
        sh "yarn"
        sh "yarn build"
    }
}

Директива agent использует свойство docker, где вы можете указать:

  • имя сборочного контейнера согласно вашей политике нейминга;
  • аргументы необходимые для запуска сборочного контейнера, где в нашем случае мы монтируем текущую директорию как директорию внутри контейнера.

А уже в шагах сборки мы указываем, какие команды выполнить внутри сборочного Docker агента. Это может все что угодно, таким образом я так же запускаю деплой приложений при помощи ansible.

Ниже я хочу показать общий Jenkinsfile, который может собрать простое Node.JS приложение.

def DOCKER_IMAGE_BRANCH = ""
def GIT_COMMIT_HASH = ""

pipeline { 
    options {
        buildDiscarder(
            logRotator(
                artifactDaysToKeepStr: "",
                artifactNumToKeepStr: "",
                daysToKeepStr: "",
                numToKeepStr: "10"
            )
        )
        disableConcurrentBuilds()
    }

    agent any

    stages {

        stage("Prepare build image") {
            steps {
                sh "docker build -f Dockerfile.build . -t project-build:${DOCKER_IMAGE_BRANCH}"
            }
        }

        stage("Build project") {
            agent {
                docker {
                    image "project-build:${DOCKER_IMAGE_BRANCH}"
                    args "-v ${PWD}:/usr/src/app -w /usr/src/app"
                    reuseNode true
                    label "build-image"
                }
            }
            steps {
                sh "yarn"
                sh "yarn build"
            }
        }

    post {
        always {
            step([$class: "WsCleanup"])
            cleanWs()
        }
    }

}

Что же вышло?

Что же вышло?

Благодаря такому способу мы решили следующие проблемы:

  • время конфигурации сборки окружения сводится к 10 — 15 минутам на проект;
  • полностью повторяемое окружение сборки приложения, так как можно так собирать и на локальном компьютере;
  • нет проблем с конфликтами разных версий сборочных инструментов;
  • всегда чистый воркспейс, который не забивается.

Само по себе решение простое и очевидное и позволяет получить одни плюсы. Да, порог входа чуточку поднялся по сравнению с простыми командами для сборок, но зато теперь есть гарантия, что будет собираться всегда и разработчик сам может выбрать все, что необходимо для его процесса сборки.

Так же вы можете воспользоваться собранным мною образом Jenkins + Docker [8]. Все исходники открыты и лежат на rmuhamedgaliev/jenkins_docker [9].

В ходе написания статьи появилась дискуссия о использовании агентов на удаленных серверах, чтобы не грузить мастер ноду при помощи плагина docker-plugin [10]. Но об этом я расскажу в будущем.

Автор: Ринат Мухамедгалиев

Источник [11]


Сайт-источник PVSM.RU: https://www.pvsm.ru

Путь до страницы источника: https://www.pvsm.ru/node-js/340799

Ссылки в тексте:

[1] Drone.io: https://drone.io

[2] Bitbucket Pipeline: https://bitbucket.org/product/ru/features/pipelines

[3] GitLab: https://about.gitlab.com/

[4] GitHub actions: https://github.com/features/actions

[5] Docker агентами: https://jenkins.io/doc/book/pipeline/docker/

[6] Docker can now run within Docker: https://www.docker.com/blog/docker-can-now-run-within-docker/

[7] Pipeline: https://jenkins.io/doc/book/pipeline/

[8] Jenkins + Docker: https://hub.docker.com/repository/docker/rmuhamedgaliev/jenkins/general

[9] rmuhamedgaliev/jenkins_docker: https://github.com/rmuhamedgaliev/jenkins_docker

[10] docker-plugin: https://plugins.jenkins.io/docker-plugin

[11] Источник: https://habr.com/ru/post/481466/?utm_source=habrahabr&utm_medium=rss&utm_campaign=481466