安装 Jenkins

[root@control-plane jenkins]# cat compose.yaml 
services:
  jenkins:
    # Jenkins 2.516.2
    image: jenkins/jenkins:lts
    ports:
      - "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 和相应的 ClusterRoleBindingRoleBinding

[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.io
  kind: ClusterRole
  name: edit # edit 角色通常足够,它允许在命名空间内创建和管理资源
subjects:
- kind: ServiceAccount
  name: jenkins-agent
---
apiVersion: v1
kind: Secret
metadata:
  name: jenkins-agent-token
  annotations:
    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 或者直接内联
参考:

一个 Jenkinsfile 示例,包括 3 个阶段,使用 Kubernetes Pod Agent

[root@control-plane ginexample]# cat dind.yaml 
apiVersion: v1
kind: Pod
spec:
  containers:
  - name: dind
    image: docker:dind
    imagePullPolicy: Always
    args:
      - "--insecure-registry=192.168.31.162:5000"
    securityContext:
      privileged: true

pipeline {
    agent none

    environment {
        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: v1
                      kind: Pod
                      spec:
                        containers:
                          - name: go
                            image: golang:1.24-alpine
                            command:
                              - sleep
                            args:
                              - 9999999
                      '''
                }
            }
            steps {
                container('go') {
                    sh 'go test $(go list ./... | grep -v /vendor/)'
                }
            }
        }
        stage('Deploy') {
            agent {
                kubernetes {
                    yaml '''
                      apiVersion: v1
                      kind: Pod
                      spec:
                        serviceAccountName: jenkins-agent
                        containers:
                          - name: kubectl
                            image: lachlanevenson/k8s-kubectl
                            command:
                              - sleep
                            args:
                              - 9999999
                      '''
                }
            }
            steps {
                container('kubectl') {
                    sh '''
                      sed -i "s|${REGISTRY}/${REPO}.*|${REGISTRY}/${REPO}:${BUILD_TAG}|" deployment.yaml
                      kubectl 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 world
    jnSxDS5KuM7e
    -----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-lpfkn
	Container [go] terminated [Completed] No message
	Pod [Running][ContainersNotReady] containers with unready status: [go]

问题分析:go 这个容器(启动并马上)退出了。我使用的是 golang:1.24-alpine 镜像,由于没有指定 entrypointcmd 指令,容器启动就退出了。这在 Dockerfile 中作为初始镜像没有问题,但是想作为 Go 语言环境并进入容器执行 go 命令就会有问题。

解决方法:给镜像添加 entrypointargs

containers:
  - name: go
    image: golang:1.24-alpine
    command:
      - sleep
    args:
      - 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
posted on 2025-09-15 19:17  心向所想  阅读(33)  评论(0)    收藏  举报