ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Nacos 配置中心实战:从多环境隔离到敏感信息加密

Nacos 配置中心实战:从多环境隔离到敏感信息加密 最近在为线上系统迁移到 Nacos 的时候遇到了一个问题——开发、测试、生产环境的配置文件散落在各个角落每次发版都要手动替换配置还经常搞混环境。后来用上了 Nacos才体会到什么叫真正的配置管理。顺便说一下这次迁移我用 Docker 一键起了 Nacos还摸透了控制台就想着把这些经验总结下来分享给大家。这篇文章就来聊一下我是怎么从零开始部署 Nacos 配置中心逐步解决多环境隔离、配置动态刷新、以及最关键的敏感信息加密这些痛点的。为什么要用 Nacos说实话之前没用之前我也没想太多。项目初期配置就放在 application.properties 里修改配置得重新打包发布线上环境有个紧急调整都要走一遍打包、推送、重启的流程效率很低。后来数据库连接信息、Redis 密码、第三方 API key 这些敏感信息也都混在配置里明文存储心里总是忐忑不安。Nacos 就是为了解决这个问题而来的。核心价值很简单•集中管理所有配置存在一个中心改完立即生效不需要重启服务•多环境支持开发、测试、生产配置完全隔离再也不会搞混•权限控制支持加密存储敏感信息安全性更有保障•实时推送配置变更会主动推送到所有订阅的客户端快速上手Docker 一键部署说实话自己编译部署 Nacos 其实不麻烦但用 Docker 就更方便了。我的做法是用 docker-compose 搞定这样开发环境、测试环境都能快速启动。创建一个docker-compose.yml文件version: 3 services: nacos: image: nacos/nacos-server:v2.3.0 container_name: nacos-server environment: - MODEstandalone # 单机模式测试环境够用 - SPRING_DATASOURCE_PLATFORMmysql - MYSQL_SERVICE_HOSTmysql - MYSQL_SERVICE_PORT3306 - MYSQL_SERVICE_DB_NAMEnacos_config - MYSQL_SERVICE_USERnacos - MYSQL_SERVICE_PASSWORDnacos123 - NACOS_AUTH_ENABLEtrue # 启用认证 - NACOS_AUTH_TOKEN_EXPIRE_SECONDS18000 - NACOS_AUTH_TOKEN_SECRET_KEYyour_secret_key_32_chars_min ports: - 8848:8848 # 配置中心端口 - 9848:9848 # gRPC 端口 depends_on: - mysql networks: - nacos-net restart: unless-stopped mysql: image: mysql:8.0 container_name: nacos-mysql environment: - MYSQL_ROOT_PASSWORDroot123 - MYSQL_DATABASEnacos_config - MYSQL_USERnacos - MYSQL_PASSWORDnacos123 ports: - 3306:3306 volumes: - nacos-mysql-data:/var/lib/mysql - ./nacos-mysql.sql:/docker-entrypoint-initdb.d/nacos-mysql.sql networks: - nacos-net restart: unless-stopped networks: nacos-net: driver: bridge volumes: nacos-mysql-data:然后只需要一条命令启动docker-compose up -d等待 30 秒左右Nacos 就跑起来了。访问http://localhost:8848/nacos就能进控制台。默认用户名密码是nacos/nacos建议第一时间改掉。生产环境的话建议改成集群模式MODEcluster并且用外部数据库比如云数据库这样才能保证高可用。但对于初学者和开发测试环境上面这个单机配置就够用了。有一个坑要注意MySQL 初始化需要 Nacos 的数据库脚本。我通常会在 MySQL 启动时执行一个nacos-mysql.sql脚本这个文件 Nacos 官方镜像里有或者直接进 MySQL 执行docker exec nacos-mysql mysql -u root -proot123 nacos-mysql.sql这样数据库就初始化完了Nacos 就能正常连接。第一步理解 Namespace、Group、DataId 三剑客这是 Nacos 配置管理的核心概念也是我最开始最容易搞混的地方。Namespace用来实现环境隔离。大多数团队的做法是给每个环境建一个 Namespace•dev-namespace开发环境•test-namespace测试环境•prod-namespace生产环境Group用来对配置进行逻辑分组通常按应用或模块来划分•order-service订单服务的配置•user-service用户服务的配置•common-config公共配置DataId就是具体的配置文件名。比如•application.yaml•database.yaml•redis.yaml三者的关系可以这样理解要找一个配置你需要指定在哪个环境(Namespace)、哪个应用(Group)下的某个配置文件(DataId)。实际操作的时候我的习惯是这样的# bootstrap.yml 配置 spring: cloud: nacos: config: server-addr: 127.0.0.1:8848 namespace: dev-namespace # 开发环境的 namespace ID group: order-service # 服务所属的 group file-extension: yaml auto-refresh: true # 开启自动刷新 application: name: order-service profiles: active: dev关键的是namespace要用 ID 而不是名称在 Nacos 控制台新建 Namespace 时会生成一个 UUID复制那个过来就行。第二步多环境配置的实际操作控制台操作指南登进 Nacos 控制台后http://localhost:8848/nacos首先映入眼帘的是一个相对简洁的界面。左边是菜单栏右边是主要内容区。第一件事创建 Namespace点击左边菜单的命名空间或在某些版本叫Namespace然后点右上角的新建命名空间按钮。填入命名空间名称比如dev-env、prod-env描述随便填点确定系统会为你生成一个 UUID 形式的命名空间 ID这个很重要后面在 bootstrap.yml 中要用这个 ID。新建完之后点击这个 Namespace你会发现界面有个下拉框后面所有的操作都是在选中的 Namespace 下进行的。这就是环境隔离的第一层。第二件事配置管理点左边菜单的配置列表此时你在某个 Namespace 下了通常默认在 public。点右上角的号创建新配置。弹出的表单有这几个关键字段•Data Id配置文件的名字比如application.yaml、database.yaml•Group配置的分组比如order-service、user-service•描述可选随便填•配置格式选 YAML 或 JSON 或 Properties根据你的内容格式选•配置内容就是你的配置文件的内容一个实际的例子是这样的# Data Id: database.yaml # Group: order-service spring: datasource: url: jdbc:mysql://mysql.dev.internal:3306/order_db username: dev_user password: dev_password_123 driver-class-name: com.mysql.cj.jdbc.Driver jpa: hibernate: ddl-auto: update show-sql: true填完后点发布配置就保存了。第三件事快速查看和编辑回到配置列表你会看到刚才创建的配置出现在表格里有个 Search 框可以按 Data Id 或 Group 搜索。点配置那一行就能看到详细内容和最后修改时间。需要修改配置时点编辑按钮改完再点发布。改完之后所有订阅这个配置的应用都会在 1-2 秒内收到推送通过 Nacos 的长连接机制无需重启应用。第四件事历史版本追踪这是我比较喜欢的一个功能。点配置详情页面的历史版本标签你可以看到这个配置的所有修改历史甚至可以对比两个版本之间的差异或者直接回滚到某个历史版本。这对于排查为什么线上配置突然出问题了特别有用。第五件事权限和访问控制如果你是集群部署或者多人协作环境点左边菜单的权限管理可以给不同用户分配不同的权限。比如让某个用户只能看 dev 环境的配置不能看 prod 环境。虽然功能不算很复杂但对于安全性还是有一定帮助的。一个小技巧是如果你的 Nacos 开启了认证就是那个NACOS_AUTH_ENABLEtrue那么 API 请求的时候需要带上 token。但在控制台里不需要担心这个登进去就自动有权限了。实际的配置创建步骤我们以数据库连接配置为例。在 Nacos 控制台按以下步骤创建配置1. 切换到 dev-namespace点击左上角的 Namespace 下拉框选择 dev-namespace。2. 创建配置进入配置列表页面点击按钮新增配置•Data Id:database.yaml•Group:order-service•配置内容:代码语言javascriptAI代码解释spring: datasource: url: jdbc:mysql://localhost:3306/order_db_dev username: dev_user password: dev_password123 driver-class-name: com.mysql.cj.jdbc.Driver jpa: hibernate: ddl-auto: update然后对 test-namespace 和 prod-namespace 重复这个过程改成对应环境的数据库地址。在服务启动时Nacos 客户端会根据 bootstrap.yml 的配置自动从对应的 Namespace 和 Group 中拉取配置。// 在任何 Spring Bean 中就可以直接用 RestController public class OrderController { Autowired private DataSource dataSource; GetMapping(/health) public String health() { // dataSource 的配置会自动从 Nacos 拉取 return OK; } }这样做的好处是开发人员本地启动服务的时候会自动连接到 dev-namespace测试人员部署的时候只需要改 bootstrap.yml 中的 namespace 和 profiles.active就能自动切换到对应环境的配置不用修改任何业务代码。第三步配置的动态刷新——RefreshScope 的正确用法配置集中管理了但真正的威力在于能够动态刷新。修改配置之后服务无需重启就能感知到变化。这里最常用的做法是RefreshScope注解。但我在实际应用中踩过坑——如果用法不对改了配置也不会生效。错误的用法RestController RequestMapping(/api/order) public class OrderController { Value(${order.max-retry-times:3}) private Integer maxRetryTimes; // 这样不行 GetMapping(/retry-times) public Integer getRetryTimes() { return maxRetryTimes; } }即使加了RefreshScope在类上这样也不会动态刷新。为什么因为Value注入的是一个原始值Spring 无法对其进行动态更新。正确的用法 - 方法一Configuration public class OrderConfig { Bean RefreshScope public OrderProperties orderProperties() { return new OrderProperties(); } } Data Component ConfigurationProperties(prefix order) public class OrderProperties { private Integer maxRetryTimes ; private String retryStrategy exponential; } // 控制器中注入对象 RestController RequestMapping(/api/order) public class OrderController { Autowired private OrderProperties orderProperties; GetMapping(/retry-times) public Integer getRetryTimes() { // 这样就会动态刷新 return orderProperties.getMaxRetryTimes(); } }关键是用ConfigurationPropertiesComponent的方式Nacos 的监听器能捕获到配置变化然后 Spring 会重新创建这个 Bean从而实现动态刷新。正确的用法 - 方法二如果你用的是 Spring Cloud Alibaba即 Nacos 的官方 Spring Boot 集成还可以用NacosValue直接注入代码语言javascriptAI代码解释Configuration public class OrderConfig { NacosValue(value ${order.max-retry-times:3}, autoRefreshed true) private Integer maxRetryTimes; public Integer getMaxRetryTimes() { return maxRetryTimes; } }这种方式更直接但需要显式指定autoRefreshed true否则也不会生效。在 Nacos 控制台修改配置后你会看到一条日志代码语言javascriptAI代码解释2026-01-15 14:22:33 [NacosConfigListener] Refresh config from nacos... 2026-01-15 14:22:33 [OrderProperties] Properties refreshed: maxRetryTimes5然后再访问接口得到的就是新的值了不需要重启服务。这就是配置中心的魅力所在。第四步敏感信息加密——数据库密码不再裸奔说起敏感信息加密这是很多团队容易忽视的地方。我之前在一个项目里数据库密码和 API key 直接存在配置文件里代码仓库一不小心就暴露到公网后来还被 GitHub 的安全扫描工具给警告了。Nacos 从 2.0 版本开始提供了配置加密功能。虽然原理比较复杂涉及加密插件机制但使用起来很简单。第一步启用加密在 Nacos 配置中心的控制台新增配置时有一个保密字段的选项。勾选它之后这个配置的值就会被加密存储。但实际操作中我发现这个功能依赖于 Nacos 服务端的加密插件支持如果你是自建的 Nacos 集群可能需要额外配置。第二步更实用的方案 - 应用层加密与其依赖 Nacos 的加密功能有时候不是很稳定我更建议在应用层自己处理定义一个加密配置类Configuration public class EncryptedPropertyResolver { // 这里用一个固定的密钥生产环境应该从环境变量或密钥管理系统取 private static final String SECRET_KEY your-secret-key-here-32-chars; Bean public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() { PropertySourcesPlaceholderConfigurer configurer new PropertySourcesPlaceholderConfigurer(); configurer.setPlaceholderPrefix(${); configurer.setPlaceholderSuffix(}); return configurer; } /** * 解密被 ENC() 包装的属性值 */ public static String decrypt(String encryptedValue) { if (encryptedValue null || !encryptedValue.startsWith(ENC() || !encryptedValue.endsWith())) { return encryptedValue; } String encrypted encryptedValue.substring(, encryptedValue.length() -); // 使用 AES 或其他加密算法解密 // 这里用伪代码表示 return AesUtil.decrypt(encrypted, SECRET_KEY); } } // 自定义环境预处理器在配置加载时进行解密 Component public class DecryptEnvironmentPostProcessor implements EnvironmentPostProcessor { Override public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) { // 遍历所有的 property source对 ENC(...) 的值进行解密 for (PropertySource? source : environment.getPropertySources()) { if (source instanceof EnumerablePropertySource) { EnumerablePropertySource? enumerable (EnumerablePropertySource?) source; for (String name : enumerable.getPropertyNames()) { String value (String) enumerable.getProperty(name); if (value ! null value.startsWith(ENC()) { // 解密后替换 String decrypted EncryptedPropertyResolver.decrypt(value); // 这里的替换方式取决于具体的实现... } } } } } }在 Nacos 中存储敏感配置时改成这样spring: datasource: url: jdbc:mysql://localhost:3306/order_db_prod username: prod_user password: ENC(x7y8z9a0b1c2d3e4f5g6h7i8j9k0l1m2n3) # 加密后的密码 driver-class-name: com.mysql.cj.jdbc.Driver这样即使有人能访问到 Nacos 的配置存储看到的也是加密过的密码没有密钥是没法解密的。第五步踩过的坑在生产环境实际应用 Nacos 的时候我还踩过一些坑想分享给大家以避免重复坑一忘记在 bootstrap.yml 中配置 Nacos 服务器地址很多同学在迁移到 Nacos 时会把 Nacos 的配置放在 application.yml 中但实际上需要放在bootstrap.yml或bootstrap.yaml中。因为 bootstrap 文件的加载优先级更高Spring Cloud 会先加载它才能连接到 Nacos 去拉取其他配置。代码语言javascriptAI代码解释# bootstrap.yml - 正确的位置 spring: cloud: nacos: config: server-addr: nacos-server.internal:8848 namespace: prod-namespace-id group: my-service坑二Namespace ID 和 Namespace 名称混淆在 bootstrap.yml 中的namespace要填 Namespace 的 IDUUID 格式而不是 Namespace 的名称。新建 Namespace 时会自动生成一个 ID复制那个就行。坑三配置刷新不生效这个很常见。原因通常是1. 没有在类上加RefreshScope或Component2. 使用了Value却没有用ConfigurationProperties包装3. 改完配置后没等 Nacos 的推送延迟通常 1-2 秒内会推送坑四集群环境下的配置同步延迟如果你的 Nacos 是集群部署多个节点某个节点可能会因为网络抖动导致配置同步延迟。这时候客户端连到不同节点可能看到不同的配置。解决方案是在客户端加重试机制或用健康检查确保连接到主节点。坑五监听器在动态刷新时失效Component public class OrderConfigListener implements ApplicationListenerRefreshEvent { Override public void onApplicationEvent(RefreshEvent event) { // 这个监听器会在配置刷新时被触发 System.out.println(配置已刷新); } }这个监听器有时候会因为 Spring 的事件机制问题而不被触发。最稳妥的做法是在PostConstruct中手动注册 Nacos 的配置监听器。总结Nacos 配置中心的核心价值就是中心化管理 动态刷新 安全加密配合好 Namespace、Group、DataId 的三层结构就能构建一个相对完善的配置管理系统。实际项目中建议按照这个步骤来1.先做好环境隔离用 Namespace 区分不同环境不要混用2.再做配置分组用 Group 给不同服务的配置分类方便管理和查找3.然后实现动态刷新对那些可能频繁变动的配置比如降级开关、超时时间等加上RefreshScope4.最后处理敏感信息数据库密码、API key 等一定要加密存储Nacos 配置中心本身也需要高可用部署、权限控制、备份恢复等运维工作这些内容就留到下一篇文章再聊了。
返回列表