Kubesphere与Jenkins的集成解析

Posted by elrond on September 30, 2022

1. kubesphere的devops模块介绍

  • kubesphere 使用可插拔的 devops 模块实现devops功能
  • devops驱动jenkins实现具体的操作,例如流水线等

devops与kubesphere的关系如下图, 详细的组件介绍

Architecture

2. 集成的亮点

devops与jenkins集成紧密且优雅,从构建、部署到使用维护纯云原生方式实现

  • 一键部署
  • 一个参数启用devops功能
  • 一个k8s集群内即可完成从jenkins、流水线的全生命周期

3. 具体集成说明

用户使用kuberspere平台的devops功能时,调用devops-api发送请求,devops收到请求后,部分请求直接调用jenkins进行操作,部分请求通过更新devops-controller监听的资源,通过devops-controller来操作jenkins。 运行流水线阶段,jenkins配置了kubernetes动态slave

  • jenkins pod信息(镜像、卷等)发送给k8s
  • k8s启动jenkins slave pod并通过远程协议与jenkins master建立连接
  • 运行流水线
  • 运行完毕之后根据设置删除/保留创建的pod

kubesphere-devops-3-0

3.1. jenkins镜像构建

jenkins本身是一个Java应用,当前也没有提供官方的云原生方案,kubesphere通过下面几个项目定制了自己的镜像

  • custom-war-packager 定制自己的jenkins并生成docker镜像或者war镜像
  • formulas 通过formula.yaml定制自己的jenkins,针对中国区优化
  • ks-jenkins 定制了kubesphere自己的jenkins镜像 使用了jcli 集成了cwp

ks-devops项目中的formulas安装了所有需要的jenkins插件主要有

3.2. jenkins与devops的部署

  • ks-install(ansible) 生成环境变量,主要有
    • 要不要使用 ksauth
    • 生成 ksauth使用的密码
    • 主要环境变量在 https://github.com/kubesphere/ks-installer/blob/master/roles/ks-devops/templates/ks-devops-values.yaml.j2
  • helm部署 devops 和 jenkins helm项目

3.2.1. ks-devops-helm-chart

这个项目里面主要有三个chart

3.2.1.1. devops

部署 devops-api 和 devops-controller

注意⚠️ 这里有一个cronjob 作用为清理执行过的流水线记录,定期执行 ks pip gc

主要部署的资源有

  • deployment
    • devops-apiserver
    • devops-controller
  • cronjob
    • devops
  • configmap
    • devops-config
    • jenkins-agent-config

3.2.1.2. jenkins

3.2.1.2.1. jenkins配置
  • maven配置 charts/ks-devops/charts/jenkins/templates/jenkins-agent-config.yaml
  • 配置jenkins dynamic slave charts/ks-devops/charts/jenkins/templates/jenkins-casc-config.yml
    • 自动化配置jenkins Configure Clouds
    • 配置k8s认证
    • 配置pod volume image lable等
    • 会将上面的maven配置挂在到maven容器中
  • 将maven配置和casc配置以configmap的方式挂在到jenkins容器的 /var/jenkins_home/casc_configs, 从helm的value获取
  • value.yaml charts/ks-devops/charts/jenkins/values.yaml中定义了:
    • 环境变量 从value读出配置到容器中,设置了登陆用的用户名密码
    • 初始化脚本 – 在helm渲染jenkins deploy时挂载到configmap中
      • mariler插件 – 绑定邮箱
      • kubernetes插件 – 创建k8s credential
      • RBAC配置

3.2.1.3. jenkins pod初始化

  • kubernetes插件配置 charts/ks-devops/charts/jenkins/templates/config.yaml
    • config.xml
      • 创建 role kubesphere-user 所有资源只读 并绑定到authenticated用户
      • ladp配置,对接kubesphere ladp
      • cloud配置
        • pod template container配置、挂载等等,包括maven配置、docker sock等
    • apply_config.sh jenkins工作目录初始化
      • slave-to-master-security-kill-switch 禁用agent 访问控制机制
      • 拷贝config.xml 到容器 /var/jenkins_home
      • 将用于初始化的groovy文件拷贝到 /var/jenkins_home/init.groovy.d
        • 初始化用于cloud的 credential Kubernetes service account
        • Mailer模块初始化
        • RBAC初始化: 创建admin和kubesphere-user对应的role 做绑定
        • Sonarqube初始化
        • 用户初始化,创建admin用户并设置密码
  • deployment charts/ks-devops/charts/jenkins/templates/jenkins-master-deployment.yaml
    • 设置环境变量
      • jvm参数
      • admin用户名密码
      • 超时配置
      • 邮箱配置
    • limit 注意: 默认memory为2g,一般是不够用的,跑多个任务就会引起pod crash,所以至少设置成4g
    • initContainer 运行/var/jenkins_config/apply_config.sh 初始化jenkins配置,如安装插件、配置cloud、rbac等等

到这里 jenkins pod就创建出来了,我们可以直接开始使用jenkins运行流水线了

3.2.1.4. s2i

source to image 这个组件没怎么用

3.3. jenkins kubernets动态slave

3.3.1. kubesphere内置jenkins

3.3.1.1. 配置

kubesphere通过ks-install和helm都配置好了,无需单独配置

3.3.1.2. 使用

以流水线为例,groovy中添加以下字段会按照 'base' 去匹配pod的lable,匹配到了会使用这个label的pod模板启动pod运行流水线,下面有两个pipeline 脚本,第一个是选定了pod的模板的会启动一个pod来执行,第二个any, 如果设置了master节点为 Only build jobs with label expressions matching this node 将会启动base pod来运行,如果选择 Use this node as much as possible 则会在jenkins自身的容器/服务器上运行, 如果是普通job的话 勾选Restrict where this project can be run 且填写 Label Expression 选择要运行的label,和pipeline类似

pipeline {
    agent {
        node {
            label 'base'
        }
    }
    stages {
        stage('Run shell') {
            steps {
                sh 'echo hello world'
            }
        }
    }
}
pipeline {
    agent any
    stages {
        stage('Run shell') {
            steps {
                sh 'echo hello world'
            }
        }
    }
}

3.3.2. 独立部署的jenkins

3.3.2.1. cloud配置kubernetes

Manage Node ——> Configure Clouds

  • kubernetes
  • Kubernetes URL: kubernetes apiserver地址, 与kubesphere自带的jenkins不同的是使用了集群内部链接
  • Kubernetes Namespace: slave pod运行的namespace
  • Credentials: 使用secret file,上传 kubeconfig 与kubesphere自带的jenkins不同的是使用了kubernetes service account
  • 使用websocket通信 – 使用jenkins tunnel通信
  • Jenkins URL: jenkins api地址
  • 其他: 其他的pod配置按需配置即可,这里和kubesphere的一样

4. Q&A

4.1. 使用maven构建时,maven仓库如何配置

pod所使用的maven配置是挂载进去的,可以通过Jenkins->Configuration->Maven Project Configuration 配置

4.2. kubesphere->jenkins->kubernetes,认证是如何实现的

4.2.1. kubesphere与jenkin的认证

  • jenkins插件 kubesphere-token-auth-plugin 集成kubesphere的认证体系,在kubesphere调用jenkins时,都需要经过ks-apiserver进行token的review, 通过之后再调用jenkins执行实际动作

kubesphere-devops-auth

4.2.2. jenkins使用驱动kubernetes实现动态slave

  • jenkins的deployment中声明了 serviceAccountName devops-jenkins
  • 启动的pod会讲 对应serviceAccount的token写入pod文件系统中 /var/run/secrets/kubernetes.io/serviceaccount/token
  • jenkins kubernetes插件会去读pod文件系统的token,这样就可以通过 token 来调度k8s资源实现 slave pod的创建删除

如果是外置jenkins则无法通过读取token来连接kubernetes,需要手动创建serviceAccount、clusterRole、clusterRoleBinding, 然后将token以Secret text或者将ca证书以Secret file形式或将kubconfig以Secret file形式写入credentials

5. 部署使用问题

5.1. 误删 apiserivice

kubectl delete --all apiservice

5.1.1. 解决方式

参照 https://github.com/kubernetes/kubernetes/issues/75704

  • 将kube-apiserver.yaml移到其他文件夹,这时kube-apiserver的pod会down掉
mv /etc/kubernetes/manifests/kube-apiserver.yaml /etc/kubernetes/
  • 在其他api正常的节点删除这个pod,再将配置文件移回去,即可恢复

5.2. kubesphere api服务无法启动

kubesphere v3.3.0

5.2.1. 错误描述

install failed, ks-controller CrashLoopBackOff

E1116 00:55:15.113761 1 notification_controller.go:113] get /, Kind= informer error, no matches for kind "Config" in version "notification.kubesphere.io/v2beta1"
F1116 00:55:15.113806 1 server.go:340] unable to register controllers to the manager: no matches for kind "Config" in version "notification.kubesphere.io/v2beta1"

5.2.2. 解决方式

参照 kubectl apply -f https://raw.githubusercontent.com/kubesphere/notification-manager/master/config/bundle.yaml

kubectl apply -f https://raw.githubusercontent.com/kubesphere/notification-manager/master/config/bundle.yaml

5.3. jnlp 容器无法启动

jnlp是jenkin的远程调用协议

JNLP(JAVA NETWORK LAUNCH PROTOCOL) is used to Connect to/launch your java application( here Jenkins) from a remote location

[root@k8s-1 ~]# kubectl logs -f  -n kubesphere-devops-worker base-w9dpq jnlp
Warning: SECRET is defined twice in command-line arguments and the environment variable
Warning: AGENT_NAME is defined twice in command-line arguments and the environment variable
Sep 14, 2022 11:29:43 AM hudson.remoting.jnlp.Main createEngine
INFO: Setting up agent: base-w9dpq
Sep 14, 2022 11:29:44 AM hudson.remoting.jnlp.Main$CuiListener <init>
INFO: Jenkins agent is running in headless mode.
Sep 14, 2022 11:29:44 AM hudson.remoting.Engine startEngine
INFO: Using Remoting version: 4.10
Sep 14, 2022 11:29:44 AM org.jenkinsci.remoting.engine.WorkDirManager initializeWorkDir
INFO: Using /home/jenkins/agent/remoting as a remoting work directory
Sep 14, 2022 11:29:44 AM org.jenkinsci.remoting.engine.WorkDirManager setupLogging
INFO: Both error and output logs will be printed to /home/jenkins/agent/remoting
Sep 14, 2022 11:29:44 AM hudson.remoting.jnlp.Main$CuiListener status
INFO: Locating server among [http://172.16.80.38:8080/]
Sep 14, 2022 11:29:44 AM hudson.remoting.jnlp.Main$CuiListener error
SEVERE: http://172.16.80.38:8080/tcpSlaveAgentListener/ is invalid: 404 Not Found
java.io.IOException: http://172.16.80.38:8080/tcpSlaveAgentListener/ is invalid: 404 Not Found
	at org.jenkinsci.remoting.engine.JnlpAgentEndpointResolver.resolve(JnlpAgentEndpointResolver.java:219)
	at hudson.remoting.Engine.innerRun(Engine.java:724)
	at hudson.remoting.Engine.run(Engine.java:540)
Sep 14, 2022 11:33:41 AM hudson.remoting.jnlp.Main$CuiListener status
INFO: Locating server among [http://devops-jenkins.kubesphere-devops-system:80/]
Sep 14, 2022 11:33:42 AM org.jenkinsci.remoting.engine.JnlpAgentEndpointResolver resolve
INFO: Remoting server accepts the following protocols: [JNLP4-connect, Ping]
Sep 14, 2022 11:33:42 AM org.jenkinsci.remoting.engine.JnlpAgentEndpointResolver resolve
INFO: Remoting TCP connection tunneling is enabled. Skipping the TCP Agent Listener Port availability check
Sep 14, 2022 11:33:42 AM hudson.remoting.jnlp.Main$CuiListener status
INFO: Agent discovery successful
  Agent address: devops-jenkins-agent.kubesphere-devops-system
  Agent port:    50000
  Identity:      13:ea:2b:ab:b5:16:70:70:89:58:d1:66:2b:62:b1:16
Sep 14, 2022 11:33:42 AM hudson.remoting.jnlp.Main$CuiListener status
INFO: Handshaking
Sep 14, 2022 11:33:42 AM hudson.remoting.jnlp.Main$CuiListener status
INFO: Connecting to devops-jenkins-agent.kubesphere-devops-system:50000
Sep 14, 2022 11:33:42 AM hudson.remoting.jnlp.Main$CuiListener status
INFO: Trying protocol: JNLP4-connect
Sep 14, 2022 11:33:42 AM org.jenkinsci.remoting.protocol.impl.BIONetworkLayer$Reader run
INFO: Waiting for ProtocolStack to start.
Sep 14, 2022 11:33:46 AM hudson.remoting.jnlp.Main$CuiListener status
INFO: Remote identity confirmed: 13:ea:2b:ab:b5:16:70:70:89:58:d1:66:2b:62:b1:16
Sep 14, 2022 11:33:46 AM hudson.remoting.jnlp.Main$CuiListener status
INFO: Connected
Sep 14, 2022 11:33:58 AM org.csanchez.jenkins.plugins.kubernetes.KubernetesSlave$SlaveDisconnector call
INFO: Disabled agent engine reconnects.

6. 附录

6.1. 附录1: 认证备忘

  • ks-installer/roles/ks-core/init-token/tasks/main.yaml 生成一个随机值secret
  • 部署kubersphere的时候初始化通过 ks-installer/roles/ks-core/init-token/files/jwt-script/jwt.sh 生成了一个jwt token 入参为 上面生成的字符串和’{“email”: “admin@kubesphere.io”,”username”: “admin”,”token_type”: “static_token”}’
  • 通过生成的token和secret创建secret名为 kubesphere-secret
  • 部署devops的时候将填入authentication.jwtSecret devops.password 通过helm部署devops
  • 部署jenkin的密码为写死的”P@ssw0rd”
  • admin password生成了一个随机的22位字符串写入jenkins pod环境变了并通过读取configmap devops-jenkins启动jenkins

7. 参考