Jenkins. Початок роботи.

Основи роботи з Linux. -> Jenkins. Робота з GIT.

Jenkins. Робота з GIT.

Для роботи з репозиторієм на початку необхідно додати користувача та його приватні ключі, які використовуються в git.

start page

Потім можна створити таку задачу типу Pipeline.

node('pi') {
    stage('Git clone') {
        dir('/home/zdimon/project01') {
            git credentialsId: '0acbc9ab-ca88-4fbd-a13e-59c3c81c1722', url: 'git@github.com:zdimon/web-starter.git', branch: 'master'

            script {
                def stdOut = sh(script: "pwd && ls -alh",
                    returnStdout: true)
                echo stdOut
            }
        }
    }
}

node(‘pi’) - вказуємо в якій ноді (на якому сервері) працювати.

stage(‘Git clone’) - позначаємо статію.

dir(‘/home/zdimon/project01’) {…} - говоримо в якій директорії працювати.

Якщо директорію не вказувати, то jenkins для кожного завдання буде створювати окремий каталог усередині папки workspace.

При формуванні команди клону я користувався помічником.

start page

start page

Далі в блоці script ми виконуємо bash команду, заносимо її виведення у файл та виводимо його у консоль.

    script {
        def stdOut = sh(script: "pwd && ls -alh",
            returnStdout: true)
        echo stdOut
    }

Результат.

start page

start page

Прив’язуємо запит на збирання до git хуку.

У папці .git є каталог hooks з шаблонами bash скриптів, що спрацьовують у різних ситуаціях.

Створюємо новий файл post-commit

#!/bin/sh
echo "Post commit"

Додаємо права на виконання

chmod +x ./git/hooks/post-commit

Тепер цей скрипт буде виконуватись при кожному коміті.

Можна до нього додати запит на сервер із jenkins.

curl http://yourserver/jenkins/git/notifyCommit?url=<URL of the Git repository>

Цей запит просканує всі завдання, які налаштовані з перевіркою специфічного URL.

Ще один варіант запуску.

#!/bin/bash
/usr/bin/curl --user USERNAME:PASS -s \

http://jenkinsci/job/PROJECTNAME/build?token=1qaz2wsx

Для цього запиту необхідно налаштувати завдання віддаленим тригером з токеном.

Запуск певного завдання на її ім’я.

curl http://localhost:8080/job/someJob/build?delay=0sec

curl -X POST http://:/job/job-name-in-jenkins/build?delay=0sec --user user:password
Основи роботи з Linux. -> Jenkins. Скрипти на groovy.

Jenkins. Скрипти на groovy.

Послідовність операцій складання або тестування може бути оформлена у вигляді pipeline (конвейера) в окремому файлі Jenkinsfile і покладена в репозиторій.

Це дозволить:

  • автоматично згенерувати конвейєр для всіх бранч і pull request-ів.

  • тримати логіку тестів у проекті для аудиту

Jenkinsfile скрипт може бути написаний у декларативному або імперативному стилі.

Декларативний стиль передбачає використання високорівневих конструкцій мови, які вирішують комплексні завдання за один раз.

При імперативному підході кожну задачу потрібно докладніше описувати.

Приклад як може виглядати процес складання, тестування та деплою проекту.

start page

Конвейєр складається з наступних елементів.

нода (node) - сервер, на якому все виконується

стадія (stage) - блок, що визначає набір операцій для одного завдання (складання тестування, деплою і т.д.). Ці стадії потім будуть красиво оформлені інтерфейсом Jenkins із зазначенням часу, статусу та ін.

крок (step) - одна операція. Фактично вказівка ​​що робити у певний час.

Розглянемо варіанти оформлення Jenkinsfile.

Приклад оформлення конвейєра у декларативному стилі.

pipeline {
    agent any 
    stages {
        stage('Build') { 
            steps {
                // 
            }
        }
        stage('Test') { 
            steps {
                // 
            }
        }
        stage('Deploy') { 
            steps {
                // 
            }
        }
    }
}

agent any - говорить про те, що виконувати потрібно на будь-якому агенті

Для декларативних скриптів застосовується агент, для імперативних нод.

Приклад імперативного підходу.

node('node name') {  
    stage('Build') { 
        // 
    }
    stage('Test') { 
        // 
    }
    stage('Deploy') { 
        // 
    }
}

Розглянемо ще один декларативний приклад

pipeline { 
    agent any 
    options {
        skipStagesAfterUnstable()
    }
    stages {
        stage('Build') { 
            steps { 
                sh 'make' 
            }
        }
        stage('Test'){
            steps {
                sh 'make check'
                junit 'reports/**/*.xml' 
            }
        }
        stage('Deploy') {
            steps {
                sh 'make publish'
            }
        }
    }
}

pipeline - кореневий блок всього конвейєра

agent - сутність, яка виконуватиме код (нода, або комп’ютер, або докер образ)

sh - виконує shell команду

junit - плагін, що збирає статистику та формує xml звітпосилання

Встановлення агента.

Агент вказуватиме, де саме виконувати роботу.

 agent any

виконає на будь-якому доступному агенті

 agent none

При цьому ми змушуємо прописувати агента для кожного кроку, а для конвеєра встановлюємо в none.

 agent { label 'my-defined-label' }

вибере агента з мітки

 agent { node { label 'labelName' } }

Працює так само як і з міткою, але дозволяє використовувати додаткові налаштування для ноди, наприклад customWorkspace.

agent {
    node {
        label 'my-defined-label'
        customWorkspace '/some/other/path'
    }
}

customWorkspace може бути як відносним так і абсолютним шляхом та дозволяє вийти за межі дефолтного воркспейсу (робочого каталогу)

Агент для докеру зображення.

agent {
    docker {
        image 'maven:3-alpine'
        label 'my-defined-label'
        args  '-v /tmp:/tmp'
    }
}

args - передає аргумени команді docker run

Зображення можна зібрати з файлу Dockerfile

agent {
    dockerfile {
        filename 'Dockerfile.build'
        dir 'build'
        label 'my-defined-label'
        additionalBuildArgs  '--build-arg version=1.0.2'
        args '-v /tmp:/tmp'
    }
}

Те ж, що запуск

"docker build -f Dockerfile.build --build-arg version=1.0.2 ./build/

Приклад запуску команди всередині контейнера у декларативному стилі.

pipeline {
    agent { docker 'maven:3-alpine' } 
    stages {
        stage('Example Build') {
            steps {
                sh 'mvn -B clean verify'
            }
        }
    }
}

Агент можна визначати для різних стадій.

pipeline {
    agent none 
    stages {
        stage('Example Build') {
            agent { docker 'maven:3-alpine' } 
            steps {
                echo 'Hello, Maven'
                sh 'mvn --version'
            }
        }
        stage('Example Test') {
            agent { docker 'openjdk:8-jre' } 
            steps {
                echo 'Hello, JDK'
                sh 'java -version'
            }
        }
    }
}

Конструкція post

Дозволяє виконати код за певних обставин, умов або результатів виконання стадій (залежно де його всунути).

Умови.

allways - виконається завжди

failure - тільки коли провалений

success - тільки коли успішно виконано

changed - тільки якщо результати відрізняються від попереднього запуску

fixed - якщо попередній запуск був провалений, а поточний ні

regression - навпаки

приклад.

pipeline {
    agent any
    stages {
        stage('Example') {
            steps {
                echo 'Hello World'
            }
        }
    }
    post { 
        always { 
            echo 'I will always say Hello again!'
        }
    }
}

Можна визначати одразу кілька варіантів.

pipeline {
    agent any
    stages {
        stage('Test') {
            steps {
                sh 'echo "Fail!"; exit 1'
            }
        }
    }
    post {
        always {
            echo 'This will always run'
        }
        success {
            echo 'This will run only if successful'
        }
        failure {
            echo 'This will run only if failed'
        }
        unstable {
            echo 'This will run only if the run was marked as unstable'
        }
        changed {
            echo 'This will run only if the state of the Pipeline has changed'
            echo 'For example, if the Pipeline was previously failing but is now successful'
        }
    }
}

Переменные окружения. Директива environment.

Пары ключ - значение, которые передадуться внутрь каждого шага.

pipeline {
    agent any
    environment { 
        CC = 'clang'
    }
    stages {
        stage('Example') {
            environment { 
                AN_ACCESS_KEY = credentials('my-predefined-secret-text') 
            }
            steps {
                sh 'printenv'
            }
        }
    }
}

Як бачимо, існує ієрархія видимості змінних залежно куди їх втикати до всього конвейєра або конкретного кроку.

Ще приклад виведення змінних оточення.

pipeline {
    agent {
        label '!windows'
    }

    environment {
        DISABLE_AUTH = 'true'
        DB_ENGINE    = 'sqlite'
    }

    stages {
        stage('Build') {
            steps {
                echo "Database engine is ${DB_ENGINE}"
                echo "DISABLE_AUTH is ${DISABLE_AUTH}"
                sh 'printenv'
            }
        }
    }
}

Директива options.

Дозволяє конфігурувати деякі корисні опції під час виконання конвейєра.

buildDiscarder - керує збереженням артифактів складання та виведення в консоль

checkoutToSubdirectory - перевіряє наявність довільного каталогу

options { checkoutToSubdirectory('foo') }

disableConcurrentBuilds - допомагає виключити одночасне використання ресурсу, що розділяється

newContainerPerStage - створюватиме за новим контейнером на кожну стадію

timeout - тайм по якому потрібно перервати конвейєр

pipeline {
    agent any
    options {
        timeout(time: 1, unit: 'HOURS') 
    }
    stages {
        stage('Example') {
            steps {
                echo 'Hello World'
            }
        }
    }
}

retry - скільки разів повторити після провалу

Опції можна встановлювати як до всього конвейєра, так і до певних стадій.

Тригери

Визначають умову, за якої конвейєр повинен бути запущений.

cron - визначає розклад для запусків

triggers { cron('H */4 * * 1-5') }

pollSCM - визначає кроновий інтервал для перевірки змін у вихідниках. Якщо зміни виявлено, запускається конвейєр.

triggers { pollSCM('H */4 * * 1-5') }

upstream - приймає рядок із завданнями через кому та поріг. Якщо хоч одне завдання завершилося із зазначеним порогом, то конвейєр перезапуститься.

 triggers { upstream(upstreamProjects: 'job1,job2', threshold: hudson.model.Result.SUCCESS) }

приклад.

pipeline {
    agent any
    triggers {
        cron('H */4 * * 1-5')
    }
    stages {
        stage('Example') {
            steps {
                echo 'Hello World'
            }
        }
    }
}

triggers{ cron(‘H/15 * * * *’) } - кожні 15 хв.

triggers{ cron(‘H(0-29)/10 * * * *’) } - кожні 10 хв у першій половині години

Директива інструментів.

Дозволяє встановити програми на конвейєр.

pipeline {
    agent any
    tools {
        maven 'apache-maven-3.0.1' 
    }
    stages {
        stage('Example') {
            steps {
                sh 'mvn --version'
            }
        }
    }
}

Директива введення.

Дозволяє забирати дані, що вводяться користувачем.

pipeline {
    agent any
    stages {
        stage('Example') {
            input {
                message "Should we continue?"
                ok "Yes, we should."
                submitter "alice,bob"
                parameters {
                    string(name: 'PERSON', defaultValue: 'Mr Jenkins', description: 'Who should I say hello to?')
                }
            }
            steps {
                echo "Hello, ${PERSON}, nice to meet you."
            }
        }
    }
}

Наприклад, ми можемо використовувати це для отримання дозволу на деплой коду на продакшин сервер після успішних тестів.

Директива when

Визначає умову, за якої потрібно виконувати стадію.

Наприклад, якщо бранч відповідає масці.

when { branch pattern: "release-\\d+", comparator: "REGEXP"}

Коли білд містить тег.

when { buildingTag() }

Коли в лозі міститься рядок, що шукається.

 when { changelog '.*^\\[DEPENDENCY\\] .+$' }

Якщо вихідники містять файли, що цікавлять

when { changeset "**/*.js" }

За запитами Pull Request

when { changeRequest() }.

Якщо є певна змінна в оточенні.

when { environment name: 'DEPLOY_TO', value: 'production' }

Якщо є тег

when { tag "release-*" }.

Заперечення

 when { not { branch 'master' } }

Якщо спрацював певний тригер

when { triggeredBy 'TimerTrigger' }

приклад.

pipeline {
    agent any
    stages {
        stage('Example Build') {
            steps {
                echo 'Hello World'
            }
        }
        stage('Example Deploy') {
            when {
                branch 'production'
            }
            steps {
                echo 'Deploying'
            }
        }
    }
}

Умови можна поєднувати.

when { branch ‘production’ environment name: ‘DEPLOY_TO’, value: ‘production’ }

Або влагоджувати один одного.

when {
    allOf {
        branch 'production'
        environment name: 'DEPLOY_TO', value: 'production'
    }
}

Послідовні стадії

Стадії можуть бути не тільки вкладеними, а й визначати послідовність за допомогою директив steps, stages, parallel або matrix.

Паралельне виконання (paralel).

    stage('Parallel In Sequential') {
        parallel {
            stage('In Parallel 1') {
                steps {
                    echo "In Parallel 1"
                }
            }
            stage('In Parallel 2') {
                steps {
                    echo "In Parallel 2"
                }
            }
        }
    }

stage может содержать только один блок paralel.

Установка параметра failFast true дозволяє перервати виконання у разі провалу з стадій.

    stage('Parallel Stage') {
        when {
            branch 'master'
        }
        failFast true
        parallel {
            stage('Branch A') {
                agent {
                    label "for-branch-a"
                }
                steps {
                    echo "On Branch A"
                }
            }
          ....

Matrix

Дозволяє конструювати матрицю із осередків.

Наприклад, так можна створити матрицю 4 на 3 з 12 значень.

matrix {
    axes {
        axis {
            name 'PLATFORM'
            values 'linux', 'mac', 'windows'
        }
        axis {
            name 'BROWSER'
            values 'chrome', 'edge', 'firefox', 'safari'
        }
    }
    // ...
}

Якщо тепер вкласти всередину стадії, то вони виконаються для кожного осередку послідовно.

matrix {
    axes {
        axis {
            name 'PLATFORM'
            values 'linux', 'mac', 'windows'
        }
    }
    stages {
        stage('build') {
            // ...
        }
        stage('test') {
            // ...
        }
        stage('deploy') {
            // ...
        }
    }
}

Директива script

Всередину цієї директиви може бути вкладений код groovy та виконаний на рівні одного кроку.

pipeline {
    agent any
    stages {
        stage('Example') {
            steps {
                echo 'Hello World'

                script {
                    def browsers = ['chrome', 'firefox']
                    for (int i = 0; i < browsers.size(); ++i) {
                        echo "Testing the ${browsers[i]} browser"
                    }
                }
            }
        }
    }
}

Умови

node {
    stage('Example') {
        if (env.BRANCH_NAME == 'master') {
            echo 'I only execute on the master branch'
        } else {
            echo 'I execute elsewhere'
        }
    }
}

Імперативні скрипти.

Винятки

node {
    stage('Example') {
        try {
            sh 'exit 1'
        }
        catch (exc) {
            echo 'Something failed, I should sound the klaxons!'
            throw
        }
    }
}
Основи роботи з Linux. -> Jenkins. Початок роботи.

Jenkins. Початок роботи.

Встановити контейнер Dosker.

Створимо docker-compose.yaml.

version: '3.7'
services:
  jenkins:
    image: jenkins/jenkins:lts
    privileged: true
    user: root
    ports:
      - 8081:8080
      - 50000:50000
    container_name: jenkins
    volumes:
      - .data:/var/jenkins_home

Встановлюємо.

docker-compose up --build

Start page

Заходимо на сторінку http://localhost:8081/

start page

Копіюємо та вставляємо пароль з файлу .data/secrets/initialAdminPassword

Далі потрапляємо на сторінку встановлення плагінів.

start page

Вся робота в Jenkins зводиться до створення так званих конвейєрів (Pipeline).

Вони є послідовністю дій для деплою та тестування вашої програми.

Ця послідовність міститься у файлі Jenkinsfile та написана на мові Groovy.

Groovy — об’єктно-орієнтована мова програмування, розроблена для платформи Java як альтернатива мові Java з можливостями Python, Ruby та Smalltalk.

Можливості Groovy (що відрізняють його від Java):

— Статична та динамічна типізація

— Вбудований синтаксис для списків, асоціативних масивів, масивів та регулярних виразів

  • Замикання

— Перевантаження операцій

Більше того, майже завжди java-код – це валідний groovy-код.

Установка groovy.

Насамперед необхідно поставити java.

Потім запускаємо наступні команди.

curl -s get.sdkman.io | bash

source "$HOME/.sdkman/bin/sdkman-init.sh"

Створити образ для jenkins з Docker.

Створюємо файл Dockerfile з установкою всього необхідного для Docker у контейнер.

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 build.

Змінюємо файл docker-compose.yaml

image: jenkins/jenkins:lts

Замінимо на

build: .

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

volumes:
  - ./data:/var/jenkins_home
  - /var/run/docker.sock:/var/run/docker.sock
  - /usr/local/bin/docker:/usr/local/bin/docker

Перезапускаємо контейнер.

docker-compose up --build

Отримуємо проблему у правах.

Start page

Справа в тому, що користувач усередині контейнера має відмінні від локальних UID і GID і отже не може писати в папку data.

Існує варіант усі команди пропускати через скрипт entry-point.sh

#!/bin/sh
set -e

# first arg is `-f` or `--some-option`
# or first arg is `something.conf`
if [ "${1#-}" != "$1" ] || [ "${1%.conf}" != "$1" ]; then
    set -- redis-server "$@"
fi

# allow the container to be started with `--user`
if [ "$1" = 'redis-server' -a "$(id -u)" = '0' ]; then
    find . \! -user redis -exec chown redis '{}' +
    exec gosu redis "$0" "$@"
fi

exec "$@"

В якому перебиватиме права на потрібного користувача.

COPY docker-entrypoint.sh /usr/local/bin/
ENTRYPOINT ["docker-entrypoint.sh"]

джерело

обговорення в StackOverflow

Ручне встановлення.

Для роботи необхідна Java машина

java -version

Додаємо ключ репозиторію до системи.

wget -q -O - https://pkg.jenkins.io/debian/jenkins.io.key | sudo apt-key add -

Потім додайте на адресу репозиторію пакетів Debian у sources.list сервера:

sudo sh -c 'echo deb http://pkg.jenkins.io/debian-stable binary/ > /etc/apt/sources.list.d/jenkins.list'

Оновлюємо список репозиторіїв.

sudo apt update

Встановлення

sudo apt install jenkins

Старт сервера.

sudo systemctl start jenkins

Перевірка сервера.

sudo systemctl status jenkins

start page

Налаштовуємо віртуальний хост nginx.

server {
        listen 80;
        server_name jenkins.wezom.webmonstr.com;
        location / { 
                proxy_pass http://localhost:8080;
        }
}

Завдання.

Припустимо, що у нас є віддалена машина з середовищем розробки якого-небудь проекту та командами для тестування.

Ми хочемо періодично запускати тести на віддаленій машині.

Для того, щоб jenkins міг успішно встановити свого агента на віддаленому хості має стояти java.

Поставимо варіант headless без жодних зайвих графічних “прибамбасів”.

sudo apt install openjdk-8-jre-headless

Додавання нового користувача з правами заходу на віддалений хост по ssh.

start page

start page

start page

start page

start page

Ми вибираємо спосіб доступу за ключом ssh.

Тому в jenkins копіюємо приватний ключ із файлу ~/.ssh/id_rsa

А на віддаленій машині додаємо публічний ключ у файл .ssh/authorized_keys, який беремо з ~/.ssh/id_rsa.pub

Після додавання користувача можна приступити до створення нового середовища збирання (складача) і прив’язати його до віддаленого хоста, на якому ми збираємося запускати тести.

start page

start page

start page

При створенні середовища (Ноди) необхідно вказати робочий каталог, IP-адресу та порт у вкладці Додатково якщо він відрізняється від стандартного 22-го.

Також необхідно вибрати користувача, під яким буде виконано вхід на віддалену машину.

І вказати мітку для зручного прив’язування до нього завдань.

Також можна вказати кількість вокеров в пулі збирача на той випадок, якщо на віддаленій машині є додатковий. ресурси у вигляді простаюючих ядер процесора.

Start page

Нарешті можна розпочати створення нового завдання.

start page

Насамперед прив’язуємо завдання до збирача за його міткою.

start page

Далі вибираємо тип дії збирача.

У найпростішому випадку виконання команди BASH.

start page

start page

start page

У команді ми спочатку стрибаємо всередину домашньої директорії для того, щоб вийти за межі робочого каталогу jenkins, який він створює для того, щоб не “засірати” робочу область проекту.

Запускаємо складання.

start page

Перевіряємо результат.

start page

start page