MADL!AR
Code is cheap, show me the PPT!
首页
分类
Fragment
关于
CUE入门笔记
分类:
k8s
发布于: 2025-05-14
## CUE 初步教程 ### CUE是什么? CUE(Configure, Unify, Execute)是一种声明式配置语言,由 Google 开发,专注于数据验证、模板生成和配置管理。它的核心设计目标是: * 强类型约束:比 JSON/YAML 更严格的类型系统,支持复杂条件校验 * 数据合并(Unification):类似“继承”或“混入”,可安全合并多份配置 * 多格式输出:生成 JSON、YAML、INI 等格式,适配不同系统需求 有许多类似CUE的项目,或专注于配置生成,或专注模板构建,例如python的jinja2、Pydantic,OpenAPI Validate(基于JSON Schema),但CUE在此领域集成诸多优势脱颖而出,表现在: 1. 强类型 + 约束组合 CUE 支持复杂类型和复合数据解构。而OpenAPI Validate基于JSON Schema,类型有限;Pydantic支持丰富类型,但其基于Python注解,无cli工具等配套生态,与其他语言(如golang)难以结合。 2. 数据合并 CUE原生支持多&操作符来合并多源数据,类似Class的多重继承,对于复杂配置项的整合十分方便。而JSON Schema不支持合并,Pydantic可以通过update操作进行合并,但需手动声明维护,且无冲突检测机制。 3. 配置生成 原生支持YAML/JSON,通过社区工具cue2ini等可以转换成ini,或者其他配置语言。JSON Schema仅作为校验、不生成配置。Pydantic可以方便的生成静态配置,但难以实现条件化模板(如根据环境变量动态生成字段),需要借助额外的Python逻辑来辅助实现。 对于jinja2,其仅作为一个模板语言实现字符串替换,对于校验、数据合并等,完全依赖Python实现。总的来说,CUE在YAML配置自动生成方面,具有诸多的核心优势使我们不得不考虑使用,下面是简单的使用示例: ### 入门 #### 1. 安装 CUE有一个CLI命令行工具,mac执行 ```brew install cue-lang/tap/cue```可自动安装,执行```cue version```校验安装是否成功: ``` ➜ cue version cue version v0.12.1 go version go1.24.2 -buildmode exe -compiler gc -trimpath true CGO_ENABLED 0 GOARCH arm64 GOOS darwin GOARM64 v8.0 cue.lang.version v0.12.0 ``` 编写一个多环境配置的example.cue模板文件: ``` // 基础配置 Base: { env: "dev" | "prod" apiUrl: string timeout: int | *5000 } // 环境覆盖 Prod: Base & { env: "prod" apiUrl: "https://api.example.com" timeout: 10000 } // 输出最终配置 output: Prod ``` 使用cue CLI进行渲染,可以看到已经输出YAML: ``` ➜ cue export example.cue -e output --out yaml env: prod apiUrl: https://api.example.com timeout: 10000 ``` #### 2. 基础语法 基本类型 ``` // 字符串 name: "Alice" // 数字(整数/浮点数) age: 30 price: 9.99 // 布尔值 enabled: true // 空值 nothing: null ``` 复合类型 ``` // 结构体 user: { name: string // 类型约束 age: int email?: string // 可选字段 } // 列表 ports: [80, 443, 8080] users: [{name: "Alice"}, {name: "Bob"}] ``` 高级特性 ``` // 类型约束 #Config: { timeout: int & >=1 & <=10 // 1~10的整数 url: string & =~"^https://" // 必须https开头 } // 默认值 settings: { port: int | *8080 // 默认8080 env: "dev" | "prod" | *"dev" } // 条件逻辑 config: { env: "dev" | "prod" if env == "prod" { replicas: 5 } else { replicas: 1 } } ``` 循环逻辑 ``` parameter: { name: string image: string env: [string]: string } output: { spec: { containers: [{ name: parameter.name image: parameter.image env: [ for k, v in parameter.env { name: k value: v }, ] }] } } // 类型遍历 #a: { "hello": "Barcelona" "nihao": "Shanghai" } for k, v in #a { "\(k)": { nameLen: len(v) value: v } } // 切片遍历 parameter: { env: [...{name:string,value:string}] } output: { env: [ for _, v in parameter.env { name: v.name value: v.value } ] } ``` 循环与条件判断的结合 ``` parameter: [ { name: "empty" }, { name: "xx1" }, ] // 循环内使用条件判断 dataFrom: [ for _, v in parameter { if v.name != "empty" { name: v.name } }] /* 输出: [{}, { name: "xx1" }] */ // 将条件判断作为循环的条件 dataFrom: [ for _, v in parameter if v.name != "empty" { name: v.name }] /* 输出: [{ name: "xx1" }] */ ``` 数据合并与冲突 ``` // merge base: { timeout: 30, log: "info" } prod: { log: "error" } result: base & prod // {timeout:30, log:"error"} // 冲突 a: { port: 8080 } b: { port: 80 } combined: a & b // 错误:port冲突 ``` 模板化与复用 ``` // 模板定义 #Deployment: { apiVersion: "apps/v1" kind: "Deployment" spec: { replicas: int & >=1 template: { spec: containers: [{ name: string image: string }] } } } // 实例化 myApp: #Deployment & { metadata: name: "web" spec: { replicas: 3 template: { spec: containers: [{ name: "nginx" image: "nginx:latest" }] } } } ``` 校验与输出: ``` // 校验 cue vet config.cue # 检查配置合法性 // 渲染输出 cue export config.cue -e myApp --out yaml > deploy.yaml ``` #### 3. 关键概念 __值__: 值为具体的数据实例,如: ``` name: "Alice" // 字符串值 port: 8080 // 整数值 enabled: true // 布尔值 // 默认值:用 | * 指定 port: int | *8080 // 未指定时默认为8080 // 可选字段使用? user: { name: string email?: string // 可省略 } ``` __约束__: 对值的限定规则,如: ``` // 类型约束 age: int // 必须为整数 // 范围约束 timeout: >0 & <10 // 必须大于0且小于10 // 正则约束 email: =~"^\\w+@\\w+\\.\\w+$" ``` __合并__:合并策略中,包含 &(Unification)、 | (Disjunction)等. 使用"&"会要求数据同时满足两侧的所有约束,两侧字段必须兼容,即类型相同或可统一。以下是"&"的行为: * 具体值 vs. 默认值:具体值优先(表现像覆盖) * 具体值 vs. 具体值:必须相同,否则报错 * 约束 vs. 约束:叠加约束(如 >0 & <10,叠加后必须都满足,否则报错) * 约束 vs. 具体值:具体值必须满足约束,此时将会以此值进行实例化,否则报错 默认值被覆盖的一个典型情况: ``` base: { port: int | *8080 // 默认值 8080 env: string | *"dev" } prod: { port: 80 // 具体值(非默认值) env: "prod" // 具体值 } config: base & prod // 结果: {port: 80, env: "prod"} ``` "|"则表示“或”关系:值必须满足其中任意一个分支的约束,但CUE是逻辑自洽的,当发生多个值冲突时,也会报错,如: ``` port: 8080 | 80 // 错误:两个具体值冲突,无法自动选择 // 上述语句单独写,CUE无法确认到底应该选择哪个值。 // 若将8080与80看做合法的枚举值,则可以将上述语句嵌入结构体或模板中,并在上下文中提供实例化的数值,如: #Server: { // 约束端口为 80 或 8080,默认8080 port: 80 | 8080 | *8080 // 根据端口自动生成协议 protocol: if port == 80 { "http" } else { "http-alt" } } // 实例化 web: #Server & { port: 80 } // 结果: {port:80, protocol:"http"} api: #Server & { port: 8080 } // 结果: {port:8080, protocol:"http-alt"} ``` 它适用于下述情况: ``` // 具体值 vs 默认值 port: 8080 | *80 // 若用户未指定,port=80;若用户指定8080,则port=8080 // 约束 vs 默认值 timeout: int & >0 | *30 // 用户未指定时=30;若指定则必须为正整数 // 纯约束的组合 env: "dev" | "prod" // 只能是 "dev" 或 "prod",这里的"dev"、"prod"同样为可选择的枚举值 age: int | float // 可以是任意整数或浮点数 ``` __模式匹配 ...(Ellipsis)__: 这个操作符适用于两种情况: * 开放结构体:允许未显式定义的额外字段 * 开放列表:允许列表扩展 ``` // 允许任意额外字段(需符合类型约束) #OpenSchema: { name: string ... // 允许其他字段 [_:string]: int | string // 额外字段只能是int或string } // 允许列表扩展 numbers: [1, 2, ...] // 表示以1,2开头的任意长度列表 ``` #### 4. 注入实践 针对一个动态配置系统,模拟前端用户提交表单,后端生成yaml: ```cue // template.cue FlatInput: { user_name: string | *"Any" item_count: int | *0 priority: string | *"normal" } output: { person: { name: FlatInput.user_name details: { count: FlatInput.item_count config: { priority: FlatInput.priority } } } } ``` 用户输入: ```json // data.json {"FlatInput": { "user_name": "Alice", "item_count": 3, "priority": "high" }} ``` 执行命令```cue export data.json -e output template.cue --out yaml``` 输出渲染结果: ```yaml person: name: Alice details: count: 3 config: priority: high ```