1、概述

本文分享的是基于k8s环境与jenkins实现CI/CD其中的一个配置具体实现

即:不同环境下jenkins与k8s集群连接的问题

为什么会有不同的环境?我总结的原因如下:

a、在实际生产环境中,由于某些历史原因我们或许不能完美的实现所谓的一切皆“云原生”,例如有传统的jenkins和执行专有任务的slave节点

b、存在多集群共一个jenkins服务端的情况,例如k8s中集群A用作基础设施集群(包含日志、存储、devops平台),集群BCD用作不同业务线集群

因此,我们可以将不同环境定义为如下两种情况:

  • 同集群:指k8s集群内部的jenkins连接本集群

  • 跨集群:指外部的jenkins连接k8s集群,或者是jenkins连接外部的k8s集群

2、同集群

同集群下,k8s集群内部的jenkins连接所在的k8s集群。这是原生的方式:我们的环境都是全新的,全新的机器、全新安装的集群、全新的jenkins,总之一切都是新的,没有任何历史问题

由于在k8s集群内部部署jenkins时,已经对jenkins做了以下相关的角色授权绑定

---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: jenkins
  namespace: kube-system

---
kind: Role
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
  name: jenkins
  namespace: kube-system
rules:
- apiGroups: [""]
  resources: ["pods"]
  verbs: ["create","delete","get","list","patch","update","watch"]
- apiGroups: [""]
  resources: ["pods/exec"]
  verbs: ["create","delete","get","list","patch","update","watch"]
- apiGroups: [""]
  resources: ["pods/log"]
  verbs: ["get","list","watch"]
- apiGroups: [""]
  resources: ["secrets"]
  verbs: ["get"]

---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: RoleBinding
metadata:
  name: jenkins
  namespace: kube-system
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: Role
  name: jenkins
subjects:
- kind: ServiceAccount
  name: jenkins
  namespace: kube-system

因此只需要在jenkins中配置相应的连接地址就可以了

jenkins中安装好k8s插件后,打开jenkins——>系统管理——>系统配置——>新增一个kubernetes

配置名称,即这个云的别名

Kubernetes地址,即在集群内部暴露的k8s service名称

Kubernetes命名空间,这个配置就填写jenkins所属的namespace

Jenkins地址,填写jenkins svc的名称

配置完成后点击测试连接成功

后面配置pod template这里不做介绍,这里配置的pod template是默认情况下jenkins slavepod模板,当然也可以在每个流水线中单独指定

配置完成后的动态创建jenkins slave pod测试在本文后面一并给出

3、跨集群

一个实际场景:jenkins部署在A集群或部署在传统VM的环境下,想通过jenkins连接B集群,动态创建pod用以执行构建任务

3.1 端口有什么

既然是跨集群,那么首先需要考虑的就是网络问题,网络是否可达?需要开通哪些端口的安全组策略?

在这之前,就需要先了解一下jenkins的运行机制及端口有哪些?

  • http端口:默认8080,如果在jenkins前面做了反向代理并配置了域名,那么可能是常见的80/443端口,我这里通过域名+https的方式访问jenkins

  • Agent Port:基于JNLPJenkins代理通过TCP默认端口50000Jenkins进行通信

  • SSH port:jenkins作为ssh服务器,这个一般不会使用,具体使用可参考我之前的文章Jenkins workflowLibs库的使(妙)用

3.2 网络策略打通

由上面知道了有哪些端口之后,因此需要打通的网络策略包括

  • B集群节点连接jenkins暴露的http portAgent port
  • A集群节点(即jenkins server)连接B集群kube-apiserver暴露的端口

除网络策略之外,如果jenkins UI的地址,例如通过ingress设置了白名单限制访问,还需要将B集群的相关源ip设置为白名单

3.3 证书的生成和配置

3.3.1 kubeconfig文件

由于这里A集群中的jenkins并没有对B集群的操作权限,因此需要配置授权,即发起对B集群的kube apiserver的请求,和kubectl一样利用config文件用作请求的鉴权,默认在~/.kube/config下,当然我们也可以单独严格指定权限细节,生成一个jenkins专用的config文件,这里就不再延伸了,kubeconfig文件的组成如下

apiVersion: v1
clusters:
- cluster:
    certificate-authority-data: xxx
    server: https://<master-ip>:6443
  name: cluster1
contexts:
- context:
    cluster: cluster1
    user: admin
  name: context-cluster1-admin
current-context: context-cluster1-admin
kind: Config
preferences: {}
users:
- name: admin
  user:
    client-certificate-data: xxx
    client-key-data: xxx

其中包含了3段证书相关的内容,也就是我们常见的证书组成格式:ca.crtclient.crtclient.key

3.3.2 生成证书

jenkins中能够识别的证书文件为PKCS#12 certificate,因此需要先将kubeconfig文件中的证书转换生成PKCS#12格式的pfx证书文件

首先,使用yq命令行工具来解析yaml并通过base 64解码生成各个证书文件

服务端证书:

certificate-authority-data——>base 64解码——>ca.crt

yq e '.clusters[0].cluster.certificate-authority-data' .kube/config | base64 -d > ca.crt

客户端证书

client-certificate-data——>base 64解码——>client.crt

yq e '.users[0].user.client-certificate-data' .kube/config | base64 -d > client.crt

client-key-data——>base 64解码——>client.key

yq e '.users[0].user.client-key-data' .kube/config | base64 -d > client.key

然后,通过openssl进行证书格式的转换,生成Client P12认证文件cert.pfx

openssl pkcs12 -export -out cert.pfx -inkey client.key -in client.crt -certfile ca.crt
Enter Export Password:  # 输入密码加密
Verifying - Enter Export Password:

通过踩坑证明,这里必须输入密码,不然在后面添加jenkins相关配置后验证会报错

3.3.3 导入证书

生成文件后,打开jenkinsweb界面

添加全局凭据,凭据的类型选择Certificate,选择Upload PKCS#12 certificate

上传刚才生成的cert.pfx证书文件

输入通过openssl生成证书文件时输入的密码

检查上传的证书文件,此时可以查看到,jenkins已经成功加载了证书文件并读取了证书文件的相关信息

3.4 配置连接外部的k8s集群

jenkins中新增kubernetes云配置

同样的,打开jenkins——>系统管理——>系统配置——>新增一个kubernetes

配置名称,即这个云的别名,为外部的k8s集群起一个别名

Kubernetes地址,这里需要填写的是外部集群的kube-apiserver地址,即https://<master ip>:6443

Kubernetes服务证书key,填写上面服务端证书base64解码后的内容

Kubernetes命名空间,填写jenkins所属的namespace

凭据选择上面导入的证书文件作为凭据

Jenkins地址,填写A集群现有jenkins UI域名(访问地址和端口)

配置完成后点击测试连接成功,到这里跨集群的jenkins连接k8s就成功了

4、测试验证

4.1 配置pod template

这里以跨集群的环境下进行测试验证A集群的jenkins执行构建任务,在B集群中动态创建slave的预期结果

jenkins系统配置中,除了配置关联外部集群外,这里再配置一下相应的pod template,以便于在B集群中创建默认的slave pod,如图

4.2 自由风格构建测试

在自由风格中限制项目的运行节点,标签为上面配置的pod template标签即k8s-test-cluster,执行shell命令进行测试,查看控制台输出

4.3 流水线构建测试

编写测试的pipeline流水线,同样指定标签为上面配置的pod template标签即k8s-test-cluster

pipeline{
    agent{
        node {
            label 'k8s-test-cluster-jnlp-slave'
        }
    }
    stages{
        stage('Deploy to Kubernetes'){
            steps{           
                script{
                  sh """
                      kubectl version
                      kubectl get cs
                  """
                }
            }
        }
    }
}

构建后查看控制台输出

到这里,基于不同基础环境下jenkinsk8s连接配置的相关操作就分享完啦

See you ~