零、前语​

文本仅用于澄清声明式装备技能概述,KCL概念以及中心规划,以及与其他装备言语的比照。

一、声明式装备概述​

1.1 装备的重要性​

  • 软件不是一成不变的,每天有不计其数的装备更新,而且装备自身也在逐渐演进,对规划化效率有较高的诉求

    • 装备更新越来越频频:装备供给了一种改变体系功用的低开支方法,不断发展的事务需求、根底设施要求和其他因素意味着体系需求不断变化。
    • 装备规划越来越大:一份装备往往要分发到不同的云站点、不同的租户、不同的环境等。
    • 装备场景广泛:运用装备、数据库装备、网络装备、监控装备等。
    • 装备格局繁多:JSON, YAML, XML, TOML, 各种装备模版如 Java Velocity, Go Template 等。
  • 装备的稳定性至关重要,体系宕机或犯错的一个最主要原因是有很多工程师进行频频的实时装备更新,表 1 示出了几个由于装备导致的体系犯错事情。

时刻 事情
2021 年 7 月 中国 Bilibili 公司由于 SLB Lua 装备核算犯错堕入死循环导致网站宕机
2021 年 10 月 韩国 KT 公司由于路由装备过错导致在全国范围内遭受严重网络中断

表 1 装备导致的体系犯错事情

1.2 声明式装备分类​

云原生时代带来了如漫山遍野般的技能发展,呈现了很多面向终态的声明式装备实践,如图 1 所示,声明式装备一般可分为如下几种方法。

万字长文!声明式配置技术概述
图 1 声明式装备方法分类

1.2.1 结构化 (Structured) 的 KV​

结构化的 KV 能够满意最小化数据声明需求,比方数字、字符串、列表和字典等数据类型,而且随着云原生技能快速发展运用,声明式 API 能够满意 X as Data 发展的诉求,而且面向机器可读可写,面向人类可读。其好坏如下:

  • 优势

    • 语法简略,易于编写和阅览
    • 多言语 API 丰厚
    • 有各种 Path 东西方便数据查询,如 XPath, JsonPath 等
  • 痛点

    • 冗余信息多:当装备规划较大时,保护和阅览装备很困难,由于重要的装备信息被淹没在了很多不相关的重复细节中

    • 功用性不足

      • 束缚校验才能
      • 杂乱逻辑编写才能
      • 测验、调试才能
      • 不易笼统和复用
      • Kustomize 的 Patch 比较定制,根本是经过固定几种 Patch Merge 战略

结构化 KV 的代表技能有

  • JSON/YAML:十分方便阅览,以及主动化处理,不同的言语均具有丰厚的 API 支撑。
  • Kustomize:供给了一种无需模板DSL即可自界说 Kubernetes 资源根底装备和差异化装备的解决计划,自身不解决束缚的问题,需求配合很多的额定东西进行束缚查看如Kube-linter、Checkov等查看东西,图 2 示出了 Kustomize 的典型工作方法。

万字长文!声明式配置技术概述
图 2 Kustomize 典型工作方法

1.2.3 模版化 (Templated) 的 KV​

模版化 (Templated) 的 KV 赋予静态装备数据动态参数的才能,能够做到一份模版+动态参数输出不同的静态装备数据。其好坏如下:

  • 优势

    • 简略的装备逻辑,循环支撑
    • 支撑外部动态参数输入模版
  • 痛点

    • 简略落入一切装备参数都是模版参数的圈套
    • 当装备规划变大时,开发者和东西都难以保护和剖析它们

模版化代表技能有:

  • Helm:Kubernetes 资源的包办理东西,经过装备模版办理 Kubernetes 资源装备。图 3 示出了一个 Helm Jekins Package ConfigMap 装备模版,能够看出这些模版自身都十分矮小,能够书写简略的逻辑,合适 Kubernetes 根底组件固定的一系列资源装备经过包办理+额定的装备参数进行装置。比较于单纯的模版化的 KV,Helm 必定程度上供给了模版存储/引证和语义化版别办理的才能比较于 Kustomize 更合适办理外部 Charts, 可是在多环境、多租户的装备办理上不太擅长。

万字长文!声明式配置技术概述
图 3 Helm Jekins Package ConfigMap 装备模版

  • 其他各种装备模版:Java Velocity, Go Template 等文本模板引擎十分合适 HTML 编写模板。可是在装备场景中运用时,存在一切装备字段即模版参数的危险,开发者和东西都难以保护和剖析它们。

1.2.3 代码化 (Programmable) 的 KV​

Configuration as Code (CaC), 运用代码发生装备,就像工程师们只需求写高档 GPL 代码,而不是手艺编写简略犯错而且难以了解的服务器二进制代码相同。装备改变同代码改变相同严肃地对待,相同能够执行单元测验、集成测验等。代码模块化和重用是保护装备代码比手动编辑 JSON/YAML 等装备文件更简略的一个关键原因。其好坏如下:

  • 优势

    • 必要的编程才能(变量界说、逻辑判别、循环、断言等)
    • 代码模块化与笼统(支撑界说数据模版,并用模版得到新的装备数据)
    • 能够笼统装备模版+并运用装备掩盖
  • 痛点

    • 类型查看不足
    • 运转时过错
    • 束缚才能不足

代码化 KV 的代表技能有:

  • GCL:一种 Python 完成的声明式装备编程言语,供给了必要的言才能支撑模版笼统,但编译器自身是 Python 编写,且言语自身是解说执行,关于大的模版实例 (比方 K8s 型) 功用较差。
  • HCL:一种 Go 完成结构化装备言语,原生语法遭到 libuclnginx 装备等的启示,用于创立对人类和机器都友爱的结构化装备言语,主要针对 devops 东西、服务器装备及 Terraform 中界说资源装备等。
  • Jsonnet:一种 C++ 完成的数据模板言语,适用于运用程序东西开发人员,能够生成装备数据而且无副作用安排、简化、一致办理庞大的装备。

1.2.4 类型化 (Typed) 的 KV​

类型化的 KV,依据代码化 KV,多了类型查看和束缚的才能,其好坏如下:

  • 优势

    • 装备兼并彻底幂等,天然防止装备抵触
    • 丰厚的装备束缚语法用于编写束缚
    • 将类型和值束缚编写笼统为同一种方法,编写简略
    • 装备次序无关
  • 痛点

    • 图兼并和幂等兼并等概念杂乱,用户了解本钱较高
    • 类型和值混合界说进步笼统程度的一同提高了用户的了解本钱,而且一切束缚在运转时进行查看,大规划装备代码下有功用瓶颈
    • 关于想要装备掩盖、修正的多租户、多环境场景难以完成
    • 关于带条件的束缚场景,界说和校验混合界说编写用户界面不友爱

类型化 KV 的代表技能有:

  • CUE:CUE 解决的中心问题是“类型查看”,主要运用于装备束缚校验场景及简略的云原生装备场景

1.2.5 模型化 (Structural) 的 KV​

模型化的 KV 在代码化和类型化 KV 的根底上以高档言语建模才能为中心描述,期望做到模型的快速编写与分发,其好坏如下:

  • 优势

    • 引进可分块、可扩展的 KV 装备块编写方法
    • 类高档编程言语的编写、测验方法
    • 言语内置的强校验、强束缚支撑
    • 面向人类可读可写,面向机器部分可读可写
  • 不足

    • 扩展新模型及生态构建需求必定的研制本钱,或许运用东西对社区中已有的 JsonSchema 和 OpenAPI 模型进行模型转化、搬迁和集成。

模型化 KV 的代表技能有:

  • KCL:一种 Rust 完成的声明式装备战略编程言语,把运维类研制一致为一种声明式的代码编写,能够针对差异化运用交给场景笼统出用户模型并添加相应的束缚才能,期望凭借可编程 DevOps 理念解决规划化运维场景中的装备战略编写的效率和可扩展性等问题。图 4 示出了一个 KCL 编写运用交给装备代码的典型场景

万字长文!声明式配置技术概述
图 4 运用 KCL 编写运用交给装备代码

1.3 不同声明式装备方法的挑选标准与最佳实践​

  • 装备的规划:关于小规划的装备场景,彻底能够运用 YAML/JSON 等装备,比方运用自身的简略装备,CI/CD 的装备。此外关于小规划装备场景存在的多环境、多租户等需求能够凭借 Kustomize 的 overlay 才能完成简略装备的兼并掩盖等操作。
  • 模型笼统与束缚的必要性:关于较大规划的装备场景特别是对多租户、多环境等有装备模型和运维特性研制和沉积迫切需求的,能够运用代码化、类型化和模型化的 KV 方法。

此外,从不同声明式装备方法的运用场景出发

  • 假如需求编写结构化的静态的 K-V,或运用 Kubernetes 原生的技能东西,主张挑选 YAML
  • 假如期望引进编程言语便利性以消除文本(如 YAML、JSON) 模板,有杰出的可读性,或许已是Terraform的用户,主张挑选 HCL
  • 假如期望引进类型功用提高稳定性,保护可扩展的装备文件,主张挑选 CUE 之类的数据束缚言语
  • 假如期望以现代言语方法编写杂乱类型和建模,保护可扩展的装备文件,原生的纯函数和战略,和出产级的功用和主动化,主张挑选 KCL

不同于社区中的其他同类型范畴言语,KCL 是一种面向运用研制人员并采用了现代言语规划和技能的静态强类型编译言语

注意,本文将不会评论通用言语编写装备的情况,通用言语一般是 Overkill 的,即远远超过了需求解决的问题,通用言语存在林林总总的安全问题,比方才能边界问题 (启动本地线程、拜访 IO, 网络,代码死循环等不安全危险),比方像音乐范畴就有专门的音符去表明音乐,方便学习与沟通,不是一般文字言语能够表述清楚的。

此外,通用言语由于自身就样式繁多,存在一致保护、办理和主动化的本钱,通用言语一般用来编写客户端运转时,是服务端运转时的一个延续,不合适编写与运转时无关的装备,终究被编译为二进制从进程启动,稳定性和扩展性不好控制,而装备言语往往编写的是数据,再搭配以简略的逻辑,描述的是期望的终究成果,然后由编译器或许引擎来消费这个期望成果。

二、KCL 的中心规划与运用场景​

Kusion 装备言语(KCL)是一个开源的依据束缚的记载及函数言语。KCL 经过老练的编程言语技能和实践来改进对很多繁杂装备的编写,致力于构建环绕装备的更好的模块化、扩展性和稳定性,更简略的逻辑编写,以及更快的主动化集成和杰出的生态延展性。

KCL 的中心特性是其建模束缚才能,KCL 中心功用根本环绕 KCL 这个两个中心特性打开,此外 KCL 遵循以用户为中心的装备理念而规划其间心特性,能够从两个方面了解:

  • 以范畴模型为中心的装备视图:凭借 KCL 言语丰厚的特性及KCL OpenAPI等东西,能够将社区中广泛的、规划杰出的模型直接集成到 KCL 中(比方 K8s 资源模型),用户也能够依据自己的事务场景规划、完成自己的 KCL 模型 (库) ,形成一整套范畴模型架构交由其他装备终端用户运用。
  • 以终端用户为中心的装备视图:凭借 KCL 的代码封装、笼统和复用才能,能够对模型架构进行进一步笼统和简化(比方将 K8s 资源模型笼统为以运用为中心的 Server 模型),做到最小化终端用户装备输入,简化用户的装备界面,方便手动或许运用主动化 API 对其进行修正。

不管是以何为中心的装备视图,关于代码而言(包括装备代码)都存在对装备数据束缚的需求,比方类型束缚、装备字段必选/可选束缚、范围束缚、不可变性束缚等,这也是 KCL 致力于解决的中心问题之一。综上,KCL 是一个开源的依据束缚和声明的函数式言语,KCL 主要包括如图 5 所示的中心特性:

万字长文!声明式配置技术概述

图 5 KCL 中心特性

  • 简略易用:源于 Python、Golang 等高档言语,采用函数式编程言语特性,低副作用
  • 规划杰出:独立的 Spec 驱动的语法、语义、运转时和体系库规划
  • 快速建模:以Schema为中心的装备类型及模块化笼统
  • 功用齐备:依据Config、Schema、Lambda、Rule的装备及其模型、逻辑和战略编写
  • 牢靠稳定:依靠静态类型体系、束缚和自界说规矩的装备稳定性
  • 强可扩展:经过独立装备块主动兼并机制确保装备编写的高可扩展性
  • 易主动化:CRUD APIs,多言语 SDK,言语插件构成的梯度主动化计划
  • 极致功用:运用 Rust & C,LLVM完成,支撑编译到本地代码和WASM的高功用编译时和运转时
  • API 亲和:原生支撑OpenAPI、 Kubernetes CRD, Kubernetes YAML 等 API 生态标准
  • 开发友爱:言语东西(Format,Lint,Test,Vet,Doc 等)、IDE 插件构建杰出的研制体会
  • 安全可控:面向范畴,不原生供给线程、IO 等体系级功用,低噪音,低安全危险,易保护,易治理
  • 多言语API:Go,Python和REST API满意不同场景和运用运用需求
  • 出产可用:广泛运用在蚂蚁集团平台工程及主动化的出产环境实践中

万字长文!声明式配置技术概述

图 6 KCL 言语中心规划

更多言语规划和才能详见KCL 文档,虽然 KCL 不是通用言语,但它有相应的运用场景,如图 6 所示,研制者能够经过 KCL 编写装备(config)模型(schema)函数(lambda)规矩(rule) ,其间 Config 用于界说数据,Schema 用于对数据的模型界说进行描述,Rule 用于对数据进行校验,而且 Schema 和 Rule 还能够组合运用用于完整描述数据的模型及其束缚,此外还能够运用 KCL 中的 lambda 纯函数进行数据代码安排,将常用代码封装起来,在需求运用时能够直接调用。

关于运用场景而言,KCL 能够进行结构化 KV 数据验证、杂乱装备模型界说与笼统、强束缚校验避免装备过错、分块编写及装备兼并才能、主动化集成和工程扩展等才能,下面针对这些功用和运用场景进行论述。

2.1 结构化 KV 数据验证​

如图 7 所示,KCL 支撑对 JSON/YAML 数据进行格局校验。作为一种装备言语,KCL 在验证方面几乎涵盖了 OpenAPI 校验的一切功用。在 KCL 中能够经过一个结构界说来束缚装备数据,一同支撑经过 check 块自界说束缚规矩,在 schema 中书写校验表达式对 schema 界说的特点进行校验和束缚。经过 check 表达式能够十分清晰简略地校验输入的 JSON/YAML 是否满意相应的 schema 结构界说与 check 束缚。

万字长文!声明式配置技术概述

图 7 KCL 中结构化 KV 校验方法

依据此,KCL 供给了相应的校验东西直接对 JSON/YAML 数据进行校验。此外,经过 KCL schema 的 check 表达式能够十分清晰简略地校验输入的 JSON 是否满意相应的 schema 结构界说与 check 束缚。此外,依据此才能能够构建如图 8 所示的 KV 校验可视化产品。

万字长文!声明式配置技术概述

图 8 依据 KCL 结构化 KV 校验才能构建的可视化产品界面

2.2 杂乱装备模型界说与笼统​

如图 9 所示,凭借 KCL 言语丰厚的特性及KCL OpenAPI等东西,能够将社区中广泛的、规划杰出的模型直接集成到 KCL 中(比方 K8s 资源模型 CRD),用户也能够依据自己的事务场景规划、完成自己的 KCL 模型 (库) ,形成一整套范畴模型架构交由其他装备终端用户运用。

万字长文!声明式配置技术概述

图 9 KCL 杂乱装备建模的一般方法

依据此,能够像图 10 示出的那样用一个大的Konfig 库房办理悉数的 KCL 装备代码,将事务装备代码 (运用代码)、根底装备代码 (中心模型+底层模型)在一个大库中,方便代码间的版别依靠办理,主动化体系处理也比较简略,定位仅有代码库的目录及文件即可,代码互通,一致办理,便于查找、修正、保护,能够运用一致的 CI/CD 流程进行装备办理(此外,大库方法也是 Google 等头部互联网公司内部实践的方法)。

万字长文!声明式配置技术概述

图 10 运用 KCL 的言语才能集成范畴模型并笼统用户模型并运用

2.3 强束缚校验避免装备过错​

如图 11 所示,在 KCL 中能够经过丰厚的强束缚校验手法避免装备过错:

万字长文!声明式配置技术概述

图 11 KCL 强束缚校验手法

  • KCL 言语的类型体系被规划为静态的,类型和值界说别离,支撑编译时类型推导和类型查看,静态类型不只仅能够提早在编译时剖析大部分的类型过错,还能够降低后端运转时的动态类型查看的功用损耗。此外,KCL Schema 结构的特点强制为非空,能够有效避免装备遗漏。
  • 当需求导出的 KCL 装备被声明之后,它们的类型和值均不能发生变化,这样的静态特性确保了装备不会被随意篡改。
  • KCL 支撑经过结构体内置的校验规矩进一步保障稳定性。比方关于如图 12 所示的 KCL 代码,,在App中界说对containerPortservicesvolumes的校验规矩,现在校验规矩在运转时执行判别,后续 KCL 会尝试经过编译时的静态剖析对规矩进行判别从而发现问题。

万字长文!声明式配置技术概述
图 12 带规矩束缚的 KCL 代码校验

2.4 分块编写及装备兼并​

KCL 供给了装备分块编写及主动兼并装备的才能,而且支撑幂等兼并、补丁兼并和仅有装备兼并等战略。幂等兼并中的多份装备需求满意交换律,而且需求开发人员手动处理根底装备和不同环境装备抵触。 补丁兼并作为一个掩盖功用,包括掩盖、删去和添加。仅有的装备要求装备块是大局仅有的而且未修正或以任何方法重新界说。 KCL 经过多种兼并战略简化了用户侧的协同开发,减少了装备之间的耦合。

如图 13 所示,关于存在基线装备、多环境和多租户的运用装备场景,有一个根本装备 base.k。 开发和 SRE 分别保护出产和开发环境的装备 base.k 和 prod.k,他们的装备互不影响,由 KCL 编译器兼并成一个 prod 环境的等效装备代码。

万字长文!声明式配置技术概述

图 13 多环境场景装备分块编写实例

2.5 主动化集成​

在 KCL 中供给了很多主动化相关的才能,主要包括东西和多言语 API。 经过package_identifier : key_identifier的方法支撑对恣意装备键值的索引,从而完成对恣意键值的增删改查。比方图 14 所示修正某个运用装备的镜像内容,能够直接执行如下指令修正镜像,修正前后的 diff 如下图所示。

万字长文!声明式配置技术概述

图 14 运用 KCL CLI/API 主动修正运用装备镜像

此外,能够依据 KCL 的主动化才能完成如图 15 所示的一镜交给及主动化运维才能并集成到 CI/CD 傍边。

万字长文!声明式配置技术概述

图 15 典型 KCL 主动化集成链路

三、KCL 与其他声明式装备的比照​

3.1 vs. JSON/YAML​

YAML/JSON 装备等合适小规划的装备场景,关于大规划且需求频频修正的云原生装备场景,比较合适 KCL 比较合适,其间涉及到主要差异是装备数据笼统与打开的差异:

  • 关于 JSON/YAML 等静态装备数据打开的优点是:简略、易读、易于处理,可是随着静态装备规划的增加,当装备规划较大时,JSON/YAML 等文件保护和阅览装备很困难,由于重要的装备信息被淹没在了很多不相关的重复细节中。
  • 关于运用 KCL 言语进行装备笼统的优点是:关于静态数据,笼一致层的优点这意味着全体体系具有部署的灵敏性,不同的装备环境、装备租户、运转时或许会对静态数据具有不同的要求,甚至不同的安排或许有不同的标准和产品要求,能够运用 KCL 将最需求、最常修正的装备露出给用户,对差异化的装备进行笼统,笼统的优点是能够支撑不同的装备需求。而且凭借 KCL 言语等级的主动化集成才能,还能够很好地支撑不同的言语,不同的装备 UI 等。

3.2 vs. Kustomize​

Kustomize 的中心才能是其 Overlay 才能,并 Kustomize 支撑文件级的掩盖,可是存在会存在多个掩盖链条的问题,由于找到详细字段值的声明并不能确保这是终究值,由于其他地方呈现的另一个详细值能够掩盖它,关于杂乱的场景,Kustomize 文件的承继链检索往往不如 KCL 代码承继链检索方便,需求仔细考虑指定的装备文件掩盖次序。此外,Kustomize 不能解决 YAML 装备编写、装备束缚校验和模型笼统与开发等问题,较为适用于简略的装备场景,当装备组件增多时,关于装备的修正仍然会堕入很多重复不相关的装备细节中,而且在 IDE 中不能很好地显示装备之间的依靠和掩盖联系情况,只能经过搜索/替换等批量修正装备。

在 KCL 中,装备兼并的操作能够细粒度到代码中每一个装备字段,而且能够灵敏的设置兼并战略,并不局限于资源全体,而且经过 KCL 的 import 能够静态剖析出装备之间的依靠联系。

3.3 vs. HCL​

3.3.1 功用比照​

HCL KCL
建模才能 经过 Terraform Go Provider Schema 界说,在用户界面不直接感知,此外编写杂乱的 object 和必选/可选字段界说时用户界面较为繁琐 经过 KCL Schema 进行建模,经过言语等级的工程和部分面向目标特性,能够完成较高的模型笼统
束缚才能 经过 Variable 的 condition 字段对动态参数进行束缚,Resource 自身的束缚需求经过 Go Provider Schema 界说或许结合 Sentinel/Rego 等战略言语完成,言语自身的完整才能不能自闭环,且完成方法不一致 以 Schema 为中心,在进行建模的一同界说其束缚,在 KCL 内部自闭环并一一致方法完成,支撑多种束缚函数编写,支撑可选/必选字段界说
扩展性 Terraform HCL 经过分文件进行 Override, 方法比较固定,才能受限。 KCL 能够自界说装备分块编写方法和多种兼并战略,能够满意杂乱的多租户、多环境装备场景需求
言语化编写才能 编写杂乱的目标界说和必选/可选字段界说时用户界面较为繁琐 杂乱的结构界说、束缚场景编写简略,不凭借其他外围 GPL 或东西,言语编写自闭环

3.3.2 举例​

Terraform HCL Variable 束缚校验编写 vs. KCL Schema 声明式束缚校验编写

  • HCL
variable "subnet_delegations" {
  type = list(object({
    name               = string
    service_delegation = object({
      name    = string
      actions = list(string)
    })
  }))
  default     = null
  validation {
    condition = var.subnet_delegations == null ? true : alltrue([for d in var.subnet_delegations : (d != null)])
  }
  validation {
    condition = var.subnet_delegations == null ? true : alltrue([for n in var.subnet_delegations.*.name : (n != null)])
  }
  validation {
    condition = var.subnet_delegations == null ? true : alltrue([for d in var.subnet_delegations.*.service_delegation : (d != null)])
  }
  validation {
    condition = var.subnet_delegations == null ? true : alltrue([for n in var.subnet_delegations.*.service_delegation.name : (n != null)])
  }
}
  • KCL
schema SubnetDelegation:
    name: str
    service_delegation: ServiceDelegation
schema ServiceDelegation:
    name: str
    actions?: [str]  # 运用 ? 标记可选特点
subnet_delegations: [SubnetDelegation] = option("subnet_delegations")

此外,KCL 还能够像高档言语相同写类型,写承继,写内置的束缚,这些功用是 HCL 所不具备的

Terraform HCL 函数 vs. KCL Lambda 函数编写

  • 正如www.terraform.io/language/fu…文档和github.com/hashicorp/t…中展示的那样,Terraform HCL 供给了丰厚的内置函数用于供给,可是并不支撑用户在 Terraform 中运用 HCL 自界说函数 (或许需求编写杂乱的 Go Provider 来模仿一个用户的本地自界说函数);而 KCL 不只支撑用户运用 lambda 关键字直接在 KCL 代码中自界说函数,还支撑运用 Python, Go 等言语为 KCL编写插件函数
  • KCL 自界说界说函数并调用
add_func = lambda x: int, y: int -> int {
    x + y
}
two = add_func(1, 1)  # 2

HCL 删去 null 值与 KCL 运用 -n 编译参数删去 null 值

  • HCL
variable "conf" {
  type = object({
    description = string
    name        = string
    namespace   = string
    params = list(object({
      default     = optional(string)
      description = string
      name        = string
      type        = string
    }))
    resources = optional(object({
      inputs = optional(list(object({
        name = string
        type = string
      })))
      outputs = optional(list(object({
        name = string
        type = string
      })))
    }))
    results = optional(list(object({
      name        = string
      description = string
    })))
    steps = list(object({
      args    = optional(list(string))
      command = optional(list(string))
      env = optional(list(object({
        name  = string
        value = string
      })))
      image = string
      name  = string
      resources = optional(object({
        limits = optional(object({
          cpu    = string
          memory = string
        }))
        requests = optional(object({
          cpu    = string
          memory = string
        }))
      }))
      script     = optional(string)
      workingDir = string
    }))
  })
}
locals {
  conf = merge(
    defaults(var.conf, {}),
    { for k, v in var.conf : k => v if v != null },
    { resources = { for k, v in var.conf.resources : k => v if v != null } },
    { steps = [for step in var.conf.steps : merge(
      { resources = {} },
      { for k, v in step : k => v if v != null },
    )] },
  )
}
  • KCL (编译参数添加 -n 疏忽 null 值)
schema Param:
    default?: str
    name: str
schema Resource:
    cpu: str
    memory: str
schema Step:
    args?: [str]
    command?: [str]
    env?: {str:str}
    image: str
    name: str
    resources?: {"limits" | "requests": Resource}
    script?: str
    workingDir: str
schema K8sManifest:
    name: str
    namespace: str
    params: [Param]
    results?: [str]
    steps: [Step]
conf: K8sManifest = option("conf")

综上能够看出,在 KCL 中,经过 Schema 来声明方法界说其类型和束缚,能够看出比较于 Terraform HCL, 在完成相同功用的情况下,KCL 的束缚能够编写的愈加简略 (不需求像 Terraform 那样重复地书写 validation 和 condition 字段),而且额定供给了字段设置为可选的才能 (?运算符,不像 Terraform 装备字段默许可空,KCL Schema 字段默许必选),结构愈加清楚,而且能够在代码层面直接获得类型查看和束缚校验的才能。

3.4 vs. CUE​

3.4.1 功用比照​

CUE KCL
建模才能 经过 Struct 进行建模,无承继等特性,当模型界说之间无抵触时能够完成较高的笼统。由于 CUE 在运转时进行一切的束缚查看,在大规划建模场景或许存在功用瓶颈 经过 KCL Schema 进行建模,经过言语等级的工程和部分面向目标特性(如单承继),能够完成较高的模型笼统。 KCL 是静态编译型言语,关于大规划建模场景开支较小
束缚才能 CUE 将类型和值兼并到一个概念中,经过各种语法简化了束缚的编写,比方不需求泛型和枚举,求和类型和空值兼并都是一回事 KCL 供给了跟更丰厚的 check 声明式束缚语法,编写起来愈加简略,关于一些装备字段组合束缚编写愈加简略(才能上比 CUE 多了 if guard 组合束缚,all/any/map/filter 等调集束缚编写方法,编写愈加简略)
分块编写才能 支撑言语内部装备兼并,CUE 的装备兼并是彻底幂等的,关于满意杂乱的多租户、多环境装备场景的掩盖需求或许无法满意 KCL 能够自界说装备分块编写方法和多种兼并战略,KCL 一同支撑幂等和非幂等的兼并战略,能够满意杂乱的多租户、多环境装备场景需求
言语化编写才能 关于杂乱的循环、条件束缚场景编写杂乱,关于需求进行装备精确修正的编写场景较为繁琐 杂乱的结构界说、循环、条件束缚场景编写简略

3.4.2 举例​

CUE 束缚校验编写 vs. KCL Schema 声明式束缚校验编写及装备分块编写才能

CUE (执行命令cue export base.cue prod.cue)

  • base.cue
// base.cue
import "list"
#App: {
    domainType: "Standard" | "Customized" | "Global",
    containerPort: >=1 & <=65535,
    volumes: [...#Volume],
    services: [...#Service],
}
#Service: {
    clusterIP: string,
    type: string,
    if type == "ClusterIP" {
        clusterIP: "None"
    }
}
#Volume: {
    container: string | *"*"  // The default value of `container` is "*"
    mountPath: string,
    _check: false & list.Contains(["/", "/boot", "/home", "dev", "/etc", "/root"], mountPath),
}
app: #App & {
    domainType: "Standard",
    containerPort: 80,
    volumes: [
        {
            mountPath: "/tmp"
        }
    ],
    services: [
        {
            clusterIP: "None",
            type: "ClusterIP"
        }
    ]
}
  • prod.cue
// prod.cue
app: #App & {
    containerPort: 8080,  // error: app.containerPort: conflicting values 8080 and 80:
}

KCL (执行命令kcl base.k prod.k)

  • base.k
# base.k
schema App:
    domainType: "Standard" | "Customized" | "Global"
    containerPort: int
    volumes: [Volume]
    services: [Service]
    check:
        1 <= containerPort <= 65535
schema Service:
    clusterIP: str
    $type: str
    check:
        clusterIP == "None" if $type == "ClusterIP"
schema Volume:
    container: str = "*"  # The default value of `container` is "*"
    mountPath: str
    check:
        mountPath not in ["/", "/boot", "/home", "dev", "/etc", "/root"]
app: App {
    domainType = "Standard"
    containerPort = 80
    volumes = [
        {
            mountPath = "/tmp"
        }
    ]
    services = [
        {
            clusterIP = "None"
            $type = "ClusterIP"
        }
    ]
}
  • prod.k
# prod.k
app: App {
    # 能够运用 = 特点运算符对 base app 的 containerPort 进行修正
    containerPort = 8080
    # 能够运用 += 特点运算符对 base app 的 volumes 进行添加
    # 此处表明在 prod 环境增加一个 volume, 一共两个 volume
    volumes += [
        {
            mountPath = "/tmp2"
        }
    ]
}

此外由于 CUE 的幂等兼并特性,在场景上并无法运用类似 kustomize 的 overlay 装备掩盖和 patch 等才能,比方上述的 base.cue 和 prod.cue 一同编译会报错。

3.5 Performance​

在代码规划较大或许核算量较高的场景情况下 KCL 比 CUE/Jsonnet/HCL 等言语功用更好 (CUE 等言语受限于运转时束缚查看开支,而 KCL 是一个静态编译型言语)

  • CUE (test.cue)
import "list"
temp: {
        for i, _ in list.Range(0, 10000, 1) {
                "a(i)": list.Max([1, 2])
        }
}
  • KCL (test.k)
a = lambda x: int, y: int -> int {
    max([x, y])
}
temp = {"a${i}": a(1, 2) for i in range(10000)}
  • Jsonnet (test.jsonnet)
local a(x, y) = std.max(x, y);
{
    temp: {["a%d" % i]: a(1, 2) for i in std.range(0, 10000)},
}
  • Terraform HCL (test.tf, 由于 terraform range 函数只支撑最多 1024 个迭代器,将 range(10000) 拆分为 10 个子 range)
output "r1" {
  value = {for s in range(0, 1000) : format("a%d", s) => max(1, 2)}
}
output "r2" {
  value = {for s in range(1000, 2000) : format("a%d", s) => max(1, 2)}
}
output "r3" {
  value = {for s in range(1000, 2000) : format("a%d", s) => max(1, 2)}
}
output "r4" {
  value = {for s in range(2000, 3000) : format("a%d", s) => max(1, 2)}
}
output "r5" {
  value = {for s in range(3000, 4000) : format("a%d", s) => max(1, 2)}
}
output "r6" {
  value = {for s in range(5000, 6000) : format("a%d", s) => max(1, 2)}
}
output "r7" {
  value = {for s in range(6000, 7000) : format("a%d", s) => max(1, 2)}
}
output "r8" {
  value = {for s in range(7000, 8000) : format("a%d", s) => max(1, 2)}
}
output "r9" {
  value = {for s in range(8000, 9000) : format("a%d", s) => max(1, 2)}
}
output "r10" {
  value = {for s in range(9000, 10000) : format("a%d", s) => max(1, 2)}
}
  • 运转时刻(考虑到出产环境的实际资源开支,本次测验以单核为准)
环境 KCL v0.4.3 运转时刻 (包括编译+运转时刻) CUE v0.4.3 运转时刻 (包括编译+运转时刻) Jsonnet v0.18.0 运转时刻 (包括编译+运转时刻) HCL in Terraform v1.3.0 运转时刻 (包括编译+运转时刻)
OS: macOS 10.15.7; CPU: Intel(R) Core(TM) i7-8850H CPU @ 2.60GHz; Memory: 32 GB 2400 MHz DDR4; 不敞开 NUMA 440 ms (kclvm_cli run test.k) 6290 ms (cue export test.cue) 3340 ms (jsonnet test.jsonnet) 1774 ms (terraform plan -parallelism=1)

综上能够看出:CUE 和 KCL 均能够掩盖到绝大多数装备校验场景,而且均支撑特点类型界说、装备默许值、束缚校验等编写,可是 CUE 关于不同的束缚条件场景无一致的写法,且不能很好地透出校验过错,KCL 运用 check 关键字作一致处理,支撑用户自界说过错输出。

另一个杂乱的例子​

运用 KCL 和 CUE 编写 Kubernetes 装备

  • CUE (test.cue)
package templates
import (
 apps "k8s.io/api/apps/v1"
)
deployment: apps.#Deployment
deployment: {
 apiVersion: "apps/v1"
 kind:       "Deployment"
 metadata: {
  name:   "me"
  labels: me: "me"
 }
}
  • KCL (test.k)
import kubernetes.api.apps.v1
deployment = v1.Deployment {
    metadata.name = "me"
    metadata.labels.name = "me"
}
环境 KCL v0.4.3 运转时刻 (包括编译+运转时刻) CUE v0.4.3 运转时刻 (包括编译+运转时刻)
OS: macOS 10.15.7; CPU: Intel(R) Core(TM) i7-8850H CPU @ 2.60GHz; Memory: 32 GB 2400 MHz DDR4; no NUMA 140 ms (kclvm_cli run test.k) 350 ms (cue export test.cue)

四、小结​

文本对声明式装备技能做了全体概述,其间要点论述了 KCL 概念、中心规划、运用场景以及与其他装备言语的比照,期望帮助大家更好的了解声明式装备技能及 KCL 言语。更多 KCL 的概念、背景、规划与用户案例等相关内容,欢迎拜访KCL 网站

五、参阅​

  • KusionStack Cloud Native Configuration Practice Blog:kusionstack.io/blog/2021-k…
  • Terraform Language:www.terraform.io/language
  • Terraform Provider Kubernetes:github.com/hashicorp/t…
  • Terraform Provider AWS:github.com/hashicorp/t…
  • Pulumi:www.pulumi.com/docs/
  • Pulumi vs. Terraform:www.pulumi.com/docs/intro/…
  • Google SRE Work Book Configuration Design:sre.google/workbook/co…
  • Google Borg Paper:storage.googleapis.com/pub-tools-p…
  • Holistic Configuration Management at Facebook:sigops.org/s/conferenc…
  • JSON Spec:www.json.org/json-en.htm…
  • YAML Spec:yaml.org/spec/
  • GCL:github.com/rix0rrr/gcl
  • HCL:github.com/hashicorp/h…
  • CUE:github.com/cue-lang/cu…
  • Jsonnet:github.com/google/json…
  • Dhall:github.com/dhall-lang/…
  • Thrift:github.com/Thriftpy/th…
  • Kustomize:kustomize.io/
  • Kube-linter:github.com/stackrox/ku…
  • Checkov:github.com/bridgecrewi…
  • KCL Documents:kcl-lang.io/docs/refere…
  • How Terraform Works: A Visual Intro:betterprogramming.pub/how-terrafo…
  • How Terraform Works: Modules Illustrated:awstip.com/terraform-m…
  • Helm:helm.sh/
  • Helm vs. Kustomize:harness.io/blog/helm-v…
  • KubeVela:kubevela.io/docs/