恒美微站
首页
关于我们
建站服务
主题模板
案例展示
资讯中心
联系我们
Nacos 3.1.1 Docker Compose部署:注册中心容器化避坑指南
首页
资讯中心
/
Nacos 3.1.1 Docker Compose部署:注册中心容器化避坑指南
Nacos 3.1.1 Docker Compose部署:注册中心容器化避坑指南
发布时间:2026/10/11 12:52:47
这周我把团队内部最常用的注册中心从裸机进程切换到了容器方案核心动作就是用Docker部署了一套Nacos server v3.1.1。整个过程比预想中顺利但也不是没踩坑。如果你正准备上手Nacos或者已经用裸机跑过一段时间、想换个更干净的环境这篇记录应该能帮你省不少事。我会把部署前后所有关键环节摊开讲为什么用Docker、数据库怎么准备、编排文件怎么写、启动后如何验证以及我实际踩过的几个坑和完整的排查思路。这套方案的适用对象很明确微服务架构下需要快速搭建注册中心和配置中心的开发者以及不想在宿主机上装一堆JDK依赖、搞乱环境的人。整个部署方案最终由两个容器组成——Nacos Sever实例加一个MySQL实例通过Docker Compose统一编排数据和日志挂载到宿主机随时可以整套迁移。1. 为什么这次坚持用Docker部署Nacos 3.1.1裸机折腾出来的教训Nacos这个中间件本身不算复杂真正麻烦的是它的运行环境依赖和版本残留问题。我最初在服务器上直接用压缩包部署过2.x版本当时要手动装JDK、配JAVA_HOME、调启动脚本里的内存参数还要处理logs和data目录权限。本来十分钟能搞定的事经常被环境问题拖到半小时以上。后来换了一台机器要把环境重新建一遍发现完全不是解压拷贝那么简单——JVM参数、数据库连接配置、鉴权密钥散落在不同文件里迁移一次就像考古。1.1 裸机部署的隐性成本JDK、脚本、环境残留裸机部署最大的问题不是Nacos本身而是宿主机环境的不可控。举个例子如果服务器上同时跑着其他Java应用它们对JDK版本的要求可能和Nacos不一致。Nacos 2.x之后对JDK 8以上版本基本友好但如果你装了多个JDK启动脚本选错JAVA_HOME就会遇到莫名其妙的UnsupportedClassVersionError。这种问题你说它是配置问题也行说它是环境问题也行排查起来最烦人。还有启动脚本里的JVM参数默认的-Xms和-Xmx有时候并不适合小内存服务器。裸机部署时你只能手动改startup.sh改了之后还要记得自己改过什么。而容器化之后这些参数全部变成了环境变量写在编排文件里一眼就能看全换机器也只是把一份YAML文件带过去的问题。1.2 这套部署方案的整体形态Nacos和MySQL组成双容器这次我采用的不是官方镜像里那种内置Derby数据库的极简单机模式而是Nacos加外部MySQL的双容器方案。为什么不用内置Derby因为Derby是内嵌数据库数据跟着容器实例走一旦容器删了重建服务列表、配置历史全部清空。对于测试环境来说也许能接受但如果你拿它当联调环境的注册中心一次误删容器就能让所有人半天的工作白费。方案整体结构是这样的Nacos容器作为服务端负责注册发现、配置管理和控制台MySQL容器负责持久化数据包括用户、配置、服务实例、命名空间等信息。两个容器通过同一个Docker网络内网互通对外只暴露Nacos的端口。MySQL的数据目录挂载在宿主机上Nacos的日志和配置目录也做挂载这样无论容器怎么重建数据都在。1.3 动手前确认版本边界Docker、MySQL与Nacos的组合这里先敲定版本组合避免后面启动时互相不兼容。我这次验证的环境是Docker 24以上、Docker Compose v2、MySQL 8.0.x、Nacos镜像tag为v3.1.1。Nacos 3.x版本在模块划分和配置模型上有调整对MySQL 8.x的支持比较完善直接用8.0镜像没有问题。需要注意的一点是如果你手头还有跑着5.7的老库迁移时SQL脚本和字符集处理要额外小心5.7和8.0在部分语法上有差异。还有一个容易忽略的点拉镜像前先确认服务器的docker compose插件版本别用老掉牙的docker-composev1。虽然命令差别不大但v2在参数解析上更严格某些缩略写法可能直接报错。我自己就遇到过compose文件在本地正常、上传到服务器后启动失败的情况最后发现是服务器上的compose插件版本太老对depends_on条件写法不识别。2. 准备工作拉镜像、初始化数据库、规划网络与数据目录正式写编排文件之前有三件事一定要做确认镜像本身没问题、把MySQL数据库初始化好、把目录和网络先规划出来。这三件事如果跳过后面启动容器时大概率会手忙脚乱。2.1 镜像拉取与版本确认别只看tag先执行镜像拉取命令docker pull nacos/nacos-server:v3.1.1 docker pull mysql:8.0拉完之后建议用docker inspect确认一下镜像的环境变量和默认配置而不要直接用。因为Nacos官方镜像里有一些默认行为比如鉴权默认关闭、默认使用Derby等这些信息在Docker Hub页面能看到一部分但镜像内部的conf目录才是实际生效的配置来源。我习惯进入镜像看一眼再启动docker run --rm -it nacos/nacos-server:v3.1.1 bash ls /home/nacos/conf/这一步能帮你确认nacos-mysql.sql或者新版本的mysql-schema.sql实际叫什么名字。不同版本的脚本命名有差异直接把它复制到宿主机备用后面初始化数据库要用。2.2 初始化nacos_config数据库SQL脚本从哪来、怎么执行Nacos的配置、用户、服务实例元数据都存在数据库里所以首次部署必须先建库建表。脚本位置通常就在镜像内部的/home/nacos/conf/目录下。你可以在宿主机上创建一个init目录把脚本从容器里拷贝出来mkdir -p ~/nacos-docker/init docker cp 容器名:/home/nacos/conf/nacos-mysql.sql ~/nacos-docker/init/然后写一个简单的SQL初始化文件里面除了官方脚本内容再加上一行建库语句CREATE DATABASE IF NOT EXISTS nacos_config DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; USE nacos_config;文件命名为init.sql放到~/nacos-docker/init/下。到时候把它挂载到MySQL容器的/docker-entrypoint-initdb.d/目录MySQL首次启动时会自动按顺序执行。这里有个技巧MySQL官方镜像只在数据目录为空时才会自动执行初始化SQL所以你不需要单独跑到容器里手工导入但前提是第一次启动前就把挂载目录准备好。如果你之前已经启动过一次MySQL容器那这个初始化脚本不会再次执行得手动进去导库。2.3 创建专用Docker网络和挂载目录为重启和迁移留后路我建议先创建一个独立的Docker网络而不是直接用默认的bridge网络docker network create nacos-net好处有两个第一Nacos和MySQL之间可以通过容器名直接通信不管容器重启后IP怎么变mysql:3306这个地址始终有效第二后续如果你想再加一个集群节点或者加一个Nacos实例做集群模式都在同一个网络里网络互通不用重新配置省掉很多麻烦。目录规划方面建议按下面的结构来~/nacos-docker/ ├── compose.yaml ├── init/ # MySQL初始化脚本 ├── logs/ # Nacos日志目录挂载 ├── data/ # MySQL数据目录挂载 └── nacos-data/ # Nacos自身数据目录把数据目录和日志目录独立出来是这次部署最值得的一步。后面升级Nacos版本时只要数据库数据在、挂载目录在容器删了重建都无所谓服务注册信息不会丢。日志目录独立出来对排查问题也很重要docker logs只能看到控制台输出而Nacos很多关键报错写在/home/nacos/logs/下的文件里不挂载出来你只能进容器慢慢翻。3. 编写Docker Compose编排文件核心环境变量逐个讲清准备工作做完之后最重要的就是编写编排文件。我用的compose.yaml把Nacos和MySQL两个服务写在一起这样一条docker compose up -d就能把整套环境拉起来。下面这个版本是我实际在用的做了部分脱敏处理你可以直接参考。services: mysql: image: mysql:8.0 container_name: nacos-mysql environment: - MYSQL_ROOT_PASSWORDroot_strong_pwd - MYSQL_DATABASEnacos_config - MYSQL_USERnacos - MYSQL_PASSWORDchange_me_123 command: - --character-set-serverutf8mb4 - --collation-serverutf8mb4_unicode_ci - --default-time-zone08:00 ports: - 3306:3306 volumes: - ./data:/var/lib/mysql - ./init:/docker-entrypoint-initdb.d:ro networks: - nacos-net restart: always healthcheck: test: [CMD, mysqladmin, ping, -h, localhost, -pchange_me_123] interval: 5s timeout: 5s retries: 20 nacos: image: nacos/nacos-server:v3.1.1 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_PASSWORDchange_me_123 - MYSQL_SERVICE_DB_PARAMcharacterEncodingutf8connectTimeout1000socketTimeout3000autoReconnecttrueuseSSLfalseserverTimezoneAsia/ShanghaiallowPublicKeyRetrievaltrue - NACOS_AUTH_ENABLEtrue - NACOS_AUTH_TOKEN一定要超过32位长度且足够随机的密钥字符串 - NACOS_AUTH_IDENTITY_KEYserverIdentity - NACOS_AUTH_IDENTITY_VALUEsecurity_value_here - JVM_XMS512m - JVM_XMX512m - JVM_XMN256m ports: - 8848:8848 - 9848:9848 - 9849:9849 volumes: - ./logs:/home/nacos/logs - ./nacos-data:/home/nacos/data depends_on: mysql: condition: service_healthy networks: - nacos-net restart: always3.1 环境变量组模式、数据库、鉴权、JVM上述环境变量看似多其实可以分成四组理解。第一组是运行模式。MODEstandalone表示单机模式不启用集群投票。如果要做集群这里要改成cluster同时配置NACOS_SERVERS指定多个节点地址。这次先用单机把链路跑通集群方案后面可以再单独聊。第二组是数据源。SPRING_DATASOURCE_PLATFORMmysql告诉Nacos不要用内置Derby而是连接外部数据库。MYSQL_SERVICE_HOST填的是MySQL的容器名mysql不是127.0.0.1这一点很多第一次用Compose的人容易搞混。因为在容器网络内部Nacos访问MySQL是通过Docker网络用容器名解析最稳定。MYSQL_SERVICE_DB_PARAM里的serverTimezoneAsia/Shanghai和characterEncodingutf8属于必填项少了之后会出现配置内容中文乱码或者时间差八个小时的诡异问题。第三组是鉴权。NACOS_AUTH_ENABLEtrue表示开启控制台和接口鉴权。这个必须提一下Nacos默认是关闭鉴权的也就是任何人只要能访问8848端口就能直接查看你的注册服务列表、修改配置。出于安全考虑内部联调环境都建议开启。开启之后NACOS_AUTH_TOKEN是身份令牌长度至少32位NACOS_AUTH_IDENTITY_KEY和NACOS_AUTH_IDENTITY_VALUE是服务端之间的身份标识集群模式下节点之间通信要用。需要强调的是这几个值一经配置后续修改会导致旧token失效客户端需要重新登录。第四组是JVM。JVM_XMS、JVM_XMX都设成512mJVM_XMN设256m。这个尺寸在开发联调环境足够了默认镜像给的参数偏保守但在2GB小内存服务器上反而容易导致内存不足。后面我会单独讲这个坑。3.2 端口映射8848、9848、9849背后的端口演进端口映射是很多人容易漏掉的重点。如果只映射8848等你写完客户端代码一测会发现服务注册总是超时控制台也时而正常时而抽风。原因要从Nacos的端口体系说起。从2.x版本开始Nacos引入了gRPC作为客户端通信主通道端口不是独立的而是基于主端口偏移计算得到的。默认情况下主端口是8848gRPC主端口是主端口加1000即9848服务端之间用于集群通信的gRPC端口是加1001即9849。也就是说当你映射8848时实际上意味着9848也必须一起映射出来。这个设计很多文档里提得不够醒目导致实际部署时只放了8848就宣布完成。结果就是控制台能打开但客户端SDK通过8848拿到服务端地址后尝试连接9848时连不上最终报NO AVAILABLE SERVER。这个问题在Docker部署场景下尤其容易踩因为裸机部署时进程监听的是全部网卡不存在端口被挡的问题而Docker容器如果没有把9848映射出来宿主机防火墙放行再多端口也没用。下面这个表格能够比较清楚地说明端口分工端口协议用途说明8848HTTP控制台、客户端HTTP接口、健康检查9848gRPC客户端与服务端的注册、订阅、配置监听9849gRPC服务端节点间的集群通信单机模式通常不会用到但建议一并映射另外如果你在环境变量里通过NACOS_APPLICATION_PORT自定义了主端口那么gRPC端口仍然是“主端口1000”这两个值必须保持这种偏移关系。我建议不要在Docker部署时随意改主端口除非你有特别强烈的端口约束理由。3.3 启动容器与首次登录初始化等待、控制台验证确认编排文件没问题后执行启动命令docker compose up -d启动后不要急着访问控制台。MySQL容器首次启动要执行初始化SQLNacos启动时也要检查数据库表结构是否就绪前后大概需要30到60秒。我习惯先观察容器状态docker ps docker logs -f nacos-server日志里看到类似Tomcat started on port(s): 8848或者Nacos自己打印的启动完成日志再打开浏览器访问http://服务器IP:8848/nacos。首次登录的默认用户名和密码都是nacos。如果你在编排文件里开启了鉴权登录后第一件事就是去修改默认密码。还是那句话默认账号密码挂在公网上就等于不设防这个习惯必须养成。还有个细节值得注意如果你把MySQL容器的3306端口也暴露到宿主机了记得用防火墙或安全组规则限制来源IP别让数据库直接裸奔。理论上Nacos只需要在Docker内网访问MySQL把32706这种端口暴露出去不是必须的。如果只是为了本机用mysql客户端调试可以用127.0.0.1前缀或者干脆不映射端口。4. 跑起来之后的完整自检注册服务、下发配置、鉴权通关很多人部署完成的标准是“控制台能打开”我觉得这还远远不够。控制台能打开只代表HTTP服务起来了不代表注册发现链路和配置中心链路一定通。我有一次就是控制台一切正常结果客户端SDK死活连不上最后发现是服务端在日志里报了一个和数据库表字段有关的错误但控制台界面压根没暴露出来。所以部署完成后我会按下面几条路径完整过一遍。4.1 用健康检查接口判断Nacos是否就绪Nacos提供了两个健康检查接口分别是就绪检查readiness和存活检查liveness。针对这个版本我验证过下面的路径是有效的curl -X GET http://127.0.0.1:8848/nacos/v1/console/health/readiness curl -X GET http://127.0.0.1:8848/nacos/v1/console/health/liveness正常情况下返回{status:UP}。如果返回DOWN说明某个核心组件没有就绪常见原因包括数据库连接异常、权限校验问题等。这时候别急着重启容器先去翻日志。我还习惯把这两个接口集成到Docker的healthcheck里替代默认的健康检查命令。这样docker ps时就能直接看到容器是否健康而不是只看STATUS里的启动时间。4.2 注册一个虚拟服务并把它读取出来健康检查通过后我通常不等写完整客户端而是先用curl直接模拟服务注册。注册中心最核心的功能是服务实例注册与发现这一步通了客户端SDK的问题基本可以排除。关闭鉴权时的注册方式是curl -X POST http://127.0.0.1:8848/nacos/v1/ns/instance?serviceNameorder-serviceip172.18.0.2port9090如果开启了鉴权要先登录获取tokencurl -X POST http://127.0.0.1:8848/nacos/v1/auth/login \ -d usernamenacospassword修改后的密码登录接口会返回一个JSON里面有accessToken字段把它拼到后续请求里curl -X POST http://127.0.0.1:8848/nacos/v1/ns/instance?serviceNameorder-serviceip172.18.0.2port9090accessToken你的token注册完成之后再查询服务列表curl -X GET http://127.0.0.1:8848/nacos/v1/ns/instance/list?serviceNameorder-serviceaccessToken你的token返回的结果里能看到刚才注册的IP和端口说明注册发现链路是通的。这个自检动作用不了两分钟但能帮你把问题范围缩小到“Nacos本身”还是“客户端配置”上。4.3 发布与拉取一条配置验证配置中心的链路配置中心的验证更简单。Nacos原生提供了HTTP接口不需要起一个Spring Boot应用就能完成。发布一条配置curl -X POST http://127.0.0.1:8848/nacos/v1/cs/configs \ -d dataIdapp.yamlgroupDEFAULT_GROUPcontenttimeout: 5000然后拉取这条配置curl -X GET http://127.0.0.1:8848/nacos/v1/cs/configs?dataIdapp.yamlgroupDEFAULT_GROUPaccessToken你的token如果返回内容里包含timeout: 5000说明配置写入、存储、读取三个环节都是通的。这里我建议顺手测一下配置的MD5值变化。多次拉取同一个dataId响应的MD5应该保持稳定一旦变化说明客户端会感知到配置更新这对后续做动态配置是有意义的。也可以直接在控制台的“配置管理”页面里查看刚才发布的配置确认控制台与后端数据一致。4.4 开启鉴权后登录、token、控制台操作流程如果开启了鉴权上面的所有请求都要带上accessToken。这个token不是永久的默认有效期是18000秒也就是5小时过期后需要重新登录获取。客户端SDK接入时只需要在配置文件里设置用户名和密码SDK会自动完成登录并维护token所以这里真正要关注的是控制台和手工运维脚本。还有一点很容易被忽视开启鉴权后Nacos作为客户端去拉取其他服务配置时也需要在用户系统里创建对应的账号并授予权限而不是只让控制台管理员能登录。如果你在开发联调阶段不想搞太复杂的权限模型可以先创建一个只读用户给客户端用如果团队里有人需要修改配置再分配可写权限。权限模型这东西在部署阶段就顺手想清楚比后面上线前再补要轻松很多。5. 从我的实操记录里翻出的几个坑排错链路完整复盘再好的部署文档也替代不了真实环境里遇到的问题。这一节我把这次部署过程中实际碰到的四个问题完整复盘一遍重点是排查的思路而不只是最后的解决办法。5.1 MySQL 8.0认证插件引发的启动崩溃第一次启动后Nacos容器反复重启日志里报数据库连接失败关键错误信息是Access denied for user nacos...。当时第一反应是用户名密码写错了反复核对编排文件发现密码完全一致。后来才意识到问题出在MySQL 8.0默认的认证插件上。MySQL 8.0默认使用caching_sha2_password认证插件而Nacos里面使用的数据库驱动版本如果没有显式配置allowPublicKeyRetrievaltrue就可能出现公钥检索失败的情况。解决方式就是在MYSQL_SERVICE_DB_PARAM里加上allowPublicKeyRetrievaltrue。这个参数在本地客户端连接时可能不报错因为很多客户端默认允许公钥检索但Nacos服务端连接时就会出问题。排查这个问题的完整链路比结果本身更有参考价值先看容器日志确认错误来自数据库连接再验证MySQL容器本身是否能登录接着用docker exec进入Nacos容器检查环境变量里的MYSQL_SERVICE_PASSWORD是否真的被注入最后才怀疑到认证插件层面。我发现很多时候环境变量已经被注入了只是日志里的报错被翻译成了“密码错误”导致排查方向跑偏。5.2 容器状态正常但控制台页面一直打不开第二个问题更隐蔽。容器显示Up状态日志也没有报错但浏览器访问8848端口始终超时。用docker exec在容器内部执行curl http://127.0.0.1:8848/nacos是通的说明Nacos本身没问题问题出在宿主机访问路径上。顺着“容器能访问宿主机不能访问”这个思路先查端口映射docker port nacos-server输出显示8848确实映射了。接着想到防火墙。很多云服务器的安全组规则和本地iptables策略是两层都要放的。如果安全组放行了8848但本地firewalld没有放行或者反过来都会出现“只有容器组内能访问”的诡异情况。最终的解决办法是把Docker的IP转发和宿主机端口放行都检查一遍。这个经验的通用价值在于当容器内部访问正常而外部访问异常时优先怀疑端口映射和防火墙不要在Nacos配置里反复折腾。另外一点访问地址注意用http://服务器IP:8848/nacos而不是http://localhost:8848/nacos如果你是远程通过SSH隧道访问还要确认隧道是否转发正常。5.3 Java客户端反复报NO AVAILABLE SERVER根因却在端口映射这是最好玩的一个坑。服务端一切正常控制台也正常我用Java的Nacos客户端写了一个Demo服务注册结果启动就报错核心信息一直是NO AVAILABLE SERVER。最初怀疑是客户端地址写错了改了多次server-addr都没用。后来抓包发现客户端连接8848确实成功但拿到服务器返回的真实地址之后紧接着发起gRPC连接9848端口却一直超时。看到这里才意识到服务器上只映射了8848端口9848端口压根没有映射。Nacos 2.x之后的客户端和服务器通信走的是gRPC而gRPC端口是从主端口偏移出来的需要手动映射。补上9848:9848顺便加上了9849并重启容器后问题立刻消失。这个坑想强调的是服务端看起来正常不代表客户端能用。你要同时知道两件事Nacos的端口约束关系以及你的客户端SDK用的是哪种通信方式。如果你用的客户端还是1.x老版本可能只走HTTP8848就够了。但现在的SDK都默认走gRPC端口偏移这个知识点基本避不开。5.4 JVM内存参数过小导致容器反复重启最后一个坑出现在一台只有1GB内存的云主机上。整套环境启动后Nacos容器每隔几分钟就挂一次日志里频繁出现OOM相关异常。一开始以为是容器内存限制问题改了mem_limit也不行后来才发现是Nacos启动脚本里的JVM参数不合适。官方镜像在低内存机器上默认的参数分配确实会出现问题。解决办法是在环境变量里显式设置JVM_XMS512m、JVM_XMX512m、JVM_XMN256m让堆内存尽量紧凑。如果你的服务器内存更小可以把Xmx继续调低但要记住Nacos本身比较吃内存低于256m基本跑不动频繁Full GC会让控制台变得异常卡顿。顺带提一个非常实用的排查技巧容器重启后旧日志会被新进程覆盖所以排查OOM时先看日志文件的时间戳再配合docker inspect查看容器的退出码和重启次数。如果重启次数一直在累加优先怀疑内存而不是启动命令内部错误。很多新手一看到容器重启就直接docker logs结果看到的是最后一次启动的日志完全找不到崩溃原因。最后再分享一个我这次部署过程中形成的小习惯。我会把注册服务、查询服务、发布配置、拉取配置这几个curl命令保存成一份verify_nacos.sh脚本每次在全新服务器上部署完直接执行一遍脚本五分钟内就能确认整条链路是否可用。脚本里还要加上登录获取token的逻辑因为现在鉴权默认是开启的手工复制token既麻烦又容易出错。这个习惯看着不起眼但在你要同时部署多套环境的时候能省下大量重复操作的时间。