Jenkins in Kubernetes 在Kubernetes上动态运行Jenkins build agent

文章目录

  • Jenkins in Kubernetes 在Kubernetes上动态运行Jenkins build agent
    • 前言
    • 参考文档
    • 开启JNLP访问
    • 安装Kubernetes插件
    • 创建专有Namespace
    • 创建专有Service Account
    • 生成Kubernetes server certificate key
    • 生成Kubernetes Client P12 Certificate File
    • 在Jenkins上配置Kubernetes Credential
    • 在Jenkins上配置Kubernetes Cloud
    • 测试Jenkins Pipeline
    • Troubleshooting

Jenkins in Kubernetes 在Kubernetes上动态运行Jenkins build agent

前言

采用Jenkins in Kubernetes方式,可以在Kubernetes集群上动态运行Jenkins build agent。这样可以使得Jenkins build agent相互隔离,并实现自动扩容。

本文中的Jenkins Master部署在Kubernetes集群外,如果Jenkins Master部署在Kubernetes集群内,配置方法会稍有不同。

参考文档

官方文档:

  • https://github.com/jenkinsci/kubernetes-plugin
  • https://github.com/jenkinsci/kubernetes-plugin/blob/master/README.md

参考文档:

  • 基于 Kubernetes 的 Jenkins 构建集群实践
  • Configuring Certificates for Jenkins Kubernetes Plugin 0.12

  • Set Up a Jenkins CI/CD Pipeline with Kubernetes

  • 初试 Jenkins 使用 Kubernetes Plugin 完成持续构建与发布

开启JNLP访问

在Jenkins上,打开Manage Jenkins / Configure Global Security

找到Agents,在TCP port for JNLP agents选项选择Random (如果需要打开防火墙,则建议选择一个固定的端口)

点开Agent protocols,确保勾选了 Java Web Start Agent Protocol/4

如果不开启JNLP访问,Jenkins无法和Kubernetes交互

安装Kubernetes插件

检查并安装Kubernetes插件

Kubernetes插件的详细说明参见:

https://github.com/jenkinsci/kubernetes-plugin

创建专有Namespace

在Kubernetes上为Jenkins构建创建专有Namespace:

 kubectl create namespace devops

也可以使用默认的default namespace。

创建专有Service Account

在Kubernetes上为Jenkins构建创建有Cluster Admin权限的Service Account jenkins

kubectl create clusterrolebinding jenkins --clusterrole cluster-admin --serviceaccount=devops:jenkins

或者根据YAML文件创建jenkins Service Account:

wget https://raw.githubusercontent.com/cookcodeblog/OneDayDevOps/master/devops_in_k8s/jenkins/jenkins-slave-service-account.yml
kubectl apply -f jenkins-slave-service-account.yml

指定了Namespace为devopsjenkins-slave-service-account.yml内容:

---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: jenkins
  namespace: devops

---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: jenkins
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: cluster-admin
subjects:
- kind: ServiceAccount
  name: jenkins
  namespace: devops

生成Kubernetes server certificate key

如果Jenkins Master运行在Kubernetes上可跳过此步

在Kubernetes Master上,打开~/.kube/config文件,复制certificate-authority-data的内容,运行以下命令生成Kubernetes server certificate key,并保存在ca.crt文件中:

echo "" | base64 -d > ca.crt

生成Kubernetes Client P12 Certificate File

如果Jenkins Master运行在Kubernetes上可跳过此步

在Kubernetes Master上,打开~/.kube/config文件:

# 复制client-certificate-data的内容,运行以下命令生成client.crt
echo "" | base64 -d > client.crt

# 复制client-key-data的内容,运行以下命令生成client.key
echo "" | base64 -d > client.key

# 根据前面步骤生成的ca.crt, client.crt和client.key来生成PKCS12格式的cert.pfx
# 以下命令运行时,需要输入4位以上的密码
openssl pkcs12 -export -out cert.pfx -inkey client.key -in client.crt -certfile ca.crt

将生成的Kubernetes Client P12 Certificate Filecert.pfx复制到Jenkins Master服务器上,比如复制到/var/lib/jenkins/kubernetes_cert/cert.pfx

在Jenkins上配置Kubernetes Credential

如果Jenkins Master运行在Kubernetes上可跳过此步

在Jenkins上增加一个Global的Credential,选择类型为Certificate

选择“From a PKCS#12 file on Jenkins master”,输入上面生成的cert.pfx文件在Jenkins Master服务器上的路径,比如/var/lib/jenkins/kubernetes_cert/cert.pfx

输入生成cert.pfx文件时输入的密码

输入一个有意义的ID,比如kubernetes-cluster

输入有意义的Description

在Jenkins上配置Kubernetes Cloud

在Jenkins上,打开Manage Jenkins / Configure System

找到Cloud ,点击Add a new cloud

选择 Kubernetes

输入一个有意义的Name,比如kubernetes

输入Kubernetes URL:

  • 如果Jenkins Master部署在Kubernetes之外,则输入Kubernetes Master API Server URL,可运行kubectl cluster-info获得Kubernetes Master API Server URL
  • 如果Jenkins Master部署在Kubernetes上,则输入Kubernetes域名方式,格式https://..svc. ,比如https://kubernetes.default.svc.cluster.local,或短名称https://kubernetes.default

复制上面步骤生成的ca.crt文件内容到Kubernetes server certificate key (如果Jenkins Master运行在Kubernetes上可跳过此步)

输入上一步创建的Kubernetes Namespace,比如devops

选择上一步配置好的Kubernetes Credential (如果Jenkins Master运行在Kubernetes上可跳过此步)

点击“Test Connection"按钮测试Jenkins是否可以成功连接Kubernetes。

输入Jenkins URL:

  • 如果Jenkins Master部署在Kubernetes之外,则输入Jenkins URL
  • 如果Jenkins Master部署在Kubernetes上,则输入Node Port的方式

确保Pod Retention选择了Never,这样每次Jenkins构建结束后(无论成功和失败)都会销毁Pod,达到动态创建和运行Jenkins Build Agent的目的。

测试Jenkins Pipeline

创建一个Jenkins Pipeline来测试Jenkins是否可以动态地在Kubernetes上创建和运行Build Agent。

一个使用了Maven、Docker和Kubectl的Jenkins Pipeline示例:

def label = "k8s-slave-${UUID.randomUUID().toString()}"
podTemplate(label: label, containers: [
        containerTemplate(
                name: 'jnlp',
                image: 'jenkins/jnlp-slave:3.27-1-alpine',
                alwaysPullImage: false,
                privileged: true,
                args: '${computer.jnlpmac} ${computer.name}'),
        containerTemplate(name: 'maven', image: 'maven:3.6-jdk-8-alpine', command: 'cat', ttyEnabled: true,  privileged: true),
        containerTemplate(name: 'docker', image: 'docker:18.06', command: 'cat', ttyEnabled: true,  privileged: true),
        containerTemplate(name: 'kubectl', image: 'lachlanevenson/k8s-kubectl:v1.8.15', command: 'cat', ttyEnabled: true, privileged: true),
],
        namespace: 'devops',serviceAccount: 'jenkins',automountServiceAccountToken: 'true',
        volumes: [
                hostPathVolume(hostPath: '/var/run/docker.sock', mountPath: '/var/run/docker.sock'),
                hostPathVolume(hostPath: '/root/.m2', mountPath: '/root/.m2')
        ]) {
    node(label) {
        stage('Test Docker') {
            container('docker') {
                sh 'docker version'
            }
        }
        stage('Test Maven') {
            container('maven') {
                sh 'mvn -v'
            }
        }
        stage('Test Kubernetes') {
            container('kubectl') {
                sh 'kubectl get pods --all-namespaces'
            }
        }
    }
}

参见:
https://raw.githubusercontent.com/cookcodeblog/OneDayDevOps/master/devops_in_k8s/jenkins/pipeline/maven_docker_k8s.groovy

说明:

  • 定义了一个随机生成名称的Pod Template来运行Jenkins build agent,其中包含jnlpmavendockerk8s-kubectl Container Template
  • 其实Kubernetes Jenkins插件缺省提供了jnlp Container Template,但是这里还是用一个明确定义的jnlp Container Template去覆盖缺省的jnlp Container Template (这样可以指定jnlp镜像的版本和镜像仓库的地址),因此须要命名为jnlp,并带上args: '${computer.jnlpmac} ${computer.name}'
  • Jenkins build agent用jnlp容器用来和Jenkins Master通讯,用maven容器来运行Maven构建,用docker容器来运行Docker镜像打包和推送镜像,用k8s-kubectl容器来部署Kubernetes应用
  • 指定了Namespace为上面创建的devops,使用了devops Namespace下有Cluster Admin权限的Service Account jenkins
  • 挂载了/var/run/docker.sock 使得可以运行Docker,挂载了/root/.m2来做Maven本地缓存
  • 除了jnlp容器,其它容器须使用command: 'cat', ttyEnabled: true来保证容器一直保持运行状态

Troubleshooting

Q: kubectl not found
A: 检查Pod Template中是否包含了k8s-kubectl的Container Template,再检查kubectl命令是否在kubectl容器下执行的。

Q: Error from server (Forbidden): pods is forbidden: User “system:serviceaccount:devops:jenkins” cannot list pods at the cluster scope
A: 一般是Kubernetes RBAC权限问题;先检查是否使用了正确的Namespace,再检查是否使用了正确的Service Account,再检查该Service Account是否有Cluster Admin权限。

Q: 运行Docker命令失败
A: 检查是否挂载了/var/run/docker.sock

Q: 运行Maven构建时每次都要重新拉jar包,导致构建很慢
A: 检查是否挂载了/root/.m2Maven本地缓存

Troubleshooting参考:

  • https://issues.jenkins-ci.org/browse/JENKINS-33419

  • https://stackoverflow.com/questions/48827345/kubernetes-jenkins-integration

  • https://jenkins.io/blog/2018/09/14/kubernetes-and-secret-agents/

  • https://stackoverflow.com/questions/52202159/jenkins-kubenetes-how-to-use-kubectl-in-kubernetes-plugin

  • https://devopscube.com/kubernetes-api-access-service-account/

你可能感兴趣的:(Jenkins,Kubernetes,Jenkins,Kubernetes)