
個人親自錄制全套DevOps系列實戰(zhàn)教程:??手把手教你玩轉(zhuǎn)DevOps全棧技術(shù)??
【資料圖】
docker私服已經(jīng)搭建完畢,下邊我們期望jenkins做的事是:
①通過git拉取代碼②通過maven構(gòu)建生成jar包③構(gòu)建含有jar包的鏡像??④推送到docker倉庫?
???⑤通知宿主從倉庫拉取鏡像并啟動容器?
?
??有什么好處??
?避免將jar包拷貝到宿主機,而是直接將jar包打入鏡像上傳到私服。
??為什么不是jenkins直接拉取并啟動容器??
?從角色上看jenkins并不是docker服務(wù),生產(chǎn)中多數(shù)是部署docker集群,所以拉取鏡像并部署容器更應(yīng)該由docker自身操作。
??非要用jenkins拉取和部署可以嗎??
?當(dāng)然可以,但jenkins容器中一直只映射單個docker宿主機的docker.sock,如果是docker集群就不好解決了,比較麻煩。
??注意:?
?之前我們講的都是jenkins構(gòu)建完jar包后,傳輸?shù)剿拗鳈C,由宿主機通過docker命令完成構(gòu)建和啟動容器,
此處我們期望jenkins能完成這些事,有幾種方法:
在jenkins中安裝docker服務(wù)或安裝docker cli并連接到宿主直接將宿主機的docker內(nèi)核映射給jenkins容器,讓jenkins能操作宿主機的docker【推薦】# 很簡單只需要將jenkins的docker-compose.yml修改一下即可version: "3"services: jenkins: build: context: . dockerfile: Dockerfile image: "lij/jenkins:2.346.3-2-lts-centos7" restart: always container_name: "jenkins" hostname: "jenkins" environment: - JAVA_OPTS="-Dhudson.model.DownloadService.noSignatureCheck=true" ports: - "9078:8080" - "50000:50000" networks: - "exist-net-bloom" volumes: - "/docker/jenkins/home:/var/jenkins_home" - "/docker/jenkins/mvnrepo:/mvnrepo" - "/etc/timezone:/etc/timezone:ro" - "/etc/localtime:/etc/localtime:ro" # 新增加內(nèi)容 - "/var/run/docker.sock:/var/run/docker.sock" - "/usr/bin/docker:/usr/bin/docker" - "/etc/docker/daemon.json:/etc/docker/daemon.json"networks: exist-net-bloom: external: name: devops
??解釋:?
?/var/run/docker.sock 是docker服務(wù)器后臺進(jìn)程執(zhí)行docker客戶端命令的服務(wù),
不論是docker cli還是對外開放的api最終都是與/var/run/docker.sock進(jìn)行交互,所以把他映射到j(luò)enkins內(nèi)部,jenkins就可以內(nèi)部操作宿主機了。
另外還需映射docker命令(即客戶端命令)和已經(jīng)配置好的配置文件。
??問題:?
??更新配置后執(zhí)行docker info報權(quán)限問題
分析:這應(yīng)該是執(zhí)行docker.sock的權(quán)限問題,我們進(jìn)入宿主機查看docker.sock的權(quán)限
?
以root用戶登錄jenkins,我實際我們用的是jenkins用戶,這樣容器導(dǎo)致用戶權(quán)限過大,不推薦?解決:?
?這里有幾種方式
# 修改方法只需在docker-compose.yml中增加以root登錄即可user: root
將宿主機的docker.sock文件的權(quán)限改為可以讓jenkins的賬號ID=1000的用戶使用即可??【推薦】?
?這里我們粗暴些,直接777權(quán)限
# 第一種:授權(quán)777權(quán)限chmod 777 /var/run/docker.sock# 第二種:授權(quán)other組可讀寫chmod o+rw /var/run/docker.sock
當(dāng)maven構(gòu)建后,直接在jenkins工作目錄構(gòu)建鏡像并推送鏡像到私服
[此處我們以Nexus作為私服演示,對于大型項目harbor優(yōu)勢較大,成本相對也較高,比如內(nèi)置數(shù)據(jù)庫PostgreSQL,而我們一般會使用公共數(shù)據(jù)庫,所以至少要有一個PostgreSQL實例,等等,而Nexus就就相對簡單些,也能和Maven倉庫復(fù)用,成本較低]
??注意:?
?jenkins中運行shell腳本的目錄是jenkins_home/workspace/jobName目錄
# 注意:因為我們要構(gòu)建鏡像而不需要直接啟動容器,所以只需要Dockerfile文件,而暫時不需要使用docker-compose.yml# 1.構(gòu)建鏡像的命令,我們使用docker build,通過-t指定鏡像標(biāo)簽,最后指定Dockerfile的目錄,我們使用當(dāng)前目錄即可docker build -t 10.10.1.199:9082/busybox:v-${branch} .# 注意:jenkins中運行shell腳本當(dāng)前目錄是[workspace/jobName]目錄set -e \&& mv target/*.jar docker/ \ # 拷貝jar包到docker目錄&& cd docker \&& docker build -t demo:v-$branch . \ # 構(gòu)建鏡像,使用分支名作為版本號&& docker tag demo:v-$branch 10.10.1.199:9082/demo:v-$branch \ #給鏡像打標(biāo)簽,準(zhǔn)備推送私服&& docker login -u admin -p jie123456 10.10.1.199:9082 \ #登錄私服hosted倉庫&& docker push 10.10.1.199:9082/demo:v-$branch \#推送鏡像#避免構(gòu)建同一個鏡像產(chǎn)生懸空鏡像,假設(shè)已經(jīng)執(zhí)行了當(dāng)前jenkins job,那么再次執(zhí)行時,本地一定是拉取了鏡像,所以直接構(gòu)建會產(chǎn)生懸空鏡像&& docker image prune -f
???問題:?
??點擊構(gòu)建會報錯,如下:
???原因:?
??就是鏡像名稱定義的不規(guī)范,因為我們使用的git分支名,而分支默認(rèn)都是orgin/開頭,而放到鏡像中斜杠就不合適了,所以我們在git參數(shù)構(gòu)建時,將[orgin/]過濾掉,配置如下:使用正則??orgin/(.*)?
??即可
再次構(gòu)建,就沒問題了。
??解決目標(biāo):?
?既然是jenkins通知宿主機,那么就可能是多個jenkins的job都有這種通知操作,
所以我們不能寫死拉取鏡像的信息,而是通過jenkins的通知將這些參數(shù)傳遞過來,比如拉取鏡像的地址、鏡像標(biāo)識等,并且要處理鏡像重復(fù)等問題。
??思路:?
?怎么讓jenkins給宿主傳參數(shù)呢?說的比較抽象,其實我們之前已經(jīng)做過了,就是ssh。
jenkins的job做完構(gòu)建鏡像并推送后,就可以執(zhí)行ssh連接宿主,然后執(zhí)行腳本,而jenkins的job中可以設(shè)置變量,在執(zhí)行宿主機腳本是可以直接引用過來,而這次腳本比較多,我們就不再jenkins中羅列了,而是直接寫入到shell腳本,讓jenkins直接調(diào)用。
??腳本內(nèi)容:?
?在宿主機目錄創(chuàng)建shell腳本, vi /share/jenkins/demo/script/publish.sh
??注意:?
?腳本權(quán)限問題,我是root創(chuàng)建,并且jenkins中ssh也是使用的root所以沒問題,大家根據(jù)自己情況去酌情處理。
# 定義變量repositoryUrl=$1jobName=$2imageVersion=$3remoteImageName=$repositoryUrl/$jobName:$imageVersion #遠(yuǎn)程私服倉庫的完整鏡像名稱echo "remoteImageName is "$remoteImageName### 檢查是否存在同名鏡像運行的容器,如果存在則需要先刪除,然后再刪除鏡像,然后再拉取新鏡像,最后以新鏡像啟動容器#獲取已啟動的容器IDcontainerId=$(docker ps -a | grep -w "$remoteImageName" | awk "{print $1}")if [ "$containerId" ! = "" ]; thendocker stop $containerId && docker rm $containerIdfi#如果鏡像存在同樣先刪除imageId=$(docker images | grep -w "$repositoryUrl/$jobName" | grep -w "$imageVersion" | awk "{print $3}")if [ "$imageId" ! = "" ]; thendocker rmi -f $imageIdfi#從私服拉取鏡像:如果不允許匿名,則需要先登錄,因為我已經(jīng)設(shè)置允許匿名,所以可以不登錄直接拉取docker login -u admin -p 123456 $repositoryUrldocker pull $remoteImageName#啟動容器:我們之前啟動都是拿docker-compose.yml,但此時因為在宿主機,并沒有這么一個docker-compose.yml文件,#所以我們可以直接使用docker run運行容器docker run -d \-p 9099:8080 \-v /etc/timezone:/etc/timezone:ro \-v /etc/localtime:/etc/localtime:ro \--network devops \--privileged=true \--name $jobName $remoteImageName
??解釋:?
?其實我們jenkins容器已經(jīng)拿到宿主機的docker執(zhí)行權(quán)了,那這個文件不傳到宿主機執(zhí)行,在本地容器執(zhí)行也是可以的,但由于角色不同,該腳本由docker服務(wù)器執(zhí)行更合適,并且如果是docker集群那么容器執(zhí)行會很麻煩。
??優(yōu)化:?
??簡單起見,或者說為了隔離、可擴展,我們把這個腳本文件放到springboot中,讓jenkins幫我們遠(yuǎn)程拷貝到宿主機的固定目錄,然后執(zhí)行
參數(shù)配置:我們腳本里引用了那么幾個參數(shù),哪里來的?
jenkins我們知道可以參數(shù)化構(gòu)建,比如我們用的git參數(shù),當(dāng)然他也可以指定普通參數(shù),如下,我們在job中增加這些參數(shù):
[當(dāng)然也可以直接寫到腳本執(zhí)行的后邊,我這里就只配一個示意一下,其他參數(shù)直接傳遞]
標(biāo)簽: 因為我們 當(dāng)前目錄