简介:针对Kylin V10信创环境与arm64硬件架构定制的Nacos 2.4.3 Docker镜像包,面向需要在国产操作系统上部署微服务注册中心与配置中心的开发、运维工程师,可解决ARM服务器上Nacos安装配置复杂、兼容性难保障的问题。压缩包共23个文件,其中7个layer.tar为镜像分层数据,9个json文件包含manifest清单与镜像配置,7个VERSION文件记录各层元信息,整体大小215.64MB,便于离线导入与快速交付。已有277人学习下载,适用于信创项目及ARM云环境的私有化部署。使用该镜像包可直接通过docker load加载,省去在Kylin V10上手动构建镜像的繁琐过程,同时基于2.4.3稳定版本保证了服务发现与配置管理功能在arm64架构上的一致表现,有助于提升微服务运维效率并满足国产化安全合规要求。
1. 为什么我盯上 nacos 2.4.3 的 arm64 镜像:x64 镜像在 KylinV10 上直接翻车
在国产化服务器上部署注册中心,第一脚就踢到铁板。手里是一台飞腾/鲲鹏的 arm64 机器,系统是 KylinV10,想着 nacos 这种成熟中间件直接 docker pull 就完事,结果容器一启动就报 exec format error,连日志都没出来。折腾半天发现原因很简单:官方镜像默认是 x64 指令集,arm64 的 docker daemon 根本没法执行 x86 二进制。这台机器上又没法立刻换架构,只能找专门适配 arm64 的镜像包。这份 nacos-2.4.3 arm64 docker 镜像包解决的就是这个场景:离线环境、鲲鹏/飞腾/树莓派这类 arm64 机器,想跑一个干净、可复现的 nacos 注册中心。它适合所有被架构问题卡住的从业者,先确认 arm64 再动手,后面每一步都会顺很多。
2. 加载并运行 arm64 镜像包:从 load 到端口映射,细节决定成败
2.1 load 后先做三件事:确认架构、修正标签、清理残留
拿到nacos-2.4.3-arm64.tar.gz后,第一反应不是直接docker run,而是先确认包里的镜像到底是什么平台、什么标签。离线包在打包时可能保留着原来的仓库地址,也可能是从私有仓库导出的,不看清就启动,后面所有排查都会被镜像名带偏。
# 加载离线镜像包,-i 指定 tar 包路径 docker load -i nacos-2.4.3-arm64.tar.gz # 列出所有本地 nacos 镜像,确认 REPOSITORY 和 TAG docker images | grep nacos # 用 inspect 验证真实架构,Architecture 字段必须是 arm64 docker image inspect --format '{{.Architecture}} {{.Os}}' <镜像名>:<TAG>第一步docker load会把压缩包里的镜像层全部解压到 docker 本地目录。如果这台机器之前加载过同样镜像,docker 会复用已存在的层,输出里出现Loaded image和Already exists同时存在,这不代表出问题。第二步用grep nacos过滤,是为了防止本地已经有旧的 x64 镜像,避免run的时候选错对象。第三步的Architecture字段是硬标准:如果是amd64,说明这个包根本不是给 arm64 机器用的,直接删除镜像并重新获取;如果是arm64,则进入下一步。
检查完架构后,我习惯给镜像重新打一个短标签,因为离线包里的原始标签往往很长,比如registry.example.com/nacos/nacos-server:v2.4.3-arm64,每次docker run敲这么长一串很容易抄错。打标签用 IMAGE ID 最稳妥:
# 从 docker images 输出里抓 IMAGE ID,再打名为 nacos-arm64:2.4.3 的新标签 docker tag $(docker images | grep 'nacos.*2.4.3' | awk '{print $3}') nacos-arm64:2.4.3这里用$(...)命令替换自动获取 ID,前提是docker images | grep nacos里只有一行是 2.4.3。如果机器上同时存在其他 tag 的 nacos,建议手动执行docker images先看清楚,再手工把 IMAGE ID 填进去,不要靠 awk 赌输出顺序。打标签只是个软链接,同一个 IMAGE ID 可以有多个标签,不影响原始数据。
如果你担心镜像包在传输过程中被篡改,加载前可以校验压缩包的 SHA-256:
# 计算压缩包哈希,和发布方提供的哈希对比 sha256sum nacos-2.4.3-arm64.tar.gz这一步在涉密内网部署时尤其重要,多花十秒钟能避免加载到被替换过的坏包。对比一致后继续 load,不一致就重新下载。
2.2 单机模式启动参数:8848/9848/9849 一个都不能少
nacos 2.x 默认的 8848 端口是 HTTP 主端口,但客户端通信走的是 gRPC,端口基于主端口自动偏移。偏移规则是硬编码的:主端口 +1000 是客户端 gRPC 端口,就是 9848;+1001 是服务端通信端口,就是 9849。这三个端口在单机模式下都要映射,缺一个就会出现“web 控制台能登录,但服务注册一直超时”的中间态。
docker run -d \ --name nacos \ -p 8848:8848 \ -p 9848:9848 \ -p 9849:9849 \ -e MODE=standalone \ -e PREFER_HOST_MODE=hostname \ -e NACOS_AUTH_ENABLE=false \ nacos-arm64:2.4.3MODE=standalone表示单机模式,此时 nacos 会使用内置 derby 数据库,不需要外部 MySQL,适合第一次验证镜像包可用性。PREFER_HOST_MODE=hostname让 nacos 把自己注册时使用的地址设置为宿主机 hostname,配合全局配置能减少容器 IP 漂移带来的问题,如果你按自己环境已经配置好了域名解析,也可以不写。NACOS_AUTH_ENABLE=false是 2.4.3 的默认值,显式写出来是为了让读启动命令的人明确知道当前没开鉴权。
启动后,用两条命令确认状态:
# 跟踪日志,看到 "Nacos started successfully" 即为启动完成 docker logs -f nacos # 从宿主机测试 HTTP 端口是否响应 curl -I http://127.0.0.1:8848/nacoscurl -I返回 HTTP 头里的状态码如果是 200 或 302,说明 web 控制台已经就绪。这时用浏览器访问http://<服务器IP>:8848/nacos,默认账号密码nacos/nacos。进入后台后,左侧能看到“服务管理”和“配置管理”两个主菜单,说明镜像包运行正常。此时你可以先创建一个临时 namespace 测试写读,再决定是否接 MySQL。
如果容器启动后立刻退出,别急着怀疑镜像包,先执行docker logs nacos看输出。大部分 arm64 上的启动失败都和 JVM 内存计算有关,这个在后面的避坑章具体分析。
2.3 为什么不建议用 qemu 模拟 arm64:性能和稳定性都不值当
有人会想到,我在 arm64 机器上用docker run --platform linux/amd64跑 x64 镜像,让 docker 调用 qemu-user 做指令翻译,不也能跑起来吗?技术上确实能,但实际部署 nacos 时我不推荐这条路。
qemu 的二进制翻译会把每条 x86 指令翻译成 arm64 指令执行,Java 应用的方法调用、GC 扫描、反射调用都会经过翻译层,性能损耗至少 30%。nacos 作为注册中心,本质是一个高并发 IO 和心跳处理的系统,gRPC 长连接上每个包都要经过 qemu,CPU 会被拉高到接近满载。更麻烦的是,qemu 模拟下线程调度和系统调用的时序和原生环境不一样,我在模拟环境里见过容器内线程数异常增长、epoll 回调丢失导致客户端 registration 超时的诡异现象,这类问题根本没有好的日志排查入口。
这里给的 arm64 镜像包是原生二进制,不需要翻译层,CPU 指令直接执行,内存占用也少一块翻译缓存的开销。如果有人在 x64 开发机上试图加载这个 arm64 包,运行时会报exec format error,反过来也是一样。所以架构匹配是第一优先级,不要试图用模拟层绕过,生产环境绕不过去。
3. 接入 MySQL 8.4:建库脚本、连接串和配置分层
3.1 建库导表:docker cp 脚本 + utf8mb4 字符集
standalone 模式的 derby 数据库适合快速验证,但配置中心在生产环境必须接 MySQL。nacos 官方容器内自带建表脚本,路径是/home/nacos/conf/mysql-schema.sql。脚本里包含了配置、服务、权限、审计等核心表,比如config_info、config_history、service_name等。建库这一步必须用 utf8mb4,因为 nacos 配置内容可能包含中文、表情符号,旧版 utf8mb3 会报字符集转换错误。
# 在 MySQL 宿主机上执行,创建专用库 mysql -u root -p -e "CREATE DATABASE nacos_config DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;" # 从已运行的 nacos 容器里拷贝建表脚本到当前目录 docker cp nacos:/home/nacos/conf/mysql-schema.sql ./mysql-schema.sql # 使用 nacos 账号导入,前提是该账号具备建表权限 mysql -u nacos -p nacos_config < mysql-schema.sql # 验证表是否齐全,config_info 存在即可 mysql -u nacos -p nacos_config -e "show tables;"第一步建库命令在 MySQL 宿主机的 shell 里执行,不要进容器。第二步docker cp需要容器已经启动,如果你还没启动 nacos,可以先用docker create创建一个临时容器,再docker cp,用完删除。导入前确认nacos账号对nacos_config库有CREATE、ALTER、INSERT、UPDATE、DELETE、SELECT权限,否则导入会中断。导入后show tables的输出里应该有几十张表,包括config_info、config_info_aggr、tenant_info、users、roles等。如果只导入了部分表,启动时 schema 校验就会报错。
很多人在这个地方踩过坑:直接进入 nacos 容器执行mysql命令导入。实际上 nacos 基础镜像不一定装 mysql client,而且把宿主机的 MySQL 账号塞进容器会留下安全风险。正确做法是让宿主机上的 mysql client 连接远程库执行导入,容器只负责运行 nacos 进程。
3.2 连接参数拆解:caching_sha2_password 和时区
nacos 连接 MySQL 是通过环境变量驱动的,容器启动时会根据环境变量自动覆盖内部的application.properties。这种方式比手动改配置文件更符合 docker 部署习惯,升级镜像版本时不需要重新打补丁。关键参数如下表:
| 环境变量 | 作用 | 示例 |
|---|---|---|
SPRING_DATASOURCE_PLATFORM | 数据源类型,不写则默认 derby | mysql |
MYSQL_SERVICE_HOST | MySQL 地址,不能填容器内 localhost | 192.168.1.10 |
MYSQL_SERVICE_PORT | MySQL 端口 | 3306 |
MYSQL_SERVICE_DB_NAME | 数据库名 | nacos_config |
MYSQL_SERVICE_USER | 连接账号 | nacos |
MYSQL_SERVICE_PASSWORD | 连接密码 | Nacos@123 |
MYSQL_SERVICE_URL_PARAM | JDBC 连接的追加参数 | allowPublicKeyRetrieval=true&useSSL=false&... |
docker run -d \ --name nacos \ -p 8848:8848 \ -p 9848:9848 \ -p 9849:9849 \ -e MODE=standalone \ -e SPRING_DATASOURCE_PLATFORM=mysql \ -e MYSQL_SERVICE_HOST=192.168.1.10 \ -e MYSQL_SERVICE_PORT=3306 \ -e MYSQL_SERVICE_DB_NAME=nacos_config \ -e MYSQL_SERVICE_USER=nacos \ -e MYSQL_SERVICE_PASSWORD='Nacos@123' \ -e MYSQL_SERVICE_URL_PARAM="allowPublicKeyRetrieval=true&useSSL=false&serverTimezone=Asia/Shanghai" \ nacos-arm64:2.4.3注意密码里的特殊字符。如果密码包含@#等,整段值要用单引号包裹,防止 bash 解释。MYSQL_SERVICE_URL_PARAM中由于有&,bash 会把它当作后台执行符,所以整个参数必须用双引号包裹,这是一个非常隐蔽的 shell 陷阱。
为什么必须加allowPublicKeyRetrieval=true?因为 MySQL 8.4 默认账号使用caching_sha2_password认证,JDBC 驱动在非 SSL 连接下需要先从服务器获取 RSA 公钥来加密密码传输,驱动默认禁止这个行为,所以会报Public Key Retrieval is not allowed。加上这个参数就是显式放行。useSSL=false是因为内网环境多数没有给 MySQL 配证书,不开 SSL 能省掉 TLS 握手的开销和证书校验的报错。serverTimezone=Asia/Shanghai让 JDBC 用东八区解释 datetime 字段,避免控制台和数据库时间相差 8 小时。
启动后看日志,搜索关键字:
docker logs nacos | grep -E "mysql|MySQL|database"看到Connect to MySQL successful或类似的db init success输出,说明数据源已经建立。接着连到 MySQL 验证:
use nacos_config; select count(*) from config_info;能返回一个数字(可以是 0),就说明表可读。如果报Table 'nacos_config.config_info' doesn't exist,回头检查导入的脚本是不是导错了库。
3.3 配置分层在 MySQL 里的体现:namespace、group、dataId 对应哪张表
nacos 配置管理有 namespace、group、dataId 三层概念。很多人背过概念,但部署完 MySQL 后还是分不清。你可以在数据库里直接看到这三层的落盘位置,这比任何文档都直观。
tenant_info表存 namespace:默认 public 命名空间在表里并没有显式记录,而是以空字符串形式存在;只有通过控制台创建的 namespace 才会在tenant_info里插入一行,生成带namespace_id的记录。config_info表存配置正文:核心字段是data_id、group_id、tenant_id、content、md5。config_info_beta表是 beta 发布的配置,灰度发布时会写入这里。
-- 查看一条配置在数据库里的完整记录 select data_id, group_id, tenant_id, content, md5 from config_info where data_id = 'application.yml';这条 SQL 可以帮你确认客户端为什么拿不到配置。如果客户端指定了 namespacedev,那么它读取的是tenant_id = 'dev'的记录;而你通过控制台在 public 命名空间发布的配置,tenant_id是空字符串,两边就对不上。
我习惯在接入新环境时,先手动在数据库里插入一条测试配置,再通过客户端读取,验证整条链路。插入时gmt_create和gmt_modified要填当前时间,md5字段可以留空,nacos 启动后会自行计算。这样可以绕开控制台,直接确认 MySQL 权限和数据表结构是否正确。
4. 避坑:arm64 nacos 部署中我踩过的五个坑
4.1 现象:docker pull 报 no matching manifest for linux/arm64
在内网环境执行docker pull nacos/nacos-server:v2.4.3时,返回:
no matching manifest for linux/arm64 in the manifest list entries原因有两层。第一,部分镜像仓库的 tag 并没有构建 arm64 变体,manifest 列表里只有 linux/amd64。第二,内网镜像同步工具经常只同步了 amd64 的 manifest,导致仓库里缺失 arm64 的入口。docker 客户端拉镜像时会自动匹配当前机器的架构,匹配不到就直接报错。
解决思路:不要依赖在线拉取,直接用离线镜像包。加载后通过docker image inspect确认架构。如果必须在在线环境拉取,可以在拉取命令后面加--platform linux/arm64,前提是远端仓库确实存在这个平台的 manifest,否则加了也没用。我在内网遇到的情况是同一个 tag 在 Docker Hub 有 arm64,但同步到内网时只保留了 amd64,这时候离线包是唯一确定的路。
4.2 现象:容器启动即退出,日志显示 Cannot allocate memory
在 arm64 小主机上运行,容器一两秒就退出,docker logs nacos显示:
Exception in thread "main" java.lang.OutOfMemoryError: Cannot allocate memory原因:nacos 启动脚本会读取宿主机内存大小来自动计算 JVM 堆。如果宿主机只有 2G 内存,脚本算出来的最大堆可能是 1G,再加上 C2 编译器、metaspace、线程栈,容器在 cgroup 限制下直接内存超卖,malloc返回不了内存就崩了。这个现象在树莓派 4B、飞腾入门机型上特别常见。
解决:显式覆盖 JVM 参数,不要让脚本自动算。
docker run -d \ --name nacos \ -p 8848:8848 \ -p 9848:9848 \ -p 9849:9849 \ -e MODE=standalone \ -e JVM_XMS=128m \ -e JVM_XMX=256m \ -e JVM_XMN=128m \ -e JVM_MS=64m \ -e JVM_MMS=256m \ nacos-arm64:2.4.3其中JVM_XMS是初始堆,JVM_XMX是最大堆,JVM_XMN是新生代最大容量,JVM_MS是元空间初始值,JVM_MMS是元空间最大值。对配置中心来说,最大堆 256m 足够支撑日常几千条配置的读写。如果配置项超过一万条,再调到 512m,不要一开始就给 2G。改完之后重启,执行docker stats nacos观察内存占用,稳定在 200m 左右是正常的。
4.3 现象:客户端频繁重连,nginx 转发只开了 8848
服务客户端能正常注册,但每隔几秒就抛connection refused或者client reconnect报警。查了 nginx 和防火墙,只放行了 8848。
原因:nacos 2.x 的客户端注册和配置订阅默认走 gRPC,连接的端口是 8848+1000=9848。如果网络层只放行 8848,客户端向注册中心发起注册时虽然能从服务端拿到 9848 的地址,但 TCP 握手连不上,客户端就会陷入重连循环。这是 nacos 2.x 和 1.x 最核心的差异,很多从 1.x 迁移过来的人都踩过。
解决:确保 9848 和 9849 同时放通。单机模式下,客户端连 9848,服务端节点间心跳走 9849。如果前面挂了 nginx,要配置 TCP 层的 stream 转发,不能用 HTTP 代理:
# nginx.conf 中的 stream 块,注意不是在 http 块里 stream { upstream nacos_grpc { server 127.0.0.1:9848; } server { listen 9848; proxy_pass nacos_grpc; } }这段配置写在 nginx 的stream块中,而不是http块。gRPC 是长连接,HTTP 层的 proxy_pass 处理不了这种流量。配置好后,在客户端机器上测试端口连通性:
nc -vz <服务器IP> 9848返回succeeded就说明端口通了,再观察客户端日志,重连告警应该消失。
4.4 现象:MySQL 8.4 报 Public Key Retrieval is not allowed
nacos 启动日志里出现:
java.sql.SQLException: Public Key Retrieval is not allowed原因:MySQL 8.4 默认用户认证插件是caching_sha2_password,JDBC 驱动在非 SSL 连接下首次连接时需要获取服务器 RSA 公钥来加密密码传输,但驱动默认出于安全考虑禁止自动获取公钥,于是抛出这个异常。这不是 nacos 的问题,是 JDBC 和 MySQL 8 之间的兼容性约束。
解决:在MYSQL_SERVICE_URL_PARAM中加入allowPublicKeyRetrieval=true。顺手把useSSL=false&serverTimezone=Asia/Shanghai一起带上,避免这次过了下次又踩时区坑。完整的参数串如下:
allowPublicKeyRetrieval=true&useSSL=false&serverTimezone=Asia/Shanghai如果公司安全规范强制要求 SSL,那就不能关useSSL,需要给 MySQL 配置证书并在连接串里指定 trustStore,这是另一个话题。还有一个备选方案是把 nacos 账号改成mysql_native_password插件,但 MySQL 8.4 默认禁用了这个插件,需要先启用再改账号,操作复杂,不如在 JDBC 参数里放行公钥来得干净。我一般优先改 URL 参数,不动数据库账号。
4.5 现象:控制台时间是 UTC,和实际差 8 小时
进入 nacos 控制台,创建配置后看到“最近更新时间”显示凌晨 3 点,但本地时间是 11 点,整整差了 8 小时。
原因:基础镜像默认没有设置时区,容器内系统时间是 UTC,JVM 读取系统默认时区也变成 UTC,最终写入数据库的时间字段自然偏了 8 小时。
解决:启动容器时挂载宿主机时区文件,并设置 TZ 环境变量:
docker run -d \ --name nacos \ -v /etc/localtime:/etc/localtime:ro \ -e TZ=Asia/Shanghai \ ... 其他环境变量 ...在 KylinV10 上,宿主机的/etc/localtime已经是指向Asia/Shanghai的符号链接,挂载成只读文件即可。改完后重启容器,在容器内执行:
docker exec nacos date如果输出里带CST或+0800,说明时区已经生效。再到控制台刷新页面,更新时间就和本地时间一致了。这个坑不影响注册中心的通信,但影响配置变更的审计排查,值得在部署时一步修掉。
5. 上线前验证与进阶:健康检查、鉴权、集群化部署
启动只是开始,上线前至少验证三个点:健康检查接口能通、鉴权规则生效、节点能正确注册到集群。
健康检查用 nacos 自带的 readiness 接口。这个接口不走默认控制台路径,要带/v1:
curl http://localhost:8848/nacos/v1/console/health/readiness返回{"code":200,"message":"success"}才算就绪。启动过程中这里会返回 503,所以它能比日志更准确反映服务是否真正可用。我通常会把这条命令写进 docker 的--health-cmd参数里,让 docker daemon 自动做健康检查。
鉴权开关在 2.4.3 里默认关闭,生产环境必须打开。token 要求 base64 编码后的长度至少 32 字节,生成方式如下:
echo -n "nacos-arm64-$(openssl rand -hex 16)" | base64把输出填入启动参数:
-e NACOS_AUTH_ENABLE=true \ -e NACOS_AUTH_TOKEN=<生成的base64串> \ -e NACOS_AUTH_IDENTITY_KEY=serverIdentity \ -e NACOS_AUTH_IDENTITY_VALUE=nacos-arm64-cluster开鉴权后,控制台登录不再是默认的nacos/nacos,所有 Open API 请求都要带accessToken。注意 cluster 下所有节点的NACOS_AUTH_IDENTITY_KEY和NACOS_AUTH_IDENTITY_VALUE必须一致,否则节点间互相认证会失败。
集群部署时把MODE改成cluster,节点数建议至少三个且为奇数,nacos 2.4.3 内置 raft 协议选举 leader。除了 8848、9848、9849,集群节点之间还要开放 raft 选举使用的 7848 端口。为了隔离外部流量,我一般会给集群单独建一个 docker 网络:
docker network create nacos-net所有 nacos 容器加入同一网络后,容器之间可以通过容器名互相访问,不依赖宿主机 IP。节点配置中填的NACOS_SERVERS格式类似nacos1:8848,nacos2:8848,nacos3:8848,这里的名字就是网络里的容器名。
我的习惯是每次部署一个环境,先跑一个deploy.sh,把镜像架构检查、端口映射检查、MySQL 连通性检查三件事串成脚本,确认通过后线上问题至少少一半。从那以后,我在 arm64 服务器上部署 nacos 再也没有因为架构不匹配翻过车。希望这份经验能帮到你。
本文还有配套的精品资源,点击获取