CI/CD 概述
大概了解一下
CI/CD
是啥子,其實之前做過這東西,但是沒解釋過。
- 持續內建
- (Continuous Integration,CI) :代碼合并建構部署測試都在一起,不斷地執行這個過程,并對結果回報。
- 持續部署
- (Continuous Deployment,CD):部署到測試環境、生産環境
- 持續傳遞
- (Continuous Delivery,CD):将最終産品釋出到生産環境。
都有個持續,也就是說在不斷重複做這件事情,說白了這東西最終的目的就是将項目更有效的部署 / 更新,他的流程大概是這樣,以
java
為例,開發送出代碼到版本倉庫後,通過
jenkins
這個持續內建的軟體進行代碼的拉取、單元測試、代碼的編譯、和鏡像的建構,一會會用到
jenkins
的
master-slave
架構,
slave
可以分擔
master
的任務,一會部署的
jenkins
也是在
k8s
叢集中,就是一個
pod
,是以自動添加的
slave
節點就是一個
pod
,有任務觸發時,
jenkins
會自動建立一個
pod
來作為他的
slave
,這個
slave
會去完成代碼的拉取、測試、編譯、建構鏡像、推送鏡像到倉庫,推到鏡像倉庫後就可以部署在你需要的地方了,部署完之後通過
ingress
或是
NodePort
釋出你的應用,釋出之後就可以通路了撒,流程圖如下。
![](https://img.laitimes.com/img/__Qf2AjLwojIjJCLyojI0JCLiQDOxEzX3xCZlhXam9VbsUmepNXZy9CXwJWZ3xCdh1mcvZ2Lc1zaHRGcWdUYuVzVa9GczoVdG1mWfVGc5RHLwIzX39GZhh2csATMflHLwEzX4xSZz91ZsAzMfRHLGZkRGZkRfJ3bs92YskmNhVTYykVNQJVMRhXVEF1X0hXZ0xiNx8VZ6l2cssmch1mclRXY39CXldWYtlWPzNXZj9mcw1ycz9WL49zZuBnLzYjN1YGOjZWM1MmY3QGMzYzX3UzM1ATMwMzLchDMyIDMy8CXn9Gbi9CXzV2Zh1WavwVbvNmLvR3YxUjLyM3Lc9CX6MHc0RHaiojIsJye.png)
jenkins
會完成上圖的所有步驟,其實之前做的
jenkins
也可以完成這一套操作了,但不是部署到
k8s
平台的,這次是針對
K8S
的,一會會涉及到幾個
jenkins
插件,這幾個插件會幫助我們将項目部署到
k8s
平台,要知道
jenkins
百分之 90 的功能都是由插件實作的,下面要配置的插件裡比較複雜的可能就是
Pipeline
了,還好我有狗頭,哈哈。
先說一下需要準備的環境吧,首先需要一個
k8s
叢集且部署有
coredns
,這個是必須的,無論你是用啥子方式部署的,如果現在沒有叢集請參考這篇文章使用
kubeadm
快速部署一個叢集,我還是用之前二進制安裝的叢集。
還需要一個準備一個鏡像倉庫,可以自建或是直接用各種雲提供商的,自建的話建議用
Harbor
,其實我不太喜歡用自建的,非
https
,還得去改
docker
的配置檔案且需要重新開機,我最讨厭重新開機了。
再就是準備一個代碼倉庫,這裡直接用
git
了,也是目前比較主流的版本倉庫,我順便把
harbor
也裝了,但估計不會用它來存儲鏡像,下面先把
Git&harbor
裝了吧。
部署 Harbor 鏡像倉庫
這個是用來存鏡像的,我直接在
kubeadm
的
node
節點上部署了,一會把
git
也仍在這裡吧,懶得去建立虛拟機了,
Harbor
官方位址,目前有兩種安裝包,分别是線上安裝和離線安裝,我用的線上安裝,因為離線安裝包有點大,還需要
docker-compose
的支援,
[root@kubeadm-node ~]# curl -L "https://github.com/docker/compose/releases/download/1.24.0/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose && chmod +x /usr/local/bin/docker-compose
[root@kubeadm-node /]# wget https://storage.googleapis.com/harbor-releases/release-1.8.0/harbor-online-installer-v1.8.0.tgz
[root@kubeadm-node /]# tar zxf harbor-online-installer-v1.8.0.tgz
[root@kubeadm-node /]# cd harbor/
[root@kubeadm-node /harbor]# ls
harbor.yml install.sh LICENSE prepare
需要先初始化一下,
[root@kubeadm-node /harbor]# ./prepare
會下載下傳一個鏡像,目錄裡面多東西了,編輯一下
harbor.yml
改點東西,三處。
hostname: 192.168.1.248 #通路harbor的域名,沒域名寫IP
data_volume: /harbor #資料目錄,改不改看你撒
harbor_admin_password: Harbor12345 # 預設admin登陸密碼,改不改随你
改完之後就可以安裝了,直接執行
install.sh
就可以了,會下載下傳
N
個鏡像,下載下傳完成之後會自行啟動。
[root@kubeadm-node /harbor]# ./install.sh
[root@kubeadm-node /harbor]# docker-compose ps
通路一下,
部署還是比較簡單的,随便推個鏡像試試。
這個就比較煩人了,我特麼實在是不想重新開機
docker
,算了,估計一會就不用了,用雲提供商的吧,下面部署一下
Git
。
部署 Git 倉庫
git
是目前比較主流的,先把這個包裝一下吧,如果你用
SVN
自行安裝配置一下吧,不是很麻煩,
[root@kubeadm-node ~]# yum -y install git
通路倉庫的方式這裡使用
ssh
方式來通路了,随便建立一個使用者,然後設定一個密碼。
[root@kubeadm-node ~]# useradd rj-bai
[root@kubeadm-node ~]# echo Sowhat? | passwd --stdin rj-bai
切換到剛剛建立的使用者,建立一個目錄,初始化一下作為代碼倉庫。
[rj-bai@kubeadm-node ~]$ mkdir rj-bai.git
[rj-bai@kubeadm-node ~]$ cd rj-bai.git/
[rj-bai@kubeadm-node ~/rj-bai.git]$ git --bare init
Initialized empty Git repository in /home/rj-bai/rj-bai.git/
[rj-bai@kubeadm-node ~/rj-bai.git]$ ls
branches config description HEAD hooks info objects refs
這就建立完了,具體怎麼拉取這個代碼,如下,在
master
上執行了,直接
git clone
就行了。
[root@master-1 ~]# cd /tmp/ && ls
metrics-server systemd-private-5afd87d103b74339a4fac02fdb472124-chronyd.service-FVUqYN
[root@master-1 /tmp]# git clone [email protected]:/home/rj-bai/rj-bai.git
Cloning into 'rj-bai'...
Warning: Permanently added '192.168.1.248' (ECDSA) to the list of known hosts.
[email protected]'s password:
warning: You appear to have cloned an empty repository.
[root@master-1 /tmp]# ls rj-bai/
提示目錄時空的,随便建立一個檔案送出一下
[root@master-1 /tmp/rj-bai]# cat <<OEF >index.html
> testing
> OEF
[root@master-1 /tmp/rj-bai]# cat index.html
testing
[root@master-1 /tmp/rj-bai]# git add .
[root@master-1 /tmp/rj-bai]# git config --global user.email "your@email"
[root@master-1 /tmp/rj-bai]# git config --global user.name "rj-bai"
[root@master-1 /tmp/rj-bai]# git commit -m 'index'
[master (root-commit) 8f27bc9] index
1 file changed, 1 insertion(+)
create mode 100644 index.html
[root@master-1 /tmp/rj-bai]# git push origin master
[email protected]'s password:
Counting objects: 3, done.
Writing objects: 100% (3/3), 215 bytes | 0 bytes/s, done.
Total 3 (delta 0), reused 0 (delta 0)
To [email protected]:/home/rj-bai/rj-bai.git
* [new branch] master -> master
送出到了
master
分支,驗證一下,這個目錄删了,重新拉去一下,能拉到就說明沒啥子問題。
[root@master-1 /tmp]# rm -rf rj-bai/
[root@master-1 /tmp]# git clone [email protected]:/home/rj-bai/rj-bai.git
Cloning into 'rj-bai'...
[email protected]'s password:
remote: Counting objects: 3, done.
remote: Total 3 (delta 0), reused 0 (delta 0)
Receiving objects: 100% (3/3), done.
[root@master-1 /tmp]# ls rj-bai/
index.html
嗯,就是這樣,莫得問題,還有就是每次都要輸入密碼,配一個秘鑰就好了,我現在有了,我直接
copy
去了。
[root@master-1 ~]# ssh-copy-id [email protected]
[root@master-1 ~]# git clone [email protected]:/home/rj-bai/rj-bai.git
[root@master-1 ~]# ls rj-bai/
index.html
這樣就可以了,倉庫和
Git
都準好了,暫時先不動他了,下面開始在
k8s
叢集中部署
jenkins
。
在 K8S 中部署 jenkins
jenkins 官網,既然是在
k8s
中部署就直接使用
docker
方式了,github 位址,官方說明有一個目錄需要做持久化,也就是
/var/jenkins_home
,
jenkins
所有的資料都是存在這個目錄下面的,
jenkins
還需要一個唯一的網絡辨別,也就是說需要有狀态的去部署
jenkins
了,這裡就不多
BB
了,直接用
StatefulSet
方式去部署,依舊使用
NFS
動态供給作為存儲,
NFS
的部署方法之前寫過了,部署
jenkins
的
YAML
檔案是使用官方的模闆,現在直接
git clone
下來。
[root@master-1 ~]# mkdir jenkins
[root@master-1 ~]# cd jenkins/
[root@master-1 ~/jenkins]# git clone https://github.com/jenkinsci/kubernetes-plugin.git
克隆完之後進入到這個目錄。
[root@master-1 ~/jenkins/kubernetes-plugin/src/main/kubernetes]# pwd
/root/jenkins/kubernetes-plugin/src/main/kubernetes
[root@master-1 ~/jenkins/kubernetes-plugin/src/main/kubernetes]# ls
jenkins.yml service-account.yml
可以看到有兩個檔案,
service-account.yml
檔案是建立
RBAC
授權相關的東西,這個不要動,主要看一下
jenkins.yml
。
這裡使用了
PV
的模闆,是需要你提供
PV
自動供給的支援的,預設的類型是
anything
,目前我隻有一個
NFS
的,也就是這個。
是以說一會建立的時候它預設就會用這個了,如果你有兩種類型的存儲且需要指定就按正常流程走指定就完了,申請磁盤空間這裡我把它改成
5G
了,
1G
實在是有點小,還有資源限制那裡,限制最小
0.5
核
CPU&0.5G
記憶體,最大
1
核
1G
記憶體,說實話有點小,最起碼記憶體調大一點,我這裡最大限制都調成
2
了。
又看了一眼
Service
這裡,通路方式用的是
Ingress
,你可以用
NodePort
方式去通路,我這裡用
Ingress
方式去做了,好久沒搞過
nginx-ingress
了,先把
tls
方法去了,
hosts
随便改一下,我改完的如下。
如果你不想用
Ingress
方式就删除掉上圖光标的位置的注釋,
type
改為
NodePort
且略過部署下面部署
nginx-ingress
的部分,我這裡部署一下
nginx-ingress
。
部署 nginx-ingress
還是直接用官方的模闆,先
git clone
下來,進到這個目錄。
[root@master-1 ~/demo/nginx-ingress]# git clone https://github.com/nginxinc/kubernetes-ingress.git
[root@master-1 ~/demo/nginx-ingress/kubernetes-ingress/deployments]# pwd
/root/demo/nginx-ingress/kubernetes-ingress/deployments
按着官方的步驟這樣部署,當然有些暫時用不到的步驟我直接省略了,官方文檔
1. 建立命名空間和服務賬戶。
[root@master-1 ~/demo/nginx-ingress/kubernetes-ingress/deployments]# kubectl apply -f common/ns-and-sa.yaml
namespace/nginx-ingress created
serviceaccount/nginx-ingress created
2. 使用 TLS 證書和 NGINX 中預設伺服器的密鑰建立密鑰
說白了就是給
nginx
一個預設的
TLS
證書,這個證書也是他們自簽的,不受信任。
[root@master-1 ~/demo/nginx-ingress/kubernetes-ingress/deployments]# kubectl apply -f common/default-server-secret.yaml
secret/default-server-secret created
3. 建立用于自定義 nginx 的配置檔案
[root@master-1 ~/demo/nginx-ingress/kubernetes-ingress/deployments]# kubectl apply -f common/nginx-config.yaml
configmap/nginx-config created
4. 配置 RBAC
[root@master-1 ~/demo/nginx-ingress/kubernetes-ingress/deployments]# kubectl apply -f rbac/rbac.yaml
clusterrole.rbac.authorization.k8s.io/nginx-ingress created
clusterrolebinding.rbac.authorization.k8s.io/nginx-ingress created
5. 部署 ingress 控制器
這裡的話用兩種方法,分别為
deployment&&DaemonSet
,我這裡直接用
DaemonSet
方式去部署了,具體為什麼,官方說明,翻譯内容如下。
如果您建立了
DaemonSet
,則 Ingress 控制器容器的端口 80 和 443 将映射到運作容器的節點的相同端口。要通路 Ingress 控制器,請使用這些端口以及運作 Ingress 控制器的群集中任何節點的 IP 位址。
省得再去建立
Services
了,看了一下部署控制器的檔案,
args
裡面啟用了兩個,還有 6 個沒啟用,我翻譯了一下,結果如下。
好像暫時沒啥子能用到的,檢視狀态的可以考慮開啟,我這裡暫時就不開啟了,直接建立了。
[root@master-1 ~/demo/nginx-ingress/kubernetes-ingress/deployments]# kubectl apply -f daemon-set/nginx-ingress.yaml
daemonset.extensions/nginx-ingress created
[root@master-1 ~/demo/nginx-ingress/kubernetes-ingress/deployments]# kubectl -n nginx-ingress get pod
這樣就可以了撒,部署完了,所有
Node
節點都有運作一個
nginx-ingress
控制器,除了兩個
master
節點,因為我沒允許
master
運作
Pod
,
Ingress
部署完了,現在可以部署
jenkins
了。
部署 jenkins
部署檔案之前已經修改過了,是以直接建立就完了。
[root@master-1 ~/jenkins/kubernetes-plugin/src/main/kubernetes]# kubectl apply -f .
[root@master-1 ~/jenkins/kubernetes-plugin/src/main/kubernetes]# kubectl get pod -w
正常啟動了,寫個
hosts
就通路就可以了撒。
不用去看這個檔案,
pod
日志裡有輸出密碼,直接看
pod
日志就行了。
[root@master-1 ~]# kubectl logs jenkins-0
之後就需要裝插件了,不裝任何插件,一會用啥子裝啥子,
現在不裝插件最主要的原因其實就是有些插件裝了需要重新開機
jenkins
才可以,如果現在裝完了插件完成後續操作後會回到
jenkins
首頁面,但是
jenkins
首頁面就是一片空白,需要重新開機
jenkins
才可以,當然
pod
是沒有重新開機這個概念的,你得想辦法把之前的
pod
弄死,
k8s
幫你重新拉起一個之後就正常了,是以先不裝插件,正常的話完成後續操作就可以看到首頁面了。
現在
jenkins
是裝完了,下面需要
jenkins
和叢集融合一下了。
jenkins 在 k8s 動态建立代理
現在需要
jenkins
和
k8s
內建,需要用到一個名為
kubernetes
的插件,這個插件是用來動态建立代理的,也是自動建立
slave
端,
slave
就是你添加的工作節點,這裡不需要你手動添加了,在有任務的時候
jenkins
會自動建立
pod
作為自己的
slave
節點,
master-slave
架構解決了
master
單點負載的問題,使其演變成了一個分布式的架構,其實
slave
節點就是運作了一個
jar
包,我之前也搞過這個,這個
jar
包啟動後會去連接配接
master
接收任務。
目前
jenkins
是
k8s
中的一個
pod
,是以他動态建立的
slave
也是叢集中的
pod
,
master
和
slave
端通訊是通過
jnlp
協定進行的,在有任務的時候
jenkins
會請求
k8s
叢集幫他建立
slave
(也就是
pod
) 來完成任務,任務完成後這個
pod
就會自動銷毀,下次有需要再啟,想實作這個功能就需要一個插件了撒。
安裝配置插件
暫時先裝兩個插件吧,名為
git&&kubernetes
,如果你用
SVN
請安裝
Subversion
插件,點開系統設定→插件管理→
available
搜一下這兩個插件,安裝就行了,勾上安裝完成後重新開機,其實我隻選擇了上述的兩個插件,其他的都是依賴。
裝完之後需要配置一下這個插件,使
jenkins
支援
kubernetes
,點開系統管理→系統設定→拉到最下面,可以看到這個,直接點進去,
Kubernetes
這裡的話配這樣就行了,這裡寫的都是
dns
名稱。
測試連接配接莫得問題,憑據和
key
都不用到,都已經
rbac
授權了,如果是部署在叢集外
key
那裡就需要寫
apiserver
的
CA
資訊了,再然後就是配置
jenkins
這裡了。
這樣就可以了,如果你的
jenkins
在叢集外
kubernetes
位址就不要寫内部
DNS
名稱了,
key
那裡需要配置
apiserver
的
CA
證書就可以了,下面還有一個添加
pod
模闆,也就是這個,
這個東西就是定義如何建立
slave pod
的一個模闆,你可以了解為這裡就是一個
YAML
檔案定義了怎麼去建立
slave-pod
,隻不過是通過
UI
的形式去配置,這一塊也可以通過
Pipeline
腳本去定義,是以不需要在這裡配置這個了,如果在這裡配置你可能每次添加一個項目都要在這裡配置一次,不友善管理,在
Pipeline
裡面配置就比較友善了,是以目前隻需要配置怎麼連接配接
kubernetes
就可以了,這個差掉,儲存退出就可以了,下面開始建構
slave
鏡像。
建構 jenkins-slave 鏡像
做一個驗證,都配置完了,到底能不能用,直接用流水線了,目前還沒裝這個插件,是以先裝一下,插件名就是
Pipeline
這次裝的比較多,讓他裝着吧,裝完後自動重新開機,一會會寫一個
Pipeline
腳本,以測試
jenkins
能不能在叢集中動态建立
slave-pod
,這個是必需步驟,否則無法繼續下去,這個
slave-pod
的鏡像需要自己做一個,下面開始制作這個鏡像,其實是有預設的鏡像,但是不用他預設的,對于我來說功能不全,這個
slave
鏡像會完成代碼拉取、單元測試、代碼編譯、建構鏡像、推送鏡像的步驟,是以現在做的
slave
鏡像要有這些功能,下面開始編寫
Dockerfile
編寫 Dockerfile
上文提到了這個鏡像需要完成代碼拉取,單元測試 (和現在沒啥子關系),代碼編譯,鏡像建構以及推送鏡像,是以你的鏡像需要支援這些功能才可以,是以
Dockerfile
如下,按着自己的情況更改吧,官方參考位址
FROM centos:latest
RUN yum -y install java-1.8.0-openjdk maven curl git subversion libtool-ltdl-devel && \
yum clean all && \
rm -rf /var/cache/yum/* && \
mkdir -p /usr/share/jenkins
COPY slave.jar /usr/share/jenkins/slave.jar
COPY jenkins-slave /usr/bin/jenkins-slave
COPY settings.xml /etc/maven/settings.xml
RUN chmod +x /usr/bin/jenkins-slave
ENTRYPOINT ["jenkins-slave"]
settings.xml
是
maven
的配置檔案,這個檔案怎麼寫建議咨詢一下開發人員,我還是用我們私服的,用預設的也可以,可能下載下傳依賴包會比較慢,預設位址是國外的,
jenkins-slave
也是在參考位址直接
copy
過來的,也不貼了,
slave.jar
自行下載下傳吧,位址就是
$JENKINS_URL/jnlpJars/slave.jar
,我這裡也不傳了,都準備好了直接
build
就完了。
[root@master-1 /data/docker/jenkins-slave-jdk]# ls
Dockerfile jenkins-slave settings.xml slave.jar
[root@master-1 /data/docker/jenkins-slave-jdk]# docker build -t jenkins-slave-jdk:1.8 .
這樣就建構完了,然後把這個鏡像推送到鏡像倉庫,我不用剛剛搭建的
Harhor
,我直接推到
docker hub
去了,就是比較慢
[root@master-1 /data/docker/jenkins-slave-jdk]# docker tag jenkins-slave-jdk:1.8 bairuijie/jenkins-slave-jdk:1.8
[root@master-1 /data/docker/jenkins-slave-jdk]# docker push bairuijie/jenkins-slave-jdk:1.8
容器還需要建構和推送鏡像,我并沒有裝在容器裡安裝
docker
環境,之後在啟動這個
pod
的時候以資料卷的形式挂載主控端的
docker
指令和
socket
進去就可以了,這隻是一個适合拉取
git&svn
倉庫代碼和編譯
java
代碼的鏡像,如果你是别的自行琢磨吧,這種問題多去問開發,下面來了解一下
Pipeline
Pipeline 建構流水線釋出
Pipeline
就是一套插件,剛剛也安裝了,上文提到的流程從拉取到部署都需要由
Pipeline
來完成,
Pipeline
是通過特定的文法從簡單到複雜的傳輸管道進行模組化,支援兩種定義的方式,一種為聲明式,遵循
Grovvy
相同文法,使用
pipeline {}
,還有一種是腳本式,支援
Grovvy
大部分功能,也是表達靈活的工具,
node {}
,這兩種使用哪個都可以,看一下官方的栗子。
示例位址,
聲明式
腳本式
我的頭開始大了,後面主要是使用腳本式,這東西的定義就是一個文本檔案,也稱為
Jenkinsfile
,下面建立一個流水線任務來玩玩。
建立流水線任務
自行建立吧,建立完任務之後拉到最下面,選這個,會自動補全一個例子,改改這個例子。
改成這樣。
這樣就可以了,儲存,然後建構一下,建構成功後你會看到這個。
可以看到剛剛定義的三個步驟以圖表的形式展現了出來,這是一個最簡單的示例,腳本裡面都有多個
stage
,這個
stage
是腳本最基本的組成部分,它用來告訴
jenkins
要去幹什麼,之後就需要在這個腳本中實作從代碼拉取到部署到
k8s
的全部過程,官方的原理圖。
說白了還是完成了從建構到釋出的流程,是以接下來就開始搞在腳本中完成這些操作。
拉取代碼
第一步就是拉取代碼,到底怎麼拉,這些步驟的文法都可以通過
Pipeline
腳本文法去幫我們生成,這個是重點,也就是這個位置。
新到一個頁面,譬如我現在要拉取
git
倉庫
rj-bai.git
的代碼,我就可以這樣做了,
使用
master
分支,但是提示無法連接配接到這個倉庫,這裡也是需要免互動拉取代碼的,之前的操作就是将
master
公鑰拷貝到了
git
伺服器,是以現在要用到私鑰了,需要添加一個憑據,将使用者名私鑰添加進去添加即可,如果你是
SVN
請添加使用者名密碼,
SVN
怎麼拉代碼下面會提到,
添加後回到首頁面之後報錯沒了,說明可以免互動拉取代碼了,點選
Generate Pipeline Script
之後就會生拉取代碼的腳本了。
複制這一串子貼到這個位置就可以拉取代碼了,我順便執行了一條
shell
指令
儲存建構,看了一下
log
輸出,之前添加的
Index.html
已經拉取下來了,說明拉取代碼這塊莫得問題。
能拉取代碼了,下一步就要開始編譯了,下面看一下如何編譯代碼。
編譯代碼
上面執行的這些任務都是在
jenkins pod
中去做的,現在還沒有
slave
節點,一般編譯
java
需要
maven
,
maven
依賴
jdk
,不用看了,目前
jenkins
的
POD
上雖有
java
但沒有
maven
,在上文的描述中拉取編譯代碼也是由
slave
去完成的,是以現在要啟動
slave
了,既然是動态建立,就需要用到
pod
模闆了。
啟動測試 slave 節點
直接在
pipeline
腳本中定義吧,改完的
pipeline
腳本内容如下。
podTemplate(label: 'jenkins-slave', cloud: 'kubernetes', containers: [
containerTemplate(
name: 'jnlp',
image: "registry.cn-beijing.aliyuncs.com/rj-bai/jenkins-slave-jdk:1.8"
),
],
volumes: [
hostPathVolume(mountPath: '/var/run/docker.sock', hostPath: '/var/run/docker.sock'),
hostPathVolume(mountPath: '/usr/bin/docker', hostPath: '/usr/bin/docker')
],
)
{
node ("jenkins-slave"){
def mvnHome
stage('拉取') {
git credentialsId: 'efb4268b-f758-4b58-9dcd-d966faf8360e', url: '[email protected]:/home/rj-bai/rj-bai.git'
sh 'ls -l'
}
stage('建構') {
}
stage('部署') {
}
}
}
上面的那一段就是定義
slave
節點的聲明,使用
jnlp
協定,鏡像我傳到了阿裡雲的倉庫,這個是公開的,一會在建構的時候他回去拉這個鏡像作為
slave
啟動,我直接也把
docker
挂進去了,友善建構推送鏡像,這樣配置之後建構鏡像就不是由
master
來完成的了,而是由動态建立的
slave
來完成的,測試一下,儲存,然後建構,動态檢視
pod
狀态。
這裡建構成功了,看一下
k8s
叢集中
pod
的變化。
這就是動态建立
slave
了,在需要的時候建立一個,
slave
完成任務後就會被銷毀,這一塊沒啥子問題,下面準備一下要編譯的源代碼。
準備 java 源代碼
既然是編譯,你就需要有
java
的源碼了,建議在這裡生成一個,目前隻要有簡單的
web
通路就夠了,是以選這個,編譯成功後會有一個
jar
檔案,
jdk
版本
1.8
,這樣就可以了。
選完點選綠色按鈕你會下載下傳一個名為
demo.zip
的壓縮包,這就是源碼了,然後在
git
上建立一個倉庫,把代碼提上去。
[rj-bai@kubeadm-node ~]$ mkdir webstarter.git
[rj-bai@kubeadm-node ~]$ cd webstarter.git/
[rj-bai@kubeadm-node ~/webstarter.git]$ git --bare init
Initialized empty Git repository in /home/rj-bai/webstarter.git/
倉庫這裡算是建立完了,然後去
master
送出代碼。
[root@master-1 ~]# git clone [email protected]:/home/rj-bai/webstarter.git
[root@master-1 ~]# cd webstarter/
[root@master-1 ~/webstarter]# unzip demo.zip ## 檔案自行上傳
[root@master-1 ~/webstarter]# mv demo/* .
[root@master-1 ~/webstarter]# rm -rf demo*
[root@master-1 ~/webstarter]# git add .
[root@master-1 ~/webstarter]# git commit -m 'webstarter'
[root@master-1 ~/webstarter]# git push origin master
這樣就可以了撒,其實
pipeline
的配置也可以寫到一個名為
Jenkinsfile
的檔案裡,這個檔案需要放在代碼的根目錄,這個後面會涉及到,下面開始配置編譯代碼的部分。
編譯代碼建構和推送鏡像
編譯代碼這裡需要
maven
去編譯,建構鏡像的話就是将項目包傳到鏡像裡,一會會用
openjdk
的鏡像,推送鏡像就是将剛剛建構好的鏡像推送到鏡像倉庫,既然涉及到了推送鏡像就一定會給鏡像打标簽,具體這個标簽怎麼打,還是像之前那樣,項目名 + 建構次數,是以腳本暫時寫成這樣,定義了 N 多變量。
// 鏡像倉庫位址
def registry = "registry.cn-beijing.aliyuncs.com"
// 項目&鏡像配置資訊
def namespace = "rj-bai"
def app_name = "webstarter"
def image_name = "${registry}/${namespace}/${app_name}:${BUILD_NUMBER}"
def git_address = "[email protected]:/home/rj-bai/webstarter.git"
// 認證資訊ID
def docker_registry_auth = "400878cf-e740-459f-b8bf-ad2c749b833e"
def git_auth = "efb4268b-f758-4b58-9dcd-d966faf8360e"
podTemplate(label: 'jenkins-slave', cloud: 'kubernetes', containers: [
containerTemplate(
name: 'jnlp',
image: "${registry}/${namespace}/jenkins-slave-jdk:1.8"
),
],
volumes: [
hostPathVolume(mountPath: '/var/run/docker.sock', hostPath: '/var/run/docker.sock'),
hostPathVolume(mountPath: '/usr/bin/docker', hostPath: '/usr/bin/docker')
],
)
{
node("jenkins-slave"){
stage('拉取代碼'){
checkout([$class: 'GitSCM', branches: [[name: '${Branch}']], userRemoteConfigs: [[credentialsId: "${git_auth}", url: "${git_address}"]]])
}
stage('代碼編譯'){
sh "mvn clean package -Dmaven.test.skip=true"
}
stage('建構鏡像'){
withCredentials([usernamePassword(credentialsId: "${docker_registry_auth}", passwordVariable: 'password', usernameVariable: 'username')]) {
sh label: '', script: ''' echo \'
FROM openjdk:8
ADD target/*.jar /
ADD entrypoint.sh /
RUN chmod +x /entrypoint.sh
CMD ["/bin/bash","/entrypoint.sh"]
\' > Dockerfile
echo \'
#!/bin/bash
app=`ls /*.jar`
java -jar $app
\' > entrypoint.sh'''
sh """
docker build -t ${image_name} .
docker login -u ${username} -p \"${password}\" ${registry}
docker push ${image_name}
"""
}
}
}
}
你就當成
shell
腳本去看就完了,應該都能看的差不多,
def
開頭的就是定義的變量,
Branch
是分支變量,現在還沒定義,需要通過參數化建構過程去定義,其他的變量如果在這個腳本内找不到那他就是
jenkins
的内置變量,譬如
BUILD_NUMBER
,很久之前在别的文章裡提過這個,先來解釋一下我鏡像倉庫那裡為什麼這樣寫,看一下我鏡像倉庫完整的位址你就懂了。
如果你是自建的鏡像倉庫可以不加
namespace
的配置,我這裡就必須得加了,還有一些奇怪的
ID
,下面分别來解釋一下都是用來幹嘛的。
docker_registry_auth
是用來拉取推送鏡像的憑據,但是那裡寫的是憑據的
ID
,這個憑證是
slave-pod
所使用的,因為他需要向私有倉庫推送鏡像,需要登陸後才能去推送鏡像,添加方法如下。
首頁面→憑據→系統→全局憑據→添加,類型就是使用者名密碼,然後填進去點
ok
就可以了,
ID
會自動生成一個。
添加之後會自動傳回,然後點更新的按鈕就可以檢視到
ID
了,要和腳本中的對應上。
再下面的那個
git
認證之前就建立過了,自己檢視
ID
改一下吧,再看一下拉取代碼那裡有很奇怪的一段,也就是這個,
checkout([$class: 'GitSCM', branches: [[name: '${Branch}']], userRemoteConfigs: [[credentialsId: "${git_auth}", url: "${git_address}"]]])
這個也是用來拉取代碼的,之前提過一個使用
git
方式去拉取的,剛剛用的那種方式是這個,看下圖,通過這個也可以拉取代碼,而且推薦使用這個,位址
ID
分支資訊上文都用的是變量,其他的都一樣,
SVN
代碼也用這種方式去拉就行了,
SVN
拉代碼我建議用這個,
checkout([$class: 'SubversionSCM', locations: [[cancelProcessOnExternalsFail: true, credentialsId: "${svn_auth}", depthOption: 'infinity', ignoreExternalsOption: true, local: '.', remote: "${svn_address}"]], quietOperation: true, workspaceUpdater: [$class: 'UpdateUpdater']])
在看建構鏡像那裡也有奇怪的一段,也就是這個,
withCredentials([usernamePassword(credentialsId: "${docker_registry_auth}", passwordVariable: 'password', usernameVariable: 'username')]) {
這一段是用來儲存登陸鏡像倉庫的使用者名和密碼,以變量的形式,這樣做的主要原因就是不會讓使用者名密碼的明文暴露在
pipeline
腳本裡,也是動态生成的,如果所示。
和我腳本中的一緻,隻不過我腳本中定義的是
ID
的變量,說白了這樣做就是将使用者名存到了名為
username
的變量中,密碼儲存到了
password
變量中,直接引用就完了,不會暴露你使用者名密碼明文,就是這個原理,再下面寫了一個
Dockerfile
和一個啟動腳本,現在還有一個變量沒有去定義,也就是
Branch
,用來定義要建構的分支,
git
的分支會有很多,不止是一個
master
,是以說不是固定的,是以現在定義一下這個變量。
編輯這個
job
,找到參數化建構過程,配置成這樣,使用
SVN
的不用改這裡撒,
這就是一個變量,預設值是
master
,儲存回到主界面,點
Build with Parameters
,會看到這種效果。
中途失敗了好多次,各種調試,之前也成功過,但是第 25 次是才是真正意義上的成功,
現在鏡像已經推送到我阿裡雲的倉庫了,直接在伺服器上拉一下吧,試着運作一下。
[root@kubeadm-node ~]# docker run -d -p 666:8080 registry.cn-beijing.aliyuncs.com/rj-bai/webstarter:25
莫得問題,可以正常通路,說明之前的步驟都沒問題了,現在通過
pipeline
完成了
CI
階段,拉取代碼編譯建構鏡像推送鏡像,感覺是比之前的友善很多了,下一步就是需要将剛剛的東西部署到
K8s
中了,也就是
CD
階段。
jenkins 在 k8s 中持續部署
上面鏡像已經準備好了,現在該實作自動部署到
k8s
中了,要想實作這個還需要一個插件,名為
kubernetes continuous deploy
,用于将資源部署到
k8s
中,他支援絕大部分的資源類型,像是什麼
deployment&service
,自行安裝吧,簡單看一下這個插件的介紹。
官方位址,主要看一下這一段,這是配置在
pipeline
中使用的寫法
kubeconfigId
這裡是需要指定一個
kubeconfig
的
ID
,這個東西就是用于連接配接
k8s
的一個配置檔案,是以我們要把這個檔案内容儲存到
jenkins
中作為憑據,然後去引用憑據的
ID
。
config
這裡用來指定資源檔案,也就是你部署服務的
YAML
檔案。
secretNamespace: '<secret-namespace>',
secretName: '<secret-name>',
這塊是用來指定
secret
的,有兩個是必須的,一個是
kubeconfig
檔案,再就是資源檔案,這個插件的
pipeline
寫法也是可以生成的,是以還是用之前的工具來生成一下。
現在開始讓你添加
kubeconfig
的檔案了,添加吧,選擇這個,
這個檔案具體要怎麼擷取,如果你的叢集是
kubeadm
安裝的擷取很簡單,就是這個檔案。
[root@rj-bai ~]# cat .kube/config
把這個檔案的内容複制出來儲存到
jenkins
裡面就可以了,如果你是二進制部署的叢集,就要手動去生成這個檔案了,這個檔案預設是沒有的,具體怎麼生成這個檔案之前寫過,這裡就不貼了,我使用的叢集也是二進制方式部署的,我直接把之前生成的檔案拿過來就直接用了,然後那個
config Files
就是指定資源檔案了,這個檔案就是用來部署我們的項目的,emmmm,寫一個吧,我寫的如下。
apiVersion: apps/v1
kind: Deployment
metadata:
name: web
spec:
replicas: 3
selector:
matchLabels:
app: web-starter
template:
metadata:
labels:
app: web-starter
spec:
imagePullSecrets:
- name: $SECRET_NAME
containers:
- name: web-starter
image: $IMAGE_NAME
ports:
- containerPort: 8080
name: web
livenessProbe:
httpGet:
path: /favicon.ico
port: 8080
initialDelaySeconds: 30
timeoutSeconds: 5
failureThreshold: 3
readinessProbe:
httpGet:
path: /favicon.ico
port: 8080
initialDelaySeconds: 30
timeoutSeconds: 5
failureThreshold: 3
resources:
requests:
memory: "64Mi"
cpu: "250m"
limits:
memory: "128Mi"
cpu: "500m"
---
apiVersion: v1
kind: Service
metadata:
name: web
spec:
type: NodePort
selector:
app: web-starter
ports:
- protocol: TCP
port: 80
targetPort: 8080
name: web
---
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: web
spec:
rules:
- host: webstarter.rj-bai.com
http:
paths:
- path: /
backend:
serviceName: web
servicePort: 80
應該都能看懂,我部署了
nginx-ingress-controller
,是以就直接以
ingress
方式釋出出去了,如果你沒有部署
ingress
控制器就直接用
NodePort
吧,看一哈這個檔案中有兩個變量,一個是用來存放認證登陸資訊的
secrets
名字,再一個就是鏡像位址,一會使用
sed
去替換成相對應的值,暫時就定義這兩個變量,其實還有很多可以定義,隻要是經常變動的,像是什麼健康檢查端口綁定域名也可用變量,用變量的目的就是複用,自行琢磨吧,這個檔案需要存在你
git
版本代碼倉庫中,自行上傳送出一下吧,我的名為
deploy-webstarter.yaml
。
是以,最終的配置檔案
pipeline
腳本如下。
// 鏡像倉庫位址
def registry = "registry.cn-beijing.aliyuncs.com"
// 項目&鏡像配置資訊
def namespace = "rj-bai"
def app_name = "webstarter"
def image_name = "${registry}/${namespace}/${app_name}:${BUILD_NUMBER}"
def git_address = "[email protected]:/home/rj-bai/webstarter.git"
// 認證資訊ID
def docker_registry_auth = "400878cf-e740-459f-b8bf-ad2c749b833e"
def git_auth = "efb4268b-f758-4b58-9dcd-d966faf8360e"
def secret_name = "registry-secret"
def k8s_auth = "9823945c-07f6-495c-bc48-a47d8f43536c"
podTemplate(label: 'jenkins-slave', cloud: 'kubernetes', containers: [
containerTemplate(
name: 'jnlp',
image: "${registry}/${namespace}/jenkins-slave-jdk:1.8"
),
],
volumes: [
hostPathVolume(mountPath: '/var/run/docker.sock', hostPath: '/var/run/docker.sock'),
hostPathVolume(mountPath: '/usr/bin/docker', hostPath: '/usr/bin/docker')
],
)
{
node("jenkins-slave"){
stage('拉取代碼'){
checkout([$class: 'GitSCM', branches: [[name: '${Branch}']], userRemoteConfigs: [[credentialsId: "${git_auth}", url: "${git_address}"]]])
}
stage('代碼編譯'){
sh "mvn clean package -Dmaven.test.skip=true"
}
stage('建構鏡像'){
withCredentials([usernamePassword(credentialsId: "${docker_registry_auth}", passwordVariable: 'password', usernameVariable: 'username')]) {
sh label: '', script: ''' echo \'
FROM openjdk:8
ADD target/*.jar /
ADD entrypoint.sh /
RUN chmod +x /entrypoint.sh
CMD ["/bin/bash","/entrypoint.sh"]
\' > Dockerfile
echo \'
#!/bin/bash
app=`ls /*.jar`
java -jar $app
\' > entrypoint.sh'''
sh """
docker build -t ${image_name} .
docker login -u ${username} -p \"${password}\" ${registry}
docker push ${image_name}
"""
}
}
stage('部署到K8S'){
sh """
sed -i 's#\$IMAGE_NAME#${image_name}#' deploy-webstarter.yaml
sed -i 's#\$SECRET_NAME#${secret_name}#' deploy-webstarter.yaml
"""
kubernetesDeploy configs: 'deploy-webstarter.yaml', kubeconfigId: "${k8s_auth}"
}
}
}
增加了兩個變量,一個是存
kubeconfig
的
ID
,一個是
secret
的名字,這個是幹嘛的不用多說了,如果沒有自行建立吧,使用
sed
替換了鏡像位址和
secret
的名字,最後
kubernetesDeploy
配置的那裡,現在就用到的就這兩個,沒用到的全部去掉了,開始跑吧。
最終測試
開始建構,然後動态檢視
pod
的狀态,先看頁面,顯示成功。
再看
pod
的狀态,都已經正常啟動了,
然後手寫
hosts
通路一下,
莫得問題,就是這種效果,現在也完成了持續部署,生成的
demo
就是一個簡單的
web
,沒有任何的頁面,隻有預設的
404
, 健康檢查那裡我檢查的還是
favicon.ico
,反正是起來了,通路也沒問題,過。
使用 Jenkinsfile
其實這個
pipeline
腳本的内容也可以放在項目的根目錄,也就是和
deploy-webstarter.yaml
同級,不需要寫在
jenkins
裡,這樣的話比較友善管理,再加項目的時候你隻需要在
jenkins
上建立
job
直接引用倉庫位址就可以了,現在就要達到這個目的,部署一個東西撒,一個名為
DimpleBlog
的
java
部落格,感興趣的去
github
上搜一下吧,編譯好了也是一個
jar
包,拿到源碼之後還是建立倉庫,
master
拉一下把代碼将源碼傳到倉庫,先不要送出到
git
中,需要改點東西。
剛剛提到了這是一個部落格程式,要他運作起來需要
mysql&&redis
,他的
sql
檔案是存在源碼的
sql
檔案夾裡,我叢集中剛好有一個
mysql
,也是之前建立的,建立方式我就不貼了,之前貼過,我看
sql
裡用的是
test
表,我手動導入進去了,現在還莫得
redis
,建立一個
redis
出來吧,使用
StatefulSet
建立,和建立
mysql
的方式一緻
[root@master-1 ~/demo]# cat redis.yaml
apiVersion: v1
kind: Service
metadata:
name: redis
spec:
ports:
- port: 6379
name: redis
clusterIP: None
selector:
app: redis
---
apiVersion: apps/v1beta1
kind: StatefulSet
metadata:
name: redis
spec:
updateStrategy:
type: RollingUpdate
serviceName: "redis"
template:
metadata:
labels:
app: redis
spec:
containers:
- name: redis
image: redis:latest
ports:
- containerPort: 6379
name: redis
volumeMounts:
- mountPath: "/data"
name: redis-data
livenessProbe:
tcpSocket:
port: 6379
initialDelaySeconds: 10
periodSeconds: 3
failureThreshold: 3
volumes:
- name: redis-data
persistentVolumeClaim:
claimName: redis-data
volumeClaimTemplates:
- metadata:
name: redis-data
spec:
accessModes: [ "ReadWriteOnce" ]
resources:
requests:
storage: 1Gi
[root@master-1 ~/demo]# kubectl apply -f redis.yaml
service/redis created
statefulset.apps/redis created
[root@master-1 ~/demo]# kubectl get pod redis-0
NAME READY STATUS RESTARTS AGE
redis-0 1/1 Running 0 3m10s
已經啟動了,然後去改一下這個項目連接配接資料庫的配置檔案,這個位置,
[root@master-1 ~/DimpleBlog]# vim src/main/resources/application-druid.yml
# 主庫資料源
master:
url: jdbc:mysql://mysql:3306/test?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
username: root
password: Sowhat?
這是資料庫的,還要改一下
redis
的,
[root@master-1 ~/DimpleBlog]# vim src/main/resources/application.yml
redis:
host: redis
port: 6379
database: 0
這樣就可以了撒,還需要建立資源檔案和
jenkinsfile
,先把資源檔案寫了吧,按着之前的改改就行了,我改成這樣。
[root@master-1 ~/DimpleBlog]# cat deploy-dimpleblog.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: web-dimpleblog
spec:
replicas: 1
selector:
matchLabels:
app: dimpleblog
template:
metadata:
labels:
app: dimpleblog
spec:
imagePullSecrets:
- name: $SECRET_NAME
containers:
- name: dimpleblog
image: $IMAGE_NAME
ports:
- containerPort: 80
name: web-dimpleblog
livenessProbe:
httpGet:
path: /
port: 80
initialDelaySeconds: 60
timeoutSeconds: 10
failureThreshold: 5
readinessProbe:
httpGet:
path: /
port: 80
initialDelaySeconds: 60
timeoutSeconds: 10
failureThreshold: 5
resources:
requests:
memory: "512Mi"
cpu: "0.5"
limits:
memory: "1024Mi"
cpu: "1"
---
apiVersion: v1
kind: Service
metadata:
name: web-dimpleblog
spec:
type: NodePort
selector:
app: dimpleblog
ports:
- protocol: TCP
port: 80
targetPort: 80
name: web-dimpleblog
---
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: web-dimpleblog
spec:
rules:
- host: dimpleblog.rj-bai.com
http:
paths:
- path: /
backend:
serviceName: web-dimpleblog
servicePort: 80
這次健康檢查時間調長了,資源檔案有了,然後是
jenkinsfile
,放在和資源檔案目錄同級的地方,
[root@master-1 ~/DimpleBlog]# cat Jenkinsfile
// 鏡像倉庫位址
def registry = "registry.cn-beijing.aliyuncs.com"
// 項目&鏡像配置資訊
def namespace = "rj-bai"
def app_name = "dimpleblog"
def image_name = "${registry}/${namespace}/${app_name}:${BUILD_NUMBER}"
def git_address = "[email protected]:/home/rj-bai/DimpleBlog.git"
// 認證資訊ID
def docker_registry_auth = "400878cf-e740-459f-b8bf-ad2c749b833e"
def git_auth = "efb4268b-f758-4b58-9dcd-d966faf8360e"
def secret_name = "registry-secret"
def k8s_auth = "9823945c-07f6-495c-bc48-a47d8f43536c"
podTemplate(label: 'jenkins-slave', cloud: 'kubernetes', containers: [
containerTemplate(
name: 'jnlp',
image: "${registry}/${namespace}/jenkins-slave-jdk:1.8"
),
],
volumes: [
hostPathVolume(mountPath: '/var/run/docker.sock', hostPath: '/var/run/docker.sock'),
hostPathVolume(mountPath: '/usr/bin/docker', hostPath: '/usr/bin/docker')
],
)
{
node("jenkins-slave"){
stage('拉取代碼'){
checkout([$class: 'GitSCM', branches: [[name: '${Branch}']], userRemoteConfigs: [[credentialsId: "${git_auth}", url: "${git_address}"]]])
}
stage('代碼編譯'){
sh "mvn clean package -Dmaven.test.skip=true"
}
stage('建構鏡像'){
withCredentials([usernamePassword(credentialsId: "${docker_registry_auth}", passwordVariable: 'password', usernameVariable: 'username')]) {
sh label: '', script: ''' echo \'
FROM openjdk:8
ADD target/*.jar /
ADD entrypoint.sh /
RUN chmod +x /entrypoint.sh
CMD ["/bin/bash","/entrypoint.sh"]
\' > Dockerfile
echo \'
#!/bin/bash
app=`ls /*.jar`
java -jar $app
\' > entrypoint.sh'''
sh """
docker build -t ${image_name} .
docker login -u ${username} -p \"${password}\" ${registry}
docker push ${image_name}
"""
}
}
stage('部署到K8S'){
sh """
sed -i 's#\$IMAGE_NAME#${image_name}#' deploy-dimpleblog.yaml
sed -i 's#\$SECRET_NAME#${secret_name}#' deploy-dimpleblog.yaml
"""
kubernetesDeploy configs: 'deploy-dimpleblog.yaml', kubeconfigId: "${k8s_auth}"
}
}
}
也是按之前的改了改,這樣就夠了,然後送出代碼,
[root@master-1 ~/DimpleBlog]# git add -A
[root@master-1 ~/DimpleBlog]# git commit -m 'master'
[root@master-1 ~/DimpleBlog]# git push origin master
這裡準備好了,接下來就去
jenkins
操作吧,建立流水線任務,還是要添加字元參數,上文寫過了,不貼了,主要是看配置流水線那裡,選擇在代碼倉庫在去拉取
Jenkinsfile
,配置如下,
Branches to build
裡指定的是要在哪個分支去拉取
Jenkinsfile
,我們這裡指定
master
就可以了,這樣配置後腳本就不用寫在
jenkins
裡了,儲存開始建構吧,這次是一次就成功了,
寫
hosts
通路一下,
啊,就是這樣,能通路到,說明沒啥子問題,說真的這個部落格做的還是蠻不錯的,而且看上去開源的原因也是作者被逼無奈,哈哈
說實話部署這個部落格的流程和我部署我們公司項目的流程是一樣的,先部署項目需要的基礎服務,基礎服務部署完成後就可以部署項目了,部署完項目之後将項目釋出出來,當然之前是手寫
nginx
配置檔案,現在用的是
nginx-ingress
,這個東西真的好友善,自動關聯
service
後端
pod
,缺點之前也提過,隻能針對域名,不能針對端口。
剛剛扯到了基礎服務,目前公司部署項目依賴的基礎服務可不止
mysql&redis
,之前寫
swarm
實戰的時候提到過我們這裡會用到的基礎服務,之後如果沒什麼意外的話就開始做如何讓這些基礎服務跑在
k8s
上了。
其實還有一個最蛋疼的問題沒有解決,就是每添加一個項目你就要建立一個任務、寫一個資源檔案、寫一個
Jenkinsfile
,像我之前用的不是流水線,用的這個,
每次加一個項目我這裡就要建立一個任務,然後寫兩個
playbook
,一個更新的一個復原的,看一哈我之前寫的,有
29
個項目寫了
29
個釋出的
playbook
再之後
jenkins
融合
swarm
就好很多了,寫了一個可以複用的腳本去建立更新服務,定義了
N
多變量,也是通過
jenkins
傳進去的,這個就輕松很多了,我不知道我之前的方式能不能把
N
個項目串到一起,但是通過
pipeline
可以,下面給你個思路。
我這裡說的項目不是指的存在于
jenkins
中的
job
,說白了就是一個
pipeline
腳本可以将一套系統的所有項目都串起來,譬如之前
29
個項目組成一套系統,那時候建立了
29
個
job
,現在隻需一個
pipeline
就可以搞定了,但是你需要寫一個特别特别特别牛逼的
pipeline
腳本,配合
jenkins
的參數化建構去使用,通過
pipeline
去判斷變量去做對應的操作,全部更新釋出或是更新某些,這是一個很大的工程,涉及到的東西實在太多了,上面隻是對
pipeline
腳本有一個最初步的認識,更進階的使用方法自行琢磨吧,記住
pipeline
寫法能通過
jenkins
生成,思路放這裡了,自行琢磨吧,下面聊聊
k8S
滾動更新
K8S 滾動更新
k8s
更新項目的時候預設使用的政策就是滾動更新
(rollingUpdate)
,他的邏輯就是每一次更新一個或多個服務,更新完成之後會加入到
endpoints
來接收請求,不斷執行這個過程,直到叢集中的所有舊版本替換成新版本,譬如我上面的那個
webstarter
有三個副本,在執行滾動更新的時候會先更新一個或兩個,這一波沒問題的話就開始更新下一波,直到全部更新到新版本,特點就是無感覺平滑過渡,業務不受影響,預設政策,優先使用,下面看看它的原理是啥子。
滾動更新原理
其實滾動更新就是利用了再增加一個
ReplicaSet
去釋出新版本去實作新舊的替換,這個
ReplicaSet
之前也提到過,在你建立
Deployment
的時候就會生成一個
ReplicaSet
,它是用來管理你
pod
副本數量的,也是一個控制器,說白了就是
Deployment
通過
ReplicaSet
去管理你的
pod
,也做一個版本的記錄和滾動更新,這是
Deployment
引入
ReplicaSet
的目的。
在觸發滾動更新的時候,
Deployment
會再建立一個
ReplicaSet
去部署你的新版本,也就是這時候一個
Deployment
會有兩個
ReplicaSet
,這個新建立的
ReplicaSet
也會去關聯你的
service
,如果更新新版本莫得問題再去删除你的舊版本,全部更新完之後将新的
ReplicaSet
應用于目前的
Deployment
,保留舊的
ReplicaSet
用于復原,這樣就實作了滾動更新,言語是蒼白的,實際操作看一哈。
我直接用
jenkins
去更新一下最開始的那個
java-demo
了,因為他的副本數是三個,看着會比較明顯,等待更新完成,因為有健康檢查了,更新會比較慢撒,更新時候有一個居然被
OOMKilled
了,看到這個錯就是記憶體占用超了我的資源限制,是以就被殺了,是以在做資源限制的時候也要慎重,但這東西還不能不做,不做的話可能會給你的主控端帶來麻煩,現在已經更新完了。
先看一下
Deployment
的詳情,會有事件顯示,主要就是這裡,
可以看到在觸發滾動更新的時候新建立了一個名為
web-5c466f6896
的
replicasets
,設定它的副本數為
1
,這就是用來跑新版本的,這個正常啟動之後下一步操作是把名為
web-c98f55d49
的
replicasets
副本調整成了
2
,這是舊的
replicasets
,是以這時候就是有兩個舊版本一個新版本在跑,注意這不是同時進行的,是新的啟動成功之後才會去停舊的,如果新的啟動失敗了,滾動更新就會暫停,不會影響舊的
replicasets
,最終結果就是新的
web-5c466f6896
副本數設定為
3
,舊的
web-c98f55d49
設定成
0
,下面看一下
ReplicaSet
就明白了。
可以看到舊的
replicasets
副本數已經被設定成
0
了,建立時間是
26h
以前,新的建立于
12m
之前,副本數被設定成
3
,這樣就了解了撒,過程就是這樣,復原的話就是反向操作,先設定上一個版本的
replicasets
的副本數為
1
,一個舊的正常啟動後縮減一個目前的
replicasets
,我復原了一下,看圖吧。
[root@master-1 ~]# kubectl rollout undo deployment web
deployment.extensions/web rolled back
更新復原的原理就是這樣的,其實你可以利用他這個原理去實作灰階釋出,灰階釋出是啥子呢?說白了就是先更新部分的服務,譬如你有
10
個
pod
,我先更新兩個,這時候就叢集中就又有
2
個新的
8
個舊的,會有一小部分使用者去通路,如果新版本使用者沒回報就開始擴大範圍,我再更新
3
個,現在就一半一半了,如果使用者還沒啥子回報就全部更新到新版本,這種灰階釋出目前
K8s
是不支援的,變通一下就可以實作,思路如下。
需要使用兩套
deployment
,上面提到了,
deployment
會通過
replicasets
實作新舊版本的更新,灰階釋出實作的原理和他是樣的,譬如現在我要灰階釋出,現在已經有一個
deployment
了,我再建立一個部署新版應用的
deployment
,當然這個
deployment
的名字不要和之前一樣撒,相同的地方是這兩個的
deployment
所關聯的
service
是同一個,這樣當有新的
pod
啟動後就會和舊的
pod
并行提供服務,這個是重點,結合上面提到的東西,我先把新的
deployment
所關聯的
replicasets
進行擴充,譬如目前老版本有
10
個
pod
,我先啟動兩個新的,這兩個新的啟動完成之後我再去縮容舊的
pod
的數量,譬如我縮容到八個,這時候就有兩個新的八個舊的并行提供服務了,回報沒問題之後再進行同樣的操作,直到将新的擴容到
10
,舊的縮容成
0
,懂我啥意思了吧,這樣就實作了灰階釋出。
這樣做的好處是影響範圍可控,灰階釋出是目前比較主流的方案,想要實作這個方案有一個很大的難點,就是去控制每一波更新縮容的數量,要擴容多少新的,縮減多少舊的,就白了就是你要有效控制新的擴容舊的縮容,最簡單的辦法就是手動去
scale
,哈哈,最好的實作方法還是在程式層面去控制,思路就是這樣,下面來看看滾動更新的政策。
滾動更新政策
[root@master-1 ~]# kubectl get deployments.apps web -o yaml
strategy:
rollingUpdate:
maxSurge: 25%
maxUnavailable: 25%
type: RollingUpdate