app-config 的分层与合并结果预测
目标
将 Backstage 配置拆分为多个层,并亲手预测这些层重叠时最终会保留什么。最后,把生产配置迁移到 Kubernetes 对象中,确认实际部署中的值来自哪里。
为什么重要
配置分层是 CBA 中最容易出错的部分之一,因为规则本身只有两条,结果却常常不符合直觉。映射会按键进行深度合并,但列表不会合并,而是被整体替换。如果在基础文件中写了三个目录入口,而在覆盖文件中只写一个,结果不是四个,而是一个,并且不会出现任何警告。值的类型改变时也是如此:如果原来是字符串的位置被映射覆盖,字符串就会消失。此外,凭据绝不能以实际值写入任何文件,因为配置文件会提交到代码仓库并烘焙进镜像。本环境没有 Backstage,因此评分会通过读取文件和集群对象完成。
步骤
- 在
/root/cba-config/app-config.yaml中编写基础配置 —app.title可为任意值,app.baseUrl: http://localhost:3000,organization.name可为任意值,backend.baseUrl: http://localhost:7007,backend.listen.port: 7007,backend.cors.origin: http://localhost:3000,backend.database.client: better-sqlite3,backend.database.connection: /tmp/portal.sqlite。 - 在同一文件中继续编写集成和代理配置 —
integrations.github第一项的host: github.com与token(包含 GITHUB 的环境变量替换格式);在proxy.endpoints下的/argocd/api键中填写target(以 https 开头的地址)、changeOrigin: true、headers.Cookie(环境变量替换格式)。 - 在同一文件中继续编写目录配置 —
catalog.import.entityFilename: catalog-info.yaml;catalog.rules第一项的allow中包含 Component、API、Resource、System、Domain、Group、User、Location、Template 九种类型;catalog.locations包含两项(第一项为type: file,且 target 以entities.yaml结尾;第二项为type: url,且 target 是 github.com 地址);在catalog.providers.github.labhubOrg中设置organization: labhub、catalogPath: /catalog-info.yaml、schedule.frequency.minutes: 30、schedule.timeout.minutes: 3。 - 在
/root/cba-config/app-config.local.yaml中编写覆盖配置 —app.baseUrl: http://portal.labhub.test,backend.baseUrl: http://portal.labhub.test:7007,backend.database.client: pg;backend.database.connection下的host、port、user、database全部使用环境变量替换格式;catalog.locations只包含一项type: url。不要写入backend.listen.port。 - 在
/root/cba-config/merged.yaml中,原样写出将第 4 步的文件覆盖到第 1~3 步文件之上后保留下来的值。映射会深度合并,列表会被整体替换。 - 在
/root/cba-config/app-config.production.yaml中编写生产配置 —app.baseUrl和backend.baseUrl都设为https://portal.labhub.io,backend.listen.host: 0.0.0.0,backend.database.client: pg,auth.environment: production;auth.providers.github.production的clientId和clientSecret使用环境变量替换格式;同一位置的signIn.resolvers第一项resolver使用与目录 User 实体匹配的解析器名称;techdocs.builder: external,techdocs.publisher.type使用非 local 的外部存储类型。 - 在
/root/cba-config/env-names.txt中写出三个配置文件引用的环境变量名称,不带花括号,每行一个,去重并排序。 - 将生产配置部署到集群。创建命名空间
cba-config,并在其中根据第 6 步的文件创建 ConfigMapportal-app-config(键名必须是app-config.production.yaml)。然后创建 Deploymentportal— 镜像为node:22-alpine,containerPort: 7007,以卷的形式挂载该 ConfigMap,并确保环境变量APP_CONFIG_app_baseUrl的值与第 6 步文件中的app.baseUrl完全一致。
参考
- 合并规则只有两条:映射按键深度合并,列表整体替换。
- 环境变量替换格式为
${NAME},使用这种格式就不会把实际值留在文件中。 - 常见错误 1:把覆盖文件中无需修改的值也复制进去。基础文件变化后,覆盖文件中的旧值会继续保留。
- 常见错误 2:以为列表会合并。基础文件中的其余列表项会悄无声息地消失。
- 常见错误 3:在容器部署中保留
backend.listen.host的默认值。如果只监听回环地址,Service 将无法访问 Pod。
基础 app-config
在 /root/cba-config/app-config.yaml 中编写基础配置 — app.title 可为任意值,app.baseUrl: http://localhost:3000,organization.name 可为任意值,backend.baseUrl: http://localhost:7007,backend.listen.port: 7007,backend.cors.origin: http://localhost:3000,backend.database.client: better-sqlite3,backend.database.connection: /tmp/portal.sqlite。
前端和后端运行在不同端口。CORS 的 origin 应是浏览器请求的来源,也就是前端地址。
集成与代理
在同一文件中继续编写集成和代理配置 — integrations.github 第一项的 host: github.com 与 token(包含 GITHUB 的环境变量替换格式);在 proxy.endpoints 下的 /argocd/api 键中填写 target(以 https 开头的地址)、changeOrigin: true、headers.Cookie(环境变量替换格式)。
如果直接在凭据位置填写实际值,秘密会保留在 git 历史和镜像层中。请使用启动时由环境变量替换的格式。
目录规则与发现
在同一文件中继续编写目录配置 — catalog.import.entityFilename: catalog-info.yaml;catalog.rules 第一项的 allow 中包含 Component、API、Resource、System、Domain、Group、User、Location、Template 九种类型;catalog.locations 包含两项(第一项为 type: file,且 target 以 entities.yaml 结尾;第二项为 type: url,且 target 是 github.com 地址);在 catalog.providers.github.labhubOrg 中设置 organization: labhub、catalogPath: /catalog-info.yaml、schedule.frequency.minutes: 30、schedule.timeout.minutes: 3。
rules 是允许列表,不在其中的 kind 会被拒绝注册。若漏掉 Template,整个脚手架页面会看起来像是空的。
本地覆盖配置
在 /root/cba-config/app-config.local.yaml 中编写覆盖配置 — app.baseUrl: http://portal.labhub.test,backend.baseUrl: http://portal.labhub.test:7007,backend.database.client: pg;backend.database.connection 下的 host、port、user、database 全部使用环境变量替换格式;catalog.locations 只包含一项 type: url。不要写入 backend.listen.port。
覆盖文件中只写需要修改的值。如果连不变的值也复制进去,基础文件变化后,这里仍会保留旧值。
预测合并结果
在 /root/cba-config/merged.yaml 中,原样写出将第 4 步的文件覆盖到第 1~3 步文件之上后保留下来的值。映射会深度合并,列表会被整体替换。
映射按键深度合并,列表整体替换。覆盖文件未修改的键会继续保留基础文件中的值。
生产配置
在 /root/cba-config/app-config.production.yaml 中编写生产配置 — app.baseUrl 和 backend.baseUrl 都设为 https://portal.labhub.io,backend.listen.host: 0.0.0.0,backend.database.client: pg,auth.environment: production;auth.providers.github.production 的 clientId 和 clientSecret 使用环境变量替换格式;同一位置的 signIn.resolvers 第一项 resolver 使用与目录 User 实体匹配的解析器名称;techdocs.builder: external,techdocs.publisher.type 使用非 local 的外部存储类型。
如果容器只监听回环地址,Service 将无法访问 Pod。此外,认证分为登录提供方和身份解析两个阶段。
环境变量引用列表
在 /root/cba-config/env-names.txt 中写出三个配置文件引用的环境变量名称,不带花括号,每行一个,去重并排序。
三个配置文件都要纳入检查。这个列表就是部署清单中需要填入的 Secret 键列表。
将配置部署到集群
将生产配置部署到集群。创建命名空间 cba-config,并在其中根据第 6 步的文件创建 ConfigMap portal-app-config(键名必须是 app-config.production.yaml)。然后创建 Deployment portal — 镜像为 node:22-alpine,containerPort: 7007,以卷的形式挂载该 ConfigMap,并确保环境变量 APP_CONFIG_app_baseUrl 的值与第 6 步文件中的 app.baseUrl 完全一致。
从文件创建 ConfigMap 时,文件名会原样成为键名。将配置路径中的点替换为下划线所得的环境变量名,可以覆盖对应的单项配置。