背景
雖然雲原生時代有了
JenkinsX
[1]
、
Drone
[2]
、
Tekton
[3]
這樣的後起之秀,但 Jenkins 這樣一個老牌的 CI/CD 工具仍是各大公司主流的使用方案。比如我司的私有云產品打包釋出就是用這老傢伙完成的。然而傳統的 Jenkins Slave 一主多從方式會存在一些痛點,比如:
每個 Slave 的配置環境不一樣,來完成不同語言的編譯打包等操作,但是這些差異化的配置導致管理起來非常不方便,維護起來也是比較費勁
資源分配不均衡,有的 Slave 要執行的 job 出現排隊等待,而有的 Slave 處於空閒狀態
資源有浪費,每臺 Slave 可能是物理機或者虛擬機器,當 Slave 處於空閒狀態時,也不會完全釋放掉資源。
正因為上面的 Jenkins slave 存在這些種種痛點,我們渴望一種更高效更可靠的方式來完成這個 CI/CD 流程,而 Docker 虛擬化容器技術能很好的解決這個痛點,又特別是在 Kubernetes 叢集環境下面能夠更好來解決上面的問題,下圖是基於 Kubernetes 搭建 Jenkins slave 叢集的簡單示意圖:
從圖上可以看到 Jenkins Master
是
以 docker-compose 的方式執行在一個節點上。
Jenkins Slave 以 Pod 形式執行在 Kubernetes 叢集的 Node 上,並且它不是一直處於執行狀態,它會按照需求動態的建立並自動刪除。
這種方式的工作流程大致為:
當 Jenkins Master 接受到 Build 請求時,會根據配置的 Label 動態建立一個執行在 Pod 中的 Jenkins Slave 並註冊到 Master 上,當執行完 Job 後,這個 Slave 會被登出並且這個 Pod 也會自動刪除,恢復到最初狀態。
那麼我們使用這種方式帶來了以下好處:
動態伸縮
,合理使用資源,每次執行 Job 時,會自動建立一個 Jenkins Slave,Job 完成後,Slave 自動登出並刪除容器,資源自動釋放,而且 Kubernetes 會根據每個資源的使用情況,動態分配 Slave 到空閒的節點上建立,降低出現因某節點資源利用率高,還排隊等待在該節點的情況。
擴充套件性好
,當 Kubernetes 叢集的資源嚴重不足而導致 Job 排隊等待時,可以很容易的新增一個 Kubernetes Node 到叢集中,從而實現擴充套件。
上面的大半段複製貼上自
基於 Jenkins 的 CI/CD (一)
[4]
kubernetes 叢集
關於 kubernetes 叢集部署,使用 kubeadm 部署是最為方便的了,可參考我很早之前寫過的文章《
使用 kubeadm 快速部署體驗 K8s
[5]
》,在這裡只是簡單介紹一下:
使用 kubeadm 來建立一個單 master 節點的 kubernets 叢集
root@jenkins:~
# kubeadm init ——pod-network-cidr=10。244。0。0/16 ——apiserver-advertise-address=192。168。20。11
叢集成功部署完成之後會有如下提示:
Your Kubernetes control-plane has initialized successfully!
To start using your cluster, you need to run the following as a regular user:
mkdir -p
$HOME
/。kube
sudo cp -i /etc/kubernetes/admin。conf
$HOME
/。kube/config
sudo chown $(id -u):$(id -g)
$HOME
/。kube/config
檢視節點狀態和 pod 都已經正常
root@jenkins:~
# kubectl get pod -A
NAMESPACE NAME READY STATUS RESTARTS AGE
kube-system coredns-f9fd979d6-9t6qp 1/1 Running 0 89s
kube-system coredns-f9fd979d6-hntm8 1/1 Running 0 89s
kube-system etcd-jenkins 1/1 Running 0 106s
kube-system kube-apiserver-jenkins 1/1 Running 0 106s
kube-system kube-controller-manager-jenkins 1/1 Running 0 106s
kube-system kube-proxy-8pzkz 1/1 Running 0 89s
kube-system kube-scheduler-jenkins 1/1 Running 0 106s
root@jenkins:~
# kubectl get node
NAME STATUS ROLES AGE VERSION
jenkins Ready master 119s v1。19。8
去除 master 節點上的汙點,允許其他的 pod 排程在 master 節點上,不然後面 Jenkins 所建立的 pod 將無法排程在該節點上。
kubectl taint nodes $(hostname) node-role。kubernetes。io/master:NoSchedule-
Jenkins master
至於 Jenkins master 的部署方式,個人建議使用 docker-compose 來部署。執行在 kubernetes 叢集叢集中也沒什麼毛病,可以參考
基於 Jenkins 的 CI/CD (一)
[6]
這篇部落格。但從個人運維踩的坑來講,還是將 Jenkins master 獨立於 kubernetes 叢集部署比較方便 。
docker-compose。yaml
version:
‘3。6’
services:
jenkins:
image:
jenkins/jenkins:2。263。4-lts-slim
container_name:
jenkins
restart:
always
volumes:
-
。/jenkins_home:/var/jenkins_home
network_mode:
host
user:
root
environment:
-
JAVA_OPTS=-Duser。timezone=Asia/Shanghai
使用 docker-compose up 來啟動,成功啟動後會有如下提示,日誌輸出的金鑰就是
admin
使用者的預設密碼,使用它來第一次登入 Jenkins。
jenkins | 2021-03-06 02:22:31。741+0000 [id=41] INFO jenkins。install。SetupWizard
#init:
jenkins |
jenkins | *************************************************************
jenkins | *************************************************************
jenkins | *************************************************************
jenkins |
jenkins | Jenkins initial setup is required。 An admin user has been created and a password generated。
jenkins | Please use the following password to proceed to installation:
jenkins |
jenkins | 4c2361968cd94323acdde17f7603d8e1
jenkins |
jenkins | This may also be found at: /var/jenkins_home/secrets/initialAdminPassword
jenkins |
jenkins | *************************************************************
jenkins | *************************************************************
jenkins | *************************************************************
登入上去之後,建議選擇
選擇外掛來安裝
,儘可能少地安裝外掛,按需安裝即可。
在 Jenkins 的外掛管理那裡安裝上 kubernetes 外掛
接下來開始配置 Jenkins 大叔如何與 kubernetes 船長手牽手 :-)。配置 kubernets 的地方是在
系統管理 > 節點管理 > Configure Clouds
。點選
Add a new cloud
,來新增一個 kubernetes 叢集。
配置連線引數
引數
值
說明
名稱
kubernetes
也是後面 pod 模板中的
cloud
的值
憑據
kubeconfig 憑據 id
使用 kubeconfig 檔案來連線叢集
Kubernetes 地址
預設即可
Use Jenkins Proxy
預設即可
Kubernetes 服務證書 key
預設即可
禁用 HTTPS 證書檢查
預設即可
Kubernetes 名稱空間
預設即可
WebSocket
預設即可
Direct Connection
預設即可
Jenkins 地址
http://jenkins。k8s。li:8080
Jenkins pod 連線 Jenkins master 的 URL
Jenkins 通道
50000
Jenkins
JNLP
的埠,預設為 50000
Connection Timeout
預設即可
Jenkins 連線 kubernetes 超時時間
Read Timeout
預設即可
容器數量
預設即可
Jenkins pod 建立的最大數量
Pod Labels
預設即可
Jenkins pod 的 lables
連線 Kubernetes API 的最大連線數
預設即可
Seconds to wait for pod to be running
預設即可
等待 pod 正常 running 的時間
在 Jenkins 的憑據那裡新增上 kubeconfig 檔案,憑據的型別選擇為
Secret file
,然後將上面使用 kubeadm 部署生成的 kubeconfig 上傳到這裡。
點選連線測試,如果提示
Connected to Kubernetes v1。19。8
就說明已經成功連線上了 kubernetes 叢集。
關於 pod 模板
其實就是配置 Jenkins Slave 執行的 Pod 模板,個人不太建議使用外掛中的模板去配置,推薦將 pod 的模板放在 Jenkinsfile 中,因為這些配置與我們的流水線緊密相關,把 pod 的配置儲存在 Jenkins 的外掛裡實在是不太方便;不方便後續的遷移備份之類的工作;後續外掛升級後這些配置也可能會丟失。因此建議將 pod 模板的配置直接定義在 Jenkinsfile 中,靈活性更高一些,不會受 Jenkins 外掛升級的影響。總之用程式碼去管理這些 pod 配置維護成本將會少很多。
Jenkinsfile
流水線
Jenkinsfile
,下面是一個簡單的任務,用於構建
webp-server-go
[7]
專案的 docker 映象。
//
Kubernetes
pod
template
to
run。
def
JOB_NAME
=
“${env。JOB_NAME}”
def
BUILD_NUMBER
=
“${env。BUILD_NUMBER}”
def
POD_NAME
=
“jenkins-${JOB_NAME}-${BUILD_NUMBER}”
podTemplate(
# 這裡定義 pod 模版
)
{
node(POD_NAME)
{
container(JOB_NAME)
{
stage(“Build
image”)
{
sh
“”
“#!/bin/bash
git clone https://github。com/webp-sh/webp_server_go /build
cd /build
docker build -t webps:0。3。2-rc。1 。
”
“”
}
}
}
}
pod 模版如下,將模板的內容複製貼上到上面的 Jenkinsfile 中。在容器中構建映象,我們使用 dind 的方案:將 pod 所在宿主機的 docker sock 檔案掛載到 pod 的容器內,pod 容器內只要安裝好 docker-cli 工具就可以像宿主機那樣直接使用 docker 了。
podTemplate(
cloud:
“kubernetes”
,
namespace:
“default”
,
name:
POD_NAME,
label:
POD_NAME,
yaml:
“”
“
apiVersion: v1
kind: Pod
spec:
containers:
- name: ${JOB_NAME}
image: ”
debian:buster-docker“
imagePullPolicy:
IfNotPresent
tty:
true
volumeMounts:
-
name:
dockersock
mountPath:
/var/run/docker。sock
-
name:
jnlp
args:
[”\$(JENKINS_SECRET)“,
”\$(JENKINS_NAME)“
]
image:
”jenkins/inbound-agent:4。3-4-alpine“
imagePullPolicy:
IfNotPresent
volumes:
-
name:
dockersock
hostPath:
path:
/var/run/docker。sock
”“
”,
)
構建
debian:buster-docker
映象,使用它來在 pod 的容器內構建 docker 映象,使用的
Dockerfile
如下:
FROM
debian:buster
RUN
apt update \
&& apt install -y ——no-install-recommends \
vim \
curl \
git \
make \
ca-certificates \
gnupg \
&& rm -rf /var/lib/apt/lists/*
RUN
curl -fsSL
“https://download。docker。com/linux/debian/gpg”
| apt-key add -qq - >/dev/null \
&&
echo
“deb [arch=amd64] https://download。docker。com/linux/debian buster stable”
> /etc/apt/sources。list。d/docker。list \
&& apt update -qq \
&& apt-get install -y -qq ——no-install-recommends docker-ce-cli \
&& rm -rf /var/lib/apt/lists/*
定義好 jenkinsfile 檔案並且構建好 pod 模板中的映象後,接下來我們開始使用它來建立流水線任務。
流水線
在 Jenkins 上新建一個任務,選擇任務的型別為
流水線
將定義好的 Jenkinsfile 內容複製貼上到流水線定義
Pipeline script
中並點選儲存。在新建好的 Job 頁面點選
立即構建
來執行流水線任務。
在 kubernetes 叢集的機器上使用 kubectl 命令檢視 pod 是否正常 Running
root@jenkins:~
# kubectl get pod
NAME READY STATUS RESTARTS AGE
jenkins-webps-9-bs78x-5x204 2/2 Running 0 66s
Job 正常執行並且狀態為綠色表明該 job 已經成功執行了。
在 kubernetes 叢集機器上檢視 docker 映象是否構建成功
root@jenkins:~
# docker images | grep webps
webps 0。3。2-rc。1 f68f496c0444 20 minutes ago 13。7MB
踩坑
pod 無法正常 Running
Running
in
Durability level: MAX_SURVIVABILITY
[Pipeline] Start of Pipeline
[Pipeline] podTemplate
[Pipeline] {
[Pipeline] node
Created Pod: kubernetes default/debian-35a11b49-087b-4a8c-abac-bd97d7eb5a1f-fkmzq-9wm0r
[Normal][default/debian-35a11b49-087b-4a8c-abac-bd97d7eb5a1f-fkmzq-9wm0r][Scheduled] Successfully assigned default/debian-35a11b49-087b-4a8c-abac-bd97d7eb5a1f-fkmzq-9wm0r to jenkins
[Normal][default/debian-35a11b49-087b-4a8c-abac-bd97d7eb5a1f-fkmzq-9wm0r][Pulling] Pulling image
“debian:buster”
[Normal][default/debian-35a11b49-087b-4a8c-abac-bd97d7eb5a1f-fkmzq-9wm0r][Pulled] Successfully pulled image
“debian:buster”
in
2。210576896s
[Normal][default/debian-35a11b49-087b-4a8c-abac-bd97d7eb5a1f-fkmzq-9wm0r][Created] Created container debian
[Normal][default/debian-35a11b49-087b-4a8c-abac-bd97d7eb5a1f-fkmzq-9wm0r][Started] Started container debian
[Normal][default/debian-35a11b49-087b-4a8c-abac-bd97d7eb5a1f-fkmzq-9wm0r][Pulling] Pulling image
“jenkins/inbound-agent:4。3-4-alpine”
Still waiting to schedule task
‘debian-35a11b49-087b-4a8c-abac-bd97d7eb5a1f-fkmzq-9wm0r’ is offline
[Normal][default/debian-35a11b49-087b-4a8c-abac-bd97d7eb5a1f-fkmzq-9wm0r][Pulled] Successfully pulled image
“jenkins/inbound-agent:4。3-4-alpine”
in
3。168311973s
[Normal][default/debian-35a11b49-087b-4a8c-abac-bd97d7eb5a1f-fkmzq-9wm0r][Created] Created container jnlp
[Normal][default/debian-35a11b49-087b-4a8c-abac-bd97d7eb5a1f-fkmzq-9wm0r][Started] Started container jnlp
Created Pod: kubernetes default/debian-35a11b49-087b-4a8c-abac-bd97d7eb5a1f-fkmzq-qdw4m
[Normal][default/debian-35a11b49-087b-4a8c-abac-bd97d7eb5a1f-fkmzq-qdw4m][Scheduled] Successfully assigned default/debian-35a11b49-087b-4a8c-abac-bd97d7eb5a1f-fkmzq-qdw4m to jenkins
[Normal][default/debian-35a11b49-087b-4a8c-abac-bd97d7eb5a1f-fkmzq-qdw4m][Pulled] Container image
“debian:buster”
already present on machine
[Normal][default/debian-35a11b49-087b-4a8c-abac-bd97d7eb5a1f-fkmzq-qdw4m][Created] Created container debian
[Normal][default/debian-35a11b49-087b-4a8c-abac-bd97d7eb5a1f-fkmzq-qdw4m][Started] Started container debian
[Normal][default/debian-35a11b49-087b-4a8c-abac-bd97d7eb5a1f-fkmzq-qdw4m][Pulled] Container image
“jenkins/inbound-agent:4。3-4-alpine”
already present on machine
[Normal][default/debian-35a11b49-087b-4a8c-abac-bd97d7eb5a1f-fkmzq-qdw4m][Created] Created container jnlp
[Normal][default/debian-35a11b49-087b-4a8c-abac-bd97d7eb5a1f-fkmzq-qdw4m][Started] Started container jnlp
這是因為 Jenkins pod 中的 jnlp 容器無法連線 Jenkins master。可以檢查一下 Jenkins master 上
系統管理 > 節點管理 > Configure Clouds
中
Jenkins 地址
和
Jenkins 通道
這兩個引數是否配置正確。
結束
到此為止,我們就完成了讓 Jenkins 大叔與 kubernetes 船長手牽手 啦!上面使用了一個簡單的例子來展示瞭如何將 Jenkins 的 Job 任務執行在 kubernetes 叢集上,但在實際工作中遇到的情形可能比這要複雜一些,流水線需要配置的引數也要多一些。那麼我將會在下一篇部落格中再講一下高階的用法:使用 Jenkins 完成 kubespray 離線安裝包打包。
參考資料
[1]
JenkinsX:
[2]
Drone:
[3]
Tekton:
[4]
基於 Jenkins 的 CI/CD (一):
[5]
使用 kubeadm 快速部署體驗 K8s:
[6]
基於 Jenkins 的 CI/CD (一):
[7]
webp-server-go:
[8]
使用 Kubernetes 和 Jenkins 建立一個 CI/CD 流水線:
[9]
基於 Jenkins 的 CI/CD (一):
[10]
PingCAP 面試:Jenkins 和 Kubernetes:
[11]
基於 Kubernetes 的 Jenkins 服務也可以去 Docker 了:
[12]
Jenkins Pipeline 使用及除錯:
[13]
在 Kubernetes 上動態建立 Jenkins Slave:
[14]
Jenkins X 不是 Jenkins ,而是一個技術棧:
[15]
Jenkins CI/CD (一) 基於角色的授權策略:
原文連結:
https://blog。k8s。li/jenkins-with-kubernetes。html
本文轉載自:
「雲原生實驗室」,原文:
https://tinyurl。com/435h9kwu,版權歸原作者所有。