Jenkins Pipeline 流水線部署 Kubernetes 應用

Jenkins Pipeline 流水線部署 Kubernetes 應用

背景

雖然雲原生時代有了

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,版權歸原作者所有。