在容器上构建持续部署及最佳实践初探
作者 |?倚天碼農(nóng)
責(zé)編|?徐威龍
封圖|?CSDN下載于視覺(jué)中國(guó)
要想理解持續(xù)集成和持續(xù)部署,先要了解它的部分組成,以及各個(gè)組成部分之間的關(guān)系。下面這張圖是我見(jiàn)過(guò)的最簡(jiǎn)潔、清晰的持續(xù)部署和集成的關(guān)系圖。
圖源:sonatype.com
持續(xù)部署
如圖所示,開(kāi)發(fā)的流程是這樣的:
程序員從源碼庫(kù)(Source Control)中下載源代碼,編寫(xiě)程序,完成后提交代碼到源碼庫(kù),持續(xù)集成(Continuous Integration)工具從源碼庫(kù)中下載源代碼,編譯源代碼,然后提交到運(yùn)行庫(kù)(Repository),然后持續(xù)交付(Continuous Delivery)工具從運(yùn)行庫(kù)(Repository)中下載代碼,生成發(fā)布版本,并發(fā)布到不同的運(yùn)行環(huán)境(例如DEV,QA,UAT, PROD)。
圖中,左邊的部分是持續(xù)集成,它主要跟開(kāi)發(fā)和程序員有關(guān);右邊的部分是持續(xù)部署,它主要跟測(cè)試和運(yùn)維有關(guān)。持續(xù)交付(Continuous Delivery)又叫持續(xù)部署(Continuous Deployment),它們?nèi)绻?xì)分的話還是有一點(diǎn)區(qū)別的,但我們這里不分得那么細(xì),統(tǒng)稱為持續(xù)部署。本文側(cè)重講解持續(xù)部署。
持續(xù)集成和部署有下面幾個(gè)主要參與者:
源代碼庫(kù):負(fù)責(zé)存儲(chǔ)源代碼,常用的有Git和SVN。
持續(xù)集成與部署工具:負(fù)責(zé)自動(dòng)編譯和打包以及把可運(yùn)行程序存儲(chǔ)到可運(yùn)行庫(kù)。比較流行的有Jenkins,GitLab,Travis CI,CircleCI 等
庫(kù)管理器(Repository Manager):也就是圖中的Repository,我們又叫運(yùn)行庫(kù),負(fù)責(zé)管理程序組件。最常用的是Nexus。它是一個(gè)私有庫(kù),它的作用是管理程序組件。
庫(kù)管理器有兩個(gè)職能:
管理第三方庫(kù):應(yīng)用程序常常要用到很多第三方庫(kù),并且不同的技術(shù)棧需要的庫(kù)不同,它們經(jīng)常是存放在第三方公共庫(kù)里,管理起來(lái)不是很方便。一般公司會(huì)建立一個(gè)私有管理庫(kù),來(lái)集中統(tǒng)一管理各種第三方軟件,例如它既可以做為Maven庫(kù)(Java),也可以做為鏡像庫(kù)(Docker),還可以做為NPM庫(kù)(JavaScript),來(lái)保證公司軟件的規(guī)范性。
管理內(nèi)部程序的交付:所有公司在各種環(huán)境(例如DEV,QA,UAT, PROD)發(fā)布的程序都由它來(lái)管理,并賦予統(tǒng)一的版本號(hào),這樣任何交付都有據(jù)可查,同時(shí)便利于程序回滾。
持續(xù)部署步驟
各個(gè)公司對(duì)持續(xù)部署(Continuous Deployment)的要求不同,它的步驟也不相同,但主要包括下面幾個(gè)步驟:
下載源碼:從源代碼庫(kù)(例如github)中下載源代碼。
編譯代碼:編譯語(yǔ)言都需要有這一步
測(cè)試:對(duì)程序進(jìn)行測(cè)試。
生成鏡像:這里包含兩個(gè)步驟,一個(gè)是創(chuàng)建鏡像,另一個(gè)是存儲(chǔ)鏡像到鏡像庫(kù)。
部署鏡像:把生成的鏡像部署到容器上
上面的流程是廣義的持續(xù)部署流程,狹義的流程是從庫(kù)管理器中檢索可運(yùn)行程序,這樣就省去了下載源碼和編譯代碼環(huán)節(jié),改由直接從庫(kù)管理器中下載可執(zhí)行程序。但由于并不是每個(gè)公司都有單獨(dú)的庫(kù)管理器,這里就采用了廣義的持續(xù)部署流程,這樣對(duì)每個(gè)公司都適用。
持續(xù)部署實(shí)例
下面我們通過(guò)一個(gè)具體的實(shí)例來(lái)展示如何完成持續(xù)部署。我們用Jenkins來(lái)做為持續(xù)部署工具,用它部署一個(gè)Go程序到k8s環(huán)境。
我們的流程基本是上面講的狹義流程,但由于沒(méi)有Nexus,我們稍微變通了一下,改由從源碼庫(kù)直接下載源程序,步驟如下:
下載源碼:從github下載源代碼到Jenkins的運(yùn)行環(huán)境
測(cè)試:這一步暫時(shí)沒(méi)有實(shí)際內(nèi)容
生成鏡像:創(chuàng)建鏡像,并上傳到Docker hub。
部署鏡像:將生成的鏡像部署到k8s
在創(chuàng)建Jenkins項(xiàng)目之前,先要做些準(zhǔn)備工作:
建立Docker Hub賬戶
需要在Docker Hub上創(chuàng)建賬戶和鏡像庫(kù),這樣才能上傳鏡像。具體過(guò)程這里就不詳細(xì)講解了,請(qǐng)查閱相關(guān)資料。
在Jenkins上創(chuàng)建憑證(Credentials)
需要設(shè)置訪問(wèn)Docker hub的用戶和口令,以后在Jenkins腳本里可以通過(guò)變量的方式進(jìn)行引用,這樣口令就不會(huì)以明碼的方式出現(xiàn)在程序里。
用管理員賬戶登錄 Jenkins主頁(yè)面后,找到 Manage Jenkins-》Credentials-》System -》Global Credentials -》Add Credentials,如下圖所示輸入你的Docker Hub的用戶名和口令。“ID”是后面你要在腳本里引用的。
創(chuàng)建預(yù)裝Docker和k8s的Jenkins鏡像
Jenkins的默認(rèn)容器里面沒(méi)有Docker和k8s,因此我們需要在Jenkins鏡像的基礎(chǔ)上重新創(chuàng)建新的鏡像,后面還會(huì)詳細(xì)講解。
下面是鏡像文件(Dockerfile-modified-jenkins)
FROM?jenkins/jenkins:ltsUSER?rootENV?DOCKERVERSION=19.03.4RUN?curl?-fsSLO?https://download.docker.com/linux/static/stable/x86_64/docker-${DOCKERVERSION}.tgz?\&&?tar?xzvf?docker-${DOCKERVERSION}.tgz?--strip?1?\-C?/usr/local/bin?docker/docker?\&&?rm?docker-${DOCKERVERSION}.tgzRUN?curl?-LO?https://storage.googleapis.com/kubernetes-release/release/$(curl?-s?https://storage.googleapis.com/kubernetes-release/release/stable.txt)/bin/linux/amd64/kubectl?\&&?chmod??x?./kubectl?\&&?mv?./kubectl?/usr/local/bin/kubectl上面的鏡像在“jenkins/jenkins:lts”的基礎(chǔ)上又安裝了Docker和kubectl,這樣就支持這兩個(gè)軟件了。鏡像里使用的是docker的19.03.4版本。這里裝的只是“Docker CLI”,沒(méi)有Docker引擎。用的時(shí)候還是要把虛擬機(jī)的卷掛載到容器上,使用虛機(jī)的Docker引擎。因此最好保證容器里的Docker版本和虛機(jī)的Docker版本一致。
使用如下命令查看Docker版本:
vagrant@ubuntu-xenial:/$?docker?version詳細(xì)情況請(qǐng)參見(jiàn)Configure a CI/CD pipeline with Jenkins on Kubernetes:https://developer.ibm.com/tutorials/configure-a-cicd-pipeline-with-jenkins-on-kubernetes/
準(zhǔn)備工作已經(jīng)完成,現(xiàn)在要正式創(chuàng)建Jenkins項(xiàng)目:
Jenkins腳本:
項(xiàng)目的創(chuàng)建是在Jenkins的主頁(yè)上來(lái)完成,它的名字是“jenkins-k8sdemo”,它的最主要部分是腳本代碼,它也跟Go程序存放在相同的源碼庫(kù)中,文件的名字也是“jenkins-k8sdemo”。項(xiàng)目的腳本頁(yè)面如下圖所示。
如果你不熟悉安裝和創(chuàng)建Jenkins項(xiàng)目,請(qǐng)參閱在k8s上安裝Jenkins及常見(jiàn)問(wèn)題
下面就是jenkins-k8sdemo腳本文件:
def?POD_LABEL?=?"k8sdemopod-${UUID.randomUUID().toString()}" podTemplate(label:?POD_LABEL,?cloud:?'kubernetes',?containers:?[containerTemplate(name:?'modified-jenkins',?image:?'jfeng45/modified-jenkins:1.0',?ttyEnabled:?true,?command:?'cat')],volumes:?[hostPathVolume(mountPath:?'/var/run/docker.sock',?hostPath:?'/var/run/docker.sock')])?{node(POD_LABEL)?{def?kubBackendDirectory?=?"/script/kubernetes/backend"stage('Checkout')?{container('modified-jenkins')?{sh?'echo?get?source?from?github'git?'https://github.com/jfeng45/k8sdemo'}}stage('Build?image')?{def?imageName?=?"jfeng45/jenkins-k8sdemo:${env.BUILD_NUMBER}"def?dockerDirectory?=?"${kubBackendDirectory}/docker/Dockerfile-k8sdemo-backend"container('modified-jenkins')?{withCredentials([[$class:?'UsernamePasswordMultiBinding',credentialsId:?'dockerhub',usernameVariable:?'DOCKER_HUB_USER',passwordVariable:?'DOCKER_HUB_PASSWORD']])?{sh?"""docker?login?-u?${DOCKER_HUB_USER}?-p?${DOCKER_HUB_PASSWORD}docker?build?-f?${WORKSPACE}${dockerDirectory}?-t?${imageName}?.docker?push?${imageName}"""}}}stage('Deploy')?{container('modified-jenkins')?{sh?"kubectl?apply?-f?${WORKSPACE}${kubBackendDirectory}/backend-deployment.yaml"sh?"kubectl?apply?-f?${WORKSPACE}${kubBackendDirectory}/backend-service.yaml"}}} }我們逐段看一下代碼:
設(shè)定容器鏡像:
podTemplate(label:?POD_LABEL,?cloud:?'kubernetes',?containers:?[containerTemplate(name:?'modified-jenkins',?image:?'jfeng45/modified-jenkins:1.0',?ttyEnabled:?true,?command:?'cat')],volumes:?[hostPathVolume(mountPath:?'/var/run/docker.sock',?hostPath:?'/var/run/docker.sock')])這里設(shè)定Jenkins子節(jié)點(diǎn)Pod的容器鏡像,用的是“jfeng45/modified-jenkins:1.0”,也就是我們?cè)谏蟼€(gè)步驟創(chuàng)建的。所有的腳本里的步驟(stage)都用的是這個(gè)鏡像。“volumes:”用來(lái)掛載卷到Jenkins容器中,這樣Jenkins子節(jié)點(diǎn)就可以使用虛機(jī)的Docker引擎。
關(guān)于Jenkins腳本命令和設(shè)置掛載卷請(qǐng)參閱jenkinsci/kubernetes-plugin
https://github.com/jenkinsci/kubernetes-plugin
創(chuàng)建鏡像:
下面的代碼生成Go程序的Docker鏡像文件,這里我們沒(méi)有用Docker插件,而是直接調(diào)用Docker命令,它的好處后面會(huì)講到。它引用了我們前面設(shè)置的“Docker hub”的憑證去訪問(wèn)Docker庫(kù)。在腳本里,我們先登錄到“Docker hub”,然后使用上一步從GitHub下載的源代碼來(lái)創(chuàng)建鏡像,最后上傳鏡像到“Docker hub”。其中
WORKSPACE”是Jenkins預(yù)定義變量,從GitHub下載的源代碼就存放在“{WORKSPACE}”里。
stage('Build?image')?{def?imageName?=?"jfeng45/jenkins-k8sdemo:${env.BUILD_NUMBER}"def?dockerDirectory?=?"${kubBackendDirectory}/docker/Dockerfile-k8sdemo-backend"container('modified-jenkins')?{withCredentials([[$class:?'UsernamePasswordMultiBinding',credentialsId:?'dockerhub',usernameVariable:?'DOCKER_HUB_USER',passwordVariable:?'DOCKER_HUB_PASSWORD']])?{sh?"""docker?login?-u?${DOCKER_HUB_USER}?-p?${DOCKER_HUB_PASSWORD}docker?build?-f?${WORKSPACE}${dockerDirectory}?-t?${imageName}?.docker?push?${imageName}"""}}}如果你想了解Jenkins命令詳情,請(qǐng)參閱Set Up a Jenkins CI/CD Pipeline with Kubernetes
https://akomljen.com/set-up-a-jenkins-ci-cd-pipeline-with-kubernetes/
我們這里并沒(méi)有重新生成Go程序的鏡像文件,而是復(fù)用了以前就有的k8s創(chuàng)建Go程序的鏡像文件,Go程序的鏡像文件路徑是“\script\kubernetes\backend\docker\Dockerfile-k8sdemo-backend”。
它的代碼如下。后面還會(huì)講到這樣做的好處。
#?vagrant@ubuntu-xenial:~/app/k8sdemo/script/kubernetes/backend$ #?docker?build?-t?k8sdemo-backend?.FROM?golang:latest?as?builder#?Set?the?Current?Working?Directory?inside?the?container WORKDIR?/appCOPY?go.mod?go.sum?./RUN?go?mod?downloadCOPY?.?.WORKDIR?/app/cmd#?Build?the?Go?app #RUN?CGO_ENABLED=0?GOOS=linux?go?build?-a?-installsuffix?cgo?-o?main.exeRUN?go?build?-o?main.exe########?Start?a?new?stage?from?scratch?####### FROM?alpine:latestRUN?apk?--no-cache?add?ca-certificatesWORKDIR?/root/RUN?mkdir?/lib64?&&?ln?-s?/lib/libc.musl-x86_64.so.1?/lib64/ld-linux-x86-64.so.2#?Copy?the?Pre-built?binary?file?from?the?previous?stage COPY?--from=builder?/app/cmd/main.exe?.#?Command?to?run?the?executable #?CMD?exec?/bin/bash?-c?"trap?:?TERM?INT;?sleep?infinity?&?wait" CMD?關(guān)于Go鏡像文件詳情,請(qǐng)參閱創(chuàng)建優(yōu)化的Go鏡像文件以及踩過(guò)的坑
https://blog.csdn.net/weixin_38748858/article/details/102714799
部署鏡像:
下面部署Go程序到k8s上,這里也沒(méi)有用kubectl插件,而是直接用kubectl命令調(diào)用已經(jīng)存在的k8s的部署和服務(wù)配置文件(文件里會(huì)引用生成的Go鏡像),它的好處后面也會(huì)講到。
?stage('Deploy')?{container('modified-jenkins')?{sh?"kubectl?apply?-f?${WORKSPACE}${kubBackendDirectory}/backend-deployment.yaml"sh?"kubectl?apply?-f?${WORKSPACE}${kubBackendDirectory}/backend-service.yaml"}}關(guān)于k8s的部署和服務(wù)配置文件詳情,請(qǐng)參閱把應(yīng)用程序遷移到k8s需要修改什么?
https://blog.csdn.net/weixin_38748858/article/details/102758381
為什么沒(méi)用Declarative?
用腳本來(lái)寫(xiě)Pipeline有兩種方法,“Scripted Pipleline”和“Declarative Pipleline”,這里用的是第一種方法。“Declarative Pipleline”是新的方法,之所以沒(méi)用它,是因?yàn)殚_(kāi)始用的是Declarative模式但沒(méi)調(diào)出來(lái),然后就改用“Scripted Pipleline”,結(jié)果成功了。后來(lái)才發(fā)現(xiàn)設(shè)置Declarative的方法,特別是如何掛載卷,但看了一下,比起“Scripted Pipleline”要復(fù)雜不少,就偷了一下懶,沒(méi)有再改。
如果你想知道怎樣在Declarative模式下設(shè)置掛載卷,請(qǐng)參閱Jenkins Pipeline Kubernetes Agent shared Volumes?
https://devops.stackexchange.com/questions/4695/jenkins-pipeline-kubernetes-agent-shared-volumes
自動(dòng)執(zhí)行項(xiàng)目:
現(xiàn)在的Jenkins中的項(xiàng)目需要手動(dòng)啟動(dòng),如果你需要自動(dòng)啟動(dòng)項(xiàng)目的話就要?jiǎng)?chuàng)建webhook,GitHub和dockerhub都支持webhook,在它們的頁(yè)面上都有設(shè)置選項(xiàng)。“webhook”是一個(gè)反向調(diào)用的URL,每當(dāng)有新的代碼或鏡像提交時(shí),GitHub和dockerhub都會(huì)調(diào)用這個(gè)URL,URL被設(shè)置成Jenkins的項(xiàng)目地址,這樣相關(guān)的項(xiàng)目就會(huì)自動(dòng)啟動(dòng)。
檢驗(yàn)結(jié)果
現(xiàn)在Jenkins的項(xiàng)目就完全配置好了,需要運(yùn)行項(xiàng)目,檢驗(yàn)結(jié)果。啟動(dòng)項(xiàng)目后,
查看“Console Output”,下面是部分輸出(全部輸出太長(zhǎng),請(qǐng)看附錄),說(shuō)明部署成功。
。。。kubectl?apply?-f?/home/jenkins/workspace/test1/script/kubernetes/backend/backend-deployment.yaml deployment.apps/k8sdemo-backend-deployment?created [Pipeline]?sh??kubectl?apply?-f?/home/jenkins/workspace/test1/script/kubernetes/backend/backend-service.yaml service/k8sdemo-backend-service?created [Pipeline]?} [Pipeline]?//?container [Pipeline]?} [Pipeline]?//?stage [Pipeline]?} [Pipeline]?//?node [Pipeline]?} [Pipeline]?//?podTemplate [Pipeline]?End?of?Pipeline Finished:?SUCCESS查看運(yùn)行結(jié)果:
獲得Pod名字:
vagrant@ubuntu-xenial:/home$?kubectl?get?pod NAME???????????????????????????????????????????READY???STATUS????RESTARTS???AGE envar-demo?????????????????????????????????????1/1?????Running???15?????????32d k8sdemo-backend-deployment-6b99dc6b8c-8kxt9????1/1?????Running???0??????????50s k8sdemo-database-deployment-578fc88c88-mm6x8???1/1?????Running???9??????????20d k8sdemo-jenkins-deployment-675dd574cb-r57sb????1/1?????Running???0??????????2d23h登錄Pod并運(yùn)行程序:
vagrant@ubuntu-xenial:/home$?kubectl?exec?-ti?k8sdemo-backend-deployment-6b99dc6b8c-8kxt9?--?/bin/sh ~?#?./main.exe DEBU[0000]?connect?to?database DEBU[0000]?dataSourceName:dbuser:dbuser@tcp(k8sdemo-database-service:3306)/service_config?charset=utf8 DEBU[0000]?FindAll() DEBU[0000]?created=2019-10-21 DEBU[0000]?find?user:{1?Tony?IT?2019-10-21} DEBU[0000]?find?user?list:[{1?Tony?IT?2019-10-21}] DEBU[0000]?user?lst:[{1?Tony?IT?2019-10-21}]結(jié)果正確。
Jenkins原理
實(shí)例部分已經(jīng)結(jié)束,下面來(lái)探討最佳實(shí)踐。在這之前,先要搞清楚Jenkins的原理。
可執(zhí)行命令
我一直有一個(gè)問(wèn)題就是那些命令是Jenkins可以通過(guò)shell執(zhí)行的?Jenkins和Docker、k8s不同,后者有自己的一套命令,只要把它們學(xué)會(huì)了就行了。而Jenkins是通過(guò)與別的系統(tǒng)集成來(lái)工作的,因此它的可執(zhí)行命令與其他系統(tǒng)有關(guān),這導(dǎo)致了你很難知道那些命令是可以執(zhí)行的,那些不行。你需要弄懂它的原理,才能得到答案。當(dāng)Jenkins執(zhí)行腳本時(shí),主節(jié)點(diǎn)會(huì)自動(dòng)生成一個(gè)子節(jié)點(diǎn)(Docker容器),所有的Jenkins命令都是在這個(gè)容器里執(zhí)行的。所以能執(zhí)行的命令與容器密切相關(guān)。一般來(lái)講,你可以通過(guò)shell來(lái)運(yùn)行Linux命令。那下面的問(wèn)題就來(lái)了:
1、為什么我不能用Bash?
因?yàn)槟闶褂玫淖庸?jié)點(diǎn)的容器可能使用的是精簡(jiǎn)版的Linux,例如Alpine,它是沒(méi)有Bash的。
2、為什么我不能運(yùn)行Docker命令或Kubectl?
因?yàn)樗哪J(rèn)容器是jenkinsci/jnlp-slave,而它里面沒(méi)有預(yù)裝Docker或kubectl。你可以不使用默認(rèn)容器,而是指定你自己的容器,并在其中預(yù)裝上述軟件,那么就可以執(zhí)行這些命令了。
如何共享文件
一個(gè)Jenkins項(xiàng)目通常要分成幾個(gè)步驟(stage)來(lái)完成,例如你下載的源碼要在幾個(gè)步驟之間共享,那怎么共享呢?Jenkins為每個(gè)項(xiàng)目分配了一個(gè)WORKSPACE(磁盤(pán)空間), 里面存儲(chǔ)了所有從源碼庫(kù)和其他地方下載的文件,不同stage之間可以通過(guò)WORKSPACE來(lái)共享文件。
關(guān)于WORKSPACE詳情,請(qǐng)參閱Jenkins Project Artifacts and Workspace
https://stackoverflow.com/questions/39397329/jenkins-project-artifacts-and-workspace
最佳實(shí)踐
要總結(jié)最佳實(shí)踐就要理解持續(xù)部署在整個(gè)開(kāi)發(fā)流程中的作用和位置,它主要起一個(gè)串接各個(gè)環(huán)節(jié)的作用。而程序的部署是由k8s和Docker來(lái)完成的,因此程序部署的腳本也都在k8s中,并由k8s來(lái)維護(hù)。我們不想在Jenkins里再維護(hù)一套類似的腳本,因此最好的辦法是把Jenkins的腳本壓縮到最小,盡可能多地直接調(diào)用k8s的腳本。
另外能寫(xiě)代碼就不要在頁(yè)面上配置,只有代碼是可以重復(fù)執(zhí)行并保證穩(wěn)定結(jié)果的,頁(yè)面配置不能移植,而且不能保證每次配置都產(chǎn)生一樣的結(jié)果。
盡量少使用插件
Jenkins有許多插件,基本上你想要完成什么功能都有相應(yīng)的插件。例如你需要使用Docker功能,就有“Docker Pipeline”插件,你要使用k8s功能就有“kubectl”插件。但它會(huì)帶來(lái)很多的問(wèn)題。
第一,每個(gè)插件都有他自己的設(shè)置方式(一般要在Jenkins插件頁(yè)面進(jìn)行設(shè)置),但這種設(shè)置是與其他持續(xù)部署工具不兼容的。如果以后你要遷移到其他持續(xù)部署工具,這些設(shè)置都需要廢棄。
第二,每個(gè)插件都有自己的命令格式,因此你需要另外學(xué)習(xí)一套新的命令。
第三,這些插件往往只支持部分功能,使你能做的事情受到了限制。
例如,你需要?jiǎng)?chuàng)建一個(gè)Docker鏡像文件,命令如下,它將創(chuàng)建一個(gè)名為"jfeng45/jenkins-k8sdemo"的鏡像,鏡像的默認(rèn)文件是在項(xiàng)目的根目錄下的Dockerfile。
app?=?docker.build("jfeng45/jenkins-k8sdemo")但創(chuàng)建Docker鏡像文件命令有許多參數(shù)選項(xiàng),例如,你的鏡像文件名不是Dockerfile,并且目錄不是在項(xiàng)目根目錄下,應(yīng)如何寫(xiě)呢?這在以前的版本是不支持的,后來(lái)的版本支持了,但畢竟不太方便,還要學(xué)新的命令。最好的辦法是能直接使用Docker命令,這樣就完美的解決了上面說(shuō)的三個(gè)問(wèn)題。答案就在前面講的Jenkins原理里,其實(shí)絕大多數(shù)插件都是不需要的,你只要自己創(chuàng)建一個(gè)Jenkins子節(jié)點(diǎn)容器,并安裝相應(yīng)的軟件就能圓滿解決。
下面是使用插件的腳本和不使用的對(duì)比,不使用的看起來(lái)更長(zhǎng),那時(shí)因?yàn)槭褂貌寮哪_本和Jenkins里的憑證設(shè)置有更好的集成,而不使用的腳本沒(méi)有。但除了這個(gè)小缺點(diǎn),其他方面不使用的腳本都要遠(yuǎn)遠(yuǎn)優(yōu)于使用插件的。
使用插件的腳本(用插件命令):
stage('Create?Docker?images')?{container('docker')?{app?=?docker.build("jfeng45/codedemo",?"-f?${WORKSPACE}/script/kubernetes/backend/docker/Dockerfile-k8sdemo-test?.")docker.withRegistry('',?'dockerhub')?{//?Push?image?and?tag?it?with?our?build?number?for?versioning?purposes.app.push("${env.BUILD_NUMBER}")}}}不使用插件的腳本(直接用Docker命令):
stage('Create?a?d?ocker?image')?{def?imageName?=?"jfeng45/codedemo:${env.BUILD_NUMBER}"def?dockerDirectory?=?"${kubBackendDirectory}/docker/Dockerfile-k8sdemo-backend"container('modified-jenkins')?{withCredentials([[$class:?'UsernamePasswordMultiBinding',credentialsId:?'dockerhub',usernameVariable:?'DOCKER_HUB_USER',passwordVariable:?'DOCKER_HUB_PASSWORD']])?{sh?"""docker?login?-u?${DOCKER_HUB_USER}?-p?${DOCKER_HUB_PASSWORD}docker?build?-f?${WORKSPACE}${dockerDirectory}?-t?${imageName}?.docker?push?${imageName}"""}}}盡量多使用k8s和Dcoker
例如我們要?jiǎng)?chuàng)建一個(gè)應(yīng)用程序的鏡像,我們可以寫(xiě)一個(gè)Docker文件,并在Jenkins腳本里調(diào)用這個(gè)Docker文件來(lái)創(chuàng)建,也可以寫(xiě)一個(gè)Jenkins腳本,在腳本里來(lái)創(chuàng)建鏡像。比較好的方法是前者。因?yàn)镈ocker和k8s都是事實(shí)上的標(biāo)準(zhǔn),移植起來(lái)很方便。
Jenkins腳本的代碼越少越好
如果你認(rèn)同前面兩個(gè)原則,那么這一條就是順理成章的,原因也和上面是一樣的。
常見(jiàn)問(wèn)題:
1.變量要放在雙引號(hào)里
Jenkins的腳本即可以使用單引號(hào)也可以使用雙引號(hào),但如果你在引號(hào)里引用了變量,那么就要使用雙引號(hào)。
正確的命令:
sh?"kubectl?apply?-f?${WORKSPACE}${kubBackendDirectory}/backend-deployment.yaml"錯(cuò)誤的命令:
sh?'kubectl?apply?-f?${WORKSPACE}${kubBackendDirectory}/backend-deployment.yaml'2.docker not found
如果Jenkins的容器里沒(méi)有Docker,但你又調(diào)用了Docker命令,那么“Console Output”里就會(huì)有如下錯(cuò)誤:
??docker?inspect?-f?.?k8sdemo-backend:latest /var/jenkins_home/workspace/k8sdec@2@tmp/durable-01e26997/script.sh:?1:?????/var/jenkins_home/workspace/k8sdec@2@tmp/durable-01e26997/script.sh:?docker:?????not?found3.Jenkins宕機(jī)了
在調(diào)試Jenkins時(shí),我新創(chuàng)建了一個(gè)鏡像文件并上傳到“Docker hub”之后就發(fā)現(xiàn)Jenkins宕機(jī)了。檢查了Pod,發(fā)現(xiàn)了問(wèn)題,k8s找不到Jenkins的鏡像文件了(鏡像文件從磁盤(pán)上消失了)。因?yàn)镴enkins的部署文件的設(shè)置是“imagePullPolicy: Never”,所以一旦鏡像沒(méi)有了,它不會(huì)自動(dòng)重新下載。后來(lái)找到了原因,Vagrant的默認(rèn)磁盤(pán)大小是10G,如果空間不夠,它會(huì)自動(dòng)從磁盤(pán)上刪除其他鏡像文件,騰出空間,結(jié)果就把Jenkins的鏡像文件給刪了,解決方案是擴(kuò)充Vagrant的磁盤(pán)大小。
下面是修改之后的Vagrantfile,把磁盤(pán)空間改成了16G。
Vagrant.configure(2)?do?|config|。。。config.vm.box?=?"ubuntu/xenial64"config.disksize.size?=?'16GB'。。。 end詳情請(qǐng)見(jiàn)How can I increase disk size on a Vagrant VM?
https://askubuntu.com/questions/317338/how-can-i-increase-disk-size-on-a-vagrant-vm
源碼
完整源碼的github鏈接:https://github.com/jfeng45/k8sdemo
下面是項(xiàng)目中與本文有關(guān)的部分:
附錄:
下面是Jenkins項(xiàng)目運(yùn)行后的完整的“Console Output”:
Running?in?Durability?level:?MAX_SURVIVABILITY [Pipeline]?Start?of?Pipeline [Pipeline]?podTemplate [Pipeline]?{ [Pipeline]?node Still?waiting?to?schedule?task ‘k8sdemopod-030ed100-cb28-4770-b6de-c491970e5baa-twb8s-k9pn3’?is?offline Agent?k8sdemopod-030ed100-cb28-4770-b6de-c491970e5baa-twb8s-k9pn3?is?provisioned?from?template?Kubernetes?Pod?Template Agent?specification?[Kubernetes?Pod?Template]?(k8sdemopod-030ed100-cb28-4770-b6de-c491970e5baa):? *?[modified-jenkins]?jfeng45/modified-jenkins:1.0Running?on?k8sdemopod-030ed100-cb28-4770-b6de-c491970e5baa-twb8s-k9pn3?in?/home/jenkins/workspace/jenkins-k8sdemo [Pipeline]?{ [Pipeline]?stage [Pipeline]?{?(Checkout) [Pipeline]?container [Pipeline]?{ [Pipeline]?shecho?get?source?from?github get?source?from?github [Pipeline]?git No?credentials?specified Cloning?the?remote?Git?repository Cloning?repository?https://github.com/jfeng45/k8sdemo>?git?init?/home/jenkins/workspace/jenkins-k8sdemo?#?timeout=10 Fetching?upstream?changes?from?https://github.com/jfeng45/k8sdemo>?git?--version?#?timeout=10>?git?fetch?--tags?--force?--progress?--?https://github.com/jfeng45/k8sdemo??refs/heads/*:refs/remotes/origin/*>?git?config?remote.origin.url?https://github.com/jfeng45/k8sdemo?#?timeout=10>?git?config?--add?remote.origin.fetch??refs/heads/*:refs/remotes/origin/*?#?timeout=10>?git?config?remote.origin.url?https://github.com/jfeng45/k8sdemo?#?timeout=10 Fetching?upstream?changes?from?https://github.com/jfeng45/k8sdemo>?git?fetch?--tags?--force?--progress?--?https://github.com/jfeng45/k8sdemo??refs/heads/*:refs/remotes/origin/* Checking?out?Revision?90c57dcd8ff362d01631a54125129090b503364b?(refs/remotes/origin/master)>?git?rev-parse?refs/remotes/origin/master^{commit}?#?timeout=10>?git?rev-parse?refs/remotes/origin/origin/master^{commit}?#?timeout=10>?git?config?core.sparsecheckout?#?timeout=10>?git?checkout?-f?90c57dcd8ff362d01631a54125129090b503364b>?git?branch?-a?-v?--no-abbrev?#?timeout=10>?git?checkout?-b?master?90c57dcd8ff362d01631a54125129090b503364b Commit?message:?"added?jenkins?continous?deployment?files" [Pipeline]?}>?git?rev-list?--no-walk?90c57dcd8ff362d01631a54125129090b503364b?#?timeout=10 [Pipeline]?//?container [Pipeline]?} [Pipeline]?//?stage [Pipeline]?stage [Pipeline]?{?(Build?image) [Pipeline]?container [Pipeline]?{ [Pipeline]?withCredentials Masking?supported?pattern?matches?of?$DOCKER_HUB_USER?or?$DOCKER_HUB_PASSWORD [Pipeline]?{ [Pipeline]?shdocker?login?-u?****?-p?**** WARNING!?Using?--password?via?the?CLI?is?insecure.?Use?--password-stdin. WARNING!?Your?password?will?be?stored?unencrypted?in?/home/jenkins/.docker/config.json. Configure?a?credential?helper?to?remove?this?warning.?See https://docs.docker.com/engine/reference/commandline/login/#credentials-storeLogin?Succeededdocker?build?-f?/home/jenkins/workspace/jenkins-k8sdemo/script/kubernetes/backend/docker/Dockerfile-k8sdemo-backend?-t?****/jenkins-k8sdemo:7?. Sending?build?context?to?Docker?daemon??218.6kBStep?1/13?:?FROM?golang:latest?as?builder--->?dc7582e06f8e Step?2/13?:?WORKDIR?/app--->?Running?in?c5770704333e Removing?intermediate?container?c5770704333e--->?73445078c82d Step?3/13?:?COPY?go.mod?go.sum?./--->?6762344c7bc8 Step?4/13?:?RUN?go?mod?download--->?Running?in?56a1f253c3f5 [91mgo:?finding?github.com/davecgh/go-spew?v1.1.1 [0m[91mgo:?finding?github.com/go-sql-driver/mysql?v1.4.1 [0m[91mgo:?finding?github.com/konsorten/go-windows-terminal-sequences?v1.0.1 [0m[91mgo:?finding?github.com/pkg/errors?v0.8.1 [0m[91mgo:?finding?github.com/pmezard/go-difflib?v1.0.0 [0m[91mgo:?finding?github.com/sirupsen/logrus?v1.4.2 [0m[91mgo:?finding?github.com/stretchr/objx?v0.1.1 [0m[91mgo:?finding?github.com/stretchr/testify?v1.2.2 [0m[91mgo:?finding?golang.org/x/sys?v0.0.0-20190422165155-953cdadca894 [0mRemoving?intermediate?container?56a1f253c3f5--->?455ef98244eb Step?5/13?:?COPY?.?.--->?092444c8a5ef Step?6/13?:?WORKDIR?/app/cmd--->?Running?in?558240a3dcb1 Removing?intermediate?container?558240a3dcb1--->?044e01b8184b Step?7/13?:?RUN?go?build?-o?main.exe--->?Running?in?648899ba522c Removing?intermediate?container?648899ba522c--->?69f6652bc706 Step?8/13?:?FROM?alpine:latest--->?965ea09ff2eb Step?9/13?:?RUN?apk?--no-cache?add?ca-certificates--->?Using?cache--->?a27265887a1e Step?10/13?:?WORKDIR?/root/--->?Using?cache--->?b9c048c97f07 Step?11/13?:?RUN?mkdir?/lib64?&&?ln?-s?/lib/libc.musl-x86_64.so.1?/lib64/ld-linux-x86-64.so.2--->?Using?cache--->?95a2b77e3e0a Step?12/13?:?COPY?--from=builder?/app/cmd/main.exe?.--->?Using?cache--->?c5dc6dfdf037 Step?13/13?:?CMD?exec?/bin/sh?-c?"trap?:?TERM?INT;?(while?true;?do?sleep?1000;?done)?&?wait"--->?Using?cache--->?b141558cb0f3 Successfully?built?b141558cb0f3 Successfully?tagged?****/jenkins-k8sdemo:7docker?push?****/jenkins-k8sdemo:7 The?push?refers?to?repository?[docker.io/****/jenkins-k8sdemo] 0e5809dd35f7:?Preparing 8861feb71103:?Preparing 5b63d4bd63b4:?Preparing 77cae8ab23bf:?Preparing 77cae8ab23bf:?Mounted?from?****/codedemo 8861feb71103:?Mounted?from?****/codedemo 5b63d4bd63b4:?Mounted?from?****/codedemo 0e5809dd35f7:?Mounted?from?****/codedemo 7:?digest:?sha256:95c780bb08793712cd2af668c9d4529e17c99e58dfb05ffe8df6a762f245ce10?size:?1156 [Pipeline]?} [Pipeline]?//?withCredentials [Pipeline]?} [Pipeline]?//?container [Pipeline]?} [Pipeline]?//?stage [Pipeline]?stage [Pipeline]?{?(Deploy) [Pipeline]?container [Pipeline]?{ [Pipeline]?shkubectl?apply?-f?/home/jenkins/workspace/jenkins-k8sdemo/script/kubernetes/backend/backend-deployment.yaml deployment.apps/k8sdemo-backend-deployment?created [Pipeline]?shkubectl?apply?-f?/home/jenkins/workspace/jenkins-k8sdemo/script/kubernetes/backend/backend-service.yaml service/k8sdemo-backend-service?created [Pipeline]?} [Pipeline]?//?container [Pipeline]?} [Pipeline]?//?stage [Pipeline]?} [Pipeline]?//?node [Pipeline]?} [Pipeline]?//?podTemplate [Pipeline]?End?of?Pipeline Finished:?SUCCESS不堆砌術(shù)語(yǔ),不羅列架構(gòu),不迷信權(quán)威,不盲從流行,堅(jiān)持獨(dú)立思考。
注:本文轉(zhuǎn)自「CSDN博客」
原文鏈接:https://blog.csdn.net/weixin_38748858/article/details/102967540
「AI大師課」是CSDN發(fā)起的“百萬(wàn)人學(xué)AI”倡議下的重要組成部分,4月份AI大師課以線上技術(shù)峰會(huì)的形式推出,來(lái)自微軟、硅谷TigerGraph、北郵等產(chǎn)學(xué)界大咖就圖計(jì)算+機(jī)器學(xué)習(xí),語(yǔ)音技術(shù)、新基建+AI、AI+醫(yī)療等主題展開(kāi)分享,掃描下方二維碼免費(fèi)報(bào)名,限時(shí)再送299元「2020AI開(kāi)發(fā)者萬(wàn)人大會(huì)」門票一張。
推薦閱讀:Spark3.0發(fā)布了,代碼拉過(guò)來(lái),打個(gè)包,跑起來(lái)!| 附源碼編譯 跟面試官侃半小時(shí)MySQL事務(wù)隔離性,從基本概念深入到實(shí)現(xiàn) 技術(shù)大佬的肺腑之言:“不要為了 AI 而 AI”! | 刷新 CTO 無(wú)代碼時(shí)代來(lái)臨,程序員如何保住飯碗? 154 萬(wàn) AI 開(kāi)發(fā)者用數(shù)據(jù)告訴你,中國(guó) AI 如何才能彎道超車?| 中國(guó) AI 應(yīng)用開(kāi)發(fā)者報(bào)告 業(yè)內(nèi)最大的“空氣幣”——以太坊??? 真香,朕在看了!總結(jié)
以上是生活随笔為你收集整理的在容器上构建持续部署及最佳实践初探的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 云+X案例展 | 传播类:富通云腾加速联
- 下一篇: 一群阿里人如何用 10 年自研洛神云网络