KCL:声明式的云原生配置策略语言
楔子: 以螞蟻集團(tuán)典型的建站場景為例,在接入 Kusion 后,用戶側(cè)配置代碼減少到 5.5%,用戶面對的 4 個平臺通過接入統(tǒng)一代碼庫而消減,在無其他異常的情況下交付時間從 2 天下降到 2 小時……
注:本文是柴樹杉在 2021 GIAC 大會上分享的內(nèi)容。
00
你好 GIAC
大家好,我是來自螞蟻集團(tuán)的同學(xué),很高興能在 GIAC 的編程語言新范式板塊和大家分享《KCL 配置策略語言》。KCL 語言是螞蟻內(nèi)部的 Kusion 解決方案中針對云原生基礎(chǔ)設(shè)施配置代碼化自研的 DSL 語言,目前已經(jīng)在建站場景等一些場景開始小范圍推廣試用。
我們先看一下簡單的 KCL 代碼:
schema GIACInvitation[name: str]:Name: str = nameTopic: str = "分享主題"Company?: str = NoneType: str = "分享嘉賓"Address: str = "深圳"invitation = GIACInvitation("姓名") {Topic: "KCL 配置策略語言"Company: "螞蟻集團(tuán)" }這個例子代碼先通過 schema 定義了一個 GIACInvitation 結(jié)構(gòu)體:該結(jié)構(gòu)體有一個 str 類型的 Name 參數(shù),同時還有一組標(biāo)注了類型和默認(rèn)值的屬性。然后通過聲明式的語法構(gòu)造了 GIACInvitation 的實例 invitation。
這個例子雖然簡單,但是包含了 KCL 最重要的 schema 語言結(jié)構(gòu)。從例子可以看出 KCL 嘗試通過聲明式的語法、靜態(tài)類型檢查特性來改進(jìn)配置代碼的編寫和維護(hù)工作。這也是設(shè)計 KCL 語言的初衷,我們希望通過編程領(lǐng)域成熟的技術(shù)理論來解決云原生領(lǐng)域的配置代碼化的問題。
01
KCL 語言的誕生背景
在經(jīng)典的 Linux/UNIX 操作系統(tǒng)中,我們通過 Shell 和系統(tǒng)內(nèi)置的各種工具和內(nèi)核進(jìn)行交互,同時通過 Shell 腳本來管理更上層的 App。可以說 Shell 語言極大地簡化了內(nèi)核的編程界面,不僅僅提升了操作系統(tǒng)易用性也簡化了上層 App 的管理和運維,也提高了生產(chǎn)效率。而 Kubernetes 作為容器管理領(lǐng)域的事實標(biāo)準(zhǔn),已經(jīng)成為云計算時代的 Linux/UNIX。類比 UNIX 系統(tǒng),Kubernetes 目前還缺少一種符合其聲明式、開放、共享設(shè)計理念的交互語言及工具。
1.1 為何要設(shè)計 KCL 語言?
K8s 已經(jīng)成為云計算的操作系統(tǒng),但是目前尚缺少功能完備的 Shell 交互界面。目前雖然有很多而且開源方案,但是還沒有像 UNIX 的 Shell 那種出現(xiàn)比較成熟的方案,特別是尚無法滿足頭部互聯(lián)網(wǎng)企業(yè)大規(guī)模工程化的要求。云原生技術(shù)與企業(yè)落地之間存在 Gap 需要填補,這正是云原生工程化要解決的問題,也是設(shè)計 KCL 語言的出發(fā)點。
1.2 目前是一個好時機
云原生的思路是高度的開放化和民主化,結(jié)果就是萬物可配置,一切配置都是代碼。在配置代碼面前人人平等,每個用戶都可以通過調(diào)整配置代碼和基礎(chǔ)平臺設(shè)施進(jìn)行交互。因此對配置的編寫和維護(hù)正在成為云計算時代軟件工程師的必備的技能和需求。基于對云原生配置代碼化需求的日益旺盛,硅谷的諸多頭部公司已經(jīng)對這個方向進(jìn)行了大規(guī)模的實踐和驗證,這些都給了我們大量可以參考的經(jīng)驗。
因此螞蟻的 Kusion 項目嘗試通過 KCL 配置策略語言正是為了簡化云原生技術(shù)設(shè)施的接入方式設(shè)計,其設(shè)計目標(biāo)不僅僅是為了提升螞蟻基礎(chǔ)設(shè)施的開放程度及使用效率,同時希望能夠優(yōu)化共享、協(xié)同的開發(fā)流程,可以說其定位正是云原生時代的 Shell 語言。雖然目前還處于探索和實踐階段,我們通過此文和大家分享下 KCL 語言的設(shè)計與實現(xiàn)的一些理念,為云原生的快速到來貢獻(xiàn)一點綿薄之力。
1.3 KCL 誕生歷史
KCL 語言從 2019 年開始初期的調(diào)研和設(shè)計工作。到 2020 年 3 月發(fā)布 kcl-0.1,基于 Python 定制語法,采用 Go 版本的 Grumpy 和 AntLR 等工具開發(fā)。2020 年下半年改用 Python 語言并加快了開發(fā)和迭代速度,發(fā)布的 kcl-0.2.x 引入了大量語言特性、增加了 Plugin 擴展支持、同時支持 IDEA 插件。2021 年上半年開始統(tǒng)一優(yōu)化和整合語言特性,發(fā)布的 kcl-0.3 優(yōu)化類型系統(tǒng)、集成單元測試工具、優(yōu)化執(zhí)行性能并提供了 Go 等多語言的 API 支持、同時通過 LSP 為 VSCode 提供支持。2021 年下半年開始在建站等常見落地,同時引入靜態(tài)類型檢查和優(yōu)化性能,完善語言的文檔支持。
02
KCL 語言的設(shè)計原則
基于螞蟻踐行多年的經(jīng)典運維中臺沉淀的經(jīng)驗和對各種問題利弊的思考,Kusion 項目對如何充分利用云原生技術(shù)帶來的紅利,打造一個開放、透明、聲明式、可協(xié)同的運維體系進(jìn)行了探索和思考,提出并實踐了基于基礎(chǔ)設(shè)施代碼化的云原生協(xié)同開發(fā)的模型。而 KCL 語言正是 Kusion 項目為了解決云原生協(xié)同開發(fā)而設(shè)計的聲明式的配置編程語言,簡單、穩(wěn)定、高效和工程化是 KCL 語言設(shè)計的設(shè)計理念。
2.1 簡單為王
簡單不僅僅可以降低學(xué)習(xí)和溝通的成本,而且可以減少代碼出問題的風(fēng)險。不論是 UNIX 奉行的 KISS 原則還是 Go 語言推崇的 Less is more 設(shè)計理念,簡化易用的界面始終是各種成功產(chǎn)品追求的一個目標(biāo)。同樣從簡單原則出發(fā),KCL 語言在參考現(xiàn)代編程語言之上只保留了必要的元素,同時通過類型自動推導(dǎo)、引入受限的控制流和 schema 提供了基礎(chǔ)靈活的配置定義編寫能力,刪減語言特性始終是 KCL 語言設(shè)計工作的一個重要目標(biāo)。
2.1.1 聲明式語法
聲明式編程是和命令式編程并列的一種編程范式,聲明式編程只告訴你想要的結(jié)果,執(zhí)行引擎負(fù)責(zé)執(zhí)行的過程。聲明式編程使用更加簡單,可以降低命令式拼裝造成的復(fù)雜性和副作用,保持配置代碼清晰可讀,而復(fù)雜的執(zhí)行邏輯已經(jīng)由 Kubernetes 系統(tǒng)提供支持。
KCL 語言通過簡化對 schema 結(jié)構(gòu)體實例化的語法結(jié)構(gòu)對聲明式語法提供支持,通過僅提供少量的語句來減少命令過程式編程帶來的復(fù)雜性。圍繞 schema 和配置相關(guān)的語法,KCL 希望每種配置需求盡可能通過固定的寫法完成,使得配置代碼盡可能的統(tǒng)一化。
比如作為 KCL 聲明式語法的核心結(jié)構(gòu) schema 可以采用聲明式方式實例化:
schema Name:firstName: strlastName: strschema Person:name: Name = {firstName: "John"lastName: "default"}JohnDoe = Person {name.lastName: "Doe" }首先通過 schema 定義了一個 Name 結(jié)構(gòu),結(jié)構(gòu)包含 2 個字符串類型的必填屬性。
然后在 Person 中復(fù)用 Name 類型聲明一個 Name 屬性,并且給 Name 屬性設(shè)置了默認(rèn)值以簡化用戶使用。
最終在定義 JohnDoe 配置定義的時候,只需填寫 Name.lastName 一個屬性參數(shù)即可,其他部分屬性均采用默認(rèn)的參數(shù)。
對于一些標(biāo)準(zhǔn)的業(yè)務(wù)應(yīng)用,通過將可復(fù)用的模型封裝為 KCL schema,這樣可以為前端用戶提供最簡單的配置界面。比如基于螞蟻內(nèi)部 Konfig 大庫中 sofa.SofaAppConfiguration 只需添加少量的配置參數(shù)就可以定制一個 App。
appConfiguration = sofa.SofaAppConfiguration {resource: resource.Resource {cpu: "4"memory: "8Gi"disk: "50Gi"}overQuota: True }通過聲明式語法描述必要的參數(shù)(其他的參數(shù)全部采用默認(rèn)配置),可以極大簡化普通用戶的配置代碼。
2.1.2 順序無關(guān)語法
有別于命令式編程,KCL 推崇的是更適合于配置定義的聲明式語法。以斐波那契數(shù)列為例,可以把一組聲明式的定義看作一個方程組,方程式的編寫順序本質(zhì)上不影響方程組的求解,而計算屬性依賴并“求解”的過程由 KCL 解釋器完成,這樣可以避免大量命令式拼裝過程及順序判斷代碼。
schema Fib:n1: int = n - 1n2: int = n1 - 1n: intvalue: intif n <= 1:value = 1elif n == 2:value = 1else:value = (Fib {n: n1}).value + (Fib {n: n2}).valuefib8 = (Fib {n: 8}).value # 21代碼中 Fib 定義的成員 n、n1 和 n2 有一定的依賴關(guān)系,但是和它們書寫的順序并無關(guān)系。KCL 語言引擎會根據(jù)聲明式代碼中的依賴關(guān)系自動計算出正確的執(zhí)行順序,同時對類似循環(huán)引用等異常狀態(tài)告警。
2.1.3 同名配置合并
當(dāng)整個業(yè)務(wù)和開發(fā)維護(hù)團(tuán)隊都變得復(fù)雜時,配置代碼的編寫和維護(hù)也將變得復(fù)雜化:同一份配置參數(shù)可能散落在多個團(tuán)隊的多個模塊中,同時一個完整的應(yīng)用配置則需要合并這些散落在不同地方的相同和不同配置參數(shù)才可以生效,而相同的配置參數(shù)可能因為不同團(tuán)隊的修改而產(chǎn)生沖突。通過人工方式同步這些同名配置和合并不同的配置都是一個極大的挑戰(zhàn)。
比如 Konfig 大庫中應(yīng)用配置模型分為 base 和各環(huán)境 stack 配置,要求程序運行時按照某一 merge 策略合并為一份應(yīng)用配置,相當(dāng)于要求大庫前端配置能夠自動合并,即能夠分開多次定義并且合并,然后實例化生成相應(yīng)的唯一前端配置。
借助 KCL 語言的能力和 Konfig 的最佳實踐,可通過將基線配置和環(huán)境配置自動合并簡化配置的編寫。比如對于標(biāo)準(zhǔn) SOFA 應(yīng)用 opsfree,其基線配置和環(huán)境配置分別維護(hù),最終交由平臺工具進(jìn)行配置合并和檢查。KCL 語言通過自動化合并同名配置實現(xiàn)簡化團(tuán)隊協(xié)同開發(fā)的設(shè)計目標(biāo)。
比如 base 配置收集的通用的配置:
appConfiguration = sofa.SofaAppConfiguration {mainContainer: container.Main {readinessProbe: probe_tpl.defaultSofaReadinessProbe}resource: res_tpl.mediumreleaseStrategy: "percent" }然后再預(yù)發(fā)環(huán)境在 base 配置的基礎(chǔ)之上針對某些參數(shù)進(jìn)行微調(diào):
appConfiguration = sofa.SofaAppConfiguration {resource: resource.Resource {cpu: "4"memory: "8Gi"disk: "50Gi"}overQuota: True }合并的 pre 配置實際是一份 SofaAppConfiguration 配置(相當(dāng)于如下等效代碼,環(huán)境配置的優(yōu)先級默認(rèn)高于基線配置)
appConfiguration = sofa.SofaAppConfiguration {mainContainer: container.Main {readinessProbe: probe_tpl.defaultSofaReadinessProbe}resource: resource.Resource {cpu: "4"memory: "8Gi"disk: "50Gi"}overQuota: TruereleaseStrategy: "percent" }目前的同名配置雖然只針對應(yīng)用的主包配置有效,但已經(jīng)帶來了可觀察的收益。
2.2?穩(wěn)定壓倒一切
越是基礎(chǔ)的組件對穩(wěn)定性要求越高,復(fù)用次數(shù)越多的穩(wěn)定性帶來的收益也更好。因為穩(wěn)定性是基礎(chǔ)設(shè)施領(lǐng)域一個必備的要求,不僅僅要求邏輯正確,而且需要降低錯誤出現(xiàn)的幾率。
2.2.1 靜態(tài)類型和強不可變性
很多配置語言采用運行時動態(tài)檢查類型。動態(tài)類型最大的缺點只能檢查正在被執(zhí)行屬性的類型,這非常不利于開發(fā)階段提前發(fā)現(xiàn)類型的錯誤。靜態(tài)類型不僅僅可以提前分析大部分的類型錯誤,還可以降低后端運行時的動態(tài)類型檢查的性能損耗。
除了靜態(tài)類型,KCL 還通過 final 關(guān)鍵字禁止某些重要屬性被修改。靜態(tài)類型再結(jié)合屬性的強不可變性,可以為配置代碼提供更強的穩(wěn)定性保障。
schema CafeDeployment:final apiVersion: str = "apps.cafe.cloud.alipay.com/v1alpha1"final kind: str = 123 # 類型錯誤schema ContainerPort:containerPort: int = 8080protocol: "TCP" | "UDP" | "SCTP" = "TCP"ext? : str = None比如對于 CafeDeployment 中的 apiVersion 信息是一種常量類型的配置參數(shù),final 為這類配置提供保障:
代碼中 apiVersion 和 kind 屬性都被 final 保護(hù)禁止被修改。但是 kind 因為屬性類型初始值不同而隱含一個錯誤,通過靜態(tài)類型檢查很容易在開發(fā)階段發(fā)現(xiàn)錯誤并改正。
2.2.2運行時類型和邏輯 check 驗證
KCL 的 schema 不僅僅是帶類型的結(jié)構(gòu)體,也可以用于在運行時校驗存量的無類型的 JSON 和 YAML 數(shù)據(jù)。此外 schema 的 check 塊可以編寫語義檢查的代碼,在運行時實例化 schema 時會自動進(jìn)行校驗。同時,基于 schema 的繼承和 mixin 可以產(chǎn)生跟多關(guān)聯(lián)的 check 規(guī)則。
比如以下的例子展示 check 的常見用法:
schema sample:foo: strbar: intfooList: [str]check:bar > 0 # minimum, also support the exclusive casebar < 100, "message" # maximum, also support the exclusive caselen(fooList) > 0 # min length, also support exclusive caselen(fooList) < 100 # max length, also support exclusive caseregex.match(foo, "^The.*Foo$") # regex matchisunique(fooList) # uniquebar?in?range(100)?#?rangebar in [2, 4, 6, 8] # enummultiplyof(bar, 2) # multipleOfcheck 中每個語句都是一個可以產(chǎn)生 bool 結(jié)果的表達(dá)式和可選的錯誤信息組成(每個普通的 bool 表達(dá)式其實是 assert 語句的簡化而來)。通過內(nèi)置的語法和函數(shù)可以實現(xiàn)在運行時對屬性值的邏輯驗證。
2.2.3 內(nèi)置測試支持
單元測試是提升代碼質(zhì)量的有效手段。KCL 基于已有的 schema 語法結(jié)構(gòu),配合一個內(nèi)置 kcl-test 命令提供靈活的單元測試框架(結(jié)合 testing 包可指定面值類型的命令行參數(shù))。
內(nèi)置測試工具
schema TestPerson:a = Person{}assert a.name == 'kcl'schema TestPerson_age:a = Person{}assert a.age == 1kcl-test 命令不僅僅執(zhí)行單元測試,還會統(tǒng)計每個測試執(zhí)行的時間,而且可以通過正則表達(dá)式參數(shù)選擇執(zhí)行指定的測試。此外通過 kcl-test ./... 可以遞歸執(zhí)行子目錄的單元測試,同時支持集成測試和 Plugin 測試。
2.3?高效是永恒的追求
KCL 代碼不僅僅通過聲明式的風(fēng)格簡化編程,同時通過模塊支持、mixin 特性、內(nèi)置的 lint 和 fmt 工具、以及 IDE 插件提供高效的開發(fā)體驗。
2.3.1 schema 中好用的語法
schema 是 KCL 編寫配置程序的核心語法結(jié)構(gòu),其中幾乎每個特性均是針對具體的業(yè)務(wù)場景提效而設(shè)計。比如在定義和實例化深層次嵌套的配置參數(shù)時,均可以直接指定屬性的路徑定義和初始化。
schema A:a: b: c: inta: b: d: str = 'abc'A {a.b.c: 5 }同時為了安全,對于每個屬性默認(rèn)都是非空的字段,在實例化時會自動進(jìn)行檢查。
schema 不僅僅是一個獨立的帶類型注解的配置對象,我們也可以通過繼承的方式來擴展已有的 schema:
schema Person:firstName: strlastName: str# schema Scholar inherits schema Person schema Scholar(Person):fullName: str = firstName + '_' + lastNamesubject: strJohnDoe = Scholar {firstName: "John",lastName: "Doe",subject: "CS" }代碼中 Scholar 從 Person 繼承,然后又?jǐn)U展了一些屬性。作為子類的 Scholar 可以直接訪問父類中定義的 firstName 等屬性信息。
繼承是 OOP 編程中基礎(chǔ)的代碼復(fù)用手段,但同時也有多繼承導(dǎo)致的菱形繼承的技術(shù)問題。KCL 語言刻意簡化了繼承的語法,只保留了單繼承的語法。同時 schema 可以通過 mixin 特性混入復(fù)用相同的代碼片段,對于不同的能力配套,我們通過 mixin 機制編寫,并通過 mixin 聲明的方式“混入”到不同的結(jié)構(gòu)體中。
比如通過在 Person 中混入 FullnameMixin 可以給 schema 增加新的屬性或邏輯(包括 check 代碼塊):
schema FullnameProtocol:firstName : str = "default"lastName : strmixin FullnameMixin for FullnameProtocol:fullName : str = "${firstName} ${lastName}"schema relax Person:mixin [FullnameMixin]firstName : str = "default"lastName : str通過 KCL 的語言能力,平臺側(cè)同學(xué)可以通過單繼承的方式擴展結(jié)構(gòu)體,通過 mixin 機制定義結(jié)構(gòu)體內(nèi)屬性的依賴關(guān)系及值內(nèi)容,通過結(jié)構(gòu)體內(nèi)順序無關(guān)的編寫方式完成聲明式的結(jié)構(gòu)體定義,此外還支持如邏輯判斷、默認(rèn)值等常用功能。
2.3.2 doc、fmt、lint 和外圍的 LSP 工具
在編程領(lǐng)域代碼雖然是最核心的部分,但是代碼對應(yīng)的文檔和配套的工具也是和編程效率高度相關(guān)的部分。KCL 配置策略語言設(shè)計哲學(xué)并不局限于語言本身,還包括文檔、代碼格式化工具、代碼風(fēng)格評估工具和 IDE 的支持等。
KCL 通過 kcl-doc 支持從配置代碼直接提取產(chǎn)生文檔,自動化的文檔不僅僅減少了手工維護(hù)的成本,也降低的學(xué)習(xí)和溝通成本。kcl-fmt 則很方便將當(dāng)前目錄下的全部代碼(包含嵌套的子目錄)格式化為唯一的一種風(fēng)格,而相同格式的代碼同樣降低的溝通和代碼評審的成本。
kcl-lint 工具則是通過將一些內(nèi)置的風(fēng)險監(jiān)測策略對 KCL 代碼平行評估,方便用戶根據(jù)評估結(jié)果優(yōu)化代碼的風(fēng)格。
2.4?工程化的解決方案
任何語言想要在工程中實際應(yīng)用,不僅僅需要很好的設(shè)計,還需要為升級、擴展和集成等常規(guī)的場景提供完整的解決方案。
2.4.1 多維度接口
KCL 語言設(shè)計通過在不同的抽象層次為普通用戶(KCL 命令行)、KCL 語言定制者(Go-API、Python-API)、KCL 庫擴展者(Plugin)和 IDE 開發(fā)者(LSP 服務(wù))均提供了幾乎等價的功能界面,從而提供了最大的靈活度。
2.4.2 千人千面的配置 DB
KCL 是面向配置的編程語言,而配置的核心是結(jié)構(gòu)化的數(shù)據(jù)。因此,我們可以將完整 KCL 代碼看做是一種配置數(shù)據(jù)庫。通過 KCL 的配置參數(shù)的查詢和更新(override/-O 命令)可以和對應(yīng)的配置屬性路徑,可以實現(xiàn)對屬性參數(shù)的查詢、臨時修改和存盤修改。
將代碼化的配置作為 DB 的唯一源,不僅僅可以集成 DB 領(lǐng)域成熟的查詢和分析手段,而且可以通過配置代碼視角調(diào)整配置代碼的邏輯結(jié)構(gòu)。特別是在自動化運維實踐中,通過程序自動生成的配置代碼修改的 PullRequest 可以方便引入開發(fā)人員進(jìn)行代碼評審,很好地達(dá)到人機通過不同界面配合運維。
2.4.3 版本平滑升級
隨著業(yè)務(wù)和代碼的演化,相關(guān)模塊的 API 也會慢慢腐化。KCL 語言設(shè)計通過嚴(yán)格的依賴版本管理,然后結(jié)合語言內(nèi)置的語法和檢查工具保障 API 平滑的升級和過渡,再配合代碼集成測試和評審流程提升代碼安全。KCL 語言通過 @deprecated 特性在代碼出現(xiàn)腐化早期給出提示,同時為用戶的過渡升級留出一定的時間窗口,甚至等到 API 徹底腐爛前通過報錯的方式強制要求同步升級相關(guān)的代碼。
比如在某次升級中,Name 屬性被 fullName 替代了,則可以通過 @deprecated 特性標(biāo)志:
schema Person:@deprecated(version="1.1.0", reason="use fullName instead", strict=True)name: str... # Omitted contentsperson = Person {# report an error on configing a deprecated attributename: "name" }這樣在實例化 Person 時,Name 屬性的初始化語句將會及時收到報錯信息。
2.4.4 內(nèi)置模塊、KCL 模塊、插件模塊
KCL 是面向配置的編程語言,通過內(nèi)置模塊、KCL 模塊和插件模塊提供工程化的擴展能力。
用戶代碼中不用導(dǎo)入直接使用 builtin 的函數(shù)(比如用 len 計算列表的長度、通過 typeof 獲取值的類型等),而對于字符串等基礎(chǔ)類型也提供了一些內(nèi)置方法(比如轉(zhuǎn)化字符串的大小寫等方法)。
對于相對復(fù)雜的通用工作則通過標(biāo)志庫提供,比如通過 import 導(dǎo)入 math 庫就可以使用相關(guān)的數(shù)學(xué)函數(shù),可以通過導(dǎo)入 regex 庫使用正則表達(dá)式庫。而針對 KCL 代碼也可以組織為模塊,比如 Konfig 大庫中將基礎(chǔ)設(shè)施和各種標(biāo)準(zhǔn)的應(yīng)用抽象為模塊供上層用戶使用。
此外還可以通過 Plugin 機制,采用 Python 為 KCL 開發(fā)插件,比如目前有 meta 插件可以通過網(wǎng)絡(luò)查詢中心配置信息,app-context 插件則可以用于獲取當(dāng)前應(yīng)用的上下文信息從而簡化代碼的編寫。
03
KCL語言的實現(xiàn)原理
3.1 整體架構(gòu)
KCL 雖然作為一個專用于云原生配置和策略定義的語言,但是保持大多數(shù)過程式和函數(shù)式編程語言的相似實現(xiàn)架構(gòu),其內(nèi)部整體架構(gòu)組成也是經(jīng)典的編譯器 “三段式” 架構(gòu)。下面是 KCL 實現(xiàn)的架構(gòu)圖:
主要有以下幾個關(guān)鍵模塊:
解析器 Parser:解析器分析 KCL 源代碼產(chǎn)生 AST(抽象語法樹)。
編譯器 Compiler:對 AST 進(jìn)行多次遍歷,對 AST 進(jìn)行語義檢查(比如進(jìn)行類型檢查、無效代碼檢查)并對 AST 進(jìn)行優(yōu)化(合并常量表達(dá)式等),最終產(chǎn)生虛擬機可以執(zhí)行的字節(jié)碼。
虛擬機 Virtual Machine (VM):執(zhí)行 Compiler 產(chǎn)生的字節(jié)碼,計算產(chǎn)生相應(yīng)的配置結(jié)果,并將配置結(jié)果序列化為 YAML/JSON 進(jìn)行輸出。
整體架構(gòu)分為三段式的好處是可以把針對 KCL 源語言的前端和針對目標(biāo)機器的后端組合起來,這種創(chuàng)建編譯器組合的方法可以大大減少工作量。
比如目前的 KCL 字節(jié)碼定義和后端虛擬機采用自研實現(xiàn),KCL 虛擬機主要用于計算產(chǎn)生配置結(jié)果并序列化為 YAML/JSON 進(jìn)行輸出。
如果遇到在其他特殊使用 KCL 的場景比如在瀏覽器中執(zhí)行 KCL,則可以重寫一個適配 WASM 的后端,就可輕易將 KCL 移植到瀏覽器中使用,但是 KCL 本身的語法和語義不需要發(fā)生任何變化,編譯器前端代碼也無需任何改動。
3.2 Go 和 Python 通信原理
為了更好地釋放 KCL 配置策略語言的能力以及遍于上層自動化產(chǎn)品集成(比如著名的編譯器后端 LLVM 就因其 API 設(shè)計良好,開發(fā)人員可以利用其 API 快速地構(gòu)建自己的編程語言),KCLVM 目前提供了 Python 和 Go 兩種語言的 API,使得用戶可以使用相應(yīng)的 API 快速地構(gòu)建語言外圍工具,語言自動化查詢修改工具等提升語言的自動化能力,并且進(jìn)一步可以基于此構(gòu)建服務(wù)化能力,幫助更多的用戶構(gòu)建自己云原生配置代碼化應(yīng)用或者快速接入基礎(chǔ)設(shè)施。
KCLVM 主體采用 Python 代碼實現(xiàn),而很多的云原生應(yīng)用以 Go 程序構(gòu)建,因此為了更好地滿足云原生應(yīng)用用戶訴求。KCLVM 首先基于 CGo 和 CPython 構(gòu)建了 Go 程序和 Python 程序通信媒介,基于此設(shè)計了 Python 函數(shù)到 Go 函數(shù)的 RPC 調(diào)用,調(diào)用參數(shù)以 JSON 形式存儲,使得 KCLVM-Python 編譯器的能力平滑地過度到 Go 代碼中,通過 Go 一行 import 調(diào)用即可操作 KCL 代碼。
補充:在服務(wù)化實踐的過程中,基于 CGO 調(diào)用 Python 的方案也遇到了一些問題:首先是 Go + CGO + Python 導(dǎo)致交叉編譯困難,對 ACI 的自動化測試和打包產(chǎn)生了挑戰(zhàn);其次是 CGO 之后的 Python 不支持多語言多線程并發(fā),無法利用多核的性能;最后即使通過 CGO 將 Python 虛擬機編譯到了 Go 程序中,依然還是需要安裝 Python 的標(biāo)準(zhǔn)庫和第三方庫。
3.3 協(xié)同配置原理
當(dāng)有了一個簡單易用并能夠保證穩(wěn)定性的配置語言后,另一個面臨的問題是如何使用配置代碼化的方式提升協(xié)同能力。基于此,KCL 配置可分為用戶側(cè)和平臺側(cè)配置兩類,最終的配置內(nèi)容由各自用戶側(cè)和平臺側(cè)的配置內(nèi)容共同決定,因此存在兩個方面的協(xié)同問題:
平臺側(cè)配置與用戶側(cè)配置之間的協(xié)同;
用戶側(cè)配置之間的協(xié)同。
針對上述協(xié)同問題,KCL 在技術(shù)側(cè)提出了順序無關(guān)語法,同名配置合并等抽象模型來滿足不同的協(xié)同配置場景。
以上圖為例,首先 KCL 代碼在編譯過程中形成兩張圖(用戶不同配置直接的引用和從屬關(guān)系一般形式一張有向無環(huán)圖),分別對應(yīng)結(jié)構(gòu)體內(nèi)部聲明代碼及結(jié)構(gòu)體使用聲明代碼。編譯過程可以簡單分為三步:
首先定義平臺側(cè)的結(jié)構(gòu)體并形成結(jié)構(gòu)體內(nèi)部聲明代碼圖;
其次聲明并合并不同用戶側(cè)配置代碼圖;
最后將用戶側(cè)配置代碼圖計算的結(jié)果代入平臺側(cè)結(jié)構(gòu)體內(nèi)部聲明代碼圖求解,最終得到完整配置圖定義。
通過這樣簡單的計算過程,可以在編譯時完成大部分代換運算,最終運行時僅進(jìn)行少量計算即可得到最終的解。同時在編譯合并圖過程中仍然能夠執(zhí)行類型檢查和值的檢查,區(qū)別是類型檢查是做泛化、取偏序上確界(檢查某個變量的值是否滿足既定類型或者既定類型的子類型),值檢查是做特化、取偏序下確界(比如將兩個字典合并為一個字典)
04
對未來的展望
KCL 語言目前依然處于一個高速發(fā)展的階段,目前已經(jīng)有一些應(yīng)用開始試用。我們希望通過 KCL 語言為 Kusion 技術(shù)棧提供更強的能力,在運維、可信、云原生架構(gòu)演進(jìn)方面起到積極的作用。同時對于一些特殊的非標(biāo)應(yīng)用提供靈活的擴展和集成方案,比如我們正在考慮如何讓后端支持 WebAssembly 平臺,從而支持更多的集成方案。
在合適的時間我們希望能夠開放 KCL 的全部代碼,為云原生代碼化的快速落地貢獻(xiàn)綿薄之力。
05
本周推薦文章
螞蟻集團(tuán)萬級規(guī)模 K8s?集群 etcd 高可用建設(shè)之路
2021-07-29
我們做出了一個分布式注冊中心
2021-07-27
開啟云原生 MOSN 新篇章 — 融合 Envoy 和 GoLang 生態(tài)
2021-07-06
MOSN 子項目 Layotto:開啟服務(wù)網(wǎng)格+應(yīng)用運行時新篇章
2021-06-21
總結(jié)
以上是生活随笔為你收集整理的KCL:声明式的云原生配置策略语言的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 《微服务架构设计模式》总结,文末送书
- 下一篇: 彷徨编程十几年,终于盯上 Rust