1. 概览
Jenkins 是最受欢迎的持续集成解决方案之一。它用于自动执行软件开发过程中非人为操作的重要部分。通过将 Jenkins 部署到 Google Cloud 上的 Kubernetes 并利用 GKE 插件,我们能够根据需要快速自动扩缩构建执行器。结合使用 Cloud Storage,我们可以轻松构建和测试应用。
实践内容
- 将 Jenkins 部署到 Kubernetes 集群
- 部署并配置 Jenkins GKE 插件,以使 Jenkins 能够创建和销毁 pod 作为执行器节点
- 构建并测试示例 SpringBoot 应用
- 构建容器并将其发布到 Google Container Registry
- 将示例应用部署到预演和生产 GKE 环境
所需条件
- 已设置结算功能的 Google Cloud 项目。如果您没有 Google 账号,则必须创建一个。
2. 准备工作
此 Codelab 可完全在 Google Cloud Platform 上运行,无需进行任何本地安装或配置。
Cloud Shell
在此 Codelab 中,我们将使用 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 个集群来托管示例应用的预演和正式版 build:
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
验证
创建集群后,我们可以使用 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
我们将使用 Kubernetes 的应用软件包管理系统 Helm 在集群上安装 Jenkins。首先,下载包含我们将用于部署 Jenkins 的 Kubernetes 清单的项目:
git clone https://github.com/GoogleCloudPlatform/continuous-deployment-on-kubernetes.git ~/continuous-deployment-on-kubernetes
将当前工作目录更改为项目目录:
cd ~/continuous-deployment-on-kubernetes/
创建一个集群角色绑定,以授予您自己 cluster-admin 角色权限:
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 是在 Kubernetes 集群上运行的 Helm 服务器端。我们来创建一个名为 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
验证
运行 ./helm version 确认 Helm 正常运行 - 这应该会返回客户端和服务器的版本号:
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
验证
我们来检查一下 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 正在我们的集群上运行,但为了访问界面,我们来设置从 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 顶部,点击“网页预览”图标
,然后选择“在端口 8080 上预览”

我们应该会看到 Jenkins 的登录界面,可以在其中输入 admin 作为用户名,并输入上一步返回的密码:

点击登录后,我们应该会被定向到 Jenkins 的主页。

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 Secret。首先,使用以下 gcloud 命令生成文件:
gcloud iam service-accounts keys create /tmp/kaniko-secret.json --iam-account kaniko-role@${GOOGLE_CLOUD_PROJECT}.iam.gserviceaccount.com
我们将使用该文件在 Kubernetes Secret 存储区中创建一个密钥:
kubectl create secret generic jenkins-int-samples-kaniko-secret --from-file=/tmp/kaniko-secret.json
通过 Cloud Shell 的三点状菜单访问“下载文件”项,将 JSON 文件下载到本地磁盘:

输入文件路径 /tmp/kaniko-secret.json,然后点击“下载”。
返回 Jenkins 页面,在左侧窗格中,依次点击凭据和系统。


在标题为系统的部分下,依次点击左侧的“全局凭据”和“添加凭据”:


在“种类”下拉菜单中,选择私钥中的 Google 服务账号。输入“kaniko-role”作为名称,然后上传您在上一步中创建的 JSON 密钥,并点击“确定”。

环境变量
在创建多分支流水线之前,我们需要先为 Jenkins 定义一些环境变量。它们分别是:
- 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一致
如需添加这些内容,请前往管理 Jenkins:

然后配置系统:

系统会显示一个名为全局属性的部分,当我们勾选环境变量对应的复选框时,系统会显示一个添加按钮,点击该按钮即可将上述变量添加为键值对:

点击页面底部的保存按钮以应用更改。
7. 设置流水线
在 Jenkins 中,点击“New Item”(新建项目):

输入“jenkins-integration-sample”作为名称,选择“多分支流水线”作为项目类型,然后点击“确定”:

系统会将我们重定向到流水线配置页面。在分支来源下,输入 https://github.com/GoogleCloudPlatform/jenkins-integration-samples.git 作为项目代码库。在构建配置下,输入“gke/Jenkinsfile”作为脚本路径。

点击保存以应用这些设置。保存后,Jenkins 将开始扫描代码库,并为每个分支执行后续构建。随着构建的进行,您会在“Kubernetes 工作负载”页面上看到 pod 的创建、运行和销毁过程。
构建完成后,您会在 Kubernetes 工作负载页面上看到两个名为 jenkins-integration-samples-gke 的项,每个项分别对应于生产集群或测试集群。状态将显示为“正常”:

使用以下 gcloud 命令,我们可以看到已将容器映像上传到与流水线对应的 Google Container Registry:
gcloud container images list
如需在浏览器中查看工作负载,请获取生产集群的凭据:
gcloud container clusters get-credentials prod --zone us-east1-d --project ${GOOGLE_CLOUD_PROJECT}
运行以下命令,将 shell 的端口 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 上预览”


8. 清理
我们已经了解了如何在 Kubernetes 上部署 Jenkins 和示例多分支流水线。现在,我们应该清理项目中创建的所有资源了。
删除项目
如果您愿意,可以删除整个项目。 在 GCP Console 中,转到 Cloud Resource Manager 页面:
在项目列表中,选择我们一直使用的项目,然后点击删除。此时,系统会提示您输入项目 ID。输入项目 ID,然后点击关停。
此外,您也可以使用 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 能够将 build 工件部署到 Kubernetes 集群
- 我们配置了 Jenkins,以设置一个多分支流水线,该流水线会将工作分派给 GKE 集群