KubeSphere 與 Jenkins 的整合解析

KubeSphere發表於2023-02-28
作者:gfengwong
原文連結

KubeSphere 的 DevOps 模組介紹

  • KubeSphere 使用可插拔的 DevOps 模組實現 DevOps 功能;
  • DevOps 驅動 Jenkins 實現具體的操作,例如流水線等。

DevOps 與 KubeSphere 的關係如下圖, 詳細的元件介紹

整合的亮點

DevOps 與 Jenkins 整合緊密且優雅,從構建、部署到使用維護純雲原生方式實現:

  • 一鍵部署;
  • 一個引數啟用 DevOps 功能;
  • 一個 K8s 叢集內即可完成從 Jenkins、流水線的全生命週期。

具體整合說明

使用者使用 KubeSphere 平臺的 DevOps 功能時,呼叫 devops-api 傳送請求,DevOps 收到請求後,部分請求直接呼叫 jenkins 進行操作,部分請求透過更新 devops-controller 監聽的資源,透過 devops-controller 來操作 Jenkins。

執行流水線階段,Jenkins 配置了 K8s 動態 slave:

  • Jenkins pod 資訊(映象、卷等)傳送給 K8s;
  • K8s 啟動 Jenkins slave pod 並透過遠端協議與 Jenkins master 建立連線;
  • 執行流水線;
  • 執行完畢之後根據設定刪除/保留建立的 pod。

Jenkins 映象構建

Jenkins 本身是一個 Java 應用,當前也沒有提供官方的雲原生方案,KubeSphere 透過下面幾個專案定製了自己的映象:

  • custom-war-packager 定製自己的 Jenkins 並生成 Docker 映象或者 war 映象;
  • formulas 透過 formula.yaml 定製自己的 Jenkins,針對中國區最佳化;
  • ks-jenkins 定製了 KubeSphere 自己的 Jenkins 映象 使用了 jcli 整合了 cwp。

ks-devops 專案中的 formulas 安裝了所有需要的 Jenkins 外掛主要有

  • Blue Ocean 提供了 Jenkins 的 restful API;
  • kubernetes 提供了動態 slave 能力;
  • kubesphere-token-auth 整合 KubeSphere 許可權體系;
  • 其他。

Jenkins 與 DevOps 的部署

ks-installer(Ansible) 生成環境變數,主要有:

ks-devops-helm-chart:

這個專案裡面主要有三個 chart。

  1. DevOps

部署 devops-apiserver 和 devops-controller。

注意 ⚠️ 這裡有一個 cronjob 作用為清理執行過的流水線記錄,定期執行 ks pip gc

主要部署的資源有:

  • deployment

    • devops-apiserver
    • devops-controller
  • cronjob

    • devops
  • configmap

    • devops-config
    • jenkins-agent-config
  1. Jenkins

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 外掛 -- 繫結郵箱
      • K8s 外掛 -- 建立 K8s credential
      • RBAC 配置

Jenkins pod 初始化:

  • K8s 外掛配置 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 執行流水線了。

Jenkins K8s 動態 slave

KubeSphere 內建 Jenkins

配置:

KubeSphere 透過 ks-install 和 helm 都配置好了,無需單獨配置。

使用:

以流水線為例,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'
            }
        }
    }
}

獨立部署的 Jenkins

cloud 配置 K8s:

Manage Node → Configure Clouds。

  • K8s;
  • K8s URL:K8s apiserver 地址, 與 KubeSphere 自帶的 Jenkins 不同的是使用了叢集內部連結;
  • K8s Namespace:slave pod 執行的 namespace;
  • Credentials:使用 secret file,上傳 kubeconfig 與 KubeSphere 自帶的 Jenkins 不同的是使用了 kubernetes service account
  • 使用 WebSocket 通訊 -- 使用 Jenkins tunnel 通訊;
  • Jenkins URL:Jenkins API 地址;
  • 其他:其他的 pod 配置按需配置即可,這裡和 KubeSphere 的一樣。

Q & A

使用 Maven 構建時,Maven 倉庫如何配置?

pod 所使用的 Maven 配置是掛載進去的,可以透過 Jenkins->Configuration->Maven Project Configuration 配置

KubeSphere → Jenkins → K8s,認證是如何實現的?

KubeSphere 與 Jenkins 的認證:

Jenkins 外掛 kubesphere-token-auth-plugin 整合 KubeSphere 的認證體系,在 KubeSphere 呼叫 Jenkins 時,都需要經過 ks-apiserver 進行 token 的 review, 透過之後再呼叫 Jenkins 執行實際動作

Jenkins 使用驅動 K8s 實現動態 slave:

  • Jenkins 的 deployment 中宣告瞭 serviceAccountName devops-jenkins
  • 啟動的 pod 會將對應 serviceAccount 的 token 寫入 pod 檔案系統中 /var/run/secrets/kubernetes.io/serviceaccount/token
  • Jenkins K8s 外掛會去讀 pod 檔案系統的 token,這樣就可以透過 token 來排程 K8s 資源實現 slave pod 的建立刪除。

如果是外接 Jenkins 則無法透過讀取 token 來連線 K8s,需要手動建立 serviceAccount、clusterRole、clusterRoleBinding,然後將 token 以 Secret text 或者將 ca 證照以 Secret file 形式或將 kubconfig 以 Secret file 形式寫入 credentials。

部署使用問題

誤刪 apiserivice

kubectl delete --all apiservice

解決方式

參照:https://github.com/kubernetes...

kube-apiserver.yaml 移到其他資料夾,這時 kube-apiserver 的 pod 會 down 掉。

mv /etc/kubernetes/manifests/kube-apiserver.yaml /etc/kubernetes/

在其他 API 正常的節點刪除這個 pod,再將配置檔案移回去,即可恢復。

KubeSphere API 服務無法啟動

版本:KubeSphere v3.3.0。

錯誤描述

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"

解決方式

參照: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

JNLP 容器無法啟動

JNLP 是 Jenkin 的遠端呼叫協議。

[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.

附錄:認證備忘

  • ks-installer/roles/ks-core/init-token/tasks/main.yaml 生成一個隨機值 secret;
  • 部署 KubeSphere 的時候初始化透過 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。

參考資料

本文由部落格一文多發平臺 OpenWrite 釋出!

相關文章