☰
微服务治理实践:Docker部署Nacos Seata Sentinel黄金组合
2026/10/20 21:09:44 网站建设 项目流程

微服务拆得越细,三个问题就越绕不开:数据一致性、流量保护和配置管理。Seata 1.4.2、Sentinel 以及 Nacos 2.0.3 这一套组合,几乎是现阶段做微服务中间件验证时最常碰到的“黄金三角”。我见过不少团队在代码层面已经写得七七八八,最后却卡在环境部署上——版本不咬合、配置对不上、事务老回滚、限流规则一去就丢。这篇文章就把我在 Docker 环境里搭这套东西的完整过程、关键参数和个人心得一次性说透。

这套方案适合谁?正在搞 Spring Cloud Alibaba 微服务、需要把分布式事务和流控熔断落到实处的 Java 研发,或者负责中间件交付的运维同学。无论你是从零开始搭一套验收环境,还是想在本地复现生产拓扑,下面这些内容都能直接抄作业。

1. 这套组合到底是干嘛的:场景与选型逻辑

1.1 三件套各管什么,配合起来解决什么问题

先统一口径。Nacos 2.0.3 的核心职责是注册中心和配置中心:服务上下线、服务发现、配置统一管理,都靠它。Seata 1.4.2 处理的是分布式事务,尤其是跨库、跨服务的业务一致性。Sentinel 则负责流量防护,限流、熔断、系统自适应保护,防止某个下游抖动把整条链路拖死。

这三者单独用没什么稀奇的,但组合起来就形成了微服务的基础治理闭环:服务实例先注册到 Nacos,Seata 通过注册中心拿到事务协调者的地址,Sentinel 把限流规则也交给 Nacos 统一管理。这样一来,规则的增删改查和配置变更全部集中到一个入口,服务实例只管执行,不用担心状态不一致。

我实际接触过的项目里,最常见的拓扑是一个订单服务调库存服务,下单时先开启全局事务,Seata 保证两边要么都成功要么都回滚;同时订单接口挂了 Sentinel 限流规则,峰值流量超过阈值直接返回快速失败;整个服务的配置和限流规则全部动态推送到 Nacos。这样一套环境,哪怕只有两台机器,也能把微服务治理的主要路径完整跑一遍。

1.2 为什么挑这几个版本,版本咬合关系要先搞清楚

版本选择是整个部署过程中最容易翻车的一环。Seata 1.4.2 对应的客户端集成版本是 spring-cloud-alibaba 2.2.x,这个版本对 Nacos 2.x 的兼容性经过了大量验证。如果直接把 Seata 升到 1.5+,跟 Nacos 2.0.3 配合时反而容易出现 gRPC 端口配置和客户端版本不匹配的问题;而降到 1.3 以下,许多新特性又用不上。

Sentinel 我选的是官方 1.8.2 稳定版,这个版本对 Spring Cloud Alibaba 的适配最成熟,社区打包的 Docker 镜像也最多。如果为了追新直接用 1.8.6+,控制台默认登录方式和配置项会有变化,反而增加上手成本。

Nacos 2.0.3 相比 1.x 最大的变化是引入了 gRPC 通信,客户端和服务端之间除了 8848 端口,还会用到 9848 端口。这意味着 Docker 端口映射、防火墙规则、网络策略都要跟着调整。这也是很多人在迁移到 2.0 之后遇到的第一个大坑:控制台能打开,服务却死活注册不上。

版本选型的核心逻辑是“匹配优先,追新排后”。你用的客户端框架版本决定了服务端版本的上限,与其让所有组件都跑在最新版上互相试探,不如锁定一套经过验证的组合,把精力放在业务本身。

2. 动手之前先画图:网络、端口、存储规划

2.1 端口和命名空间规划

部署前先列一张端口规划表,避免后边启动时手忙脚乱。我的分配方案如下:

组件端口说明
Nacos8848客户端 HTTP 通信、控制台访问
Nacos9848gRPC 通信端口,2.x 必开
Seata8091事务协调者服务端口
Seata8092高可用模式下 Raft 端口,非必需
Sentinel8080控制台服务端口
MySQ L3306Nacos 和 Seata 的元数据库

命名空间同样要提前规划。我会在 Nacos 里建一个独立命名空间,比如ms-platform,Seata 和业务服务的配置都收拢到这个命名空间下。千万别把所有环境塞进public命名空间,测试和生产的配置一旦互相覆盖,排查起来非常痛苦。

2.2 数据目录与网络模式

容器生命周期短,配置和日志如果写在容器内部,重启一次就全没了。我把三个组件的数据目录都明确挂载到宿主机:

  • /opt/nacos/logs映射到容器内 Nacos 日志目录
  • /opt/seata/config映射到 Seata 配置目录
  • /opt/sentinel/logs映射到 Sentinel 日志目录

网络模式上,我建议创建一个独立的 Docker 网络:

docker network create ms-platform-net

所有容器都加入这个网络,容器之间用服务名互相访问。例如 Seata 容器里配置 Nacos 地址时,直接写nacos-server:8848,不依赖宿主机 IP 的具体值。这样后期迁移宿主机,或者把容器挪到另一台机器,内部连接不会断。

不过要注意一点:如果采用 bridge 网络,容器启动时拿到的 IP 是动态的。Seata 服务端向 Nacos 注册时,默认会用容器 IP,这会导致业务应用无法从外部访问 Seata。这一点在第 4 节会专门讲解决办法。

2.3 镜像准备

镜像标签要精确到版本,不要用latest。我用的是这几个:

docker pull nacos/nacos-server:v2.0.3 docker pull seataio/seata-server:1.4.2 docker pull bladex/sentinel-dashboard:1.8.2

其中 Sentinel 的镜像我选的是社区打包版本,主要原因是官方 release 需要自己拉代码构建,社区镜像里已经把控制台放好了,逻辑简单。如果你有安全合规要求,也可以基于官方 release 包自己打一个镜像,原理一样,只是多一步构建过程。

3. 先把地基打好:Nacos 2.0.3 的 Docker 部署与初始化

3.1 元数据库初始化

Nacos 2.0.3 生产级部署必须用 MySQL 存储,不能用内嵌 Derby。第一步是建库并初始化脚本:

CREATE DATABASE nacos_config DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;

然后用 Nacos 官方提供的nacos-mysql.sql初始化表结构。这个脚本在 Nacos release 包的conf目录里,也可以从 GitHub 仓库的distribution/conf路径下找。执行完毕之后,核心表应该有config_info、config_metadata、his_config_info、tenant_info等。

这里有个很容易忽略的点:MySQL 连接串要显式加上时区和字符集参数。我在连接配置里用的是:

jdbc:mysql://mysql-host:3306/nacos_config?serverTimezone=Asia/Shanghai&characterEncoding=utf8

不加时区参数的话,Nacos 记录配置变更历史时,时间会偏 8 个小时,你排查问题时看日志会觉得很诡异。

3.2 容器启动与关键参数

数据库准备就绪后,启动 Nacos 容器。我用的完整命令如下:

docker run -d \ --name nacos-server \ --network ms-platform-net \ -p 8848:8848 \ -p 9848:9848 \ -e MODE=standalone \ -e SPRING_DATASOURCE_PLATFORM=mysql \ -e MYSQL_SERVICE_HOST=mysql-host \ -e MYSQL_SERVICE_PORT=3306 \ -e MYSQL_SERVICE_DB_NAME=nacos_config \ -e MYSQL_SERVICE_USER=nacos \ -e MYSQL_SERVICE_PASSWORD=你的密码 \ -e NACOS_AUTH_ENABLE=true \ -e NACOS_AUTH_TOKEN=SecretKey012345678901234567890123456789012345678901234567890123456789 \ -v /opt/nacos/logs:/home/nacos/logs \ nacos/nacos-server:v2.0.3

几个关键参数单独解释一下。

MODE=standalone表示单机模式。如果改成cluster,需要额外配置几个节点的地址列表,测试环境单机就够。

NACOS_AUTH_ENABLE=true是开启控制台登录鉴权。Nacos 社区默认不强制开启鉴权,但实际部署时一定要开,否则配置中心直接裸奔在公网上,后果不堪设想。

NACOS_AUTH_TOKEN必须是一个 Base64 编码的字符串,且长度要足够。Nacos 2.0.3 有个已知问题,默认的SecretKey被大量文章引用后,已经变成了公开的“默认密钥”,网上有工具能直接伪造请求。换成你自己生成的随机值,是部署后第一件要做的事。

启动完成后,访问http://宿主机IP:8848/nacos,登录账号密码默认是nacos/nacos。第一次登录后立刻去修改密码,这一步别偷懒。

3.3 启动后的自检清单

Nacos 容器起来不代表环境真的可用。我每次部署完都会做四个检查:

  • 控制台能不能打开,登录后能不能正常获取配置列表。
  • 从业务容器里 pingnacos-server:8848,确认网络互通。
  • 检查 9848 端口是否真的在监听。2.0.x 的 gRPC 端口映射缺失,会导致服务注册异常但控制台死活看不出来问题。
  • 在控制台“配置管理”里手动新建一条测试配置,确认写入和读取正常。

其中 9848 端口是 2.0 版本的特殊之处,我用一个土办法验证:在宿主机上执行telnet 宿主机IP 9848,如果能连通,说明 gRPC 通道是通的;如果不通,大概率是 Docker 端口映射漏了。

4. 核心戏份:Seata 1.4.2 部署与 Nacos 配置中心对接

4.1 Seata 的配置体系先理清楚

Seata 1.4.2 的配置涉及两个文件加一套配置项。registry.conf决定服务端和客户端去哪里找注册中心、配置中心;file.conf里的store部分决定事务会话信息存到哪里;而config.txt则需要通过脚本导入到 Nacos 配置中心,由 Seata 从 Nacos 动态拉取。

初次部署的人最容易把这三层搞混。简单来说:

  • registry.conf是引路人,告诉 Seata 去 Nacos 里拿配置。
  • config.txt是 Nacos 里的配置本体。
  • file.conf是兜底文件,当配置中心不可用时还能用本地文件模式撑一下。

生产环境推荐把配置放到 Nacos 统一管理,本地file.conf只保留最基本的兜底配置。

4.2 修改配置并导入到 Nacos

先从 Seata 1.4.2 的 release 包中取出config.txt和nacos-config.sh。重点修改下面几个配置项:

store.mode=db store.db.url=jdbc:mysql://mysql-host:3306/seata?useUnicode=true&characterEncoding=utf8 store.db.user=seata store.db.password=你的密码 service.vgroupMapping.default_tx_group=default service.default.grouplist=seata-server:8091

store.mode=db表示事务会话信息保存到数据库,而不是默认的文件模式。文件模式重启后就丢状态,对账困难,测试场景临时用一下还行,正式环境必须切 db。

改成 db 模式后,需要初始化 Seata 的全局事务表。在 Seata 的数据库中执行官方提供的script/server/db/mysql.sql,里面包含global_table、branch_table、lock_table三张核心表的建表语句。

接下来执行导入脚本:

sh nacos-config.sh -h nacos-server -p 8848 -g SEATA_GROUP -t 命名空间ID -u nacos -w 你的密码

注意脚本里的-t参数,传的是命名空间 ID,不是命名空间名称。如果省略这个参数,配置会写进public命名空间,后续 Seata 服务端和客户端都要用同一个命名空间才能对上。

导入完成后,到 Nacos 控制台刷新配置列表,应该能看到service.vgroupMapping.default_tx_group和一组store.*配置项。如果没有看到,说明脚本执行时的网络或参数有问题,回到第 3.3 节的自检流程重新排查。

4.3 Seata 服务端容器启动与客户端配置

Seata 服务端启动命令如下:

docker run -d \ --name seata-server \ --network ms-platform-net \ -p 8091:8091 \ -e SEATA_IP=宿主机IP \ -e SEATA_PORT=8091 \ -e SEATA_CONFIG_NAME=registry \ -v /opt/seata/config:/seata-server/resources \ seataio/seata-server:1.4.2

这里的SEATA_IP必须显式指定为宿主机 IP,而不是容器 IP。否则 Seata 服务端向 Nacos 注册时,会把自己注册成容器内部地址,业务应用拿到这个地址后根本连不上。这个问题我在实际项目里踩过不下三次。

另外,我把registry.conf放到宿主机/opt/seata/config目录下,通过目录挂载直接替换容器内配置。修改配置后重启容器即可生效,不用进容器内部改文件。

业务应用那边需要做的配置对齐:

spring: cloud: alibaba: seata: tx-service-group: default_tx_group seata: registry: type: nacos nacos: server-addr: nacos-server:8848 namespace: 命名空间ID group: SEATA_GROUP config: type: nacos nacos: server-addr: nacos-server:8848 namespace: 命名空间ID group: SEATA_GROUP

注意tx-service-group必须和 Nacos 里的service.vgroupMapping.default_tx_group保持一致。如果客户端配置的是default_tx_group,而服务端映射的是另一个名字,全局事务会直接失效,而且报错信息非常隐晦,经常只是提示找不到事务协调者。

Seata 1.4.2 的事务分组机制可以理解为:客户端通过一个逻辑分组名找到一组实际的事务协调者地址。这个映射关系放在配置中心,由服务端启动时维护。所以只要分组名不一致,整个链路就断掉。

5. 流量守卫:Sentinel 部署与 Nacos 持久化整合

5.1 Sentinel 控制台启动

Sentinel 控制台本质上是一个 Spring Boot 应用,官方默认端口是 8080。我运行时把宿主机端口映射成 8858,避免和业务应用冲突:

docker run -d \ --name sentinel-dashboard \ --network ms-platform-net \ -p 8858:8080 \ -v /opt/sentinel/logs:/root/logs \ bladex/sentinel-dashboard:1.8.2

启动后访问http://宿主机IP:8858,1.8.2 及更早版本默认不需要登录,直接进控制台。如果你的版本比较新,默认账号密码是sentinel/sentinel。

这句话我多说一句:社区镜像很多是个人打包的,镜像里可能带着作者自己的配置偏好,所以上线前最好确认一下镜像内容,或者干脆用官方源码自己构建,本质就是打个 Jar 包再写个 Dockerfile。

5.2 客户端上报与规则持久化

Sentinel 控制台只是一个展示和操作界面,真正的限流规则逻辑在客户端应用里执行。客户端要上报数据、接收规则推送,需要在业务应用启动参数里指定控制台地址:

-Dcsp.sentinel.dashboard.server=宿主机IP:8858 -Dproject.name=order-service

只加上报还不够。默认情况下,控制台里创建的规则只保存在内存中,应用一重启规则就全没了。想实现规则持久化,需要把 Sentinel 和 Nacos 打通。业务应用引入sentinel-datasource-nacos依赖后,在配置文件中声明数据源:

spring: cloud: sentinel: datasource: ds1: nacos: server-addr: nacos-server:8848 dataId: order-service-flow-rules groupId: DEFAULT_GROUP rule-type: flow

这里的rule-type可以是flow、degrade、system、authority等,分别对应限流、熔断、系统保护、授权规则。一个 dataId 对应一种规则类型,这是我在项目里反复确认过的,不能把多种规则塞进同一个 dataId。

配置完成后,在 Nacos 控制台创建order-service-flow-rules这个配置,内容格式是 JSON 数组。例如:

[ { "resource": "POST:/api/order/create", "limitApp": "default", "grade": 1, "count": 100, "strategy": 0, "controlBehavior": 0, "clusterMode": false } ]

规则字段里,grade=1表示 QPS 维度限流,count=100是阈值,controlBehavior=0表示快速失败。配置发布后,Sentinel 客户端会自动感知并加载到本地,控制台也会同步显示。

这里有一个细节:在 Nacos 里修改规则发布后,Sentinel 控制台上可能不会立刻刷新展示,这是正常的,规则已经实际生效,控制台只是显示滞后。不要因为控制台上没看到变化就疯狂重发配置,很容易把规则搞乱。

5.3 三件套同机运行时的资源占用

如果三个组件都在同一台机器上跑,内存分配要有预期。Nacos 默认 JVM 参数吃 2G 左右,Seata 服务端大约 512M,Sentinel 控制台在 256M 到 512M 之间。加上业务应用,一台 4G 内存的机器基本会见底。

我的建议是:本地验证环境给 Nacos 单独留 2G,Seata 和 Sentinel 共用 1G。如果机器内存不够,可以给 Nacos 调 JVM 参数,在启动命令里加上-e JVM_XMS=512m -e JVM_XMX=512m,单机测试够用。

6. 从实践里筛出来的坑与排查速查表

6.1 高频故障对照表

我把实际部署中遇到频率最高的问题整理成了一张表,方便排查时对着看:

现象可能原因处理方式
Nacos 控制台打开但服务注册不上9848 gRPC 端口未映射检查 Docker 端口映射,确保 9848 已暴露
Seata 启动日志一直连不上注册中心registry.conf 里的 namespace 或地址写错进容器看真实配置,对比 Nacos 命名空间 ID
分布式事务不回滚客户端事务分组和服务端 vgroupMapping 不一致统一tx-service-group名称
业务服务找不到 Seata 地址SEATA_IP 是容器 IP启动时显式指定宿主机 IP
Sentinel 控制台看不到应用上报csp.sentinel.dashboard.server参数缺失检查应用启动参数,确保指向宿主机 IP
限流规则重启后丢失未接 Nacos 数据源引入sentinel-datasource-nacos并配置规则
配置中心修改后长时间不生效客户端未开启配置监听或数据 ID 不一致核对 dataId、groupId、命名空间等关键信息

6.2 三个高性价比排查手段

第一招是查日志。容器部署下,所有日志都走挂载目录,不需要进容器。Nacos 的日志在/opt/nacos/logs,Seata 的日志在容器logs目录下,用docker logs或者直接看宿主机挂载目录都行。定位问题前先把服务端日志的 ERROR 和 WARN 过一遍,大多数问题在这里就有明确线索。

第二招是查 Nacos 配置列表。Seata 和 Sentinel 的问题,80% 都能在“配置管理”里找到端倪。配置中心没有对应配置,说明导入脚本有问题;配置有但业务应用不生效,说明命名空间或 dataId 对不上。这一条是排查效率最高的路径。

第三招是抓网络请求。如果客户端和服务端都在同一宿主机,用tcpdump抓 8091 和 8848 端口很容易定位是连接拒绝还是数据包异常。实在没有tcpdump,先telnet一下端口,能通不一定没问题,但连不通一定有问题。

6.3 值得坚持的几个规范

第一,命名空间隔离。测试环境一个命名空间,验收环境一个命名空间,生产环境另一个命名空间。配置中心的 key 一样,靠命名空间隔离,不会互相污染。

第二,配置统一外置。所有组件的配置文件都挂载到宿主机的固定目录,避免进容器改文件。容器重建就是一次性的,进容器改的配置会随容器销毁。

第三,固定版本号。部署脚本里所有镜像标签写死,不要用latest。我见过一次 Nacos 服务端意外升级到 2.1,结果 gRPC 端口和客户端行为全部变化,整个环境一晚上没消停。

最后再分享一个小技巧。整套环境部署完成后,我会把所有启动命令整理成一个 shell 脚本,按“Nacos -> MySQL -> Seata -> Sentinel”的顺序排列。为什么要这个顺序?因为 Seata 启动时要连接 MySQL 和 Nacos,Sentinel 不用等前两者,但业务应用注册时需要三个组件都在线。顺序反了,虽然容器会自动重试,但启动日志里全是连接失败的红字,很影响判断。

这套环境我前前后后搭过很多遍,最大的体会是版本之间互相咬合,配置对不上返工成本极高。建议第一次搭建时,严格复制参数,不要自己“优化”掉任何一个看似多余的环境变量。搭通之后再逐步调整,那时候每一步改动会有什么影响,你心里就有数了。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询