1. 程式人生 > >Kerrigan:配置中心管理UI的實現思路和技術細節

Kerrigan:配置中心管理UI的實現思路和技術細節

去年寫過一篇文章『中小團隊落地配置中心詳解』,介紹了我們藉助etcd+confd實現的配置中心方案,這是一個對運維友好,與開發解耦的極佳方案,經過了一年多的實踐也確實幫我們解決了配置檔案無版本、難回滾、更新複雜等問題

這套配置中心解決方案的特點是,對整個配置檔案進行管理,而非配置項,且在配置中心修改的配置,客戶端可以實時自動更新。同時藉助於我們自研的配置中心管理UI(kerrigan)還能夠實現記錄修改歷史,快速回滾配置,與線上配置做對比等實用功能

陸續有小夥伴問我能否寫篇文章介紹一下配置中心的管理UI(Kerrigan)的實現,咖啡君就通過本篇文章來介紹Kerrigan的設計思路,以及用到的技術和部分核心程式碼,由於kerrigan有過一次改版,所以介面會與上面文章中的截圖有出入

介面與功能

使用者登陸進入會看到一個簡單的統計頁面,展示配置檔案相關資料

這個實現非常簡單就是對資料庫資料進行查詢統計,都是類似於下邊這樣的語句輸出的結果

Config.objects.all().count()

當點選“我的專案”標籤時,會出現所有的專案,在這裡可以搜尋你要操作的專案,或是新建/匯入專案

當點選“新建/匯入專案”時,可以選擇從CMDB同步專案,或者自己填寫專案名稱新建配置中心中的專案,但由於我們配置中心和CMDB是打通的,配置中心裡的所有專案都來源於CMDB,保證專案資訊一致性,所以新建專案功能並沒有被用到

與CMDB系統的同步是通過http協議進行了,當點選“與CMDB同步”按鈕時,會發送個get請求到cmdb伺服器獲取專案資訊,cmdb採用JWT認證,主要程式碼如下:

headers = {
    "WWW-Authenticate": "Token",
    "Content-Type": "application/json",
    "Authorization": "Token eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1NDgyMjg4MzgsImlhdCI6MTU0ODE0MjQzOCwiZGF0YSI6eyJ1c2VybmFtZSI6ImFkbWluQDE2My5jb20ifX0.oKc0SafgksMT9ZIhTACupUlz49Q5kI4oJA-B8-GHqLA"
}

requests.get('https://ops-coffee.cn/api/cmdb/project', headers=headers)

在我的專案頁面點選專案時,會進入專案的配置管理頁面,這個頁面列出了專案下的所有配置檔案,也可以通過右上角的“新增配置”按鈕新增配置檔案

當新增配置檔案時,會做三件事情:

  1. 配置檔案表(Config)新增一條新資料
  2. 歷史記錄表(History)新增一條新資料,作為歷史版本
  3. 往etcd裡寫入一條新的KV資料,其中key為:專案+環境+服務+檔名稱的組合,保證在etcd內唯一

操作etcd的程式碼如下:

class EtcdApi:

    def __init__(self):
        self.client = etcd.Client(
            host=str(self.ETCD_HOST),
            port=int(self.ETCD_PORT),
            username=str(self.ETCD_USER),
            password=str(self.ETCD_PASS)
        )

    def read(self, key):
        try:
            kx = self.client.read(key)
            return {"state": 1, "message": "", "action": kx.action, "key": kx.key, "value": kx.value,
                    "newKey": kx.newKey, "dir": kx.dir, "_children": kx._children}
        except Exception as e:
            return {"state": 0, "message": str(e)}

    def write(self, key, value):
        try:
            kx = self.client.write(key, value)
            return {"state": 1, "message": "", "action": kx.action, "key": kx.key, "newKey": kx.newKey,
                    "dir": kx.dir, "_children": kx._children}
        except Exception as e:
            return {"state": 0, "message": str(e)}

    def delete(self, key, recursive=False, dir=False):
        try:
            if dir:
                kx = self.client.delete(key, recursive, True)
                return {"state": 1, "message": "", "action": kx.action, "key": kx.key, "newKey": kx.newKey,
                        "dir": kx.dir, "_children": kx._children}
            else:
                kx = self.client.delete(key)
                return {"state": 1, "message": kx}
        except Exception as e:
            return {"state": 0, "message": str(e)}

當編輯和刪除配置檔案時,操作與新建類似,修改Config表資料-->Histror表新增新資料-->修改或刪除etcd資料,History表在每次新建或修改配置時都需要新增一條新資料,這裡使用到了Django的訊號Signales來實現,主要程式碼如下:

@receiver(signals.post_init, sender=Config)
def migrate_notify_init(instance, **kwargs):
    instance.old_content = instance.content


@receiver(signals.post_save, sender=Config)
def migrate_notify_post(instance, created, **kwargs):
    _t = Setting.objects.get(key='enable_etcd')

    # 每次新建或者content變更都往歷史表裡插入一條歷史資料
    if created or instance.old_content != instance.content:
        History.objects.create(
            config=instance,
            user=instance.user,
            content=instance.content
        )

除了History表操作之外,對於etcd的操作以及下邊要說到的釋出功能也是在signales裡完成的,signals可以簡化程式碼強化邏輯

當點選“編輯”按鈕後,會進入配置檔案編輯頁面,在這裡可以修改、儲存或釋出配置檔案,也可以拿當前配置檔案與已釋出配置檔案做對比

這裡“儲存”和“釋出”的區別在於,儲存只會將配置檔案儲存在Kerrigan內,不會修改etcd裡的資料,從而實現客戶端不更新,而釋出會直接修改etcd裡的資料,客戶端能夠直接更新,對於未釋出的配置檔案,當你點選配置檔案時會有如下的提示,你可以對比或者釋出

判斷是否釋出主要是在Config表裡加入了is_published欄位,同樣通過signals的post_save訊號在每次儲存時檢查這個欄位,如果為True,則修改對應etcd的值,否則不處理

@receiver(signals.post_save, sender=Config)
def migrate_notify_post(instance, created, **kwargs):
    ...
    
    # 判斷狀態為釋出且開啟了etcd,則更新資料到etcd
    if instance.is_published:
        _r = EtcdApi().write(key, instance.content)
        if _r.get('state') == 0:
            raise '寫入key:%s失敗' % (key)

系統中多次出現“對比”功能,都指的是當前配置檔案和已釋出配置檔案的對比,通過對比可以清晰的看出修改的內容,對比結果展示如下

對比功能主要用到了difflib模組,主要程式碼如下:

difflib.HtmlDiff().make_file(src_value, diff_value, context=True, numlines=3)

每一次的新增或者修改都會往History表裡寫入一條新資料,“歷史版本”便是直接讀的History表,展示出誰在什麼時間修改了什麼內容

當點選歷史版本時可以檢視此版本的配置檔案內容,同時在必要的時候回滾,有了歷史版本的內容,回滾也只是將歷史內容覆蓋到etcd

至此,Kerrigan介紹完成,其最主要的功能是通過web瀏覽器來操作etcd裡的KV資料,在此基礎上做了擴充套件,對每一次的修改都做了記錄,以實現實用的儲存、釋出、歷史、回滾等功能

最後再回顧一下整個配置中心的工作流程,配置管理員通過Kerrigan來新增或修改配置檔案,Kerrigan記錄修改,同時將修改同步至etcd,客戶端上的confd服務在檢測到etcd對應key的資料發生變化時,會自動拉取資料覆蓋至本地配置檔案,然後配合check_cmdreload_cmd指令對配置檔案進行檢查和過載,更多細節原理回顧文章『中小團隊落地配置中心詳解』


相關文章推薦閱讀:

  • 我們自研的那些Devops工具
  • 中小團隊基於Docker的devops實踐