ARM64 DSM 7.2 手动部署 Docker 引擎与容器管理界面

发布时间:2026/10/23 5:31:19
ARM64 DSM 7.2 手动部署 Docker 引擎与容器管理界面
DSM 7.2 的套件中心里翻不到 Container Manager这件事困扰了不少用 ARM64 机型的用户。我自己手上两台 ARM64 设备一台是早年买的四盘位、跑 Realtek 四核芯片的型号另一台是后来换的双盘位、内存只有 2GB 的入门款都在 DSM 7.2 上升级过系统。打开套件中心搜 Container Manager得到的只有一个空结果在 x86 机型上明明一键就能装好的东西到了 ARM64 上就变成了需要自己动手的活儿。这篇内容就把我前后折腾了三四轮、踩过 cgroup 未挂载、iptables nat 缺失、重启后守护进程失效这些坑之后最终稳定运行的完整流程写下来。这里说的“手动部署”包含两件事一是把 Docker 引擎dockerd containerd docker CLI在 ARM64 的 DSM 7.2 上跑起来二是补上日常管理所需的那层“可视化壳子”也就是类似 Container Manager 的界面能力。前者是硬骨头后者是可选项。适合谁看手里有 ARM64 机型、想在 NAS 上跑容器、又不甘心因为架构白名单被挡在门外的朋友命令行基础薄弱也没关系我把每条命令的意图都写清楚了照着做基本能通。1. 先搞清楚 ARM64 机型为什么没有官方 Container Manager1.1 套件白名单的判定逻辑与架构差异Container Manager 在套件包里对应的是一个 SPK 格式的安装包包内 INFO 文件里有一个arch字段写着这个包允许安装的 CPU 架构比如x86_64、avoton、apollolake这些代号。安装时系统会做一次比对当前设备的架构代号不在列表里套件中心直接不显示你手动上传 SPK 也会被拒绝安装。这不是“藏起来”的问题是包本身就没为 ARM64 编译过。为什么不编译我个人的判断是两条一是 Docker 引擎在高版本内核上对 cgroup v2、overlayfs 的依赖越来越重而 ARM64 机型的系统内核版本和模块裁剪程度各不相同维护一套能覆盖所有 ARM 芯片的二进制分发成本不低二是这些入门机型的物理内存普遍在 1GB 到 2GB 之间官方大概认为跑容器的体验撑不住索性收窄了支持范围。这个判断在实操里被反复验证内存确实是 ARM64 上跑容器最紧的资源比 CPU 更早成为瓶颈。关键点在于“没有官方套件”和“跑不了容器”是两件事。Docker 引擎本身是跨架构的官方为aarch64提供了静态编译的二进制包解压即用、不依赖发行版的包管理器。DSM 底子是 Linux只要内核特性够理念上就能跑起来。我实测下来4.x 系列内核的 ARM64 机型是可以正常拉起 dockerd 的只是需要手动补几样系统没默认加载的东西。1.2 三条可选路线的取舍分析动手之前先想清楚走哪条路能省掉大量返工。我试过三条路线做法优点缺点我的评价A. 改 SPK 强装解包官方或第三方 SPK改 INFO 的 arch 字段后重新打包安装有图形界面管理方便依赖包缺失、版本升级必挂、失败后残留难清不推荐作为首选B. 静态二进制裸部署下载 aarch64 版 docker 静态包手动落地并配置自启干净、可控、升级简单、不污染系统套件体系没有图形界面全靠命令行推荐作为基础层C. 社区打包 SPK用社区维护的 ARM64 容器套件包装完即用界面齐全来源需自行甄别随 DSM 大版本升级有兼容风险可选作为补充层我的最终方案是B C 的组合先用 B 把引擎稳稳落地保证命令行能跑、开机自启、网络正常等这套跑顺了再看要不要加 C 那层界面。为什么不反过来因为 C 方案出问题时你很难判断是套件包的问题、引擎的问题还是系统模块的问题。先把引擎这层跑通相当于建立了一个可验证的基线后面无论装什么界面出问题都能快速定位是不是引擎层的锅。这里还有个小取舍静态二进制不要放在系统分区。DSM 的/usr/local/bin名义上重启不清空但系统大版本更新、套件重装这类操作有可能重置该目录。我吃过一次亏一次系统更新后 docker 命令莫名其妙消失了。后来改成把全部文件放到存储卷上比如/volume1/docker/bin再在系统路径下建软链接稳定得多。存储卷不参与系统更新文件一直在。2. 部署前的硬性条件盘点2.1 内核能力与 cgroup 挂载状态自检先说最容易被跳过、但决定成败的一步先确认内核给不给面子。SSH 登录后提权到 rootDSM 7.x 的写法是sudo -i然后依次敲这几条uname -m # 期望输出 aarch64 uname -r # 内核版本常见 4.4.x / 5.10.x cat /proc/filesystems | grep -E overlay|cgroup mount | grep cgroup cat /proc/cgroupsuname -m出aarch64才说明架构对得上如果出的是armv7l那一类就需要换 32 位的二进制本文的包不适用。/proc/filesystems里能不能看到overlay直接决定了存储驱动的选型有overlay就用overlay2没有就只能退到vfs。vfs能用但每一层镜像都会完整拷贝一份文件空间占用和启动速度都很难看后面第 5 章会给出实测对比。cgroup 这块要注意Docker 需要 cgroup 来做资源限制和统计。有的 ARM64 机型默认只挂了cgroup根控制器没有挂memory和cpu子系统表现为docker stats里内存数据显示N/A--memory参数不生效。检查方法是看/proc/cgroups里各子系统的enabled列是不是 1以及mount输出里有没有/sys/fs/cgroup/memory。如果是没挂载的情况临时挂载可以这样处理重启失效正式做法见 2.2 节之后的启动脚本mkdir -p /sys/fs/cgroup/memory mount -t cgroup -o memory cgroup /sys/fs/cgroup/memory注意有些 ARM64 机型的系统内核在编译时就把memorycgroup 控制器关掉了这种情况下挂载会报错。遇到这种机器容器仍然能跑但没法做内存硬限制只能靠应用自身配置节制这点要提前有心理准备。顺带把 iptables 的 nat 表也验证一下端口映射全靠它lsmod | grep -E nf_nat|iptable_nat iptables -t nat -L -n | headiptables -t nat -L报Table does not exist是最典型的翻车现场说明内核模块没加载容器跑起来了但外部访问不进去。2.2 存储目录规划与资源预算目录规划这件事我建议一次做对别后面搬家。我用的结构是这样/volume1/docker/ # 数据根目录daemon 的>mkdir -p /volume1/docker/bin /volume1/docker/etc cd /volume1/docker/bin # 用 curl 或 wget 取回压缩包按实际版本号替换文件名 curl -L -o docker.tgz 静态包下载地址/docker-20.10.24.tgz tar xzvf docker.tgz cp docker/* /volume1/docker/bin/ rm -rf docker docker.tgz复制完之后确认一下三个核心可执行文件的权限和架构ls -l /volume1/docker/bin/{dockerd,containerd,docker} file /volume1/docker/bin/dockerd # 期望看到 ARM aarch64接下来在系统路径建软链接让docker命令可以在任何目录下直接调用ln -sf /volume1/docker/bin/docker /usr/local/bin/docker ln -sf /volume1/docker/bin/dockerd /usr/local/bin/dockerd ln -sf /volume1/docker/bin/containerd /usr/local/bin/containerd注意软链接只是方便调用守护进程实际启动时我建议用绝对路径指向/volume1/docker/bin/dockerd避免系统更新后/usr/local/bin被重置导致找不到命令。还有个细节容易漏containerd 的 socket 目录和 runc 的位置。静态包里带了runc但 dockerd 默认会在/usr/bin/runc找它。稳妥做法是同时建一个软链接或者在 daemon 配置里显式指定runtimes。我一开始没注意这条docker run报failed to create task: exec: runc: executable file not found找了半天才发现是路径问题。3.2 守护进程配置与开机自启的落地细节配置文件放在/volume1/docker/etc/daemon.json内容我用的这套{ data-root: /volume1/docker, exec-root: /volume1/docker/run, pidfile: /volume1/docker/run/docker.pid, storage-driver: overlay2, log-driver: json-file, log-opts: { max-size: 10m, max-file: 3 }, iptables: true, live-restore: true }逐条解释一下为什么这么配>#!/bin/bash # 加载必要内核模块失败不阻断 for m in overlay nf_nat iptable_nat nf_conntrack bridge br_netfilter; do modprobe $m 2/dev/null done # 开启转发 sysctl -w net.ipv4.ip_forward1 /dev/null 21 # 启动守护进程 /volume1/docker/bin/dockerd --config-file /volume1/docker/etc/daemon.json \ /volume1/docker/dockerd.log 21 chmod x之后先手动跑一次看日志有没有报错。开机自启我推荐用 DSM 自带的计划任务 → 触发的任务 → 开机启动把脚本路径填进去比往/usr/local/etc/rc.d塞脚本更省心也不会被系统更新覆盖。这个思路在 DSM 上通用各种自制服务都用它做自启。3.3 网络转发链路补全与验证引擎起来了不代表容器能被访问到。三道关卡依次验证第一关容器内部网络通不通。跑个最轻量的测试容器docker run --rm --platform linux/arm64 alpine ping -c 2 8.8.8.8能通说明 bridge 网络和 DNS 正常。第二关端口映射通不通。起一个带 web 服务的容器映射到宿主机端口docker run -d --name testweb -p 18080:80 --platform linux/arm64 某轻量web镜像 curl -I http://127.0.0.1:18080第三关局域网其他设备能不能访问。这一步最容易失败典型表现是本机 curl 正常、别的机器访问超时说明 nat 转发没生效。排查顺序sysctl net.ipv4.ip_forward # 必须是 1 iptables -t nat -L POSTROUTING -n # 看有没有 MASQUERADE 规则 iptables -t nat -L DOCKER -n # 看有没有端口映射规则如果 nat 表存在但规则是空的多半是 dockerd 启动时 iptables 模块还没加载完。解决办法是在启动脚本里把modprobe放在dockerd之前并在 daemon.json 里保留iptables: true。我第一版脚本把模块加载放在后台执行结果和 dockerd 抢跑十次里有一两次失败改成顺序执行后再没出现过。提示DSM 自带的防火墙如果开着记得给映射的端口放行否则前面全都通了最后一步还是会被拦。4. 让命令行之上有个可用的管理界面4.1 界面层的两种落地方式与风险控制命令行能跑之后日常管理换成图形界面会轻松很多看容器状态、读日志、改环境变量、一键重启不用每次翻文档敲命令。两条路一是社区维护的 ARM64 容器管理套件装完在 DSM 主菜单里出现图标体验接近原生的 Container Manager。挑这类包的时候我的经验是看三样有没有标明适配的 DSM 大版本和内核版本、更新频率如何、卸载时是否会清理残留。第三点特别重要我见过装完之后在系统里留下一堆服务的包想卸干净得手动翻好几个目录。二是自己在容器里跑一个通用水管面板然后把它当成唯一入口。这条路更“干净”一些因为它本质上只是个容器出问题删掉重来即可不会动系统层。启动命令docker run -d --name panel \ --restart unless-stopped \ -p 19000:9000 \ -v /var/run/docker.sock:/var/run/docker.sock \ -v /volume1/docker/appdata/panel:/data \ --platform linux/arm64 \ 面板镜像名注意挂载/var/run/docker.sock这一行它等于把宿主机的容器控制权交给了这个面板安全性上要自己权衡。家用内网环境问题不大但如果 NAS 上有对外暴露的服务建议给这个面板加上访问密码并且不要映射到公网。4.2 权限与会话维持的几个细节docker命令每次都要sudo很烦加个用户组能省事synogroup --add docker # DSM 特有的用户组管理 synogroup --member docker 你的用户名 chown root:docker /var/run/docker.sock chmod 660 /var/run/docker.sock改完要重新登录 SSH 会话才生效。这里有个坑/var/run/docker.sock是 dockerd 启动时创建的每次重启文件属主都会重置回 root:root。所以这条chown得写进启动脚本放在 dockerd 起来之后执行。我第一次配置完用得好好的重启一次又变成权限拒绝查了半天才反应过来。还有一点静态部署的引擎不会随 DSM 服务框架一起被管理也就是说系统状态页里看不到它、套件中心里也没有卸载入口。这不是缺陷是设计使然但你要自己维护一套操作习惯停服务用kill或docker system prune前先确认没在跑任务升级时先停容器再换二进制。我给自己写了个小脚本做启停和状态检查放在同一个目录下用起来比记命令舒服。5. 常见问题与排查技巧实录5.1 存储驱动选型与性能实测对比overlay2和vfs的差距不是一点点。我在同一台 2GB 内存的 ARM64 机型上做了组对照用的是同一个五层镜像跑同一个轻量 web 服务对比项overlay2vfs镜像占用磁盘约 210MB约 780MB首次启动耗时约 1.8 秒约 4.5 秒批量删除容器后空间回收立即释放需手动清理偶有残留内核要求需 overlay 模块无特殊要求数据只是我这台机器的参考值不同机型肯定有出入但量级上的差异是稳定的。所以只要/proc/filesystems里能看到overlay就一定优先用它。真正需要退到vfs的情况很少通常是内核裁剪得比较狠的早期机型。如果overlay模块没加载先试modprobe overlay。报module not found就去/lib/modules/$(uname -r)/下翻一下有没有overlay.ko有就insmod全路径加载。这两条都没有说明内核编译时就没带只能接受vfs。5.2 典型报错逐条拆解下面这几个是我实际撞过的按出现频率排序报错一failed to start daemon: error initializing graphdriver: driver not supported说明 daemon.json 里写的存储驱动内核不支持。解决先cat /proc/filesystems | grep overlay确认没有就改成storage-driver: vfs或者先加载模块再启动。报错二Cannot connect to the Docker daemon at unix:///var/run/docker.sock两种可能守护进程没起来或者 socket 路径不一致。先ps | grep dockerd看进程在不在不在就看/volume1/docker/dockerd.log的末尾几十行在的话检查 daemon.json 里有没有改过hosts配置导致 socket 换了位置。报错三iptables: No chain/target/match by that namenat 相关模块缺失。modprobe iptable_nat nf_nat后重启 dockerd。如果 modprobe 报错说明内核里压根没编这些模块那端口映射这条路基本走不通得考虑用 host 网络模式跑容器牺牲端口隔离换可用性。报错四no matching manifest for linux/arm64拉镜像时架构不匹配。说明该镜像只发布了 x86 版本。这时候有两种处理找 ARM64 的替代镜像首选或者加--platform linux/amd64走模拟层。后者能跑但性能极差我试过一个 x86 镜像在 ARM 上跑启动要一分多钟CPU 占用常驻 80% 以上体验上完全不可接受除非只是临时验证一下功能。报错五容器跑几分钟就 OOM 被杀ARM64 机型内存本来就紧加上没做 cgroup 内存限制时容器会无限索取。解决是给每个容器显式设上限在docker run里加--memory256m --memory-swap256m。注意--memory-swap要和--memory相等否则会触发额外的 swap 行为反而更慢。排查速查表整理如下现象最可能原因一条命令定位命令找不到 docker软链接丢失或路径错误ls -l /usr/local/bin/docker端口映射不生效nat 模块未加载iptables -t nat -L -ndocker stats内存显示 N/Acgroup memory 未挂载mount | grep cgroup磁盘被日志吃满未限制日志大小du -sh /volume1/docker/containers/*重启后容器全没了live-restore没开或自启脚本失效看任务计划执行记录拉镜像卡在Pulling fs layerDNS 或网络问题docker run --rm alpine nslookup 域名6. 实测心得ARM64 上跑容器的能力边界6.1 哪些服务适合哪些别硬上跑了小半年我对“什么能跑”有了比较清楚的判断。适合的轻量 DNS 过滤、小型 web 服务、定时任务类工具、文件同步客户端、下载工具的 web 前端。这些共同特点是内存占用在一两百 MB 以内、CPU 峰值短、对磁盘 IO 要求不高。我常驻跑四五个整体内存占用控制在 900MB 上下机器还算从容。不适合的带完整数据库的重型应用、需要转码的多媒体服务、跑编译或爬虫这类长时间高 CPU 的任务。我在 ARM64 上试过一个带内置数据库的笔记应用光启动就吃掉 600MB加上系统占用直接顶到天花板系统开始频繁回收页面SSH 都卡。这类需求老实换 x86 机型别在 ARM64 上死磕。还有一个隐性成本值得说ARM64 的镜像生态比 x86 窄不少。很多服务虽然发布了多架构镜像但 ARM64 版本更新滞后或者干脆只维护 x86。挑服务时先看镜像支持的架构列表能省掉大量试错时间。6.2 升级与回滚的稳妥做法静态部署的好处是升级简单坏处是没有版本管理全靠自己记。我的做法是在/volume1/docker/bin下留两个目录current和previous升级时先把旧二进制整体移到previous新的放current软链接指向current。出问题一条命令切回去比重新下载快得多。升级顺序也有讲究先停所有容器docker stop $(docker ps -q)再停守护进程然后换二进制最后重新执行启动脚本。跳过停容器这步可能出现容器还在跑但引擎换了版本runc 状态对不上得手动清理/volume1/docker/run下的 sock 和 pid 文件才能恢复。DSM 系统本身的大版本更新要格外小心。虽然我们刻意避开了系统分区但系统更新有可能会重置/usr/local/bin下的软链接甚至重建/var/run。更新完第一件事就是检查docker version还能不能用、任务计划里的自启脚本有没有被保留。我一般会在大版本更新前先把/volume1/docker整个目录做一次快照出问题能立刻还原比事后排查省事太多。这套方案跑到今天已经挺稳中间除了我自己手贱改配置导致过一次故障没有出现过自发性的问题。真正需要提醒的是心态ARM64 机型跑容器属于“能用但不是最舒服”的状态把它当成一个低功耗常驻小服务宿主预期放对体验就不会差。最后分享个自己一直在用的小习惯所有容器的--restart策略统一设成unless-stopped但在docker run之前先在/volume1/docker/appdata下给每个服务建好对应目录把配置、缓存、日志全部挂载出去。容器本身随时可以删了重建数据一份不丢升级、迁移、换机器都只是复制目录的事这才是容器化最省心的地方。