GKE 上的 Jenkins 多分支管道

1. 總覽

Jenkins 是目前最受歡迎的持續整合解決方案之一。這項工具可用於自動化軟體開發流程中非人為介入的必要部分。我們將 Jenkins 部署至 Google Cloud 上的 Kubernetes 並使用 GKE 外掛程式,就能視需要快速且自動地調度建構執行器。搭配使用 Cloud Storage,我們就能輕鬆建構及測試應用程式。

執行步驟

  • 將 Jenkins 部署至 Kubernetes 叢集
  • 部署並設定 Jenkins GKE 外掛程式,讓 Jenkins 能夠建立及刪除 Pod 做為執行緒節點
  • 建構及測試範例 SpringBoot 應用程式
  • 建構並發布至 Google Container Registry 的容器
  • 將範例應用程式部署至測試和實際工作 GKE 環境

軟硬體需求

  • 已設定帳單的 Google Cloud 專案。如果沒有,請建立

2. 開始設定

這個程式碼研究室可以完全在 Google Cloud Platform 上執行,無須在本機安裝或設定任何內容。

Cloud Shell

在本程式碼研究室中,我們將透過 Cloud Shell 使用指令列來佈建及管理不同的雲端資源和服務。

啟用 API

以下是我們需要在專案中啟用的 API:

  • Compute Engine API - 建立及執行虛擬機器
  • Kubernetes Engine API:建構及管理容器型應用程式
  • Cloud Build API - Google Cloud 的持續整合和持續部署平台
  • Service Management API:允許服務供應者在 Google Cloud Platform 上發布服務
  • Cloud Resource Manager API:建立、讀取及更新 Google Cloud 資源容器的結構描述

使用下列 gcloud 指令啟用必要的 API:

gcloud services enable compute.googleapis.com \
container.googleapis.com \
cloudbuild.googleapis.com \
servicemanagement.googleapis.com \
cloudresourcemanager.googleapis.com \
--project ${GOOGLE_CLOUD_PROJECT}

建立 GCS 值區

我們需要 GCS 儲存格來上傳測試工作。我們將使用專案 ID 做為值區名稱,確保名稱不重複:

gsutil mb gs://${GOOGLE_CLOUD_PROJECT}-jenkins-test-bucket/ 

3. 建立 Kubernetes 叢集

建立叢集

接下來,我們將建立 GKE 叢集,用於代管 Jenkins 系統,包括要以工作節點形式調派的 Pod。--scopes 旗標所指定的額外範圍可讓 Jenkins 存取 Cloud Source Repositories 和 Container Registry。在 Cloud 控制台中執行下列操作:

gcloud container clusters create jenkins-cd \
--machine-type n1-standard-2 --num-nodes 1 \
--zone us-east1-d \
--scopes "https://www.googleapis.com/auth/source.read_write,cloud-platform" \
--cluster-version latest

我們也要部署 2 個叢集,用於代管範例應用程式的測試群組和正式版版本:

gcloud container clusters create staging \
--machine-type n1-standard-2 --num-nodes 1 \
--zone us-east1-d \
--cluster-version latest
gcloud container clusters create prod \
--machine-type n1-standard-2 --num-nodes 2 \
--zone us-east1-d \
--cluster-version latest

28b45298e1e82748.png 驗證

建立叢集後,我們可以確認叢集是否正在執行 gcloud container clusters list

輸出內容的 STATUS 欄應包含 RUNNING

NAME        LOCATION    MASTER_VERSION  MASTER_IP     MACHINE_TYPE   NODE_VERSION  NUM_NODES  STATUS
jenkins-cd  us-east1-d  1.15.9-gke.9    34.74.77.124  n1-standard-2  1.15.9-gke.9  2          RUNNING
prod        us-east1-d  1.15.9-gke.9    35.229.98.12  n1-standard-2  1.15.9-gke.9  2          RUNNING
staging     us-east1-d  1.15.9-gke.9    34.73.92.228  n1-standard-2  1.15.9-gke.9  2          RUNNING

4. 使用 Helm 部署 Jenkins

安裝 Helm

我們會使用 Helm (Kubernetes 的應用程式套件管理工具) 在叢集上安裝 Jenkins。如要開始使用,請下載包含 Kubernetes 資訊清單的專案,我們會用這些資訊清單部署 Jenkins:

git clone https://github.com/GoogleCloudPlatform/continuous-deployment-on-kubernetes.git ~/continuous-deployment-on-kubernetes

將目前的工作目錄變更為專案目錄:

cd ~/continuous-deployment-on-kubernetes/

建立叢集角色繫結,授予自己叢集管理員角色權限:

kubectl create clusterrolebinding cluster-admin-binding --clusterrole=cluster-admin --user=$(gcloud config get-value account)

取得 Jenkins 叢集的憑證,然後連線至該叢集:

gcloud container clusters get-credentials jenkins-cd --zone us-east1-d --project ${GOOGLE_CLOUD_PROJECT}

並將 Helm 二進位檔下載至 Cloud 控制台:

wget https://storage.googleapis.com/kubernetes-helm/helm-v2.14.1-linux-amd64.tar.gz

解壓縮檔案,並將內含的 helm 檔案複製到目前的工作目錄:

tar zxfv helm-v2.14.1-linux-amd64.tar.gz && \
cp linux-amd64/helm .

Tiller 是 Helm 的伺服器端,會在 Kubernetes 叢集中執行。我們來建立名為 tiller 的服務帳戶:

kubectl create serviceaccount tiller \
--namespace kube-system

並將其繫結至 cluster-admin 叢集角色,以便進行變更:

kubectl create clusterrolebinding tiller-admin-binding \
--clusterrole=cluster-admin \
--serviceaccount=kube-system:tiller

我們現在可以初始化 Helm 並更新 Repo:

./helm init --service-account=tiller && \
./helm repo update

28b45298e1e82748.png 驗證

確認 Helm 可搭配 ./helm version 使用 - 這應該會傳回用戶端和伺服器的版本號碼:

Client: &version.Version{SemVer:"v2.14.1", GitCommit:"5270352a09c7e8b6e8c9593002a73535276507c0", GitTreeState:"clean"}
Server: &version.Version{SemVer:"v2.14.1", GitCommit:"5270352a09c7e8b6e8c9593002a73535276507c0", GitTreeState:"clean"}

安裝 Jenkins

在叢集上安裝 Helm 後,我們就可以安裝 Jenkins:

./helm install stable/jenkins -n cd \
-f jenkins/values.yaml \
--version 1.2.2 --wait

28b45298e1e82748.png 驗證

我們來看看 Pod 的狀態:

kubectl get pods

輸出結果應顯示 Jenkins Pod 的狀態為 RUNNING:

NAME                          READY     STATUS    RESTARTS   AGE
cd-jenkins-7c786475dd-vbhg4   1/1       Running   0          1m

確認是否已正確建立 Jenkins 服務:

kubectl get svc

輸出內容應如下所示:

NAME               TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)     AGE
cd-jenkins         ClusterIP   10.35.241.170   <none>        8080/TCP    2m27s
cd-jenkins-agent   ClusterIP   10.35.250.57    <none>        50000/TCP   2m27s
kubernetes         ClusterIP   10.35.240.1     <none>        443/TCP     75m

Jenkins 安裝程序會使用 Kubernetes 外掛程式建立建構工具代理程式。當 Jenkins 主要執行個體需要執行建構作業時,就會自動啟動這些代理程式。當這些代理程式的工作完成後,系統就會自動終止代理程式,其中的資源會新增回叢集的資源集區。

連線至 Jenkins

Jenkins 正在叢集中執行,但為了存取 UI,我們要從 Cloud Shell 設定通訊埠轉送:

export POD_NAME=$(kubectl get pods --namespace default -l "app.kubernetes.io/component=jenkins-master" -l "app.kubernetes.io/instance=cd" -o jsonpath="{.items[0].metadata.name}") &&
kubectl port-forward $POD_NAME 8080:8080 >> /dev/null &

系統會在安裝期間產生管理員密碼。讓我們來擷取:

printf $(kubectl get secret cd-jenkins -o jsonpath="{.data.jenkins-admin-password}" | base64 --decode);echo

在 Cloud Shell 頂端,按一下「網頁預覽」圖示 7ddf5a65fd556dd6.png,然後選取「透過以下通訊埠預覽:8080」。

1d614c831a621cff.png

我們應該會看到 Jenkins 的登入畫面,在這裡我們可以輸入使用者名稱 admin 和先前步驟中傳回的密碼:

9cba23e856cbc84f.png

按一下「Sign in」後,系統應會將我們導向 Jenkins 主頁面。

9261f3e914829137.png

5. 安裝及設定 GKE 外掛程式

Google Kubernetes Engine 外掛程式可讓我們將在 Jenkins 中建構的部署作業發布至在 GKE 中執行的 Kubernetes 叢集。您需要在專案中使用 IAM 權限進行一些設定。我們會使用 Terraform 部署該設定。

首先,請下載 GKE 外掛程式專案:

git clone https://github.com/jenkinsci/google-kubernetes-engine-plugin.git ~/google-kubernetes-engine-plugin

自動設定 IAM 權限

將目前的工作目錄變更為先前複製的 GKE 專案的 rbac 目錄:

cd ~/google-kubernetes-engine-plugin/docs/rbac/

gcp-sa-setup.tf 是 Terraform 設定檔,可建立具有受限權限的自訂 GCP IAM 角色,以及要授予該角色的 GCP 服務帳戶。這個檔案需要專案、區域和服務帳戶名稱變數的值。我們會先宣告下列環境變數,再提供這些值:

export TF_VAR_project=${GOOGLE_CLOUD_PROJECT}
export TF_VAR_region=us-east1-d
export TF_VAR_sa_name=kaniko-role

初始化 Terraform、產生計畫並套用:

terraform init
terraform plan -out /tmp/tf.plan
terraform apply /tmp/tf.plan && rm /tmp/tf.plan

服務帳戶需要儲存空間管理員權限,才能儲存至 Cloud Storage 值區:

gcloud projects add-iam-policy-binding ${GOOGLE_CLOUD_PROJECT} \
--member serviceAccount:kaniko-role@${GOOGLE_CLOUD_PROJECT}.iam.gserviceaccount.com \
--role 'roles/storage.admin'

它也需要管道部署階段的容器權限:

gcloud projects add-iam-policy-binding ${GOOGLE_CLOUD_PROJECT} --member \
serviceAccount:kaniko-role@${GOOGLE_CLOUD_PROJECT}.iam.gserviceaccount.com --role 'roles/container.developer'

我們現在可以使用 Helm,透過 gke 機器人部署程式為 GKE 外掛程式設定叢集權限。將工作目錄變更為 GKE 專案的 helm 目錄:

cd ~/google-kubernetes-engine-plugin/docs/helm/

並使用提供的 Helm 資訊套件進行安裝:

export TARGET_NAMESPACE=kube-system && \
envsubst < gke-robot-deployer/values.yaml | helm install ./gke-robot-deployer --name gke-robot-deployer -f -

6. 設定 Jenkins

服務帳戶金鑰

為了讓服務帳戶正常運作,我們需要產生私密金鑰檔案,並將其新增為 Kubernetes 密鑰。首先,請使用下列 gcloud 指令產生檔案:

gcloud iam service-accounts keys create /tmp/kaniko-secret.json --iam-account kaniko-role@${GOOGLE_CLOUD_PROJECT}.iam.gserviceaccount.com

我們會使用該檔案在 Kubernetes 密鑰儲存庫中建立密鑰:

kubectl create secret generic jenkins-int-samples-kaniko-secret --from-file=/tmp/kaniko-secret.json 

透過 Cloud Shell 的 3 點選單存取「下載檔案」項目,將 JSON 檔案下載到本機磁碟:

c40378e72013b843.png

輸入檔案路徑 /tmp/kaniko-secret.json,然後點選「下載」。

返回 Jenkins 頁面,在左側窗格中依序按一下「憑證」和「系統」

6c140f7e6bb82f8.png

3b874912cdc8019b.png

在頁面中點選「系統」專區下方的「全域憑證」,然後點選左側的「新增憑證」:

4350c0e68561119b.png

3d3526551cdae8b.png

在「Kind」下拉式選單中,選取「Google Service Account from private key」。輸入「kaniko-role」做為名稱,然後上傳先前步驟中建立的 JSON 金鑰,然後按一下「OK」。

b0502213408e730e.png

環境變數

在建立多分支管道之前,我們需要定義一些環境變數。這 3 個子類型如下:

  • JENK_INT_IT_ZONE:Kubernetes 叢集的區域。在本例中,us-east1-d
  • JENK_INT_IT_PROJECT_ID:指的是代管此 Jenkins 執行個體的 GCP 專案 ID
  • JENK_INT_IT_STAGING - 我們的「暫存」叢集名稱,為示範目的,請使用 staging
  • JENK_INT_IT_PROD - 我們的「prod」叢集名稱。為了示範目的,它是 prod
  • JENK_INT_IT_BUCKET:先前步驟中建立的 Google Cloud Storage 值區
  • JENK_INT_IT_CRED_ID - 是指前一個步驟中使用 JSON 建立的憑證。這個值應與我們提供的名稱相符,即 kaniko-role

如要新增這些項目,請前往「Manage Jenkins」

d54f279190a07878.png

然後點選「Configure System」

ce79d218b2799640.png

畫面上會顯示「Global properties」部分,勾選「Environment variables」方塊後,畫面上會顯示「Add」按鈕,點選這個按鈕即可將上述變數新增為鍵/值組合:

81aa222a2b17b2cc.png

按一下頁面底部的「儲存」按鈕,套用變更。

7. 設定管道

在 Jenkins 中按一下「New Item」:

8d1270ce4d7b6a8a.png

輸入名稱「jenkins-integration-sample」,選取「Multibranch Pipeline」做為專案類型,然後按一下「OK」:

eb071ecfbb4d775b.png

系統會將您重新導向至管道設定頁面。在「Branch Sources」下方,輸入「https://github.com/GoogleCloudPlatform/jenkins-integration-samples.git」https://github.com/GoogleCloudPlatform/jenkins-integration-samples.git做為「Project Repository」。在「Build Configuration」下方,輸入「gke/Jenkinsfile」做為「Script Path」

5135bd6b0374508c.png

按一下「儲存」套用這些設定。儲存後,Jenkins 就會開始掃描存放區,並為每個分支版本執行後續建構作業。在建構過程中,您會在 Kubernetes Workloads 頁面上看到 Pod 的建立、執行和刪除情形。

建構作業完成後,您會在 Kubernetes 工作負載頁面上看到兩個名為 jenkins-integration-samples-gke 的項目,分別對應到正式版或測試叢集。狀態會顯示「OK」:

bdec6b1753d1ba07.png

使用下列 gcloud 指令,我們可以看到已將容器映像檔上傳至對應管道的 Google Container Registry:

gcloud container images list

如要在瀏覽器中查看工作負載,請取得正式版叢集的憑證:

gcloud container clusters get-credentials prod --zone us-east1-d --project ${GOOGLE_CLOUD_PROJECT}

接著執行下列指令,將殼層的通訊埠 8081 轉送至工作負載的通訊埠 8080:

export POD_NAME=$(kubectl get pods -o jsonpath="{.items[0].metadata.name}") &&
kubectl port-forward $POD_NAME 8081:8080 >> /dev/null &

在 Cloud Shell 頂端,按一下「網頁預覽」圖示,然後選取「透過以下通訊埠預覽:8081」

1b19b5b56f1bae7.png

e80e995e71763bb2.png

8. 清理

我們已探討如何在 Kubernetes 上部署 Jenkins 和範例的多分支管道。接下來,我們要清除專案中建立的所有資源。

刪除專案

您也可以刪除整個專案。在 GCP 主控台中,前往「Cloud Resource Manager」頁面:

在專案清單中選取我們正在使用的專案,然後按一下「刪除」。系統會提示您輸入專案 ID。輸入專案 ID 後,按一下「Shut Down」

或者,您也可以使用 gcloud 直接從 Cloud Shell 刪除整個專案:

gcloud projects delete $GOOGLE_CLOUD_PROJECT

如果您想逐一刪除不同的可計費項目,請繼續閱讀下一節。

Kubernetes 叢集

使用 gcloud 刪除整個 Kubernetes 叢集:

gcloud container clusters delete jenkins-cd --zone=us-east1-d

儲存空間值區

使用 gsutil 移除所有已上傳的檔案,並刪除儲存體:

gsutil rm -r gs://${GOOGLE_CLOUD_PROJECT}-jenkins-test-bucket

Google Container Registry 映像檔

我們會使用映像檔摘要刪除 Google Container Registry 映像檔。首先,請使用下列指令擷取摘要:

gcloud container images list-tags gcr.io/${GOOGLE_CLOUD_PROJECT}/jenkins-integration-samples-gke --format="value(digest)"

然後針對每個傳回的摘要:

gcloud container images delete gcr.io/${GOOGLE_CLOUD_PROJECT}/jenkins-integration-samples-gke@sha256:<DIGEST>

9. 恭喜!

太厲害了!你做到了。您已瞭解如何在 GKE 上部署 Jenkins,並將工作調度至 Kubernetes 叢集。

涵蓋內容

  • 我們部署了 Kubernetes 叢集,並使用 Helm 安裝 Jenkins
  • 我們安裝並設定 GKE 外掛程式,讓 Jenkins 將建構構件部署到 Kubernetes 叢集
  • 我們設定 Jenkins 來設定多分支管道,將工作分派至 GKE 叢集