
前言
Jenkins在日常工作中占據了一個非常重要的角色,幫助我們節省了大量用于建構的時間。有些公司有運維大哥對Jenkins進行維護,如果沒有那隻能自己動手了。俗話說的好自己動手豐衣足食,是以本文就從0開始搭建屬于自己的Jenkins持續平台。主要包含,普通項目建構、流水線建構、多分支流水線建構并将建構結果輔以釘釘通知。
前期準備
- centos7 伺服器一台
确認是否能安裝docker
Docker要求CentOS系統的核心版本高于3.10.通過
uname -r
指令檢視你目前的核心版本。
[root@CentOS ~]# uname -r
3.10.0-1127.8.2.el7.x86_64
更改yum源為阿裡雲
備份舊源
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
安裝docker
官方安裝文檔yum install -y yum-utils
添加docker源
yum-config-manager \
--add-repo \
https://download.docker.com/linux/centos/docker-ce.repo
yum install docker-ce
啟動docker
systemctl start docker
更改docker鏡像源
vim /etc/docker/daemon.json
加入阿裡雲源位址
{
"registry-mirrors":["https://6kx4zyno.mirror.aliyuncs.com"]
}
重新讀取配置
systemctl daemon-reload
重新開機docker
systemctl restart docker
安裝jenkins
下載下傳jenkins鏡像
docker pull jenkins
啟動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 docker鏡像遇到的volume權限問題
在運作啟動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
Jenkins初始化
啟動成功後輸入
http://伺服器: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的更新主要是替換jenkins鏡像裡面的war包 ,我們可以把下載下傳好的war包使用
docker cp
直接進行複制指令如下:
docker cp jenkins.war jenkins:/usr/share/jenkins
重新啟動Jenkins即可完成更新。
docker restart jenkins
更插件源
https://mirrors.tuna.tsinghua.edu.cn/jenkins/updates/update-center.json
- 替換完源以後點選送出。
- 然後進入插件管理頁面将出錯的插件重新安裝。
- 及時更新插件。
安裝必要的插件
- Localization: Chinese (Simplified) 1.0.14 漢化包 搜尋關鍵字 chinese
- Publish Over SSH 1.20.1 搜尋關鍵字 ssh
- DingTalk 釘釘通知 2.3.0
配置jenkins
全局工具配置
主要配置 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
反之出現錯誤資訊,可以根據錯誤資訊,調整配置參數。
配置釘釘
釘釘主要用于建構通知,在配置前需要在釘釘群内,添加自定義機器人。
自由風格的軟體項目
以
https://gitee.com/huangxunhui/jenkins_demo.git
為例。
建立項目
設定項目簡介
源碼管理
- 配置倉庫位址。
- 配置憑證-主要用于拉取代碼。
- 配置需要建構的分支。
添加憑證
如果項目是開源,則可以跳過這一步。反之需要設定憑證,要不然将無法拉取代碼進行建構。
建構觸發器
可以根據實際情況選擇,案例采用輪詢的方式進行建構。
建構
建構後操作
- 将jar包發送到相應的伺服器。
-
jar包的路徑。支援通配符比對.Source files
-
移除字首,一般jar包的路徑都存在于Remove prefix
下,如果不移除,會在目标伺服器上建立相應的目錄結構。**/target
-
遠端目錄。Remote directory
注意的點, 在之前配置伺服器時也配置了 Remote directory
,這時候部署的實際目錄是,伺服器設定的遠端目錄+現在配置的遠端目錄。
-
執行腳本,主要用于将Exec command
發送到目标伺服器後,執行相應的啟動腳本。jar
配置完成點選儲存即可。
點選開始建構
發送釘釘通知
流水線
流水線建構,将上述建構步驟代碼化,友善調整。
項目建立
流水線編寫
由于配置步驟類似,前面簡單的步驟可以參照,
自由風格的軟體項目
。這裡主要講流水線如何編寫。
注意右下角的流水線文法,後續會用上。
我們可以點選右上角的下拉按鈕,生成一個簡單的流水線。比如說hello world。
pipeline {
// 表示所有機器都能運作
agent any
stages {
stage('Hello') {
steps {
echo 'Hello World'
}
}
}
}
通過上面的
pipeline
可以知道,有一個
Hello
的步驟,這個步驟執行的是,輸出
hello world
。依葫蘆畫瓢,一次完整的建構我們可以總結出如下幾個步驟:拉取代碼(checkout) -> 打包(build) -> 部署(deploy)。
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
檔案
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
釘釘機器人插件使用文檔 建構結果
結尾
如果覺得對你有幫助,可以多多評論,多多點贊哦,也可以到我的首頁看看,說不定有你喜歡的文章,也可以随手點個關注哦,謝謝。
我是不一樣的科技宅,每天進步一點點,體驗不一樣的生活。我們下期見!