ARTICLE DETAIL

资讯详情

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

Docker部署Seata与Nacos:版本不匹配问题排查与避坑指南

Docker部署Seata与Nacos:版本不匹配问题排查与避坑指南 先交代下背景这周帮同事排查一个分布式事务问题看到日志第一行can not connect to services server我就知道又碰到Seata和Nacos的版本匹配问题了。这种情况在Docker部署环境里我已经遇到不止一次而且每次都不太一样有时候是Seata Server启动后没注册上去有时候是客户端明明能连上Nacos却找不到Seata实例还有时候是配置文件压根没拉到。这次趁着部署过程完整记录下来把Docker部署Nacos、部署Seata Server、微服务接入这几步都捋一遍重点讲讲怎么系统性地解决版本不匹配这个老大难。这篇文章适合正在做Spring Cloud Alibaba微服务改造、需要引入分布式事务或者已经在用Seata但被版本问题折磨的团队参考。Docker只是部署载体核心思路换到K8s、裸机都一样适用。1. Seata与Nacos的协作原理和版本坑的由来1.1 它们到底是怎么协作的要理解版本不匹配得先搞清楚Seata和Nacos之间的关系。Seata本身是一个独立的分布式事务协调器它的Server端是一个中心化的服务负责全局事务的提交、回滚判断和协调。而Nacos在整套体系里承担两个角色注册中心和配置中心。Seata Server启动的时候会把自己作为一个服务实例注册到Nacos上这样微服务里的Seata客户端才能通过Nacos找到它。同时Seata Server的一些核心配置比如存储模式file还是db、事务分组映射关系、数据库连接信息等也可以存放在Nacos的配置中心里这样服务一旦启动就会自动拉取。微服务里的Seata客户端启动时也做同样的事情先从Nacos读取全局配置再通过服务发现找到Seata Server的地址然后建立连接。一个比较生活化的类比把Nacos当成大厦前台Seata Server是大楼里某个服务窗口的负责人微服务是来办事的人。负责人要先到前台登记自己所在的房间号服务注册办事的人到了之后先问前台这个负责人在哪服务发现还要拿一份办事指南配置拉取才知道该去哪一层、找谁办、按什么流程办。任何一个环节的信息对不上事情就办不成。1.2 版本不匹配的典型症状真实环境里的版本不匹配症状其实很统一我列几个最常见的第一Seata Server能启动但Nacos控制台服务列表里看不到它。这种一般不是版本问题而是registry.conf里配置的Nacos地址、命名空间不对或者Seata版本太旧导致注册逻辑不兼容新Nacos。第二微服务启动时一直报can not connect to services server。这个报错太经典了意味着客户端启动时找不到Seata Server的实例。常见原因是事务分组tx-service-group和vgroupMapping没有对上或者客户端SDK版本与服务端不匹配导致服务发现问题失败。第三Seata Server日志里出现Nacos相关的注册报错、超时、No DataSource报错。这种需要看具体异常栈如果是NacosException、failed to req API这类大概率是Seata内置的nacos-client版本和Nacos Server版本不兼容。第四配置拉取失败启动日志提示找不到seataServer.properties。这个表现为本地registry.conf里明明写了config.typenacos但Nacos配置中心里没有对应dataId的配置或者dataId、group写错了。归根结底是Seata不同版本对配置中心的dataId和group默认值不一样。第五事务执行时抛no available service。客户端能启动但真正发起分布式事务时就找不到可用的Seata Server。这种往往也是事务分组或集群名不一致造成的严格说也算一种“配置版本不匹配”。1.3 版本不匹配的根因分析说到底Seata和Nacos版本不匹配的根因主要来自三个方面。第一个是Seata Server和Seata客户端的版本不一致。这是所有人最开始容易踩的坑。Seata官方对版本的要求很明确客户端SDK版本与服务端版本必须一致或者至少保持大版本一致不能用1.6的客户端去连2.0的服务端反之也不行。因为Seata的协议在版本迭代里会有变化旧客户端发给新服务端的请求服务端可能根本解析不了。第二个是Seata内置的nacos-client依赖版本和Nacos Server版本不匹配。这个很多人没意识到。Seata本身就使用了Nacos的客户端SDK来和Nacos Server通信如果Seata版本里内置的nacos-client是1.4.x而你的Nacos Server是2.2.x虽然Nacos 2.x向后兼容了1.x的HTTP API但一些边界场景还是会出现问题尤其是配置变更推送和服务实例的动态感知上。第三个是Nacos 2.x引入的gRPC通信机制。Nacos从2.0开始客户端与服务端的核心通信变成了gRPC端口是884810009848。如果Nacos容器只暴露了8848没有暴露9848客户端能打开Nacos控制台但实际的服务发现和配置推送会失败。很多“版本不匹配”的表象查到最后其实是端口没暴露或者是防火墙拦截了gRPC流量。1.4 版本组合选型建议根据我实际部署和线上运行的经验给出几个比较稳妥的版本组合可以直接参考Seata Server版本Nacos Server版本客户端SDK版本稳定性评价1.4.21.4.x1.4.2旧项目常见能用但别升级Nacos1.5.22.0.x/2.1.x1.5.2中等稳定注意gRPC端口1.6.12.1.x/2.2.x1.6.1社区验证较多较推荐2.0.02.2.x2.0.0生产环境推荐带控制台2.2.02.2.x/2.3.x2.2.0较新特性全建议新项目选型原则就一句话新项目用新版本老项目别乱升级。如果项目已经在线上跑着Seata 1.4.2没有必要为了“新”而升级保持现状就是最稳定的。如果是新项目直接上Seata 2.x Nacos 2.2.x少走很多弯路。我后文的实操部分就以Seata 2.0.0 Nacos 2.2.3作为主组合来演示。2. Docker环境准备与网络规划2.1 Docker环境的安装确认这篇文章默认你已经装好了Docker毕竟现在Docker Desktop、Linux包管理器安装都很成熟了。但有几个容易出问题的地方还是提一下。在Windows上如果Docker Desktop启动失败最常见的是虚拟化没开或者驱动冲突比如提示virtualization support not detected或者vmmem、vmx86驱动版本不对。这类问题处理思路很直白去BIOS里确认VT-x/AMD-V开启Windows侧确认Hyper-V、WSL2功能正常VMware和Docker Desktop共存时检查vmx86驱动是否被其他软件覆盖了版本。这些问题看着低级但在实际工作环境里出现频率相当高。Docker装好后先跑一个命令确认环境正常docker version docker infodocker version的输出里Server和Client版本都能看到docker info能确认存储驱动、网络模式这些基本信息。如果Server部分报错说明Docker守护进程没起来优先解决这个再往下走。2.2 创建一个独立的Docker网络部署Seata和Nacos我强烈建议先创建一个自定义的Docker网络。原因有两个。第一容器之间通过容器名直接互访比通过宿主机IP或者写死IP要灵活。Nacos容器和Seata容器都挂到同一个网络里Seata里配置Nacos地址就可以直接写容器名nacos-server不用关心IP有没有变。第二自定义网络天然支持DNS解析容器重启后IP变了也没关系服务间的地址不会断。创建网络docker network create seata-network查看网络是否创建成功docker network ls后面启动Nacos和Seata时都加上--network seata-network参数。如果微服务也用Docker部署同样建议挂到这个网络上这样服务和Seata之间的网络链路完全打通。2.3 镜像选择策略镜像版本tag一定要指定不要用latest。这一点很多教程不会强调但实际生产环境里因为镜像tag漂移导致的环境不一致问题太多了。我这次使用的镜像版本是Nacosnacos/nacos-server:v2.2.3Seata Serverseataio/seata-server:2.0.0先拉取镜像docker pull nacos/nacos-server:v2.2.3 docker pull seataio/seata-server:2.0.0拉取完成后可以确认一下镜像信息docker images看到两个镜像都存在再继续。镜像拉取慢的话记得配置镜像加速器这个每个云厂商都有不展开说了。3. 用Docker部署Nacos含关键端口说明3.1 启动Nacos容器Nacos的启动命令要注意几个关键点。首先是单机模式通过MODEstandalone环境变量指定。其次是端口映射这个非常关键Nacos 2.x除了8848之外还必须要暴露9848和9849原因前面已经解释过一个是客户端gRPC通信端口一个是服务端gRPC通信端口。完整启动命令docker run -d \ --name nacos-server \ --network seata-network \ -p 8848:8848 \ -p 9848:9848 \ -p 9849:9849 \ -e MODEstandalone \ -e NACOS_AUTH_ENABLEfalse \ nacos/nacos-server:v2.2.3这里我关闭了Nacos的鉴权。生产环境建议开启鉴权本地测试环境关闭可以减少干扰项先把功能跑通再说权限问题。启动后验证一下容器状态docker ps或者直接看日志docker logs -f nacos-server看到类似Nacos started successfully的日志就说明启动OK了。3.2 Nacos端口到底该怎么映射很多人不理解为什么Nacos要暴露那么多端口我单独说一下。8848是Nacos的HTTP API和控制台端口浏览器访问Nacos控制台用的是它Seata和Seata客户端通过HTTP调用Nacos的Open API也是用它。9848是Nacos 2.x新增的gRPC端口客户端包括Seata、Spring Cloud应用在完成服务发现后会通过gRPC和Nacos保持长连接用于配置变更的实时推送、服务实例变化的感知。这个端口不暴露最典型的症状就是控制台能看到服务但客户端收不到配置变更或者连接不稳定。9849是gRPC服务端之间的通信端口单机模式下其实用不太到但集群模式或者将来要扩展节点时会用到。建议从开始就把这几个端口都映射出去省得以后拓展还要改启动命令。在Rancher或K8s里部署Nacos也是同样的原则Service要同时暴露8848、9848、9849三个端口并且要配置成NodePort或LoadBalancer否则集群外部访问不到Nacos控制台即使能访问控制台客户端也可能因为9848没暴露而连接失败。3.3 验证Nacos服务打开浏览器访问http://localhost:8848/nacos默认账号密码都是nacos登录后能看到控制台首页。在服务列表里确认一下现在应该是空的因为还没有任何服务注册进来。此刻只要能打开控制台就说明Nacos本身是健康的。验证一下gRPC端口有没有通可以这样处理在宿主机执行telnet 127.0.0.1 9848如果能连上说明9848端口是通的。如果telnet命令没有也可以直接启动后面的Seata Server看它能不能正常连接用实际结果来验证。4. 用Docker部署Seata Server重点4.1 在Nacos配置中心初始化Seata配置Seata Server启动时会根据registry.conf里的config配置去Nacos读取配置文件。这个配置文件就是seataServer.propertiesdataId固定叫这个名group我们统一用SEATA_GROUP。登录Nacos控制台在“配置管理”里新建配置Data IDseataServer.propertiesGroupSEATA_GROUP配置格式Properties配置内容如下这是最简化、可以直接跑起来的版本# 存储模式file是本地文件存储测试环境足够 store.modefile store.lock.modefile store.session.modefile # 如果要使用db模式改这几行并保证数据库里建好表 # store.modedb # store.db.datasourcedruid # store.db.dbTypemysql # store.db.driverClassNamecom.mysql.cj.jdbc.Driver # store.db.urljdbc:mysql://127.0.0.1:3306/seata?useUnicodetruecharacterEncodingutf8useSSLfalseserverTimezoneAsia/Shanghai # store.db.userroot # store.db.passwordyourpassword # 事务分组映射 service.vgroupMapping.my_test_tx_groupdefault # default集群对应的Seata Server地址 service.default.grouplist127.0.0.1:8091这里重点解释两个概念。service.vgroupMapping.my_test_tx_groupdefault这一行是把客户端定义的事务分组my_test_tx_group映射到Seata Server的default集群。客户端在配置里指定的tx-service-group必须和这里的key对应否则客户端通过Nacos发现了seata-server实例也会因为分组不匹配而找不到可用的服务。service.default.grouplist127.0.0.1:8091这一行是给Seata Server所在的集群指定一个直接地址列表。其实如果走的是Nacos注册中心这一行理论上可以不用配因为客户端会从Nacos拿到Seata Server的地址。但很多版本在有这个配置时会优先走grouplist所以最好还是写上避免因为注册信息解析不到导致连接失败。4.2 准备registry.conf文件在宿主机上创建一个目录用来存放Seata的配置文件。比如/opt/seata/conf。然后创建registry.confmkdir -p /opt/seata/conf cd /opt/seata/conf vim registry.conf内容如下registry { type nacos nacos { serverAddr 127.0.0.1:8848 namespace group DEFAULT_GROUP username password } } config { type nacos nacos { serverAddr 127.0.0.1:8848 namespace group SEATA_GROUP username password dataId seataServer.properties } }注意这里的serverAddr如果Seata容器和应用容器都挂在seata-network网络上为了容器间通信应该写nacos-server:8848。但这里有个细节需要注意如果serverAddr写了容器名那么Seata Server启动后它注册到Nacos上的IP地址会是什么这里就涉及到SEATA_IP环境变量的作用了。如果不设置SEATA_IPSeata Server会尝试获取容器自己的IP地址这个IP在容器内是正常的但微服务如果不在同一个Docker网络可能根本访问不到这个IP。所以通常情况下要么设置SEATA_IP为宿主机IP要么保证微服务和Seata在同一个Docker网络里。我建议的方案是Seata容器和微服务容器都在seata-network网络上registry.conf里的serverAddr写成nacos-server:8848同时设置SEATA_IP为宿主机可访问的IP。这样客户端通过Nacos拿到的Seata地址是宿主机IP:8091只要端口映射正常就能连上。4.3 启动Seata容器配置准备完毕后开始启动Seata容器。这里我推荐用挂载配置文件的方式兼容性最好不管你用的是1.5版本还是2.0版本都能用。docker run -d \ --name seata-server \ --network seata-network \ -p 8091:8091 \ -p 7091:7091 \ -v /opt/seata/conf/registry.conf:/seata-server/resources/registry.conf \ -e SEATA_IP127.0.0.1 \ -e SEATA_PORT8091 \ seataio/seata-server:2.0.0这里有两个端口8091是Seata Server的事务协调器端口所有分布式事务的协调通信都是走这个端口。7091是Seata 2.x新增的管理控制台端口浏览器访问http://localhost:7091可以看到Seata的控制台能查看全局事务状态、锁信息等。如果用的是1.5之前的版本没有7091这个端口不用映射也没关系。SEATA_IP和SEATA_PORT这两个环境变量的作用是告诉Seata Server在注册到Nacos时对外广播的IP和端口。如果客户端和Seata在同一个Docker网络可以写容器名和8091如果客户端在宿主机或者别的机器上就必须写宿主机IP和映射后的端口。启动后看日志docker logs -f seata-server日志里出现类似Server started successfully的关键字就说明Seata Server启动成功了。4.4 验证Seata是否成功注册到Nacos回到Nacos控制台刷新服务列表正常情况下能看到一个服务名是seata-server的实例IP和端口就是SEATA_IP和SEATA_PORT配置的值。如果看不到优先排查registry.conf里的serverAddr是否写对Nacos控制台的地址和这个是否一致namespace是不是空字符串如果账号登录后默认namespace是public但配置里没指定namespace注册是到public下的Docker网络是否通了可以在seata-server容器里ping一下nacos-server的容器名docker exec -it seata-server ping nacos-server如果ping不通说明网络配置有问题检查是否都挂在了seata-network上。5. 微服务接入Seata客户端5.1 Maven依赖引入如果项目用的是Spring Cloud Alibaba直接引入spring-cloud-starter-alibaba-seata这个starter它会自动把Seata客户端相关依赖拉进来。版本号由Spring Cloud Alibaba的BOM统一管控。dependency groupIdcom.alibaba.cloud/groupId artifactIdspring-cloud-starter-alibaba-seata/artifactId /dependency但这里有个容易踩的坑Spring Cloud Alibaba的Seata版本和Seata Server版本可能存在不一致。比如Spring Cloud Alibaba 2021.0.5.0这个版本管理的Seata版本是1.5.2如果你部署的Seata Server是2.0.0那就要注意了——虽然一个小版本差异可能也能跑但保险起见建议显式覆盖Seata版本。显式覆盖的方式是单独引入seata-spring-boot-starter指定和Server一致的版本dependency groupIdio.seata/groupId artifactIdseata-spring-boot-starter/artifactId version2.0.0/version /dependency这里再次强调客户端SDK版本必须和服务端版本保持一致这是解决“can not connect to services server”报错最直接的手段。5.2 application.yml配置在微服务里配置Seata客户端主要分两部分注册中心配置和事务分组配置。spring: application: name: order-service cloud: nacos: discovery: server-addr: 127.0.0.1:8848 alibaba: seata: tx-service-group: my_test_tx_group seata: registry: type: nacos nacos: server-addr: 127.0.0.1:8848 namespace: group: DEFAULT_GROUP application: seata-server username: password: config: type: nacos nacos: server-addr: 127.0.0.1:8848 namespace: group: SEATA_GROUP >
返回列表