天天看點

使用 jenkins 建構 CI/CD 平台

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​

​ 釋出你的應用,釋出之後就可以通路了撒,流程圖如下。

使用 jenkins 建構 CI/CD 平台

​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      
使用 jenkins 建構 CI/CD 平台

通路一下,

使用 jenkins 建構 CI/CD 平台

部署還是比較簡單的,随便推個鏡像試試。

使用 jenkins 建構 CI/CD 平台

這個就比較煩人了,我特麼實在是不想重新開機 ​

​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​

​。

使用 jenkins 建構 CI/CD 平台

這裡使用了 ​

​PV​

​​ 的模闆,是需要你提供 ​

​PV​

​​ 自動供給的支援的,預設的類型是 ​

​anything​

​​,目前我隻有一個 ​

​NFS​

​ 的,也就是這個。

使用 jenkins 建構 CI/CD 平台

是以說一會建立的時候它預設就會用這個了,如果你有兩種類型的存儲且需要指定就按正常流程走指定就完了,申請磁盤空間這裡我把它改成 ​

​5G​

​​ 了,​

​1G​

​​ 實在是有點小,還有資源限制那裡,限制最小 ​

​0.5​

​​ 核 ​

​CPU&0.5G​

​​ 記憶體,最大 ​

​1​

​​ 核 ​

​1G​

​​ 記憶體,說實話有點小,最起碼記憶體調大一點,我這裡最大限制都調成 ​

​2​

​ 了。

又看了一眼 ​

​Service​

​​ 這裡,通路方式用的是 ​

​Ingress​

​​,你可以用 ​

​NodePort​

​​ 方式去通路,我這裡用 ​

​Ingress​

​​ 方式去做了,好久沒搞過 ​

​nginx-ingress​

​​ 了,先把 ​

​tls​

​​ 方法去了,​

​hosts​

​ 随便改一下,我改完的如下。

使用 jenkins 建構 CI/CD 平台

如果你不想用 ​

​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 個沒啟用,我翻譯了一下,結果如下。

使用 jenkins 建構 CI/CD 平台

好像暫時沒啥子能用到的,檢視狀态的可以考慮開啟,我這裡暫時就不開啟了,直接建立了。

[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      
使用 jenkins 建構 CI/CD 平台

這樣就可以了撒,部署完了,所有 ​

​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      
使用 jenkins 建構 CI/CD 平台

正常啟動了,寫個 ​

​hosts​

​ 就通路就可以了撒。

使用 jenkins 建構 CI/CD 平台

不用去看這個檔案,​

​pod​

​​ 日志裡有輸出密碼,直接看 ​

​pod​

​ 日志就行了。

[root@master-1 ~]# kubectl logs jenkins-0      
使用 jenkins 建構 CI/CD 平台

之後就需要裝插件了,不裝任何插件,一會用啥子裝啥子,

使用 jenkins 建構 CI/CD 平台

現在不裝插件最主要的原因其實就是有些插件裝了需要重新開機 ​

​jenkins​

​​ 才可以,如果現在裝完了插件完成後續操作後會回到 ​

​jenkins​

​​ 首頁面,但是 ​

​jenkins​

​​ 首頁面就是一片空白,需要重新開機 ​

​jenkins​

​​ 才可以,當然 ​

​pod​

​​ 是沒有重新開機這個概念的,你得想辦法把之前的 ​

​pod​

​​ 弄死,​

​k8s​

​ 幫你重新拉起一個之後就正常了,是以先不裝插件,正常的話完成後續操作就可以看到首頁面了。

使用 jenkins 建構 CI/CD 平台

現在 ​

​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 建構 CI/CD 平台

裝完之後需要配置一下這個插件,使 ​

​jenkins​

​​ 支援 ​

​kubernetes​

​​,點開系統管理→系統設定→拉到最下面,可以看到這個,直接點進去,​

​Kubernetes​

​​ 這裡的話配這樣就行了,這裡寫的都是 ​

​dns​

​ 名稱。

使用 jenkins 建構 CI/CD 平台

測試連接配接莫得問題,憑據和 ​

​key​

​​ 都不用到,都已經 ​

​rbac​

​​ 授權了,如果是部署在叢集外 ​

​key​

​​ 那裡就需要寫 ​

​apiserver​

​​ 的 ​

​CA​

​​ 資訊了,再然後就是配置 ​

​jenkins​

​ 這裡了。

使用 jenkins 建構 CI/CD 平台

這樣就可以了,如果你的 ​

​jenkins​

​​ 在叢集外 ​

​kubernetes​

​​ 位址就不要寫内部 ​

​DNS​

​​ 名稱了,​

​key​

​​ 那裡需要配置 ​

​apiserver​

​​ 的 ​

​CA​

​​ 證書就可以了,下面還有一個添加 ​

​pod​

​ 模闆,也就是這個,

使用 jenkins 建構 CI/CD 平台

這個東西就是定義如何建立 ​

​slave pod​

​​ 的一個模闆,你可以了解為這裡就是一個 ​

​YAML​

​​ 檔案定義了怎麼去建立 ​

​slave-pod​

​​,隻不過是通過 ​

​UI​

​​ 的形式去配置,這一塊也可以通過 ​

​Pipeline​

​​ 腳本去定義,是以不需要在這裡配置這個了,如果在這裡配置你可能每次添加一個項目都要在這裡配置一次,不友善管理,在 ​

​Pipeline​

​​ 裡面配置就比較友善了,是以目前隻需要配置怎麼連接配接 ​

​kubernetes​

​​ 就可以了,這個差掉,儲存退出就可以了,下面開始建構 ​

​slave​

​ 鏡像。

建構 jenkins-slave 鏡像

做一個驗證,都配置完了,到底能不能用,直接用流水線了,目前還沒裝這個插件,是以先裝一下,插件名就是 ​

​Pipeline​

使用 jenkins 建構 CI/CD 平台

這次裝的比較多,讓他裝着吧,裝完後自動重新開機,一會會寫一個 ​

​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 .      
使用 jenkins 建構 CI/CD 平台

這樣就建構完了,然後把這個鏡像推送到鏡像倉庫,我不用剛剛搭建的 ​

​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 {}​

​,這兩種使用哪個都可以,看一下官方的栗子。

示例位址,

聲明式

使用 jenkins 建構 CI/CD 平台

腳本式

使用 jenkins 建構 CI/CD 平台

我的頭開始大了,後面主要是使用腳本式,這東西的定義就是一個文本檔案,也稱為 ​

​Jenkinsfile​

​,下面建立一個流水線任務來玩玩。

建立流水線任務

自行建立吧,建立完任務之後拉到最下面,選這個,會自動補全一個例子,改改這個例子。

使用 jenkins 建構 CI/CD 平台

改成這樣。

使用 jenkins 建構 CI/CD 平台

這樣就可以了,儲存,然後建構一下,建構成功後你會看到這個。

使用 jenkins 建構 CI/CD 平台

可以看到剛剛定義的三個步驟以圖表的形式展現了出來,這是一個最簡單的示例,腳本裡面都有多個 ​

​stage​

​​,這個 ​

​stage​

​​ 是腳本最基本的組成部分,它用來告訴 ​

​jenkins​

​​ 要去幹什麼,之後就需要在這個腳本中實作從代碼拉取到部署到 ​

​k8s​

​ 的全部過程,官方的原理圖。

使用 jenkins 建構 CI/CD 平台

說白了還是完成了從建構到釋出的流程,是以接下來就開始搞在腳本中完成這些操作。

拉取代碼

第一步就是拉取代碼,到底怎麼拉,這些步驟的文法都可以通過 ​

​Pipeline​

​ 腳本文法去幫我們生成,這個是重點,也就是這個位置。

使用 jenkins 建構 CI/CD 平台

新到一個頁面,譬如我現在要拉取 ​

​git​

​​ 倉庫 ​

​rj-bai.git​

​ 的代碼,我就可以這樣做了,

使用 jenkins 建構 CI/CD 平台

使用 ​

​master​

​​ 分支,但是提示無法連接配接到這個倉庫,這裡也是需要免互動拉取代碼的,之前的操作就是将 ​

​master​

​​ 公鑰拷貝到了 ​

​git​

​​ 伺服器,是以現在要用到私鑰了,需要添加一個憑據,将使用者名私鑰添加進去添加即可,如果你是 ​

​SVN​

​​ 請添加使用者名密碼,​

​SVN​

​ 怎麼拉代碼下面會提到,

使用 jenkins 建構 CI/CD 平台

添加後回到首頁面之後報錯沒了,說明可以免互動拉取代碼了,點選 ​

​Generate Pipeline Script​

​ 之後就會生拉取代碼的腳本了。

使用 jenkins 建構 CI/CD 平台

複制這一串子貼到這個位置就可以拉取代碼了,我順便執行了一條 ​

​shell​

​ 指令

使用 jenkins 建構 CI/CD 平台

儲存建構,看了一下 ​

​log​

​​ 輸出,之前添加的 ​

​Index.html​

​ 已經拉取下來了,說明拉取代碼這塊莫得問題。

使用 jenkins 建構 CI/CD 平台

能拉取代碼了,下一步就要開始編譯了,下面看一下如何編譯代碼。

編譯代碼

上面執行的這些任務都是在 ​

​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​

​ 狀态。

使用 jenkins 建構 CI/CD 平台

這裡建構成功了,看一下 ​

​k8s​

​​ 叢集中 ​

​pod​

​ 的變化。

使用 jenkins 建構 CI/CD 平台

這就是動态建立 ​

​slave​

​​ 了,在需要的時候建立一個,​

​slave​

​ 完成任務後就會被銷毀,這一塊沒啥子問題,下面準備一下要編譯的源代碼。

準備 java 源代碼

既然是編譯,你就需要有 ​

​java​

​​ 的源碼了,建議在這裡生成一個,目前隻要有簡單的 ​

​web​

​​ 通路就夠了,是以選這個,編譯成功後會有一個 ​

​jar​

​​ 檔案,​

​jdk​

​​ 版本 ​

​1.8​

​,這樣就可以了。

使用 jenkins 建構 CI/CD 平台

選完點選綠色按鈕你會下載下傳一個名為 ​

​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      
使用 jenkins 建構 CI/CD 平台

這樣就可以了撒,其實 ​

​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​

​,很久之前在别的文章裡提過這個,先來解釋一下我鏡像倉庫那裡為什麼這樣寫,看一下我鏡像倉庫完整的位址你就懂了。

使用 jenkins 建構 CI/CD 平台
使用 jenkins 建構 CI/CD 平台

如果你是自建的鏡像倉庫可以不加 ​

​namespace​

​​ 的配置,我這裡就必須得加了,還有一些奇怪的 ​

​ID​

​,下面分别來解釋一下都是用來幹嘛的。

​docker_registry_auth​

​​ 是用來拉取推送鏡像的憑據,但是那裡寫的是憑據的 ​

​ID​

​​,這個憑證是 ​

​slave-pod​

​ 所使用的,因為他需要向私有倉庫推送鏡像,需要登陸後才能去推送鏡像,添加方法如下。

首頁面→憑據→系統→全局憑據→添加,類型就是使用者名密碼,然後填進去點 ​

​ok​

​​ 就可以了,​

​ID​

​ 會自動生成一個。

使用 jenkins 建構 CI/CD 平台

添加之後會自動傳回,然後點更新的按鈕就可以檢視到 ​

​ID​

​ 了,要和腳本中的對應上。

使用 jenkins 建構 CI/CD 平台

再下面的那個 ​

​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']])      
使用 jenkins 建構 CI/CD 平台

在看建構鏡像那裡也有奇怪的一段,也就是這個,

withCredentials([usernamePassword(credentialsId: "${docker_registry_auth}", passwordVariable: 'password', usernameVariable: 'username')]) {      

這一段是用來儲存登陸鏡像倉庫的使用者名和密碼,以變量的形式,這樣做的主要原因就是不會讓使用者名密碼的明文暴露在 ​

​pipeline​

​ 腳本裡,也是動态生成的,如果所示。

使用 jenkins 建構 CI/CD 平台

和我腳本中的一緻,隻不過我腳本中定義的是 ​

​ID​

​​ 的變量,說白了這樣做就是将使用者名存到了名為 ​

​username​

​​ 的變量中,密碼儲存到了 ​

​password​

​​ 變量中,直接引用就完了,不會暴露你使用者名密碼明文,就是這個原理,再下面寫了一個 ​

​Dockerfile​

​​ 和一個啟動腳本,現在還有一個變量沒有去定義,也就是 ​

​Branch​

​​,用來定義要建構的分支,​

​git​

​​ 的分支會有很多,不止是一個 ​

​master​

​,是以說不是固定的,是以現在定義一下這個變量。

編輯這個 ​

​job​

​​,找到參數化建構過程,配置成這樣,使用 ​

​SVN​

​ 的不用改這裡撒,

使用 jenkins 建構 CI/CD 平台

這就是一個變量,預設值是 ​

​master​

​​,儲存回到主界面,點 ​

​Build with Parameters​

​,會看到這種效果。

使用 jenkins 建構 CI/CD 平台

中途失敗了好多次,各種調試,之前也成功過,但是第 25 次是才是真正意義上的成功,

使用 jenkins 建構 CI/CD 平台

現在鏡像已經推送到我阿裡雲的倉庫了,直接在伺服器上拉一下吧,試着運作一下。

[root@kubeadm-node ~]# docker run -d -p 666:8080 registry.cn-beijing.aliyuncs.com/rj-bai/webstarter:25      
使用 jenkins 建構 CI/CD 平台

莫得問題,可以正常通路,說明之前的步驟都沒問題了,現在通過 ​

​pipeline​

​​ 完成了 ​

​CI​

​​ 階段,拉取代碼編譯建構鏡像推送鏡像,感覺是比之前的友善很多了,下一步就是需要将剛剛的東西部署到 ​

​K8s​

​​ 中了,也就是 ​

​CD​

​ 階段。

jenkins 在 k8s 中持續部署

上面鏡像已經準備好了,現在該實作自動部署到 ​

​k8s​

​​ 中了,要想實作這個還需要一個插件,名為 ​

​kubernetes continuous deploy​

​​,用于将資源部署到 ​

​k8s​

​​ 中,他支援絕大部分的資源類型,像是什麼 ​

​deployment&service​

​,自行安裝吧,簡單看一下這個插件的介紹。

官方位址,主要看一下這一段,這是配置在 ​

​pipeline​

​ 中使用的寫法

使用 jenkins 建構 CI/CD 平台

​kubeconfigId​

​​ 這裡是需要指定一個 ​

​kubeconfig​

​​ 的 ​

​ID​

​​,這個東西就是用于連接配接 ​

​k8s​

​​ 的一個配置檔案,是以我們要把這個檔案内容儲存到 ​

​jenkins​

​​ 中作為憑據,然後去引用憑據的 ​

​ID​

​。

​config​

​​ 這裡用來指定資源檔案,也就是你部署服務的 ​

​YAML​

​ 檔案。

secretNamespace: '<secret-namespace>',
                 secretName: '<secret-name>',      

這塊是用來指定 ​

​secret​

​​ 的,有兩個是必須的,一個是 ​

​kubeconfig​

​​ 檔案,再就是資源檔案,這個插件的 ​

​pipeline​

​ 寫法也是可以生成的,是以還是用之前的工具來生成一下。

使用 jenkins 建構 CI/CD 平台

現在開始讓你添加 ​

​kubeconfig​

​ 的檔案了,添加吧,選擇這個,

使用 jenkins 建構 CI/CD 平台

這個檔案具體要怎麼擷取,如果你的叢集是 ​

​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​

​ 的狀态,先看頁面,顯示成功。

使用 jenkins 建構 CI/CD 平台

再看 ​

​pod​

​ 的狀态,都已經正常啟動了,

使用 jenkins 建構 CI/CD 平台

然後手寫 ​

​hosts​

​ 通路一下,

使用 jenkins 建構 CI/CD 平台

莫得問題,就是這種效果,現在也完成了持續部署,生成的 ​

​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​

​,配置如下,

使用 jenkins 建構 CI/CD 平台

​Branches to build​

​​ 裡指定的是要在哪個分支去拉取 ​

​Jenkinsfile​

​​,我們這裡指定 ​

​master​

​​ 就可以了,這樣配置後腳本就不用寫在 ​

​jenkins​

​ 裡了,儲存開始建構吧,這次是一次就成功了,

使用 jenkins 建構 CI/CD 平台
使用 jenkins 建構 CI/CD 平台

寫 ​

​hosts​

​ 通路一下,

使用 jenkins 建構 CI/CD 平台

啊,就是這樣,能通路到,說明沒啥子問題,說真的這個部落格做的還是蠻不錯的,而且看上去開源的原因也是作者被逼無奈,哈哈

說實話部署這個部落格的流程和我部署我們公司項目的流程是一樣的,先部署項目需要的基礎服務,基礎服務部署完成後就可以部署項目了,部署完項目之後将項目釋出出來,當然之前是手寫 ​

​nginx​

​​ 配置檔案,現在用的是 ​

​nginx-ingress​

​​,這個東西真的好友善,自動關聯 ​

​service​

​​ 後端 ​

​pod​

​,缺點之前也提過,隻能針對域名,不能針對端口。

剛剛扯到了基礎服務,目前公司部署項目依賴的基礎服務可不止 ​

​mysql&redis​

​​,之前寫 ​

​swarm​

​​ 實戰的時候提到過我們這裡會用到的基礎服務,之後如果沒什麼意外的話就開始做如何讓這些基礎服務跑在 ​

​k8s​

​ 上了。

其實還有一個最蛋疼的問題沒有解決,就是每添加一個項目你就要建立一個任務、寫一個資源檔案、寫一個 ​

​Jenkinsfile​

​,像我之前用的不是流水線,用的這個,

使用 jenkins 建構 CI/CD 平台

每次加一個項目我這裡就要建立一個任務,然後寫兩個 ​

​playbook​

​​,一個更新的一個復原的,看一哈我之前寫的,有 ​

​29​

​​ 個項目寫了 ​

​29​

​​ 個釋出的 ​

​playbook​

使用 jenkins 建構 CI/CD 平台

再之後 ​

​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​

​ 了,看到這個錯就是記憶體占用超了我的資源限制,是以就被殺了,是以在做資源限制的時候也要慎重,但這東西還不能不做,不做的話可能會給你的主控端帶來麻煩,現在已經更新完了。

使用 jenkins 建構 CI/CD 平台

先看一下 ​

​Deployment​

​ 的詳情,會有事件顯示,主要就是這裡,

使用 jenkins 建構 CI/CD 平台

可以看到在觸發滾動更新的時候新建立了一個名為 ​

​web-5c466f6896​

​​ 的 ​

​replicasets​

​​,設定它的副本數為 ​

​1​

​​,這就是用來跑新版本的,這個正常啟動之後下一步操作是把名為 ​

​web-c98f55d49​

​​ 的 ​

​replicasets​

​​ 副本調整成了 ​

​2​

​​,這是舊的 ​

​replicasets​

​​,是以這時候就是有兩個舊版本一個新版本在跑,注意這不是同時進行的,是新的啟動成功之後才會去停舊的,如果新的啟動失敗了,滾動更新就會暫停,不會影響舊的 ​

​replicasets​

​​,最終結果就是新的 ​

​web-5c466f6896​

​​ 副本數設定為 ​

​3​

​​,舊的 ​

​web-c98f55d49​

​​ 設定成 ​

​0​

​​,下面看一下 ​

​ReplicaSet​

​ 就明白了。

使用 jenkins 建構 CI/CD 平台

可以看到舊的 ​

​replicasets​

​​ 副本數已經被設定成 ​

​0​

​​ 了,建立時間是 ​

​26h​

​​ 以前,新的建立于 ​

​12m​

​​ 之前,副本數被設定成 ​

​3​

​​,這樣就了解了撒,過程就是這樣,復原的話就是反向操作,先設定上一個版本的 ​

​replicasets​

​​ 的副本數為 ​

​1​

​​,一個舊的正常啟動後縮減一個目前的 ​

​replicasets​

​,我復原了一下,看圖吧。

[root@master-1 ~]# kubectl rollout undo deployment web
deployment.extensions/web rolled back      
使用 jenkins 建構 CI/CD 平台

更新復原的原理就是這樣的,其實你可以利用他這個原理去實作灰階釋出,灰階釋出是啥子呢?說白了就是先更新部分的服務,譬如你有 ​

​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      

繼續閱讀