基于Jenkins實(shí)現(xiàn)的CI/CD方案
前言
最近基于Jenkins的基座,搭建了一套適用于我們項(xiàng)目小組的持續(xù)集成環(huán)境?,F(xiàn)在把流程整理分享出來(lái),希望可以給大家提供一些幫助和思路。
使用到的組件和版本
組件名稱 | 組件版本 | 作用 |
---|---|---|
Harbor | 2.7.3 | 鏡像倉(cāng)庫(kù) |
Jenkins | 2.319.2 | 持續(xù)集成工具 |
Pipeline | 2.6 | Jenkins插件,編排流水線腳本 |
SSH Pipeline Steps | 2.0.0 | Jenkins插件,提供遠(yuǎn)程執(zhí)行ssh能力 |
Git | 4.10.1 | Jenkins插件,提供拉取git代碼倉(cāng)庫(kù)的能力 |
Httpd | 2.4.18 | HTTP服務(wù)器,用于歸檔編譯后的軟件包,鏡像包 |
Maven | 3.6.3 | 后端代碼構(gòu)建工具 |
Node | 8.17.0 | 前端代碼構(gòu)建工具 |
Docker | 20.10.0 | 容器版本 |
Docker Compose | v2.18.1 | 容器編排 |
我這邊是基于Jenkins的Pipeline+Docker的方式進(jìn)行的任務(wù)編排,Jenkins是找的一個(gè)別人做好的,內(nèi)置了絕大多數(shù)插件的容器版本,鏈接地址:https://hub.docker.com/r/h1kkan/jenkins-docker。這個(gè)做好的鏡像里面沒(méi)有SSH Pipeline Steps這個(gè)插件,需要自己額外下載一下,這邊需要注意一下插件和Jenkins的對(duì)應(yīng)版本。
基本流程圖
這邊有幾個(gè)需要注意的地方,簡(jiǎn)單說(shuō)明一下
-
首先需要配置代碼倉(cāng)庫(kù)的webhook,這個(gè)網(wǎng)上有很多資料,可以自行參考配置一下
-
dockerfile,docker-compose.yml這些文件需要內(nèi)置在代碼倉(cāng)庫(kù)中或者服務(wù)器內(nèi)(我們是dockerfile文件內(nèi)置在代碼倉(cāng)庫(kù),docker-compose.yml文件放在服務(wù)器里面固定目錄)
-
在第五步,執(zhí)行遠(yuǎn)端ssh時(shí),需要去更改docker-compose.yml中的image節(jié)點(diǎn),一開(kāi)始準(zhǔn)備用shell去做的,但是實(shí)現(xiàn)有點(diǎn)難度,然后就內(nèi)置了一個(gè)python腳本,用yaml這個(gè)庫(kù)去實(shí)現(xiàn)的,具體腳本updateImageLabel.py參考:
import yaml import sys filename = sys.argv[1] # 加載docker-compose.yml文件 with open(filename,'r') as f: data = yaml.load(f) # 更新鏡像標(biāo)簽 data['services'][sys.argv[2]]['image'] = sys.argv[3] with open(filename, 'w') as yaml_file: yaml_file.write(yaml.dump(data, default_flow_style=False))
使用時(shí),傳入三個(gè)參數(shù),分別是docker-compose.yml文件路徑,需要更新的服務(wù),更新后的鏡像標(biāo)簽
python3 updateImageLabel.py ${dockerComposePath} ${service} ${image}
-
Harbor的安裝可以參考我之前的博客,安裝完成如果是http的話,還需要配置一下insecure-registries倉(cāng)庫(kù)信息,并且執(zhí)行docker login命令登錄鏡像倉(cāng)庫(kù),用于拉取Harbor倉(cāng)庫(kù)鏡像
{ "insecure-registries": [ "harbor服務(wù)器地址" ] }
-
第七步是可選的,做這個(gè)是為了方便每一個(gè)版本的歸檔,這邊可以歸檔軟件包或者save之后的鏡像包
具體實(shí)現(xiàn)步驟
下面具體寫(xiě)一下實(shí)現(xiàn)的步驟,因?yàn)樯婕暗臇|西很多,有一些能找到的通用的步驟我就先不寫(xiě)了,大家可以自行去百度或者Google。
基礎(chǔ)中間件部署
這邊只講Jenkins和Httpd的部署,Harbor部署可以參考我之前的文檔。
Jenkins部署
通過(guò)docker的方式拉起Jenkins
docker run -u root -e TZ=Asia/Shanghai --name=jenkins -d -e TZ="Asia/Shanghai" -p 8080:8080 -p 50000:50000 -v /data/jenkins:/var/jenkins_home -v /run/docker.sock:/var/run/docker.sock -v /data/archive:/data/archive h1kkan/jenkins-docker:2.319.2
容器起來(lái)之后,需要進(jìn)入到容器內(nèi)部執(zhí)行一下docker login的命令,在Jenkins容器內(nèi)部也生成一套Harbor的憑證。
第三個(gè)掛載目錄是用來(lái)開(kāi)給Httpd服務(wù)器的,用于歸檔軟件包和鏡像包。
Httpd服務(wù)器部署
通過(guò)docker拉起Httpd服務(wù)器
docker run -p 8001:80 -v /data/archive:/usr/local/apache2/htdocs/ -d --name httpd httpd:2.4.18
這邊掛載目錄就是Jenkins開(kāi)出來(lái)的目錄
Maven、Node、JDK等基礎(chǔ)鏡像
可以先從外網(wǎng)拉取對(duì)應(yīng)版本,然后本地打成tar包之后上傳到服務(wù)器解壓,再tag之后推送到自己的Harbor倉(cāng)庫(kù)。
在Jenkins配置Git憑證
這個(gè)網(wǎng)上也很多,就不細(xì)致展開(kāi)了,可以自己查一下
Pipeline流水線編排
后端流水線pipeline腳本
import java.text.SimpleDateFormat
import java.util.TimeZone
// 構(gòu)建版本
def createVersion() {
def simpleDateFormat = new SimpleDateFormat("yyyyMMddHHmmss")
simpleDateFormat.timeZone = TimeZone.getTimeZone("Asia/Shanghai")
return simpleDateFormat.format(new Date()) + "_${env.branch}"
}
def getTime() {
def simpleDateFormat = new SimpleDateFormat("yyyyMMddHHmmss")
simpleDateFormat.timeZone = TimeZone.getTimeZone("Asia/Shanghai")
return simpleDateFormat.format(new Date())
}
// 獲取遠(yuǎn)端服務(wù)器信息
def GetRemoteServer(ip, username, password) {
def remote = [:]
remote.name = ip
remote.host = ip
remote.user = username
remote.password = password
remote.allowAnyHosts = true
return remote
}
pipeline {
agent none
environment {
_version = createVersion()
_time = getTime()
}
stages {
stage('Git Checkout') {
agent any
steps {
checkout([
$class: 'GitSCM',
branches: [[name: "${branch}"]],
doGenerateSubmoduleConfigurations: false,
extensions: [],
gitTool: 'Default',
submoduleCfg: [],
userRemoteConfigs: [[url: '{{git_url}}',credentialsId: '{{credentialsId}}',]]
])
}
}
stage('Source Package') {
agent {
docker {
image 'local-maven:3.6.3-openjdk-8'
args '-v maven-repo:/usr/share/maven/ref'
}
}
steps {
sh 'mvn clean install -Dmaven.test.skip=true'
}
}
stage('Build Image') {
agent any
steps {
sh 'docker build -f $WORKSPACE/CI/dockerfile --build-arg JARNAME=backend.jar -t 127.0.0.1:18080/library/backend:${_version} $WORKSPACE/backend/target/'
sh 'docker push 127.0.0.1:18080/library/backend:${_version}'
sh 'docker rmi 127.0.0.1:18080/library/backend:${_version}'
}
}
stage('Publish To Env') {
agent any
steps {
script {
def remote = [:]
remote = GetRemoteServer('127.0.0.1', 'username', 'password')
sshCommand remote: remote, command: "python3 updateImageLabel.py docker-compose.yml backend 127.0.0.1:18080/library/backend:${_version}"
sshCommand remote: remote, command: "docker-compose -f docker-compose.yml up -d --build backend"
}
}
}
stage('Archive Package') {
agent any
steps {
sh 'mkdir -p /data/archive/backend/${branch}/${_time}'
sh 'cp $WORKSPACE/backend/backend.jar /data/archive/backend/${branch}/${_time}'
}
}
}
}
解釋一下這個(gè)腳本
-
首選鏡像的版本是時(shí)間戳+分支的格式,類似20231026095511_dev,可以根據(jù)項(xiàng)目組的要求進(jìn)行生成。
-
GetRemoteServer方法主要是提供獲取遠(yuǎn)端服務(wù)器的信息,主要使用了SSH Pipeline Steps這個(gè)插件的能力,傳入ip,username,password三個(gè)參數(shù),這邊密碼是明文寫(xiě)死在腳本里面的,這個(gè)插件也支持密碼加密保存,如果安全性要求比較高的可以換成加密的方式。
-
Git Checkout 這個(gè)stage主要是進(jìn)行代碼的下載,根據(jù)${branch}這個(gè)參數(shù)來(lái)指定代碼倉(cāng)庫(kù)的版本,參數(shù)的具體配置可以看下Jenkins參數(shù)化構(gòu)建的相關(guān)指導(dǎo)和文章。
-
Source Package就是代碼倉(cāng)庫(kù)的打包,這邊指定使用Docker鏡像作為執(zhí)行的Agent,只要指定鏡像和編譯命令就可以。這邊我還掛載了一個(gè)maven-repo的volume,主要是為了緩存Jar包,不用每次都去下載。
-
Build Image步驟進(jìn)行代碼的構(gòu)建,通過(guò)docker-build命令去生成鏡像,這邊的dockerfile文件是內(nèi)置在我們代碼庫(kù)中的,參考如下:
FROM 127.0.0.1:18080/library/java:8.0 ARG JARNAME COPY ${JARNAME} /data/
-
Publish To Env 推送到環(huán)境,這邊核心就是連接到遠(yuǎn)程服務(wù)器,通過(guò)修改鏡像標(biāo)簽的腳本去更新docker-compose.yml文件中的鏡像標(biāo)簽,然后重新構(gòu)建容器
-
Archive Package 是否歸檔,演示的腳本里面只是歸檔了編譯后的文件,還可以歸檔鏡像等等。歸檔的路徑為分支名/時(shí)間戳
前端流水線pipeline腳本
import java.text.SimpleDateFormat
import java.util.TimeZone
// 構(gòu)建版本
def createVersion() {
def simpleDateFormat = new SimpleDateFormat("yyyyMMddHHmmss")
simpleDateFormat.timeZone = TimeZone.getTimeZone("Asia/Shanghai")
return simpleDateFormat.format(new Date()) + "_${env.branch}"
}
def getTime() {
def simpleDateFormat = new SimpleDateFormat("yyyyMMddHHmmss")
simpleDateFormat.timeZone = TimeZone.getTimeZone("Asia/Shanghai")
return simpleDateFormat.format(new Date())
}
// 獲取遠(yuǎn)端服務(wù)器信息
def GetRemoteServer(ip, username, password) {
def remote = [:]
remote.name = ip
remote.host = ip
remote.user = username
remote.password = password
remote.allowAnyHosts = true
return remote
}
pipeline {
agent none
environment {
_version = createVersion()
_time = getTime()
}
stages {
stage('Git Checkout') {
agent any
steps {
checkout([
$class: 'GitSCM',
branches: [[name: "${branch}"]],
doGenerateSubmoduleConfigurations: false,
extensions: [],
gitTool: 'Default',
submoduleCfg: [],
userRemoteConfigs: [[url: '{{git_url}}',credentialsId: '{{credentialsId}}',]]
])
}
}
stage('Source Package') {
agent {
docker {
image 'node:8.17.0'
}
}
steps {
script {
sh 'npm install --registry=http://registry.npm.taobao.org'
}
}
}
stage('Image Build') {
agent any
steps {
sh 'docker build -f $WORKSPACE/CI/dockerfile -t 127.0.0.1:18080/library/frontend:${_version} $WORKSPACE/target/'
sh 'docker push 127.0.0.1:18080/library/frontend:${_version}'
sh 'docker rmi 127.0.0.1:18080/library/frontend:${_version}'
}
}
stage('Publish To Env') {
agent any
steps {
script {
def remote = [:]
remote = GetRemoteServer('127.0.0.1', 'username', 'password')
// 更新docker-compose.yml文件,修改鏡像
sshCommand remote: remote, command: "python3 updateImageLabel.py docker-compose.yml frontend 127.0.0.1:18080/library/frontend:${_version}"
sshCommand remote: remote, command: "docker-compose -f docker-compose.yml up -d --build frontend"
}
}
}
stage('Archive Package') {
agent any
steps {
sh 'mkdir -p /data/archive/frontend/${branch}/${environment}/${_time}'
sh 'cd $WORKSPACE/target/dist/ && zip -r dist.zip *'
sh 'cp $WORKSPACE/target/dist/dist.zip /data/archive/frontend/${branch}/${environment}/${_time}'
}
}
}
}
前端和后端除了構(gòu)建方式不一樣,其他基本都相同。
結(jié)語(yǔ)
參考地址:
https://www.jenkins.io/doc/book/pipeline/文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-836122.html
https://www.jenkins.io/doc/pipeline/steps/ssh-steps/文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-836122.html
到了這里,關(guān)于基于Jenkins實(shí)現(xiàn)的CI/CD方案的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!