以下是基于 Kubernetes v1.33.4 部署 Jenkins Master-Slave 架构 的完整教程,使用 JNLP(Java Web Start)方式连接从节点,从节点执行 拉取代码 → 编译 → 构建镜像 → 推送镜像仓库 → 销毁,Jenkins Master 以 root 用户运行,Pod 名为 jenkins-0,数据持久化在节点 k8s-node1 上。


✅ 目标总结

组件 要求
Jenkins Master 常驻,Pod 名 jenkins-0,运行在 k8s-node1,使用 root 用户,数据落盘
Jenkins Slave 使用 JNLP 模式动态创建,完成任务后自动销毁
存储 使用 hostPath + nodeAffinity 固定在 k8s-node1
权限 Master Pod 使用 root 用户,配置 SecurityContextPodSecurityPolicy(或等效的 PodSecurity
CI/CD 流程 从节点拉代码 → 编译 → 构建 Docker 镜像 → 推送到镜像仓库(如 Harbor/Registry)

📦 环境准备

  • Kubernetes 集群 v1.33.4
  • kubectl 已配置
  • k8s-node1 节点已存在且可调度
  • Docker 或 containerd 已安装(用于构建镜像)
  • 镜像仓库地址(如:registry.example.com
  • 已配置 docker login 凭据(用于推送镜像)

🔧 第一步:准备 Jenkins Master 存储

1. 在 k8s-node1 上创建 Jenkins 数据目录

# 在 k8s-node1 执行
sudo mkdir -p /data/jenkins_home
sudo chown -R 1000:1000 /data/jenkins_home  # Jenkins 默认用户,但我们将用 root 覆盖

⚠️ 注意:Jenkins 官方镜像默认使用用户 1000,但我们将在 Pod 中以 root 运行,所以需确保目录可写。


🛠 第二步:创建命名空间

# namespace.yaml
apiVersion: v1
kind: Namespace
metadata:
  name: jenkins
kubectl apply -f namespace.yaml

🔐 第三步:创建 ServiceAccount 和 RBAC 权限

# rbac.yaml
apiVersion: v1
kind: ServiceAccount
meta
  name: jenkins
  namespace: jenkins
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
meta
  name: jenkins
rules:
  - apiGroups: [""]
    resources: ["pods", "services", "secrets", "configmaps", "events"]
    verbs: ["get", "list", "watch", "create", "delete", "update"]
  - apiGroups: ["batch"]
    resources: ["jobs"]
    verbs: ["get", "list", "watch", "create", "delete"]
  - apiGroups: ["apps"]
    resources: ["deployments", "statefulsets"]
    verbs: ["get", "list", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
meta
  name: jenkins
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: jenkins
subjects:
  - kind: ServiceAccount
    name: jenkins
    namespace: jenkins
kubectl apply -f rbac.yaml

💾 第四步:创建 PersistentVolume 和 PersistentVolumeClaim

# pv-pvc.yaml
apiVersion: v1
kind: PersistentVolume
meta
  name: jenkins-pv
spec:
  capacity:
    storage: 20Gi
  accessModes:
    - ReadWriteOnce
  persistentVolumeReclaimPolicy: Retain
  storageClassName: manual
  hostPath:
    path: /data/jenkins_home
  nodeAffinity:
    required:
      nodeSelectorTerms:
        - matchExpressions:
            - key: kubernetes.io/hostname
              operator: In
              values:
                - k8s-node1
---
apiVersion: v1
kind: PersistentVolumeClaim
meta
  name: jenkins-pvc
  namespace: jenkins
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 20Gi
  storageClassName: manual
  volumeName: jenkins-pv
kubectl apply -f pv-pvc.yaml

🐳 第五步:部署 Jenkins Master (StatefulSet)

# jenkins-master.yaml
apiVersion: apps/v1
kind: StatefulSet
meta
  name: jenkins
  namespace: jenkins
  labels:
    app: jenkins
spec:
  serviceName: jenkins
  replicas: 1
  selector:
    matchLabels:
      app: jenkins
  template:
    metadata:
      labels:
        app: jenkins
    spec:
      serviceAccountName: jenkins
      securityContext:
        runAsUser: 0        # 以 root 用户运行
        fsGroup: 0          # 文件系统组为 root
      nodeSelector:
        kubernetes.io/hostname: k8s-node1  # 固定调度到 k8s-node1
      containers:
        - name: jenkins
          image: jenkins/jenkins:lts-jdk17  # 最新版 LTS
          imagePullPolicy: IfNotPresent
          ports:
            - containerPort: 8080
            - containerPort: 50000  # JNLP 端口
          env:
            - name: JAVA_OPTS
              value: "-Djenkins.install.runSetupWizard=false"
          volumeMounts:
            - name: jenkins-home
              mountPath: /var/jenkins_home
          resources:
            limits:
              memory: "4Gi"
              cpu: "2000m"
            requests:
              memory: "2Gi"
              cpu: "1000m"
          livenessProbe:
            httpGet:
              path: /login
              port: 8080
            initialDelaySeconds: 60
            timeoutSeconds: 5
            failureThreshold: 12
          readinessProbe:
            httpGet:
              path: /login
              port: 8080
            initialDelaySeconds: 60
            timeoutSeconds: 3
            periodSeconds: 5
      volumes:
        - name: jenkins-home
          persistentVolumeClaim:
            claimName: jenkins-pvc
---
apiVersion: v1
kind: Service
meta
  name: jenkins
  namespace: jenkins
  labels:
    app: jenkins
spec:
  type: NodePort
  ports:
    - name: http
      port: 8080
      targetPort: 8080
      nodePort: 30080
    - name: jnlp
      port: 50000
      targetPort: 50000
  selector:
    app: jenkins

✅ Pod 名为 jenkins-0(StatefulSet 自动命名)

kubectl apply -f jenkins-master.yaml

🔐 第六步:初始化 Jenkins(首次运行)

查看初始密码

kubectl logs jenkins-0 -n jenkins | grep "Jenkins initial setup is required"
# 或直接查看文件
kubectl exec -it jenkins-0 -n jenkins -- cat /var/jenkins_home/secrets/initialAdminPassword

访问 Jenkins

http://<k8s-node1-ip>:30080

登录后:

  • 跳过插件安装(可自定义)
  • 创建管理员用户
  • 进入 Dashboard

⚙️ 第七步:安装必要插件

在 Jenkins 中安装以下插件(通过 Manage Jenkins > Plugins):

  • Kubernetes Plugin(关键:用于动态创建 Slave Pod)
  • Docker Pipeline
  • Git
  • Credentials Binding
  • Blue Ocean(可选)

重启 Jenkins。


☸️ 第八步:配置 Kubernetes Cloud(动态 Slave)

进入:Manage Jenkins > Configure Clouds > Add a new cloud > Kubernetes

填写以下内容:

字段
Name kubernetes
Kubernetes URL https://kubernetes.default.svc.cluster.local
Kubernetes Namespace jenkins
Credentials 添加 kubeconfig 或使用 ServiceAccount(推荐)
Jenkins URL http://jenkins.jenkins.svc.cluster.local:8080
Jenkins Tunnel jenkins.jenkins.svc.cluster.local:50000

✅ 勾选 “Use the Kubernetes plugin’s internal defaults for Pod Templates”

添加 Pod Template(Slave 模板)

  • Name: jenkins-slave
  • Namespace: jenkins
  • Labels: jenkins-slave
  • Usage: Use this node as much as possible
添加 Container Template
  • Name: jnlp
  • Docker Image: jenkins/inbound-agent:jdk17(官方 JNLP 客户端)
  • Args: ${computer.jnlpmac} ${computer.name}
  • Working Directory: /home/jenkins
  • Run in privileged mode: ✅ 勾选(用于 Docker in Docker)
  • Run as User: 0(root)
  • Run as Group: 0

⚠️ 如果要构建 Docker 镜像,建议挂载 /var/run/docker.sock 或使用 dind


🐳 可选:挂载 Docker Socket(推荐用于构建镜像)

修改 jenkins-slave 的 Pod Template,在容器中添加:

Volume Mounts:
  - Host Path: /var/run/docker.sock
    Mount Path: /var/run/docker.sock

或在 Jenkins UI 中添加:

  • Volume: Host Path /var/run/docker.sock/var/run/docker.sock

🧪 第九步:创建 Jenkins Pipeline Job

新建 Pipeline 任务

pipeline {
    agent {
        kubernetes {
            label 'jenkins-slave'
            defaultContainer 'jnlp'
            yaml '''
apiVersion: v1
kind: Pod
spec:
  containers:
  - name: jnlp
    image: jenkins/inbound-agent:jdk17
    args: ['\$(JENKINS_SECRET)', '\$(JENKINS_AGENT_NAME)']
    workingDir: /home/jenkins
    securityContext:
      runAsUser: 0
      privileged: true
    volumeMounts:
    - name: docker-sock
      mountPath: /var/run/docker.sock
  volumes:
  - name: docker-sock
    hostPath:
      path: /var/run/docker.sock
'''
        }
    }
    environment {
        REGISTRY = 'registry.example.com'
        IMAGE_NAME = 'myapp'
        IMAGE_TAG = env.BUILD_ID
    }
    stages {
        stage('Clone Code') {
            steps {
                git branch: 'main', url: 'https://github.com/youruser/your-repo.git'
            }
        }
        stage('Build') {
            steps {
                sh 'mvn clean package'  // 或 npm build 等
            }
        }
        stage('Build and Push Image') {
            steps {
                script {
                    docker.build("${REGISTRY}/${IMAGE_NAME}:${IMAGE_TAG}")
                    sh "docker login -u youruser -p yourpass ${REGISTRY}"
                    sh "docker push ${REGISTRY}/${IMAGE_NAME}:${IMAGE_TAG}"
                }
            }
        }
    }
    post {
        always {
            cleanWs()  // 清理工作区
        }
    }
}

✅ 从节点执行完后自动销毁


🔐 安全建议(可选)

  1. 避免长期使用 root:生产环境建议使用非 root 用户 + 适当权限
  2. 使用 ImagePullSecrets:如果私有镜像仓库
  3. 使用 Secret 管理凭证:如 docker login 密码
  4. 启用 Pod Security Admission:设置 baselinerestricted 策略,但需为 Jenkins 特例放行

🧰 常见问题排查

问题 解决方案
Slave 连接失败 检查 jenkins-tunnel 地址是否正确
构建镜像失败 检查 /var/run/docker.sock 是否可访问
权限拒绝 确保容器以 root 运行,privileged: true
PV 无法绑定 检查 nodeAffinityhostPath 路径是否存在
插件安装慢 设置 Jenkins 更新站点为国内镜像

✅ 验证部署

# 查看 Master Pod
kubectl get pod -n jenkins -o wide
# NAME          READY   STATUS    RESTARTS   AGE   IP           NODE
# jenkins-0     1/1     Running   0          10m   10.244.x.x   k8s-node1

# 查看 Slave Pod(运行 Pipeline 时自动创建)
kubectl get pod -n jenkins -w

📌 总结

你已成功部署:

  • Jenkins Master:常驻 jenkins-0,运行在 k8s-node1,使用 root,数据持久化
  • Jenkins Slave:通过 JNLP 动态创建,完成构建和推送后自动销毁
  • 支持拉取代码、编译、Docker 构建、推送镜像
  • 完整的 RBAC、存储、权限配置

📚 参考文档

  • Jenkins Kubernetes Plugin: https://plugins.jenkins.io/kubernetes/
  • Jenkins Docker Agent: https://hub.docker.com/r/jenkins/inbound-agent
  • Kubernetes v1.33 官方文档

如需 高可用 Jenkins使用 Helm 部署集成 Harbor 镜像仓库,可继续扩展。需要我提供 Helm 版本或 GitOps 集成方案吗?

Logo

火山引擎开发者社区是火山引擎打造的AI技术生态平台,聚焦Agent与大模型开发,提供豆包系列模型(图像/视频/视觉)、智能分析与会话工具,并配套评测集、动手实验室及行业案例库。社区通过技术沙龙、挑战赛等活动促进开发者成长,新用户可领50万Tokens权益,助力构建智能应用。

更多推荐