Создайте собственную платформу непрерывной интеграции Jenkins с нуля

Jenkins

предисловие

  Дженкинс играет очень важную роль в нашей повседневной работе, помогая нам сэкономить много времени, затрачиваемого на сборку. В некоторых компаниях есть братья по эксплуатации и обслуживанию, которые обслуживают Jenkins, если нет, то они могут делать это только сами. Как говорится, хорошо делать это самому, поэтому в этой статье мы создадим вашу собственную непрерывную платформу Jenkins с нуля. в основном включает,нормальная сборка проекта,сборка конвейера,Строительство многоветвевого трубопроводаИ дополняйте результаты сборки уведомлениями DingTalk.

Предварительная подготовка

  • Один сервер Centos7

Проверьте, можно ли установить докер

 Docker требует, чтобы версия ядра системы CentOS была выше 3.10.uname -rКоманда для проверки текущей версии ядра.

[root@CentOS ~]# uname -r
3.10.0-1127.8.2.el7.x86_64

Измените источник yum на Alibaba Cloud

резервное копирование старого источника

mv /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.backup

Загрузите последний исходный код

wget -O /etc/yum.repos.d/CentOS-Base.repo https://mirrors.aliyun.com/repo/Centos-7.repo

Создать кеш

yum makecache

возобновить

yum update

установить докер

Официальная документация по установке

yum install -y yum-utils

добавить источник докера

yum-config-manager \
    --add-repo \
    https://download.docker.com/linux/centos/docker-ce.repo

установить докер

yum install docker-ce

запустить докер

systemctl start docker

Изменить источник образа докера

vim /etc/docker/daemon.json

Присоединяйтесь к облачному исходному адресу Alibaba

{
    "registry-mirrors":["https://6kx4zyno.mirror.aliyuncs.com"]
}

прочитать конфигурацию еще раз

systemctl daemon-reload 

перезапустить докер

systemctl restart docker

установить Дженкинс

Скачать образ Дженкинса

docker pull jenkins

начать Дженкинс

  Установите порт на 9090 и сопоставьтеjenkins_homeхозяину/home/jenkins_home.

docker run -d --name jenkins -p 9090:8080 -v /home/jenkins_home:/var/jenkins_home jenkins

в состоянии пройтиdocker psПросмотр запущенных контейнеров.

[root@CentOS home]# docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED              STATUS              PORTS                               NAMES
ec6a4da6b83f        jenkins             "/bin/tini -- /usr/l…"   About a minute ago   Up About a minute   50000/tcp, 0.0.0.0:9090->8080/tcp   jenkins
[root@CentOS home]#

Играйте с проблемой разрешения тома, с которой сталкивается образ докера jenkins

  При запуске команды для запуска jenkins jenkins может не запуститься.

[root@CentOS home]# docker ps -a
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS                     PORTS               NAMES
b571f16dafbf        jenkins             "/bin/tini -- /usr/l…"   8 minutes ago       Exited (1) 8 minutes ago                       jenkins
[root@CentOS home]#

в состоянии пройтиdocker logs 镜像名称Проверьте журнал запуска.

[root@CentOS home]# docker logs jenkins
touch: cannot touch '/var/jenkins_home/copy_reference_file.log': Permission denied
Can not write to /var/jenkins_home/copy_reference_file.log. Wrong volume permissions?
[root@CentOS home]#

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

docker rm jenkins

 Добавлена ​​команда «Выполнить».-u 0Повтор.

docker run -d --name jenkins -p 9090:8080 -v /home/jenkins_home:/var/jenkins_home -u 0 jenkins

Ссылка https://blog.csdn.net/minicto/article/details/73539986

Инициализация Дженкинса

 Введите http://server:9090/ после успешного запуска

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

   Вам нужно ввести пароль при первом запуске jenkins, и вам нужно войти в контейнер, чтобы получить пароль. Пароль находится на/var/jenkins_home/secrets/initialAdminPassword.

в контейнер

docker exec -it jenkins /bin/bash

получить пароль

cat /var/jenkins_home/secrets/initialAdminPassword
[root@CentOS jenkins_home]# docker exec -it jenkins /bin/bash
root@ec6a4da6b83f:/# cat /var/jenkins_home/secrets/initialAdminPassword
68eed23ad39541949972468e4f2ce1fd
root@ec6a4da6b83f:/#

   Так как мы будем/var/jenkins_home-- монтировать в -->/home/jenkins_homeТаким образом, вы также можете напрямуюcat /home/jenkins_home/secrets/initialAdminPasswordПолучите пароль.

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

Установите учетную запись для входа по умолчанию и пароль jenkins

Обработка ошибок установки плагина

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

автоматическое обновление

Jenkins предоставляет возможность автоматического обновления

Обновление вручную

 Вы можете перейти на официальный сайт Jenkins, чтобы загрузить последний пакет jar и загрузить его на сервер, или вы можете использоватьwgetЗаказ.

wget http://jenkins新版本的下载地址
#目前最新2.239
wget http://updates.jenkins-ci.org/download/war/2.239/jenkins.war

Обновление   Jenkins в основном предназначено для замены пакета war в образе jenkins, мы можем использовать загруженный пакет wardocker cpКоманда прямого копирования выглядит следующим образом:

docker cp jenkins.war jenkins:/usr/share/jenkins

  Перезапустите Jenkins, чтобы завершить обновление.

docker restart jenkins

больше исходников плагина

https://mirrors.tuna.tsinghua.edu.cn/jenkins/updates/update-center.json
  • После замены источника нажмите Submit.
  • Затем войдите на страницу управления подключаемым модулем, чтобы переустановить неисправный подключаемый модуль.
  • Своевременно обновляйте плагины.

Установите необходимые плагины

  • Локализация: Китайский (упрощенный) 1.0.14 Китайский пакет Поисковое слово китайский
  • Опубликовать через SSH 1.20.1 Поиск по ключевому слову ssh
  • DingTalk Уведомление DingTalk 2.3.0

настроить Дженкинс

Глобальная конфигурация инструмента

   В основном настройка общих сред, таких как jdk, maven и git. Нужно обратить внимание на настроенный алиас, который будет использоваться в последующих билдах.

настроить jdk

  Поскольку образ jenkins поставляется с jdk, его можно использовать напрямую без установки, войдите в контейнер Jenkins, используйтеjava -verboseПроверьте путь установки Java.

docker exec -it jenkins /bin/bash
java -verbose
настроить git

 В контейнер для использованияwhereis gitВы можете запросить путь установки git.

root@6a9fbb129cbe:~# whereis git
git: /usr/bin/git /usr/share/man/man1/git.1.gz
root@6a9fbb129cbe:~#
настроить maven

  maven может использовать автоматическую установку напрямую.

системные настройки

сервер конфигурации

Нажмите Добавить, чтобы добавить сервер, основная конфигурация:

  • Nameимя - будет использоваться при построении
  • Hostnameадрес сервера
  • Usernameимя пользователя
  • Remote DirectoryУдаленный каталог — каталог, в который загружаются файлы, достаточно корневого каталога конфигурации по умолчанию./.

Нажмите «Дополнительно», чтобы настроить другие параметры.

  • Проверьте, нужно ли вам войти с паролемUse password authentication, or use a different keyУстановите флажок, как показано на рисунке ниже.

   Помимо настройки пароля, вы также можете настроить портPort, трамплинJump Hostпараметры могут быть настроены в соответствии с реальной ситуацией. По умолчанию можно использовать пароль.

  Нажмите после завершения настройкиTest Configurationкнопка, если конфигурация нормальная, она появитсяSuccessНаоборот, если появляется сообщение об ошибке, вы можете настроить параметры конфигурации в соответствии с сообщением об ошибке.

Настроить DingTalk

  DingTalk в основном используется для создания уведомлений.Перед настройкой необходимо добавить пользовательского робота в группу DingTalk.

Программные проекты фристайл

отhttps://gitee.com/huangxunhui/jenkins_demo.gitНапример.

Новый проект

Введение в проект установки

управление исходным кодом

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

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

триггер сборки

   можно выбрать в соответствии с реальной ситуацией, а случай строится путем опроса.

Построить

Действия после сборки

  • Отправьте пакет jar на соответствующий сервер.
  • Source filesПуть к пакету jar. Сопоставление с подстановочными знаками поддерживается.
  • Remove prefixУдалите префикс, путь к общему пакету jar существует в**/targetЕсли не удалить, соответствующая структура каталогов будет установлена ​​на целевом сервере.
  • Remote directoryудаленный каталог.

Обратите внимание, что он также был настроен при настройке сервера ранее.Remote directory, фактическим каталогом, развернутым в это время, является удаленный каталог, заданный сервером, + удаленный каталог, сконфигурированный в данный момент.

  • Exec commandВыполнение сценария, в основном используемого дляjarПосле отправки на целевой сервер выполняется соответствующий сценарий запуска.

После завершения настройки нажмите Сохранить.

Нажмите, чтобы начать строительство

Отправка уведомлений DingTalk


сборочная линия

  Конвейерная конструкция, которая кодирует вышеуказанные этапы конструкции для легкой регулировки.

создание проекта

Написание конвейера

   Поскольку шаги настройки аналогичны, вы можете обратиться к предыдущим простым шагам.自由风格的软件项目. Здесь мы в основном говорим о том, как написать конвейер.

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

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

pipeline {

    // 表示所有机器都能运行   
   agent any

   stages {
      stage('Hello') {
         steps {
            echo 'Hello World'
         }
      }
   }
}

   через указанное вышеpipelineВы знаете, естьHelloШаг, который выполняет этот шаг, - выводhello world. Согласно тыкве, мы можем обобщить следующие шаги для полной сборки: вытащить код (оформить заказ) -> упаковать (сборка) -> развернуть (развернуть).

pipeline {
   agent any

   stages {
      stage('checkout') {
         steps {

         }
      }

      stage('build') {
         steps {

         }
      }

    stage('deploy') {
         steps {

         }
      }
   }
}

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

Скопируйте сгенерированный сценарий пайплайна на соответствующий шаг.

Примечание. Если вы используете maven, вам нужно представить maven,toolsСоответствующее содержание является конфигурациейmavenПсевдоним, настроенный при .

pipeline {
   agent any

    // 工具
    tools {
        maven 'maven'
        jdk 'jdk'
    }

   stages {
      stage('checkout') {
         steps {
            checkout([$class: 'GitSCM', branches: [[name: '*/master']], doGenerateSubmoduleConfigurations: false, extensions: [], submoduleCfg: [], userRemoteConfigs: [[url: 'https://gitee.com/huangxunhui/jenkins_demo.git']]])
         }
      }

      stage('build') {
         steps {
            sh 'mvn clean package -Dmaven.test.skip=true'            
         }
      }

    stage('deploy') {
         steps {
            sshPublisher(publishers: [sshPublisherDesc(configName: 'dev', transfers: [sshTransfer(cleanRemote: false, excludes: '', execCommand: '''cd /home/project 
nohup java -jar jenkins_demo.jar > nohup.out 2>&1 &''', execTimeout: 120000, flatten: false, makeEmptyDirs: false, noDefaultExcludes: false, patternSeparator: '[, ]+', remoteDirectory: '/home/project', remoteDirectorySDF: false, removePrefix: '/target', sourceFiles: '**/target/jenkins_demo.jar')], usePromotionTimestamp: false, useWorkspaceInPromotion: false, verbose: false)])
         }
      }

   }
}

После завершения настройки нажмите Применить.

построить тест

Вышеприведенная демонстрация предназначена для настройки конвейера в jenkins, Фактически, мы также можем начать сSCMполучено, напримерgit.

Мы можем создать репозиторий, предназначенный для поддержки сценариев сборки для разных проектов.Jenkinsfile, вы также можете создать соответствующийJenkinsfile.

  На заметку: в проектеJenkinsfileОн должен соответствовать конфигурации. Например, приведенная выше конфигурация предназначена для сканирования корневого каталога проекта с именемJenkinsfileдокумент.

так что мы можемjenkins_demoдобавить на складJenkinsfileдокумент.

Нажмите Готово, чтобы настроить.


многоветвевой конвейер

  В ежедневном развитии, обычно на основеgit-flowДля разработки первые два основаны на построении одной ветки, если настроить каждую ветку, то это займет много времени. такмноговетвевой конвейериспользуется для решения этой проблемы.

Создать проект

Настроить источник ветки

конфигурация сборки

триггер сканирования

После завершения вышеуказанной настройки нажмите «Применить».

Напишите файл `jenkinsfile`

Основная идея    состоит в том, чтобы использовать разные команды упаковки в соответствии с разными ветками и отправлять их на разные серверы для запуска.

pipeline {
    // 指定集群 any 表示所有
    agent any

    // 工具
    tools {
        maven 'maven'
        jdk 'jdk'
    }

    // 定义常量
    environment {

        // 钉钉机器人编号
        rebootId = 'a3c07482-d031-47a6-8542-05ac56c5f17a'

        // 开始logo
        imageOfStart = 'https://www.easyicon.net/api/resizeApi.php?id=1229977&size=128'

        // 成功logo
        imageOfSuccess = 'https://www.easyicon.net/api/resizeApi.php?id=1194837&size=128'

        // 失败logo
        imageOfFailure = 'https://www.easyicon.net/api/resizeApi.php?id=1201052&size=128'

        // 不稳定logo
        imageOfUnstable = 'https://www.easyicon.net/api/resizeApi.php?id=1219854&size=128'

        // 终止logo
        imageOfAborted = 'https://www.easyicon.net/api/resizeApi.php?id=1183198&size=128'

        // 认证Id
        credentialsId = '98e9c197-f0ae-44c3-8f67-4ca0339028a8'

        // 仓库地址
        repositoryUrl = 'https://gitee.com/huangxunhui/jenkins_demo.git'

        // 打包命令 - 项目需要配置maven多环境
        mavenProd = 'mvn clean package -P prod -Dmaven.test.skip=true'
        mavenTest = 'mvn clean package -P test -Dmaven.test.skip=true'
        mavenDev = 'mvn clean package -P dev -Dmaven.test.skip=true'

        // 服务器名称 - 案例测试-全部部署到dev环境
        devServer = 'dev'
        testServer = 'dev'
        prodServer = 'dev'

        // sshPublisher 配置
        removePrefix = '/target'
        remoteDirectory = '/home/project/jenkins_demo'
        sourceFiles = '**/target/jenkins_demo.jar'

        execCommandProd = 'cd /home/project && ./manage.sh jenkins_demo/ restart'
        execCommandTest = 'cd /home/project && ./manage.sh jenkins_demo/ restart'
        execCommandDev = 'cd /home/project && ./manage.sh jenkins_demo/ restart'

    }

    stages {

        stage('开始构建通知'){
            steps {
                dingtalk (
                        robot: "${rebootId}",
                        type: 'LINK',
                        title: "${env.JOB_NAME}",
                        text: [
                            "开始构建-编号为#${BUILD_NUMBER}"
                        ],
                        messageUrl: "${env.BUILD_URL}",
                        picUrl: "${imageOfStart}"
                )
            }
        }

        stage('拉取代码'){
            steps {
                echo "拉取 ${BRANCH_NAME} 分支的代码。"
                checkout([$class: 'GitSCM', branches: [[name: "*/${BRANCH_NAME}"]], doGenerateSubmoduleConfigurations: false, extensions: [], submoduleCfg: [], userRemoteConfigs: [[credentialsId: "${credentialsId}", url: "${repositoryUrl}"]]])
            }
        }

        stage('进行打包'){
            steps {
                script {
                    if (env.BRANCH_NAME == 'master') {
                        sh "${mavenProd}"
                    } else if (env.BRANCH_NAME == 'test') {
                        sh "${mavenTest}"
                    } else if (env.BRANCH_NAME == 'dev') {
                        sh "${mavenDev}"
                    } else {
                        sh "${mavenDev}"
                    }
                }
            }
        }

        stage('项目部署'){
            steps {
                script {
                    if (env.BRANCH_NAME == 'master') {
                        // 部署生产环境
                        sshPublisher(publishers: [sshPublisherDesc(configName: "${prodServer}", transfers: [sshTransfer(cleanRemote: false, excludes: '', execCommand: "${execCommandProd}", execTimeout: 120000, flatten: false, makeEmptyDirs: false, noDefaultExcludes: false, patternSeparator: '[, ]+', remoteDirectory:  "${remoteDirectory}", remoteDirectorySDF: false, removePrefix: "${removePrefix}", sourceFiles: "${sourceFiles}")], usePromotionTimestamp: false, useWorkspaceInPromotion: false, verbose: false)])
                    } else if (env.BRANCH_NAME == 'test') {
                        // 部署测试环境
                        sshPublisher(publishers: [sshPublisherDesc(configName: "${testServer}", transfers: [sshTransfer(cleanRemote: false, excludes: '', execCommand: "${execCommandTest}", execTimeout: 120000, flatten: false, makeEmptyDirs: false, noDefaultExcludes: false, patternSeparator: '[, ]+', remoteDirectory:  "${remoteDirectory}", remoteDirectorySDF: false, removePrefix: "${removePrefix}", sourceFiles: "${sourceFiles}")], usePromotionTimestamp: false, useWorkspaceInPromotion: false, verbose: false)])
                    } else if (env.BRANCH_NAME == 'dev') {
                        // 部署开发环境
                        sshPublisher(publishers: [sshPublisherDesc(configName: "${devServer}", transfers: [sshTransfer(cleanRemote: false, excludes: '', execCommand: "${execCommandDev}", execTimeout: 120000, flatten: false, makeEmptyDirs: false, noDefaultExcludes: false, patternSeparator: '[, ]+', remoteDirectory:  "${remoteDirectory}", remoteDirectorySDF: false, removePrefix: "${removePrefix}", sourceFiles: "${sourceFiles}")], usePromotionTimestamp: false, useWorkspaceInPromotion: false, verbose: false)])
                    } else {
                        sshPublisher(publishers: [sshPublisherDesc(configName: "${devServer}", transfers: [sshTransfer(cleanRemote: false, excludes: '', execCommand: "${execCommandTest}", execTimeout: 120000, flatten: false, makeEmptyDirs: false, noDefaultExcludes: false, patternSeparator: '[, ]+', remoteDirectory:  "${remoteDirectory}", remoteDirectorySDF: false, removePrefix: "${removePrefix}", sourceFiles: "${sourceFiles}")], usePromotionTimestamp: false, useWorkspaceInPromotion: false, verbose: false)])
                    }
                }
            }
        }
    }

    // 流水线结束通知
    post {

        // 成功通知
        success {
            dingtalk (
                    robot: "${rebootId}",
                    type: 'LINK',
                    title: "${env.JOB_NAME}",
                    text: [
                        "构建成功-编号为#${BUILD_NUMBER}"
                    ],
                    messageUrl: "${env.BUILD_URL}",
                    picUrl: "${imageOfSuccess}"
                )
        }

        // 失败通知
        failure {
            dingtalk (
                    robot: "${rebootId}",
                    type: 'LINK',
                    title: "${env.JOB_NAME}",
                    text: [
                        "构建失败-编号为#${BUILD_NUMBER}"
                    ],
                    messageUrl: "${env.BUILD_URL}",
                    picUrl: "${imageOfFailure}"
            )
        }

        // 构建不稳定通知
        unstable {
            dingtalk (
                    robot: "${rebootId}",
                    type: 'LINK',
                    title: "${env.JOB_NAME}",
                    text: [
                        "构建不稳定-编号为#${BUILD_NUMBER}"
                    ],
                    messageUrl: "${env.BUILD_URL}",
                    picUrl: "${imageOfUnstable}"
            )
        }

        // 构建终止通知
        aborted {
            dingtalk (
                    robot: "${rebootId}",
                    type: 'LINK',
                    title: "${env.JOB_NAME}",
                    text: [
                        "构建终止-编号为#${BUILD_NUMBER}"
                    ],
                    messageUrl: "${env.BUILD_URL}",
                    picUrl: "${imageOfAborted}"
            )
        }
    }
}

Используемый сценарий запускаmanage.sh.

Документация по подключаемому модулю робота DingTalk

построить результат

конец

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