Jenkins 和 CMDB 優雅的互動

2022-01-13 13:00:16

簡述

CMDB在按業務、叢集、模組、IP 納管了基礎設施資源後,如何為上層應用提供資料支撐,我想到的場景如下:

  • 監控平臺資產按業務/叢集/模組/IP 進行管理,告警內容可以幫助我們快速定位故障;
  • 堡壘機資產按業務/叢集/模組/IP 進行管理,資產授權可以按不同粒度進行許可權分配;
  • 防火牆許可權策略按模組區分,避免重複的許可權分配及設定混亂;
  • 為CI/CD構建按模組名提供動態化IP引數,有效避免IP的靜態分配;

從以上幾個場景可以看出,CMDB給我們提供瞭解決問題的基本思路。

本章我們就從為CI/CD構建提供動態化引數的實際案例來介紹下。

需求

在CI/CD中構建一項job需要目標主機的IP,我們可以通過以下幾種方式獲得:

  • 構建引數中手動指定
  • ssh publisher 或其他ssh 外掛中提前定義
  • 在設定job過程中直接指定

以上幾種方式都需要提前知道目標主機的IP,對執行構建人員來說很不友好。那麼我們能不能參照DNS的套路,通過特定的名稱查詢CMDB來獲取IP呢? 因此,jenkins從CMDB中根據模組名,動態獲取模組對應IP的解決方案就有了。

通常情況下,較大的企業都會基於CMDB配套企業服務匯流排(ESB),來給各個平臺提供服務。顯然,我們的小企業是不具備這種能力的。因此我們需要藉助CMDB API,自行將其封裝成標準格式,然後通過Jenkins 呼叫API 來提供動態化引數,實現引數化構建。

整理流程如下:
在這裡插入圖片描述

API封裝

通常情況下CMDB 自帶的API 返回的結果資料比較多,上層的應用呼叫需要進行層層過濾篩選才能獲得最終的結果。

例如藍鯨CMDB 根據模組查詢IP 介面,獲取的結果資料為:

# 請求cmdb api 
curl http://paas.bk.net/api/c/compapi/v2/cc/search_host/ -d
{
    "bk_app_code": "bk-test",
    "bk_app_secret": "62bc9c22-334d-4fcd-9523-991662163b67",
    "bk_username": "admin",
    "condition": [
        {
            "bk_obj_id": "biz",
            "fields": [],
            "condition": [
                {
                    "field": "bk_biz_name",
                    "operator": "$in",
                    "value": ["生產環境"]
                }
            ]
        },
        {
            "bk_obj_id":"module",
            "fields":[""],
            "condition":[
                {
                    "field": "bk_module_name",
                    "operator": "$in",
                    "value": ["test"]
                }
            ]
        }
    ],
    "page": {
        "start": 0,
        "limit": 1,
        "sort": "bk_host_id"
    },
    "pattern": ""
}


# 返回資料
{
    "message": "success",
    "code": 0,
    "data": {
        "count": 2,
        "info": [
            {
                "host": {
                    "bk_cpu": null,
                    "bk_isp_name": null,
                    "host_host_manageip": "",
                    "bk_os_name": "",
                    "bk_province_name": null,
                    "bk_host_id": 1861,
                    "import_from": "3",
                    "bk_os_version": "",
                    "bk_disk": null,
                    "operator": null,
                    "create_time": "2019-12-08T14:24:04.924+08:00",
                    "bk_mem": null,
                    "bk_host_name": "",
                    "bk_host_innerip": "10.168.202.58",
                    "bk_host_type": "2",
                    "bk_comment": "",
                    "bk_host_jiwei": "",
                    "bk_host_band": "",
                    "bk_os_bit": "",
                    "bk_outer_mac": "",
                    "bk_host_jigui": "",
                    "bk_asset_id": "",
                    "bk_service_term": null,
                    "bk_cloud_id": [
                        {
                            "bk_obj_name": "",
                            "id": "0",
                            "bk_obj_id": "plat",
                            "bk_obj_icon": "",
                            "bk_inst_id": 0,
                            "bk_inst_name": "default area"
                        }
                    ],
                    "bk_sla": null,
                    "bk_cpu_mhz": null,
                    "bk_service_limit": null,
                    "bk_host_outerip": "",
                    "bk_state_name": null,
                    "bk_os_type": null,
                    "bk_host_stat": "1",
                    "bk_mac": "",
                    "bk_bak_operator": null,
                    "bk_supplier_account": "0",
                    "bk_host_model": "",
                    "bk_sn": "",
                    "bk_cpu_module": ""
                },
                "set": [],
                "biz": [
                    {
                        "bk_biz_id": 3,
                        "language": "1",
                        "life_cycle": "2",
                        "bk_biz_developer": "",
                        "bk_biz_maintainer": "admin",
                        "bk_biz_tester": "",
                        "time_zone": "Asia/Shanghai",
                        "default": 0,
                        "create_time": "2020-12-09T11:12:04.449+08:00",
                        "bk_biz_productor": "",
                        "bk_supplier_account": "0",
                        "operator": "admin",
                        "bk_biz_name": "生產環境",
                        "last_time": "2021-11-29T09:17:09.837+08:00",
                        "bk_supplier_id": 0
                    }
                ],
                "module": [
                    {
                        "default": 0,
                        "TopModuleName": "生產環境",
                        "bk_module_id": 1358
                    }
                ]
            }
        ]
    },
    "result": true,
    "request_id": "02ed84f480e0412980923cb1959973ba"
}

其中從請求的json資料可以得出查詢條件:

  • 業務名: 生產環境
  • 模組名: test

返回的結果包含了很多資訊,但是我們只想要test模組所部署的IP:10.168.202.58,其他資訊可以忽略。如果直接使用原生API ,我們要花費很大的精力在解析json資料上。因此我們需要單獨封裝一個API,功能需求不變,即根據業務、模組查詢IP


# 請求封裝後的api 
curl http://10.168.209.88:8080/cmdb/getHost/?module=test&env=pro

{"status": 200, "module": "test", "env": "pro", "hosts": ["10.168.202.58"]}

通過簡單的引數查詢,即可得到我們想要的結果,後面只需愉快的接入了。

Jenkins接入

Jenkins我們以管理應用的job為例,功能為啟動、停止、重新啟動指定應用。

在引數化構建過程中,我們只需輸入應用名,就可以獲得該應用名所對應的部署IP,後面的一系列操作就可以基於此IP按需實現。

1.引數化構建

在這裡插入圖片描述
其中:

  • APP_NAME: 對應CMDB中的模組名
  • ACTION: 啟動、停止、重新啟動操作
  • HOST: 模組名對應的IP,呼叫api後結果以單選框的形式展現。

2.Active Choices Reactive Parameter

在這裡插入圖片描述

由於Jenkins 是基於grovvy開發,因此我們需要在Groovy Script處呼叫封裝的API ,實現根據模組名動態獲取IP的功能,得到的結果以Single Select即單選框的形式展現。

另外,Referenced parameters 是將實際輸入的APP_NAME引數指,傳遞到Groovy Script中,實現按需呼叫。

3.構建結果

在這裡插入圖片描述

總結

通過CMDB給Jenkins提供動態引數,使CMDB成為資料孤島的可能性進一步降低,同時也給Jenkins帶來了另一種最佳實踐。這種方式類似於DNS,我們不需要知道IP地址,只需知道應用名就可以快速獲取IP進行操作了。