Как стать автором
Обновить

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

Время на прочтение4 мин
Количество просмотров36K

image


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


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


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


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


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

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


Jenkins в Docker


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


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


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


FROM jenkins/jenkins:lts

ARG DOCKER_COMPOSE_VERSION=1.25.0

USER root
RUN apt-get update && \
   apt-get upgrade -y && \
   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 && \
   apt-get clean autoclean && apt-get autoremove && rm -rf /var/lib/{apt,dpkg,cache,log}/

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

RUN usermod -aG docker jenkins && gpasswd -a jenkins docker

USER jenkins

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


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


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


Так давайте же мы поместим в сам репозиторий специальный 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, где вы можете указать:


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

jenkins


А уже в шагах сборки мы указываем, какие команды выполнить внутри сборочного 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. Все исходники открыты и лежат на rmuhamedgaliev/jenkins_docker.


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

Теги:
Хабы:
Если эта публикация вас вдохновила и вы хотите поддержать автора — не стесняйтесь нажать на кнопку
Всего голосов 11: ↑9 и ↓2+13
Комментарии16

Публикации

Истории

Работа

DevOps инженер
51 вакансия

Ближайшие события

19 сентября
CDI Conf 2024
Москва
24 сентября
Конференция Fin.Bot 2024
МоскваОнлайн
30 сентября – 1 октября
Конференция фронтенд-разработчиков FrontendConf 2024
МоскваОнлайн