Kubernetes 部署 GitLab Runner 及 Java CI/CD 实践指南
本文档包含如何在 Kubernetes 集群中部署 GitLab Runner,并配置基于 Git 管理部署清单的 Java 项目 CI/CD 流水线。
1. 在 Kubernetes 中部署 GitLab Runner
推荐使用官方 Helm Chart 部署 GitLab Runner。
1.1 前置准备
- 确保已安装 Helm。
- 在 GitLab 中获取全局 Runner 注册令牌 (Registration Token):访问
http://gitlab.aioil.top/admin/runners(Admin area -> Runners),点击页面右上角 "New instance runner" 旁边的三个点⋮按钮,在弹出的菜单中点击复制图标获取 "Registration token"。
1.2 添加 Helm 仓库并配置
bash
helm repo add gitlab https://charts.gitlab.io/
helm repo update
创建 values.yaml 配置文件:
yaml
# GitLab 服务器的 URL
cat << 'EOF' > values.yaml
# GitLab 服务器的 URL
gitlabUrl: "http://gitlab.aioil.top/"
# 刚才获取的 Runner Token
runnerRegistrationToken: "_J5xUEZT-4tbU-yhAghi"
# 开启 RBAC 权限(允许 Runner 在 K8s 中创建 Pod 来执行 Job)
rbac:
create: true
clusterWideAccess: true
# Runner 的并发数限制
concurrent: 10
runners:
# 默认的镜像,当 .gitlab-ci.yml 中没有指定 image 时使用
image: ubuntu:22.04
# 分配给这个 Runner 的标签,必须与注册时填写的匹配
tags: "k8s-runner"
# K8s 执行器配置
config: |
[[runners]]
[runners.kubernetes]
namespace = "{{.Release.Namespace}}"
image = "ubuntu:22.04"
# 推荐使用非特权模式
privileged = false
EOF
注意:现代 K8s CI/CD 推荐使用 Kaniko 构建镜像,因此不需要开启 privileged = true。
1.3 执行部署
bash
helm upgrade --install gitlab-runner gitlab/gitlab-runner \
--namespace gitlab-runner --create-namespace \
-f values.yaml
部署完成后,可以通过以下几种方式验证服务是否正常运行:
1. 检查 Kubernetes 中的 Pod 状态
bash
kubectl get pods -n gitlab-runner
正常情况下,你应该能看到类似 gitlab-runner-xxxx-xxxx 的 Pod 处于 Running 状态。
2. 查看 Runner 注册日志
bash
kubectl logs -f -l app=gitlab-runner -n gitlab-runner
在日志中,寻找包含 Registering runner... succeeded 的输出,这说明 Runner 已成功连接到你的 GitLab 实例。
3. 在 GitLab 界面验证
访问您的 GitLab 管理后台:http://gitlab.aioil.top/admin/runners(Admin area -> Runners)。
如果一切正常,你应该能在页面上看到一个**带绿色圆点(在线状态)**的新 Runner,并且带有 k8s-runner 的标签。
2. Java CI/CD 流水线配置
通过 .gitlab-ci.yml 实现构建、测试、打包(Kaniko)以及使用 Git 中管理的 Kubernetes YAML 文档进行部署。
2.1 目录结构要求
项目代码库中除了 Java 代码,还应包含 Dockerfile 和用于部署的 k8s-manifests 目录。
text
my-java-app/
├── src/
├── pom.xml
├── Dockerfile
├── k8s-manifests/ <-- Git 管理的部署测试文档
│ ├── deployment.yaml
│ └── service.yaml
└── .gitlab-ci.yml
2.2 编写 .gitlab-ci.yml
yaml
stages:
- build
- test
- package
- deploy
variables:
MAVEN_OPTS: "-Dmaven.repo.local=.m2/repository"
# 镜像标签,使用本次提交的 SHA
IMAGE_TAG: $CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA
# Git 管理的部署文档所在目录
K8S_DEPLOY_DIR: "k8s-manifests"
# 缓存 Maven 依赖加速构建
cache:
paths:
- .m2/repository/
- target/
# 1. 编译
build:
stage: build
image: maven:3.8.6-openjdk-11
script:
- mvn clean compile
tags:
- kubernetes
# 2. 测试
test:
stage: test
image: maven:3.8.6-openjdk-11
script:
- mvn test
tags:
- kubernetes
# 3. 构建 Docker 镜像 (使用 Kaniko,无需 Docker in Docker)
package:
stage: package
image:
name: gcr.io/kaniko-project/executor:debug
entrypoint: [""]
script:
# 准备打包用的 jar 包 (Kaniko 容器内没有 maven)
- mvn package -DskipTests
# 配置 Registry 认证
- mkdir -p /kaniko/.docker
- echo "{\"auths\":{\"$CI_REGISTRY\":{\"username\":\"$CI_REGISTRY_USER\",\"password\":\"$CI_REGISTRY_PASSWORD\"}}}" > /kaniko/.docker/config.json
# 构建并推送镜像 (由于线上 Registry 配置为 HTTP 协议,需要增加 insecure 参数)
- /kaniko/executor --context $CI_PROJECT_DIR --dockerfile $CI_PROJECT_DIR/Dockerfile --destination $IMAGE_TAG --insecure --insecure-pull
tags:
- kubernetes
# 4. 部署到 K8s
deploy:
stage: deploy
image: bitnami/kubectl:latest
script:
# 将 Git 部署文档中的镜像占位符替换为刚刚构建的镜像 TAG
- sed -i "s|APP_IMAGE_PLACEHOLDER|$IMAGE_TAG|g" $K8S_DEPLOY_DIR/deployment.yaml
# 应用 Git 管理的清单文件进行部署
- kubectl apply -f $K8S_DEPLOY_DIR/
environment:
name: production
tags:
- kubernetes
only:
- main
注意:在 deploy 阶段,Runner 使用的 ServiceAccount 需要有操作对应 Namespace 资源的权限(在安装 Runner 的 values.yaml 中配置 RBAC)。
2.3 Git 管理的 Kubernetes 部署文档示例
k8s-manifests/deployment.yaml:
yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: java-app
spec:
replicas: 2
selector:
matchLabels:
app: java-app
template:
metadata:
labels:
app: java-app
spec:
containers:
- name: java-app
# 这里的 APP_IMAGE_PLACEHOLDER 会被 CI 脚本中的 sed 命令替换
image: APP_IMAGE_PLACEHOLDER
ports:
- containerPort: 8080
# 容器内健康检查测试配置
livenessProbe:
httpGet:
path: /actuator/health
port: 8080
initialDelaySeconds: 30
imagePullSecrets:
- name: gitlab-registry-secret
k8s-manifests/service.yaml:
yaml
apiVersion: v1
kind: Service
metadata:
name: java-app-svc
spec:
selector:
app: java-app
ports:
- protocol: TCP
port: 80
targetPort: 8080
2.4 配置 Kubernetes 拉取私有镜像仓库的凭证
在 Kubernetes 部署应用时,集群需要从 GitLab Container Registry 拉取我们构建好的私有镜像。最官方、最稳定、最安全的做法是使用 Deploy Tokens (部署令牌) 创建 Kubernetes Secret,而不是使用个人访问令牌或 CI 环境变量(CI_JOB_TOKEN 的有效期仅限于流水线执行期间,会导致 Pod 重启时拉取镜像失败)。
第一步:在 GitLab 中生成 Deploy Token
- 进入您的项目页面。
- 左侧菜单导航至 Settings (设置) -> Repository (仓库)。
- 展开 Deploy tokens (部署令牌) 区域。
- 点击 Add token (添加令牌) :
- Name (名称) :填入
k8s-registry-token - Expiration date (过期时间):留空(永久有效,除非手动撤销)
- Username (用户名) :留空(GitLab 会自动生成,例如
gitlab+deploy-token-1) - Scopes (权限范围) :只勾选
read_registry
- Name (名称) :填入
- 点击 Create deploy token。
- 屏幕顶部会显示生成的
Username和Token。请立刻复制它们,刷新页面后将无法再次查看该密码。
第二步:在 K8s 中创建 Registry Secret
概念辨析:为什么需要创建 Secret(2.4 章节)还要配置 DOCKER_AUTH_CONFIG(2.5 章节)?
- 2.4 章节的 Secret :是给业务应用 使用的。当 CI/CD 流水线跑完,您的代码已经被打包成镜像。此时 K8s 需要把这个业务镜像拉下来运行,它使用的是
deployment.yaml中配置的imagePullSecrets。- 2.5 章节的 DOCKER_AUTH_CONFIG :是给 GitLab Runner 的 CI 任务容器 使用的。当流水线刚开始运行时,Runner 需要拉取一个"基础环境镜像"(比如带有 Maven、Node.js 的环境)来执行编译脚本,此时 K8s 还没有开始部署业务,它拉取基础镜像的凭证来自于
DOCKER_AUTH_CONFIG变量注入。结论 :这两个配置必须同时存在。前者负责最终业务上线时的镜像拉取,后者负责 CI 过程构建环境的镜像拉取,它们作用于 CI/CD 生命周期的不同阶段。
在 Kubernetes 集群控制节点(Master 节点)上执行以下命令,将上一步复制的凭证填入:
bash
kubectl create secret docker-registry gitlab-registry-secret \
--docker-server=gitlab.aioil.top:5005 \
--docker-username=<替换为生成的Username> \
--docker-password=<替换为生成的Token> \
--namespace=default
(注意:请确保 --namespace 与您的业务应用部署的 Namespace 保持一致。)
总结优势
- 安全性高:Deploy Token 只与当前项目绑定,且仅具备读镜像权限,不会泄露个人账号权限。
- 稳定性强:Token 永久有效,无论未来 K8s 何时重启 Pod 或扩容,拉取镜像都不会报错。
- CI 解耦:CI 流水线脚本无需处理权限生成逻辑,保持干净整洁。
2.5 解决 GitLab Runner 无法拉取私有基础镜像的问题
当我们在 .gitlab-ci.yml 的 image 字段指定使用我们自己私有仓库(例如 gitlab.aioil.top:5005/...)的基础镜像时,可能会遇到拉取失败(insufficient_scope: authorization failed)的问题。
原因分析 :虽然 CI 脚本有 $CI_JOB_TOKEN 的权限,但负责底层创建 Pod 的 K8s Kubelet 默认是不携带这些认证信息的,导致被 Registry 拒绝。
解决办法 :通过配置 DOCKER_AUTH_CONFIG 变量,GitLab Runner 在向 K8s 发起创建 Pod 的请求时,会自动将拉取凭证注入给 Kubelet。
操作步骤:
1. 生成认证字符串
在任意终端执行以下命令(建议使用前面生成的 Deploy Token 用户名和密码):
bash
# 将 Username 和 Password 替换为您实际的 Deploy Token 信息
echo -n "gitlab+deploy-token-1:您的Token密码" | base64 -w 0
假设输出的结果是 Z2l0bGFiK2RlcGxveS10b2tlbS0xOnh4eHh4。
2. 组装 JSON 字符串
将上面的 Base64 结果填入以下 JSON 模板的 auth 字段中,并替换您的 Registry 地址:
json
{
"auths": {
"gitlab.aioil.top:5005": {
"auth": "Z2l0bGFiK2RlcGxveS10b2tlbS0xOnh4eHh4"
}
}
}
3. 在 GitLab 项目中配置变量
- 进入当前项目(或群组级别)的 GitLab 页面。
- 左侧菜单导航至 Settings (设置) -> CI/CD。
- 展开 Variables (变量)。
- 点击 Add variable (添加变量) :
- Key (键) : 必须严格填写
DOCKER_AUTH_CONFIG - Value (值): 把上面组装好的整段 JSON 粘贴进去。
- Type (类型) : 保持
Variable不变。 - 取消勾选
Protect variable(确保非保护分支也能使用)。
- Key (键) : 必须严格填写
- 点击 Add variable 保存。
配置完成后,重新运行流水线,Runner 就能成功拉取私有仓库中的基础镜像并启动 CI 容器了。
2.6 CI/CD 部署到 K8s 的权限配置(两种方案)
在 deploy 阶段执行 kubectl apply 时,需要有操作 K8s 集群的权限。以下提供两种常见的配置方案,您可以根据实际需求任选其一:
方案一:为 Runner 配置 RBAC 权限(推荐,同集群部署)
如果您的 GitLab Runner 与目标业务应用部署在同一个 Kubernetes 集群,最安全且原生的方式是通过 RBAC 赋予执行 Job 的 Pod 权限。
1. YAML 配置功能解析
- ClusterRole (
gitlab-runner-deploy-clusterrole): 定义了集群级别的权限规则,允许对所有命名空间下的 Deployment、Service、Pod 和 ReplicaSet 等核心资源执行增删改查操作。 - ClusterRoleBinding (
gitlab-runner-deploy-clusterrolebinding) : 将上述定义好的全局权限角色绑定给gitlab-runner命名空间下的defaultServiceAccount。因为 Runner 动态创建的执行 Job 的 Pod 默认会使用这个 ServiceAccount。
2. 生成文件并应用配置
为了方便后期排查和维护,建议将配置输出到文件中,然后再应用。在服务器终端执行以下 Shell 命令:
bash
cat << 'EOF' > gitlab-runner-rbac.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: gitlab-runner-deploy-clusterrole
rules:
- apiGroups: ["", "apps", "extensions"]
resources: ["deployments", "services", "pods", "replicasets"]
verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: gitlab-runner-deploy-clusterrolebinding
subjects:
- kind: ServiceAccount
name: default
namespace: gitlab-runner
roleRef:
kind: ClusterRole
name: gitlab-runner-deploy-clusterrole
apiGroup: rbac.authorization.k8s.io
EOF
# 应用配置
kubectl apply -f gitlab-runner-rbac.yaml
方案二:通过 CI/CD 变量注入 kubeconfig(适合跨集群部署)
如果您不需要/不想在集群内配置 RBAC,或者您需要将应用部署到其他远程 Kubernetes 集群 ,可以将具有目标集群操作权限的 kubeconfig 文件内容作为 GitLab CI 变量注入到流水线中。
1. 在 GitLab 中配置变量
- 复制有权限的集群
~/.kube/config文件内容。 - 进入 GitLab 项目 -> Settings -> CI/CD -> Variables。
- 添加一个变量:
- Key :
KUBECONFIG_CONTENT - Value: 粘贴您的 kubeconfig 文件内容
- Type: Variable(纯文本)
- 取消勾选 "Protect variable"(除非只在受保护的分支上运行)
- Key :
2. 修改 .gitlab-ci.yml 部署脚本
在 deploy 阶段的 script 中,在执行 kubectl 之前生成 .kube/config 文件:
yaml
deploy:
stage: deploy
image: bitnami/kubectl:latest
script:
# 1. 注入 kubeconfig (使用纯文本变量)
- mkdir -p ~/.kube
- echo "$KUBECONFIG_CONTENT" > ~/.kube/config
# 2. 替换镜像标签并部署
- sed -i "s|APP_IMAGE_PLACEHOLDER|$IMAGE_TAG|g" $K8S_DEPLOY_DIR/deployment.yaml
- kubectl apply -f $K8S_DEPLOY_DIR/