安装 Jenkins
[root@control-plane jenkins]# cat compose.yaml
services:jenkins:# Jenkins 2.516.2image: jenkins/jenkins:ltsports:- "8080:8080"# https://github.com/jenkinsci/docker/blob/master/README.md#connecting-agents- "50000:50000"volumes:- jenkins_home:/var/jenkins_home
volumes:jenkins_home:
安装 Docker 和 Kubernetes 插件
路径:Jenkins / 系统管理 / 插件管理 / Available
搜索:
- Docker Pipeline
- Kubernetes plugin
配置 Jenkins 与 Kubernetes 集群的连接
路径:Jenkins / 系统管理 / Clouds / New cloud
Credentials
需要创建一个凭证来认证 Jenkins 对 Kubernetes 集群的操作。通常,可以创建一个 Service Account
和相应的 ClusterRoleBinding
或 RoleBinding
。
[root@control-plane jenkins]# cat jenkins-sa.yaml
apiVersion: v1
kind: ServiceAccount
metadata:name: jenkins-agent
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:name: jenkins-agent-binding
roleRef:apiGroup: rbac.authorization.k8s.iokind: ClusterRolename: edit # edit 角色通常足够,它允许在命名空间内创建和管理资源
subjects:
- kind: ServiceAccountname: jenkins-agent
---
apiVersion: v1
kind: Secret
metadata:name: jenkins-agent-tokenannotations:kubernetes.io/service-account.name: jenkins-agent
type: kubernetes.io/service-account-token[root@control-plane jenkins]# kubectl apply -f jenkins-sa.yaml
serviceaccount/jenkins-agent created
rolebinding.rbac.authorization.k8s.io/jenkins-agent-binding created
secret/jenkins-agent-token created# 获取 Secret Token 并解码
[root@control-plane jenkins]# kubectl get secret jenkins-agent-token -o jsonpath="{.data.token}" | base64 --decode
在 Jenkins Master 中配置凭证
路径:Jenkins / 系统管理 / Clouds / your-cloud-name / Configure / 凭据
关键点:Kind (类型) 选择 Secret text
,将上一步获取到的 Token 填进去
配置 Pod Template
可以在 Jenkins UI 进行配置,也可以以 configuration as code
内联
Container Template
dind
要在 Pod 中使用 Docker 运行时,挂载 /var/run/docker.sock
到容器内这种做法在大多数 Kubernetes 环境下是错误的,因为 Pod 没有直接访问宿主机资源的权限。
正确的做法是使用 sidecar 容器来提供 Docker Daemon:
- 使用 docker:dind 镜像
- 去掉默认填充的
运行命令
,不要覆盖镜像的ENTRYPOINT
指令的值 - 去掉默认填充的
命令参数
。如果要访问 insecure registry,可以在这里指定--insecure-registry=192.168.31.162:5000
- 高级 -> 勾选以最高权限运行
替代方案是使用像 Kaniko
这样的无特权构建工具。因为启用特权模式虽然解决了功能问题,但它也带来了巨大的安全风险。一个特权容器理论上可以访问和修改宿主机的任何资源,包括内核。这意味着如果容器被入侵,攻击者可以轻易地从容器内部逃逸到宿主机上。
如何在 Jenkinsfile 中使用 Pod Template?
使用 agent
section 的参数 label
指定预定义的 Pod Template 或者直接内联
参考:
- Kubernetes plugin for Jenkins
- Docker Pipeline plugin
一个 Jenkinsfile 示例,包括 3 个阶段,使用 Kubernetes Pod Agent
[root@control-plane ginexample]# cat dind.yaml
apiVersion: v1
kind: Pod
spec:containers:- name: dindimage: docker:dindimagePullPolicy: Alwaysargs:- "--insecure-registry=192.168.31.162:5000"securityContext:privileged: truepipeline {agent noneenvironment {REGISTRY = '192.168.31.162:5000'REPO = '<your-namespace>/<your-image>'}stages {stage('Build Image') {agent {kubernetes {yamlFile 'dind.yaml'}}steps {container('dind') {script {docker.withRegistry("http://${env.REGISTRY}") {docker.build(env.REPO).push(env.BUILD_TAG)}}}}}stage('Test') {agent {kubernetes {yaml '''apiVersion: v1kind: Podspec:containers:- name: goimage: golang:1.24-alpinecommand:- sleepargs:- 9999999'''}}steps {container('go') {sh 'go test $(go list ./... | grep -v /vendor/)'}}}stage('Deploy') {agent {kubernetes {yaml '''apiVersion: v1kind: Podspec:serviceAccountName: jenkins-agentcontainers:- name: kubectlimage: lachlanevenson/k8s-kubectlcommand:- sleepargs:- 9999999'''}}steps {container('kubectl') {sh '''sed -i "s|${REGISTRY}/${REPO}.*|${REGISTRY}/${REPO}:${BUILD_TAG}|" deployment.yamlkubectl apply -f deployment.yaml'''}}}}
}
TroubleShooting
TLS/SSL 证书验证
问题现象:
Error testing connection https://192.168.31.162:6443: java.io.IOException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
问题分析:当配置 Jenkins 连接到 Kubernetes API Server (https://192.168.31.162:6443) 时,Jenkins 会尝试验证 API Server 的 TLS 证书。这个错误意味着 Jenkins 的 Java 运行时环境(JRE)无法信任这个证书,因为对于 kubeadm 部署的集群来说,Kubernetes 使用的是自签名证书,而Jenkins 的 JRE 信任库中没有这个自签名证书。
解决方法:将 Kubernetes CA 证书导入 Jenkins JRE 的信任库
# 获取 Kubernetes CA 证书
[root@control-plane ~]# kubectl get configmap kube-root-ca.crt -o yaml
apiVersion: v1
data:ca.crt: |-----BEGIN CERTIFICATE-----hello worldjnSxDS5KuM7e-----END CERTIFICATE-----[root@control-plane jenkins]# cat /etc/kubernetes/pki/ca.crt # 其实就是这个文件# 将证书导入 Jenkins 容器
[root@control-plane jenkins]# docker compose cp /etc/kubernetes/pki/ca.crt jenkins:/tmp/
[root@control-plane jenkins]# docker compose exec -u 0 -it jenkins /bin/bash # 以 root 用户身份进入 Jenkins 容器
root@b9fc90fc8f86:/# keytool -import -trustcacerts -keystore /opt/java/openjdk/lib/security/cacerts -storepass changeit -file /tmp/ca.crt -alias kubernetes-ca
Trust this certificate? [no]: yes
Certificate was added to keystore
containers with unready status
问题现象:
[PodInfo] default/multi-branch-test-dev-38-3zhmr-llddx-lpfknContainer [go] terminated [Completed] No messagePod [Running][ContainersNotReady] containers with unready status: [go]
问题分析:go
这个容器(启动并马上)退出了。我使用的是 golang:1.24-alpine
镜像,由于没有指定 entrypoint
或 cmd
指令,容器启动就退出了。这在 Dockerfile 中作为初始镜像没有问题,但是想作为 Go 语言环境并进入容器执行 go 命令就会有问题。
解决方法:给镜像添加 entrypoint
和 args
containers:- name: goimage: golang:1.24-alpinecommand:- sleepargs:- 9999999
process apparently never started
问题现象:
process apparently never started in /home/jenkins/agent/workspace/multi-branch-test_dev@tmp/durable-bbfa57e2
(running Jenkins temporarily with -Dorg.jenkinsci.plugins.durabletask.BourneShellScript.LAUNCH_DIAGNOSTICS=true might make the problem clearer)
问题分析:使用 bitnami/kubectl
镜像的容器无法启动 shell 进程
解决方法:换一个包含 Shell 环境的镜像,比如 lachlanevenson/k8s-kubectl
在 Pod 中无法访问集群
问题现象:
Error from server (Forbidden):
error when retrieving current configuration of:
Resource: "apps/v1, Resource=deployments", GroupVersionKind: "apps/v1, Kind=Deployment"
Name: "ginexample-deployment", Namespace: "default"
from server for: "deployment.yaml": deployments.apps "ginexample-deployment" is forbidden:
User "system:serviceaccount:default:default" cannot get resource "deployments" in API group "apps" in the namespace "default"
问题分析:Jenkins Pod 在执行 kubectl 命令时,使用的身份是 system:serviceaccount:default:default
,这个身份没有足够的权限在 default 命名空间下获取和应用资源(我的 manifest 部署在 default namespace)。如果没有在 Jenkins Kubernetes 插件中明确配置 Service Account,它会默认使用运行它的 Pod 所绑定的 Service Account。在大多数 Kubernetes 集群中,这个默认的服务账号就是 default 命名空间下的 default Service Account。
解决方法:为 Pod 指定在 配置 Jenkins 与 Kubernetes 集群的连接
一节中创建的 Service Account
apiVersion: v1
kind: Pod
spec:serviceAccountName: jenkins-agent