在微服务数量超过个位数之后,配置管理会迅速变成一件让人头疼的事。数据库连接串、Redis 地址、消息队列账号等分散在不同仓库中,改一处配置往往要重新打包发布。Spring Cloud Config 通过一个独立的 Config Server 进程统一管理配置源,客户端按应用名和环境拉取对应的配置项,从而让修改、回滚和审计都集中起来。

一、Config Server 的工作模型与 Git 仓库接入
Spring Cloud Config 的核心是一个 Spring Boot 应用,它对外暴露 REST 接口,客户端通过 HTTP 请求获取配置。服务端本身不保存太多业务数据,而是把 Git 仓库、本地文件系统或者 HashiCorp Vault 当成配置源。Git 作为后端是最常见的选择,因为它天然具备版本历史和变更审计能力。服务端启动后会克隆或拉取指定仓库到本地缓存,当客户端请求某个应用配置时,服务端根据应用名、profile 和 label 三个维度拼出对应的文件路径。
配置仓库的目录结构非常灵活,可以按应用名建目录,也可以把所有 yml 文件平铺在根目录。假设一个订单服务叫 order-service,它拥有 dev 和 prod 两个环境,仓库里可以放置 application.yml、order-service.yml、order-service-dev.yml、order-service-prod.yml 等文件。服务端会按照从通用到特定的顺序合并配置,例如 order-service-dev.yml 中的值会覆盖 application.yml 中同名键。要启用 Git 后端,只需要在 Config Server 的 application.yml 中配置 git.uri 即可。
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency>
server:
port: 8888
spring:
application:
name: config-server
cloud:
config:
server:
git:
uri: https://gitee.com/your-team/config-repo.git
search-paths: '{application}'
default-label: main
这里的 search-paths 设置成应用名占位符后,服务端会到仓库下对应的应用目录里查找文件,适合按应用拆分目录的团队。如果不想分目录,也可以直接去掉 search-paths。default-label 指的是默认分支,旧版默认是 master,现在很多仓库已经切到 main。服务端还支持配置多个仓库组合、基于模式匹配的仓库路由,以及配置仓库本身的加密访问,这些在文档中都有说明。
二、客户端接入与局部刷新
客户端要接入 Config Server,需要在项目中加入 spring-cloud-starter-config 和 actuator 依赖。与传统 Spring Boot 不同,配置中心的地址信息通常放在 bootstrap.yml 中,因为 bootstrap 上下文会在主应用上下文启动前加载,确保后续的 Bean 初始化能用到远程配置。bootstrap.yml 里最少需要指定 spring.application.name 和 spring.cloud.config.uri,前者决定请求哪个应用配置,后者是 Config Server 地址。
spring:
application:
name: order-service
cloud:
config:
uri: http://127.0.0.1:8888
fail-fast: true
profile: dev
management:
endpoints:
web:
exposure:
include: refresh
fail-fast 建议开启,这样当 Config Server 不可用时客户端会直接启动失败,而不是带着空配置运行。客户端拿到配置后,默认会注入到 Environment 中,可以通过 @Value 或 @ConfigurationProperties 使用。需要注意的是,远程配置不会覆盖本地 application.yml 中已有的同名属性,除非使用 spring.cloud.config.override-none 等参数调整优先级。对于大多数场景,建议本地只保留 bootstrap 和少量必要默认值,业务配置全部放到配置中心。
如果配置在 Git 仓库中发生了变化,客户端不会自动感知。最简单的做法是在需要动态更新的 Bean 上加上 @RefreshScope 注解,然后向客户端实例发送 POST 请求到 /actuator/refresh。这个注解会让 Bean 在下次访问时重新创建,从而重新绑定 @Value 注入的值。比如下面的 Controller 就能在配置更新后返回新的超时阈值,而无需重启服务。
@RestController
@RefreshScope
public class ConfigController {
@Value("${order.pay.timeout:30}")
private int payTimeout;
@GetMapping("/pay-timeout")
public int getPayTimeout() {
return payTimeout;
}
}
这种方式只对当前实例生效,如果服务部署了多个副本,就需要逐个调用 refresh 端点,或者借助 Spring Cloud Bus 把刷新事件广播出去。此外,refresh 只会重新绑定被 @RefreshScope 标记的 Bean,普通单例 Bean 不会更新,所以动态配置的使用范围要提前规划好。敏感信息通过配置中心下发时,还要考虑解密动作放在哪一端,下面单独讨论。
三、敏感信息加密:对称加密与密文存储
密码、密钥、第三方 Token 这些信息如果以明文形式存入 Git,等于把所有风险都集中到了一个仓库里。Spring Cloud Config 从早期版本就提供了加密能力,核心是服务端的 /encrypt 和 /decrypt 两个端点。开启对称加密最简单的方式是在 Config Server 的启动参数或环境变量中设置 encrypt.key,例如 ENCRYPT_KEY=mySecretKey。然后向 /encrypt 端点 POST 明文,就能得到一串 Base64 编码的密文。
curl -X POST http://127.0.0.1:8888/encrypt -d 'dbPassword123'
拿到密文后,在配置文件里不能直接写明文,而要写成 {cipher} 加密文的形式。YAML 中需要把整个值用单引号包起来,避免花括号被误解。客户端拉取配置时,Config Server 默认会把密文解密后再下发,所以客户端看到的仍然是明文。这种模式要求解密发生在服务端,客户端不需要知道 encrypt.key。如果想改成客户端解密,可以把解密逻辑放到客户端本地,但那样每个客户端都要持有密钥,管理成本更高。
spring:
datasource:
password: '{cipher}AQC8dGhpcyBpcyBhIHNhbXBsZSBjaXBoZXJ0ZXh0...'
对称加密虽然配置简单,但密钥一旦泄露,所有密文都能被解开。对于安全要求更高的项目,Spring Cloud Config 也支持基于 KeyStore 的非对称加密。配置好 key-store 的位置、密码和别名后,服务端会用公钥加密,客户端或服务端用私钥解密。密钥文件本身不建议放进 Git 仓库,可以通过挂载卷或环境变量注入。更进一步的方案是集成 Vault 或云厂商的 KMS,把加密和解密完全交给外部系统。
encrypt:
key-store:
location: classpath:/server.jks
password: changeit
alias: configkey
secret: changeit
实际使用时要特别注意,encrypt.key 不能写成硬编码提交到配置仓库中,否则等于没有加密。推荐通过环境变量 ENCRYPT_KEY 注入到 Config Server 容器,这样同一个镜像在不同环境可以配合不同密钥。另外,加密端点本身应该限制访问,不能谁都能调用 /encrypt 去批量加密或解密。
四、生产落地中的权限、回滚与密钥轮换
Config Server 一旦成为配置中心,就会变成整个微服务体系的敏感入口。生产环境至少要给它加上认证,让客户端携带 Token 或使用 Spring Security 做访问控制。如果公司已有统一的 API 网关,也可以在网关上只放行客户端网段,阻止外部直接访问。对于配置文件里的敏感数据,还要结合 Git 仓库的权限管理,比如只允许 CI 账号写入,开发人员通过 PR 修改。
Git 后端带来的一个明显好处是版本回滚。当某个配置变更导致线上故障时,可以直接在 Git 中 revert 到上一个提交,然后触发客户端刷新,配置就会恢复。这个过程不需要重新打包镜像,比传统的发版快得多。不过要注意,如果客户端本地缓存了配置,需要确认 refresh 动作是否真正拉取了新的版本。可以在 /actuator/env 端点查看当前生效的配置值,确认变更已经到达。
密钥轮换是另一个容易忽略的环节。对称加密的 encrypt.key 建议定期更换,更换后所有旧密文必须重新加密,否则客户端会解密失败。可以在维护窗口内批量调用 /encrypt 生成新密文,再更新 Git 中的配置。对于非对称加密,可以通过更换 KeyStore 别名或轮换证书来实现。无论哪种方式,都需要提前演练解密失败时的回退流程,避免把数据库密码锁死在无法解开的密文里。
最后,监控和审计也不可缺少。Config Server 的访问日志可以记录哪个应用在什么时间拉取了哪个版本的配置,结合 Git 的提交记录,基本能还原一次配置变更的完整链路。如果团队规模不大,先用 Config Server 加 Git 的方案快速落地,等后续需要更细粒度的权限和动态密钥时,再迁移到 Vault 或云平台配置管理服务也不迟。

