多数配置故障起初看起来不像编程故障。它们更像是从另一个服务复制来的一段 YAML 字段,像在一处完成校验却在另一处被静默接受的 JSON 载荷,像和部署文件并排摆放、没有进入同一个审查界面的 Kubernetes 策略,也像一份生成文件,它的 schema 之所以被信任,只因为构建流水线通常表现正常。CUE 的有趣之处在于,它没有先往这堆东西上再加一个 linter。它提出的问题是:配置、schema、策略和数据能否共享同一条语言边界。[1][2][4][5]

FOSDEM 2022 这场名为 "A practical guide to CUE: patterns for everyday use" 的会议演讲,是一个有用的锚点,因为它没有把语言理论当作独立目的来推销。活动说明把这场演讲放在实践模式之中:校验、策略、与既有非 CUE 配置集成,以及面向已经在 JSON、YAML、Protocol Buffers 或 OpenAPI 中工作的人的示例。[2] 这正是合适的运行语境。CUE 的意义较少来自更漂亮的语法,更多来自它让配置契约在变成事故之前就能被运行和检查。[1][2][3]

观看时需要抓住的核心概念是统一。CUE 的导览解释说,只要值之间没有冲突,字段就可以被多次指定,struct 和 list 也会递归统一。[4] 约束页面进一步把这个点收紧:约束本身也是值,因此一份规格既可以说明什么被允许,也可以提供具体字段,这些字段会出现在约束被统一到的地方。[5] 语言中改变工程讨论的正是这一部分。团队的问题由“schema 在哪里?”和“配置在哪里?”两条分线,转向整个配置值是否仍然能够保持一致。[4][5]

开场动作是把配置视为带有义务的数据

在这场 37 分钟演讲的开头部分,重要的转移点不在某一个语法特性,而在这样一种主张:配置应当随身携带义务。[1][2] 一份普通的 YAML 文件可以说明系统当前想要什么。一份分离的 schema 可以说明这个文件应当具备什么形状。一个策略引擎可以说明在某个环境里哪些形状可以接受。一个生成器还可以为目标系统产出另一份制品。这些层次都很熟悉,但它们的分离会制造漂移。审查批准的也许是一层,而另一层在底下发生了变化。

CUE 的模型通过让约束与数据可组合,直接处理这种漂移。当导览说统一只在值之间没有冲突时成功,它描述的是一条实际的审查规则,并且也同时是一条语言规则。[4] 如果一个服务定义声明 replicas 必须是大于零的整数,那么环境叠加层可以收窄这个值,同时保留原始契约。如果另一个叠加层试图把这个值变成无法成立的状态,这个值就不只是在运行时意义上成为“坏配置”。它会统一失败。[4][5]

这也是这段视频值得和官方文档一起打开观看的原因。演讲给出了工作流的压力:既有格式、策略检查、日常校验,以及与人们已经使用的工具集成。[1][2][3] 文档则给出了这种工作流能够成立的机械原因:CUE 把约束和具体值看成同一个值格中的成员,摆脱了用约定把互不相关文件粘在一起的脆弱方式。[4][5]

演讲中段谈的是收拢与边界整理

到了中段,实践示例应当被理解为对制品蔓延的收拢。[1][2] 面对 CUE,人们容易把它称作“带类型的 YAML”之后就停下来。这个说法会错过更有用的模式。CUE 可以在常见配置格式之间导入、校验、转换和导出数据;FOSDEM 页面明确把 JSON、YAML、Protocol Buffers 和 OpenAPI 放进这场演讲的预设地带。[2] 项目的演讲页面同样把 CUE 相关演讲组织在策略、Terraform、Go 集成和配置工程周围,这说明这门语言的目标位置跨越边界,超出单一文件类型内部的语法改良。[3]

对于一个工程团队来说,价值不在于 CUE 消灭其他所有格式。实际工作里通常不会如此。Kubernetes 仍会接收 manifest。CI 系统也许仍然想要 YAML。API 也许仍然会发布 OpenAPI。价值在于,CUE 可以成为这些输出四散之前对照共享契约完成检查的地方。由此看,最容易被 CUE 吸引的,是那些配置已经跨越多种格式、审查也已经变得痛苦的团队。[2][3][5]

危险也在这里显现。如果 CUE 只变成一个聪明的生成器,它会把复杂性藏在又一个构建步骤背后。更好的用法是让契约可以被检查:定义应当围绕领域概念命名,约束应当说明它执行的运维规则,导出的制品应当被视为契约的产物,事实来源则留在契约本身。从演讲和文档中可以推导出,CUE 在减少真相存放位置时效果最好。[1][4][5]

统一会改变代码审查

最具体的收获,是 CUE 应当改变 pull request 里的提问方式。普通配置审查经常问的是局部问题:这个字段改了吗,这个环境文件还能解析吗,生成输出更新了吗?CUE 审查可以提出更强的问题:在所有叠加层、默认值、约束和导入相遇之后,最终得到的值是否仍然连贯?[4][5]

这是一种不同的审查习惯。它会鼓励小而可组合的约束,减少巨大顶层文件带来的审查负担。它推动团队把可复用定义和特定环境选择分开。它也为策略提供了一个更贴近其治理数据的位置。容器资源限制、必需标签,或者允许区域列表,都可以写入同一份契约,而这份契约也负责生成部署数据。部署工具看到数据之前,契约已经可以被测试。[2][4][5]

这里也是 CUE 要求纪律的地方。递归统一会让组合呈现出一种顺手的自动感,但自动组合仍然属于架构。团队需要名称、包边界、所有权,以及一条清楚规则,用来判断某个约束应当进入共享模块,还是留在本地服务文件。缺少这套纪律,语言会变成一张由聪明引用织成的密网。有了这套纪律,CUE 给审查者留下的,是更少而更耐久的检查位置。[3][4]

最后一课是边界放置

进入 FOSDEM 这场演讲的后三分之一后,有用的问题已经不再是“CUE 能校验这个文件吗?”答案经常是可以。[1][2] 更好的问题是“校验边界应该放在哪里?”如果边界放得太晚,CUE 就变成最后一刻的检查,而人在这之前已经审查了错误的制品。如果边界放得太早,它会在契约成熟之前拒绝有价值的本地实验。合适的边界通常落在一个值转入共享基础设施的时刻:可复用 schema、生成出来的 manifest、策略模块,或者被多个团队消费的配置包。[2][3][5]

这种边界放置,正是 CUE 应当进入 OSS 信息流的原因:它作为工程工具发挥作用,意义超出语言观光。它给团队提供了一种方式,把运维知识编码到依赖这些知识的配置附近。它也给出一个强承诺,而这个承诺容易被误解:语言可以证明你交给它的模型内部一致,但它不能判断你的模型是否映照了生产现实。领域判断仍由人负责。CUE 的贡献,是让这种判断进入可运行、可审查,并且更难被意外绕过的形态。[1][4][5]

顺着这个角度阅读,FOSDEM 这场演讲就超出了初学者介绍。它用紧凑的方式说明,配置安全可以更早前移,同时避免被送进一层分离的官僚流程。CUE 最好的本领不在语法本身,而在于让契约和值先相遇,然后系统才会为错误付出代价。[1][2][4][5]

来源

  1. CUE,"FOSDEM 2022 - A practical guide to CUE: patterns for everyday use",YouTube 视频。
  2. FOSDEM 2022,"A practical guide to CUE: patterns for everyday use"——活动页面、演讲说明、讲者和录像链接。
  3. CUE,"Talks and Presentations by the CUE Team"——项目演讲索引,以及围绕策略、Go、Terraform 和配置工程展开的话题框架。
  4. CUE 文档导览,"Unification"——重复字段、递归统一和冲突规则。
  5. CUE 文档导览,"Constraints"——作为值的约束,以及与具体数据的统一。
  6. Zeljko Filipin,"Testing and Automation devroom at FOSDEM 2013 Photo 2",Wikimedia Commons 照片。