微服务治理三件套:Nacos、Seata、Sentinel 集群部署实战 你有没有遇到过这种情况微服务拆了十几个本地单机跑得好好的一上测试环境就开始各种“玄学超时”订单接口扣了库存但订单没生成对不上账流量稍微一波动某个下游接口直接被打挂日志里全是 TimeoutException。要根治这三类问题绕不开微服务治理三件套Nacos、Seata、Sentinel。最近我帮朋友把一个单体系统拆成微服务顺手用 Docker 把 nacos seata sentinel 集群从零搭了一遍。这篇就把完整过程、核心配置、踩过的坑全部整理出来适合正在搭基础架构的开发者也适合想从单机 demo 升级到集群环境的小型团队。必须先说清楚这篇文章不是把三个组件分别装一遍就完事而是要解决“它们之间怎么互相发现、怎么协调、怎么持久化”的问题。Nacos 要做集群Seata 要注册到 Nacos 并走事务分组Sentinel 要能通过 Nacos 做规则持久化。三套东西互相关联牵一发动全身。我用的部署方式是 Docker Compose所有组件跑在同一台 4C8G 的服务器上这套配置在小规模生产环境也能扛得住。1. 动手前先想清楚这套集群到底在解决什么问题很多新手一上来就拉镜像、写 compose结果 Nacos 起来了、Seata 也起来了但业务一接入就各种连不上。问题往往不在安装步骤而在部署之前没想清楚三个组件各自的职责以及它们之间的依赖关系。1.1 三个组件在微服务架构里的职责边界先简单划一下边界。Nacos 是注册中心和配置中心解决“服务在哪里、配置怎么管”的问题Seata 是分布式事务协调器解决“跨服务调用时数据一致性怎么保证”的问题Sentinel 是流量防护组件解决“高并发下哪些请求该放、哪些该限、哪些该降级”的问题。打个比方Nacos 像是一个商场的通讯录和钥匙箱所有店铺都要在这里登记位置、领取配置Seata 像是商场收银台背后的总账本一笔跨店消费要么全部入账要么全部撤销Sentinel 像是商场门口的闸机人多的时候按规则放行防止某个热门店铺被挤爆后波及整个商场。三个组件缺一个微服务体系都会存在明显短板。在做集群之前还要想清楚一个关键问题为什么要集群单机跑 demo 和集群跑生产是两码事。Nacos 单机挂掉所有服务发现和配置下发全部瘫痪服务间调用立刻失联Seata 单机挂掉正在进行的全局事务全部悬挂业务侧只能等超时Sentinel 控制台单机挂掉倒还好规则还在客户端内存里但也没法动态调整了。所以在生产环境Nacos 至少三节点、Seata 至少两节点Sentinel 控制台可以先用单节点但规则必须落到 Nacos 做持久化。1.2 网络拓扑为什么全放一个 Docker 网络里这次部署我把 Nacos、Seata、Sentinel、MySQL 全部放进同一个自定义 Docker 网络网络名就叫microservices。这样组件之间可以通过容器名直接访问比如 Seata 注册 Nacos 时写nacos1:8848Sentinel 持久化规则时也写nacos1:8848不需要关心宿主 IP。为什么这么设计因为 Docker 默认网络虽然也能通信但每加一个容器就要手动 link容器重建后 IP 还会变。自定义 bridge 网络自带 DNS 解析容器名就是稳定的域名互相访问既快又不容易出错。业务服务将来也部署在同一网络里微服务之间的调用链路就畅通了。对外只暴露必要的管理端口Nacos 控制台 8848、Seata 控制台 7091、Sentinel 控制台 8858。MySQL 不对外暴露只在内网使用。这样的拓扑对安全也有好处至少数据库不至于直接裸奔在公网。生产环境如果多台机器拓扑原则不变先让组件之间内网互通再让客户端通过负载均衡访问 Nacos 集群入口。1.3 存储与选型MySQL、内嵌库、Derby 怎么选择Nacos 集群模式必须外置 MySQL这一点没有商量余地。单机模式可以用内置 Derby但官方从 2.x 开始已经弱化这个选项因为 Derby 不适合多节点数据一致性。我这次直接用 MySQL 8.0字符集用utf8mb4排序规则用utf8mb4_general_ci。Seata 服务端也一样事务协调器需要记录全局事务、分支事务、全局锁这些状态必须持久化到数据库否则 Seata 一重启所有进行中的事务状态就丢了。Seata 的存储模式选db模式。Sentinel 控制台本身不需要数据库它默认把规则推给客户端后由客户端保存在内存里。但这有个致命问题客户端重启后规则就没了。所以我会把 Sentinel 的规则通过 Nacos 做持久化让规矩重启后还在。三个组件的存储选型总结成一句话Nacos 依赖 MySQLSeata 依赖 MySQLSentinel 依赖 Nacos。明白了这条依赖链后续配置就不会乱。2. Nacos 集群三节点注册中心的完整搭建Nacos 是整个架构的基石。如果 Nacos 集群没搭稳后面 Seata、Sentinel、业务服务全部跟着遭殃。这一节我会把从建库到验证的全过程拆开讲。2.1 先初始化 MySQL建库和执行官方表结构Nacos 集群需要 MySQL 存储配置、服务和历史变更记录。先创建一个专用数据库和专用账号避免直接用 root 到处跑。进入 MySQL 容器或者用客户端连上去执行CREATE DATABASE nacos_config DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; CREATE USER nacos% IDENTIFIED BY nacos123; GRANT ALL PRIVILEGES ON nacos_config.* TO nacos%; FLUSH PRIVILEGES;建完库之后必须执行 Nacos 官方的表结构脚本。这个脚本在 Nacos 的 GitHub 仓库里文件名是nacos-mysql.sql路径在distribution/conf/nacos-mysql.sql。也可以用 Docker 镜像里的文件docker run --rm nacos/nacos-server:v2.3.2 tar -C /home/nacos -cf - conf | tar -xf - -C ./nacos-conf然后找到nacos-conf/conf/mysql-schema.sql导入数据库。如果这里偷懒不导入Nacos 启动时会因为缺少表而报错很多人第一次部署 Nacos 集群失败就是栽在这一步。2.2 application.properties 里的关键项逐行拆解Nacos 节点启动时会读取application.properties数据库、鉴权、集群相关配置都在这里面。我的习惯是把配置文件放到宿主机./nacos/conf/application.properties然后挂载进容器。核心配置如下spring.datasource.platformmysql db.num1 db.url.0jdbc:mysql://mysql:3306/nacos_config?characterEncodingutf8connectTimeout1000socketTimeout3000autoReconnecttrueuseUnicodetrueuseSSLfalseserverTimezoneAsia/Shanghai db.user.0nacos db.password.0nacos123 nacos.core.auth.enabledtrue nacos.core.auth.plugin.nacos.token.secret.key这里填生成的Base64密钥 nacos.core.auth.server.identity.keycustom-server-identity nacos.core.auth.server.identity.valuecustom-server-valuespring.datasource.platformmysql告诉 Nacos 启用 MySQL 数据源db.url.0是主库连接串。注意serverTimezoneAsia/Shanghai一定要加否则时间字段会偏移配置发布记录里的时间看起来像穿越了。重点说鉴权。Nacos 2.2 之后默认鉴权是关闭的但生产环境必须打开否则任何人只要能访问 8848 端口就能把你的服务列表和配置看光甚至注册假服务。开启鉴权要配置三样东西token.secret.key、server.identity.key、server.identity.value。token 密钥有个硬性要求Base64 编码后长度不能小于 32 字节。直接用这个命令生成openssl rand -base64 32把输出填进去即可。server.identity是服务端身份识别防止被伪造请求绕过鉴权自己随便定义两个不太常见的键值对就行。2.3 Docker Compose 编排三节点我用三节点做集群镜像版本是nacos/nacos-server:v2.3.2。三台容器在同一网络里通过容器名互相通信这是最省事的方案。compose 配置片段如下nacos1: image: nacos/nacos-server:v2.3.2 container_name: nacos1 hostname: nacos1 networks: - microservices ports: - 8848:8848 - 9848:9848 - 9849:9849 - 7848:7848 environment: - MODEcluster - PREFER_HOST_MODEhostname - NACOS_SERVERSnacos1:8848,nacos2:8848,nacos3:8848 volumes: - ./nacos/conf/application.properties:/home/nacos/conf/application.properties - ./nacos/data/nacos1:/home/nacos/data - ./nacos/logs/nacos1:/home/nacos/logsMODEcluster必填否则默认以单机模式启动。NACOS_SERVERS填所有集群节点的地址这里写容器名加端口。PREFER_HOST_MODEhostname让节点间互相使用 hostname 而不是 IP配合自定义网络最稳定。nacos2、nacos3 的配置几乎一样只需要改container_name、hostname、宿主机映射端口以及数据目录。比如 nacos2 映射为8849:8848、9849:9848、9850:9849、7849:7848nacos3 以此类推。如果你访问 Nacos 控制台习惯用固定端口可以再用 Nginx 做一层 TCP 转发把 8848 转到三个节点中的一个。我这里演示的是同机场景生产环境多机部署时把NACOS_SERVERS改成各自独立 IP 即可原理完全一样。2.4 启动与验证如何确认三节点真的组队成功启动用docker compose up -d然后看日志docker logs -f nacos1看到Nacos started successfully还不够还要确认集群状态。最直接的方式是用健康检查接口curl http://127.0.0.1:8848/nacos/v1/console/health/readiness返回{status:UP}说明节点本身健康。然后打开 Nacos 控制台进入“集群管理”页面如果能看到三个节点且全部标记为 UP才算真正组队成功。这里有个容易踩的坑如果NACOS_SERVERS里写了宿主机 IP而容器之间因为网络隔离访问不到这个 IP节点日志会一直报连接失败。解决办法是统一用容器名或者确保网络互通后再用 IP。另外 7848 端口是 Raft 选举端口安全组和宿主机防火墙必须放行否则节点之间无法选主。3. Seata 服务端把分布式事务协调器挂到 Nacos 上Nacos 起来之后下一步是 Seata。Seata 服务端本身不保存业务数据但它要协调全局事务必须持久化自己的事务状态并且要能注册到 Nacos让业务客户端能发现它。3.1 先理解 Seata 的注册发现与事务分组Seata 的注册发现机制和普通服务不太一样。普通服务直接把实例 IP 注册到 Nacos 就完事但 Seata 多了一层“事务分组”的概念。在业务客户端里配一个事务分组名比如my_tx_group然后通过service.vgroupMapping.my_tx_groupdefault把分组映射到服务端集群名。服务端启动时用REGISTRY_NACOS_CLUSTERdefault声明自己属于哪个集群。客户端和服务端能不能对上就看这个映射关系是否一致。这个设计是为了让事务分组和物理集群解耦业务方只认逻辑分组名底层 Seata 集群怎么扩缩容、怎么迁移业务方完全不用感知。我最初在该配置上栽过跟头服务端注册到 Nacos 的 cluster 叫default客户端却把 vgroupMapping 配成了seata-server结果全局事务一直没有真正走到服务端。3.2 初始化事务库global_table 等四张表Seata 服务端使用db模式时需要建四张表global_table、branch_table、lock_table、distributed_lock_table。官方脚本在script/server/db/mysql.sql从镜像里取docker run --rm seataio/seata-server:1.8.0 tar -C /seata-server -cf - script | tar -xf - ./seata-script然后进 MySQL 建一个seata库执行script/server/db/mysql.sql。这里必须提醒一句业务数据库里还要建一张undo_log表这是给 AT 模式做回滚用的。这张表建在业务库不是 seata 库。很多朋友发现 Seata 回滚失败十有八九是业务库缺了这张表。脚本在script/client/mysql/undo_log.sql注意 1.8 版本的 undo_log 表结构里有branch_id、xid、context等多个字段直接用官方脚本最稳妥。3.3 环境变量部署 Seata 服务端Seata 服务端推荐直接用环境变量控制避免写一堆配置文件。compose 片段如下seata1: image: seataio/seata-server:1.8.0 container_name: seata1 networks: - microservices ports: - 8091:8091 - 7091:7091 environment: - SEATA_CONFIG_NAMEfile - REGISTRY_TYPEnacos - REGISTRY_NACOS_SERVER_ADDRnacos1:8848 - REGISTRY_NACOS_NAMESPACEpublic - REGISTRY_NACOS_CLUSTERdefault - STORE_MODEdb - STORE_DB_TYPEmysql - STORE_DB_DATABASEseata - STORE_DB_USERseata - STORE_DB_PASSWORDseata123 - STORE_DB_URLjdbc:mysql://mysql:3306/seata?useSSLfalseserverTimezoneAsia/ShanghaiallowPublicKeyRetrievaltrue volumes: - ./seata/logs/seata1:/seata-server/logsSEATA_CONFIG_NAMEfile表示用文件方式读取配置但下面的环境变量会覆盖文件里的对应项。REGISTRY_TYPEnacos决定 Seata 注册到 Nacos。注意REGISTRY_NACOS_NAMESPACEpublic如果 Nacos 开了鉴权Seata 服务端还需要配置REGISTRY_NACOS_USERNAME和REGISTRY_NACOS_PASSWORD否则注册会被 Nacos 拒绝。Seata 默认事务端口是 8091控制台端口是 7091。生产环境建议部署两个 Seata 节点做高可用两个节点的 cluster 都叫defaultNacos 里会看到两个实例业务客户端通过事务分组自动负载均衡。如果要在 Nacos 服务列表里确认 Seata 是否注册成功打开 Nacos 控制台搜索seata-server应该能看到实例列表。看不到就检查 Seata 日志里的注册报错最常出现的问题是账号密码没配或者 namespace 对不上。3.4 业务侧的最小接入配置业务服务接入 Seata除了加依赖还要在application.yml里配置spring: application: name: order-service cloud: alibaba: seata: tx-service-group: my_tx_group seata: registry: type: nacos nacos: server-addr: nacos1:8848 namespace: public username: nacos password: nacos123 service: vgroup-mapping: my_tx_group: default三个要点tx-service-group是业务侧的事务分组名必须和vgroupMapping的 key 一致vgroupMapping的 value 是服务端 cluster 名必须和 Seata 服务端的REGISTRY_NACOS_CLUSTER一致registry.nacos的地址要能让业务服务访问到 Nacos。配置完成后写一个GlobalTransactional测试接口模拟一个跨服务转账场景。如果 Seata 日志出现了Begin new global transaction说明客户端已经通过 Nacos 找到 Seata 服务端了。4. Sentinel 控制台流量治理入口与规则持久化Sentinel 这块比前两个稍微复杂一点因为涉及控制台选型。如果只是拿官方控制台随便跑一下你会发现推下去的规则客户端是能收到但客户端一重启规则就清零了。真正的生产用法是把规则持久化到 Nacos。4.1 控制台选择官方版还是改造版官方 Sentinel Dashboard 本身不带规则持久化能力它把规则推给客户端后客户端只放在内存里。社区有不少改造版解决了这个问题做法是控制台把规则写入 Nacos客户端通过 Nacos 数据源拉取并订阅变更。我这次用的是社区改造版控制台镜像名常见为bladex/sentinel-dashboard版本 1.8.6。它支持大部分常用规则类型包括流控、降级、热点参数、系统规则、授权规则并且把推送目标指向 Nacos。如果你不想用第三方镜像也可以自己改官方源码给控制台加上 Nacos 推送模块但工作量不小。选型结论很简单本地学习用官方版够了想在生产环境落地规则治理必须用改造版加 Nacos 持久化。控制台只是个管理界面真正的规则存储和分发要交给 Nacos。4.2 控制台部署与鉴权控制台本身是 Java 应用部署方式和其他服务一样直接放 compose 里sentinel-dashboard: image: bladex/sentinel-dashboard:1.8.6 container_name: sentinel-dashboard networks: - microservices ports: - 8858:8858 environment: - AUTH_USERNAMEsentinel - AUTH_PASSWORDsentinel123 - NACOS_ADDRnacos1:8848 - NACOS_NAMESPACEpublic volumes: - ./sentinel/logs:/root/logs/csp控制台登录默认账号密码这里我改成了sentinel / sentinel123。不同改造版的环境变量名可能略有差异部署时以镜像 README 为准。需要注意改造版控制台页面端口通常还是 8858但底层 API 用的 8080 端口如果网络策略要求严格这两个端口都要考虑。控制台一定要设强密码因为改造版控制台是可以直接往 Nacos 写规则的密码失守等于别人能随意修改你的限流降级规则轻则影响业务重则让整个系统裸露在流量冲击下。4.3 规则持久化到 Nacos 的原理与配置Sentinel 的客户端需要配置 Nacos 数据源才能感知到控制台写到 Nacos 的规则。以 Spring Cloud Alibaba 项目为例spring: cloud: sentinel: transport: dashboard: sentinel-dashboard:8858 port: 8719 datasource: ds-nacos: nacos: server-addr: nacos1:8848 namespace: public group-id: SENTINEL_GROUP >