安裝 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 和相應的 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.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 鏡像,由于沒有指定 entrypoint 或 cmd 指令,容器啟動就退出了。這在 Dockerfile 中作為初始鏡像沒有問題,但是想作為 Go 語言環境并進入容器執行 go 命令就會有問題。
解決方法:給鏡像添加 entrypoint 和 args
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
浙公網安備 33010602011771號