130 lines
5.4 KiB
Markdown
130 lines
5.4 KiB
Markdown
# 07. 配置与服务治理
|
||||
|
|
|
|||
|
|
## 决策
|
|||
|
|
|
|||
|
|
K8s 原生方案:ConfigMap + Secret + Spring Cloud Kubernetes,不引入 Nacos 等额外治理组件,贴合已有 Azure + GitLab CI/CD 部署方式(见 [Architecture-Diagram/deployment-architecture-diagram.drawio](../Architecture-Diagram/deployment-architecture-diagram.drawio))。
|
|||
|
|
|
|||
|
|
选这条路而不是 Spring Cloud Alibaba(Nacos)的原因:部署环境已经是 K8s,K8s 本身自带 Service(服务发现)、ConfigMap/Secret(配置)、Deployment 滚动更新(发布),再引入 Nacos 意味着多维护一套集群、多一套"配置到底以谁为准"的心智负担。除非未来有 Nacos 才能提供、K8s 原生方案覆盖不了的能力(比如更细粒度的灰度配置推送),否则先用 K8s 原生的就够。
|
|||
|
|
|
|||
|
|
## 结构约定
|
|||
|
|
|
|||
|
|
- **非敏感配置**(菜单开关、超时参数、日志级别等)放 ConfigMap,挂载成 `application-{profile}.yml` 或环境变量。
|
|||
|
|
- **敏感配置**(DB 密码、JWT secret、F6 API key)放 Secret,都不进代码库、不进镜像。
|
|||
|
|
- 各环境(dev/uat/prod)对应各自 namespace 下的 ConfigMap/Secret + Spring profile,`bootstrap` 按 `SPRING_PROFILES_ACTIVE` 加载对应配置。
|
|||
|
|
|
|||
|
|
## ConfigMap / Secret 示例
|
|||
|
|
|
|||
|
|
```yaml
|
|||
|
|
# k8s/configmap-workbench-uat.yaml
|
|||
|
|
apiVersion: v1
|
|||
|
|
kind: ConfigMap
|
|||
|
|
metadata:
|
|||
|
|
name: conti-backend-config
|
|||
|
|
namespace: retailapp-uat
|
|||
|
|
data:
|
|||
|
|
application-uat.yml: |
|
|||
|
|
resilience4j:
|
|||
|
|
circuitbreaker:
|
|||
|
|
instances:
|
|||
|
|
f6-api:
|
|||
|
|
failure-rate-threshold: 50
|
|||
|
|
workbench:
|
|||
|
|
degrade-message: "部分数据暂时无法显示,请稍后刷新"
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
```yaml
|
|||
|
|
# k8s/secret-uat.yaml(实际值由 CI/CD 从密钥管理服务注入,不手写明文提交)
|
|||
|
|
apiVersion: v1
|
|||
|
|
kind: Secret
|
|||
|
|
metadata:
|
|||
|
|
name: conti-backend-secret
|
|||
|
|
namespace: retailapp-uat
|
|||
|
|
type: Opaque
|
|||
|
|
stringData:
|
|||
|
|
SECURITY_JWT_SECRET: "__injected_by_pipeline__"
|
|||
|
|
DB_PASSWORD: "__injected_by_pipeline__"
|
|||
|
|
F6_API_KEY: "__injected_by_pipeline__"
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
```yaml
|
|||
|
|
# k8s/deployment-uat.yaml(节选)
|
|||
|
|
spec:
|
|||
|
|
containers:
|
|||
|
|
- name: conti-backend
|
|||
|
|
image: registry.example.com/conti-backend:__TAG__
|
|||
|
|
envFrom:
|
|||
|
|
- secretRef:
|
|||
|
|
name: conti-backend-secret
|
|||
|
|
env:
|
|||
|
|
- name: SPRING_PROFILES_ACTIVE
|
|||
|
|
value: uat
|
|||
|
|
volumeMounts:
|
|||
|
|
- name: config-volume
|
|||
|
|
mountPath: /app/config
|
|||
|
|
volumes:
|
|||
|
|
- name: config-volume
|
|||
|
|
configMap:
|
|||
|
|
name: conti-backend-config
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 启用 Spring Cloud Kubernetes 配置热更新
|
|||
|
|
|
|||
|
|
```groovy
|
|||
|
|
// bootstrap/build.gradle
|
|||
|
|
dependencies {
|
|||
|
|
implementation 'org.springframework.cloud:spring-cloud-starter-kubernetes-client-config'
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
```yaml
|
|||
|
|
# application.yml
|
|||
|
|
spring:
|
|||
|
|
cloud:
|
|||
|
|
kubernetes:
|
|||
|
|
config:
|
|||
|
|
enabled: true
|
|||
|
|
sources:
|
|||
|
|
- name: conti-backend-config
|
|||
|
|
reload:
|
|||
|
|
enabled: true
|
|||
|
|
mode: polling # 定期轮询 ConfigMap 变化,无需重启 Pod
|
|||
|
|
period: 15s
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
```kotlin
|
|||
|
|
// 需要热更新的配置类加 @RefreshScope
|
|||
|
|
@RefreshScope
|
|||
|
|
@ConfigurationProperties(prefix = "workbench")
|
|||
|
|
@Component
|
|||
|
|
class WorkbenchProperties {
|
|||
|
|
var degradeMessage: String = "部分数据暂时无法显示"
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 关键规则
|
|||
|
|
|
|||
|
|
- 配置变更优先走 ConfigMap 热更新(`@RefreshScope` + `spring.cloud.kubernetes.reload`),不重新构建镜像;涉及 Secret 轮换的走正常发布流程(Secret 变化通常需要重启 Pod 才能生效,不像 ConfigMap 可以做到无重启热更)。
|
|||
|
|
- 对应架构图里 `Config Center`(菜单配置、开关配置、WebView 入口策略)暂时也落在 ConfigMap;如果后续配置项复杂到需要审批流程、按门店灰度下发、版本回滚,再评估引入独立配置中心(比如 Apollo)。
|
|||
|
|
- Pod 需要有权限读取所在 namespace 的 ConfigMap(`spring-cloud-kubernetes` 底层调用 K8s API),需要配置好对应的 `ServiceAccount` + `Role`/`RoleBinding`。
|
|||
|
|
|
|||
|
|
## 附录:ConfigMap 和 Secret 的本质区别,以及为什么两者都要用
|
|||
|
|
|
|||
|
|
两者在 K8s API 层面结构几乎一样,都是 key-value 集合,区别主要在于:
|
|||
|
|
|
|||
|
|
- **Secret 的值默认 base64 编码存储**(不是加密,只是编码),K8s 对 Secret 有一些额外处理:不会出现在 `kubectl describe` 的默认输出里、可以配置只挂载到内存卷(`tmpfs`)不落盘、可以对接外部密钥管理服务(Azure Key Vault 等)做真正的静态加密和访问审计。
|
|||
|
|
- **ConfigMap 没有这些额外保护**,设计上就是给"泄漏了也不严重"的配置用的。
|
|||
|
|
|
|||
|
|
所以规则很简单:**这个值如果出现在日志里、被同事在 `kubectl get configmap -o yaml` 时看到会不会造成安全问题**——会,就放 Secret;不会,就放 ConfigMap。DB 密码、JWT 签名密钥、第三方 API key 毫无疑问要放 Secret;而"首页降级提示文案"这种放哪都无所谓的东西放 ConfigMap 就行,还能享受到热更新不用走发布流程的好处。
|
|||
|
|
|
|||
|
|
## 待补充
|
|||
|
|
|
|||
|
|
- 具体 ConfigMap/Secret 命名规范和 namespace 划分细节。
|
|||
|
|
- 多环境 profile 的详细参数列表。
|
|||
|
|
- 是否需要接入 Azure Key Vault 做 Secret 的进一步加固。
|
|||
|
|
|
|||
|
|
## 参考链接
|
|||
|
|
|
|||
|
|
- [Spring Cloud Kubernetes 官方文档](https://docs.spring.io/spring-cloud-kubernetes/reference/)
|
|||
|
|
- [Kubernetes ConfigMap 官方文档](https://kubernetes.io/docs/concepts/configuration/configmap/)
|
|||
|
|
- [Kubernetes Secret 官方文档](https://kubernetes.io/docs/concepts/configuration/secret/)
|