1. 为什么我最终把家里那台旧电脑改成了Docker自托管服务器
三年前我还在用一台群晖DS218+,两盘位,ARM架构,跑个下载器加个相册备份就快撑爆了。后来陆续试过玩客云刷机做NAS、OESPlus刷飞牛NAS、DIY NAS自己攒机器,折腾了一圈才想明白一件事:硬件只是壳,真正决定一台NAS好不好用的,是上面跑的那些容器化应用。Docker这个东西,说白了就是把每个应用装进一个独立的“小盒子”里,盒子之间互不干扰,删掉一个盒子不会影响其他盒子,迁移的时候把盒子打包带走就行。这个特性放在NAS场景下简直是天作之合——你不需要为了装一个青龙面板去污染系统环境,也不需要为了跑一个Redis主从而手动编译一堆依赖。
这篇内容我打算把自己这两年实际部署过、长期跑着没出过大问题的Docker项目梳理一遍。不是那种列个清单就完事的推荐,而是把每个项目的核心用途、部署要点、资源占用、踩过的坑都讲清楚。适合手里有群晖NAS、飞牛NAS、绿联NAS或者自己DIY的x86小主机,想从“能用”进阶到“好用”的朋友。如果你刚接触Docker,连docker compose和docker run的区别还没搞明白,也没关系,我会在关键步骤上把参数含义拆开讲。如果你已经在跑十几个容器了,也可以看看有没有你还没试过的项目,或者对照一下我的配置思路有没有值得借鉴的地方。
先说一个我自己的原则:NAS上的Docker项目,稳定性和数据安全永远排在功能丰富度前面。一个三天两头崩溃的容器,哪怕功能再花哨,也不适合放在7x24小时运行的家庭服务器上。所以下面提到的每一个项目,我都至少连续跑过三个月以上,有的已经跑了一年多。
2. 部署前的底层准备:Docker环境怎么装才不给自己挖坑
2.1 不同NAS系统的Docker安装路径差异
群晖NAS用户最省事,套件中心里直接搜“Container Manager”(老版本叫Docker),点安装就行。但这里有个细节很多人忽略:群晖的Docker默认存储路径在/volume1/@docker,如果你把容器数据卷映射到/volume1下面的其他共享文件夹,记得在“控制面板-共享文件夹”里给对应文件夹开权限,否则容器启动时会报Permission denied。我当初跑MySQL的时候就被这个坑了半小时,日志里一直提示无法写入数据目录,最后发现是文件夹权限没给对。
飞牛NAS和绿联NAS的Docker安装更接近原生Linux体验,直接在应用中心安装即可,存储路径一般可以自定义。DIY的x86小主机跑Ubuntu或者Debian的话,用官方脚本安装最稳妥:
curl -fsSL https://get.docker.com | sh装完之后记得把当前用户加入docker组,不然每次敲docker命令都要加sudo:
sudo usermod -aG docker $USER然后重新登录一次终端让组权限生效。这个步骤看起来简单,但我见过太多人装完Docker之后一直用sudo硬扛,时间长了容易在脚本里埋下权限混乱的隐患。
2.2 Docker Compose:NAS部署的标配工具
如果你还在用docker run一行一行敲参数,我强烈建议切换到docker compose。原因很简单:NAS上的容器往往需要映射多个端口、挂载多个数据卷、设置一堆环境变量,用命令行管理很快就会乱成一锅粥。Compose文件把所有这些配置写在一个YAML里,改配置就是改文本,重新部署就是一条docker compose up -d,迁移的时候把整个文件夹拷走就行。
群晖的Container Manager自带Compose支持,在“项目”里新建一个项目,把YAML粘贴进去就能跑。飞牛NAS和绿联NAS也都有类似的图形化Compose管理界面。DIY主机的话,手动安装compose插件:
sudo apt install docker-compose-plugin验证一下:
docker compose version能输出版本号就说明装好了。我自己的习惯是每个项目单独建一个文件夹,里面放一个docker-compose.yml和一个.env文件(用来存密码、端口这些变量),文件夹命名就用项目名,比如/opt/docker/qinglong、/opt/docker/redis。这样备份的时候直接打包/opt/docker整个目录,所有容器的配置和数据卷都在里面,换机器的时候解压就能恢复。
2.3 镜像加速与网络模式选择
国内拉取Docker Hub镜像的速度大家心里都有数,配置镜像加速器是第一步。在/etc/docker/daemon.json里加上:
{ "registry-mirrors": [ "https://docker.1ms.run", "https://docker.xuanyuan.me" ] }然后重启Docker服务:
sudo systemctl restart docker网络模式方面,NAS上的容器我一般用bridge模式,需要独立IP的才用host模式。bridge模式下容器之间通过Docker内部网络通信,端口映射到宿主机,安全性更好。host模式虽然省去了端口映射的麻烦,但容器直接占用宿主机端口,容易冲突,而且隔离性差。举个例子,我跑青龙面板的时候用的是bridge模式,映射5700端口;跑AdGuard Home的时候因为要做DNS服务,必须用host模式才能正常监听53端口。
注意:群晖NAS的Docker在bridge模式下,容器访问宿主机上的其他服务(比如NAS本身的文件共享)需要用宿主机的局域网IP,不能用127.0.0.1。这个细节在配置一些需要回调的容器时特别容易出错。
3. 我长期在跑的六个Docker项目及部署实录
3.1 青龙面板:自动化签到和脚本管理的首选
青龙面板是我最早部署的一批容器之一,主要用来跑各种签到脚本、定时任务。它的核心价值在于把零散的脚本统一管理起来,支持定时执行、日志查看、依赖安装,不用自己写crontab,也不用担心脚本之间的环境冲突。
部署配置如下:
version: '3' services: qinglong: image: whyour/qinglong:latest container_name: qinglong restart: unless-stopped ports: - "5700:5700" volumes: - ./data:/ql/data environment: - ENABLE_HANGUP=true - ENABLE_WEB_PANEL=true这里有几个参数值得展开说。restart: unless-stopped表示容器意外退出时自动重启,除非你手动停了它,这对NAS场景很重要,断电恢复后容器能自己起来。ENABLE_HANGUP=true开启任务挂起功能,某些脚本需要长时间运行的时候不会阻塞其他任务。数据卷映射到./data,所有脚本、日志、配置都在这个目录里,备份的时候直接打包带走。
青龙面板的依赖管理是个老生常谈的问题。Python脚本需要requests、pycryptodome这些库,Node.js脚本需要axios、crypto-js,装依赖的时候经常遇到版本冲突。我的经验是:优先用青龙面板自带的依赖管理界面安装,不要手动进容器敲pip install。面板安装的依赖会持久化到数据卷里,手动装的容器重建后就没了。如果面板安装失败,再考虑进容器手动处理,处理完记得把依赖列表导出备份。
实操心得:青龙面板的脚本来源要谨慎,不要随便跑来源不明的脚本。我一般只跑自己写的或者从可信仓库拉取的脚本,跑之前先看一遍代码逻辑,确认没有敏感操作再执行。
3.2 Redis主从:为其他容器提供稳定的缓存服务
Redis在NAS上的用途比想象中多。青龙面板用它做任务队列,一些Web应用用它做会话存储,甚至有些下载器用它做临时数据缓存。单节点Redis够用,但如果你追求更高的可用性,可以搭一个主从架构。
主节点配置:
version: '3' services: redis-master: image: redis:7-alpine container_name: redis-master restart: unless-stopped ports: - "6379:6379" volumes: - ./master-data:/data command: redis-server --appendonly yes --requirepass yourpassword从节点配置:
version: '3' services: redis-slave: image: redis:7-alpine container_name: redis-slave restart: unless-stopped ports: - "6380:6379" volumes: - ./slave-data:/data command: redis-server --appendonly yes --requirepass yourpassword --replicaof redis-master 6379 --masterauth yourpassword这里的关键点是--appendonly yes开启AOF持久化,保证Redis重启后数据不丢。--replicaof指定主节点地址,从节点会自动同步主节点的数据。密码方面,主从都要设置requirepass,从节点还要额外设置masterauth,否则同步会失败。
实测下来,这个主从架构在NAS上跑一年多了,没出现过同步中断的情况。资源占用方面,两个Redis实例加起来内存占用不到100MB,对NAS来说几乎无感。
3.3 MySQL 8.0:自托管应用的数据库底座
很多自托管应用需要MySQL作为后端数据库,比如Nextcloud、WordPress、一些记账工具。在NAS上跑MySQL,最关键的是数据卷映射和字符集配置。
version: '3' services: mysql: image: mysql:8.0 container_name: mysql restart: unless-stopped ports: - "3306:3306" volumes: - ./data:/var/lib/mysql - ./conf:/etc/mysql/conf.d environment: - MYSQL_ROOT_PASSWORD=yourrootpassword - MYSQL_DATABASE=yourdb - MYSQL_USER=youruser - MYSQL_PASSWORD=yourpassword command: --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ciutf8mb4字符集是必须的,不然存储emoji或者某些特殊字符会报错。数据卷映射到./data,这个目录会随着数据增长而变大,建议放在NAS的SSD缓存盘或者至少是RAID1的存储池上,避免单盘故障导致数据丢失。
注意:MySQL 8.0默认的认证插件是
caching_sha2_password,一些老版本的客户端可能连不上。如果遇到连接问题,可以在MySQL里执行ALTER USER 'youruser'@'%' IDENTIFIED WITH mysql_native_password BY 'yourpassword';来切换认证方式。
3.4 Ollama本地部署:在NAS上跑大模型的可行性分析
Ollama是这两年本地部署AI大模型最火的工具之一,一条命令就能拉取和运行各种开源模型。但我要泼一盆冷水:在NAS上跑Ollama,体验取决于你的硬件配置。ARM架构的群晖基本不用想,x86架构且带核显的机器可以跑小参数模型,比如qwen2:1.5b、llama3.2:3b,再大就力不从心了。
部署本身很简单:
version: '3' services: ollama: image: ollama/ollama:latest container_name: ollama restart: unless-stopped ports: - "11434:11434" volumes: - ./data:/root/.ollama deploy: resources: reservations: devices: - driver: nvidia count: all capabilities: [gpu]最后那段deploy配置是给有NVIDIA显卡的机器用的,没有独显的话删掉就行。模型文件会下载到./data目录,一个3B参数的模型大概2-3GB,7B参数的模型大概4-5GB,提前预留好空间。
实测在N100小主机上跑qwen2:1.5b,响应速度大概每秒5-8个token,用来做简单的文本总结、翻译够用,复杂推理就別为难它了。如果你只是想体验一下本地AI,可以从最小的模型开始试,觉得速度能接受再往上加参数。
3.5 AdGuard Home:全网去广告的DNS方案
AdGuard Home是我认为NAS上最值得部署的服务之一。它的原理是在DNS层面拦截广告域名,所有通过这台NAS上网的设备都能受益,不需要每台设备单独装插件。
version: '3' services: adguardhome: image: adguard/adguardhome:latest container_name: adguardhome restart: unless-stopped network_mode: host volumes: - ./work:/opt/adguardhome/work - ./conf:/opt/adguardhome/conf必须用network_mode: host,因为AdGuard Home需要监听53端口做DNS服务,bridge模式下端口映射会有问题。部署完成后访问http://NAS_IP:3000进行初始化设置,设置完成后管理端口会变成你指定的端口(我一般用8080)。
实操心得:AdGuard Home的上游DNS建议配置两个以上,比如
223.5.5.5和119.29.29.29,避免单个上游故障导致全网断网。另外,如果你的路由器支持自定义DNS,把路由器的DNS指向NAS的IP,这样所有设备自动生效,不用逐台设置。
3.6 青龙面板公益版与依赖管理进阶
青龙面板的公益版和官方版在功能上基本一致,主要区别在于一些内置的脚本仓库和依赖源。我两个版本都跑过,稳定性上没有明显差异。依赖管理方面,除了面板自带的安装功能,我还习惯在数据卷里放一个deps.sh脚本,记录所有手动安装的依赖,容器重建后一键恢复:
#!/bin/bash docker exec qinglong pip3 install requests pycryptodome docker exec qinglong npm install axios crypto-js这个脚本放在/opt/docker/qinglong/下面,每次重建容器后跑一遍就行。比在面板里一个个点安装快得多,也不容易漏。
4. 容器安全与镜像安全的实操建议
4.1 镜像来源审查:别什么镜像都往NAS上拉
Docker Hub上的镜像质量参差不齐,有些镜像几年没更新了,基础系统版本老旧,存在已知漏洞。我的做法是:优先选择官方镜像(library命名空间下的)或者有明确维护者的镜像,拉取之前看一眼镜像的更新时间和下载量。下载量低于10万、最近半年没更新的镜像,除非你非常确定它的用途,否则不要用。
另外,尽量用具体版本标签而不是latest。latest标签意味着每次拉取都可能拿到不同的版本,今天跑得好好的,明天重建容器可能就出问题了。比如MySQL就用mysql:8.0,Redis就用redis:7-alpine,明确版本号,心里有底。
4.2 容器权限最小化:能用非root就不用root
很多容器默认以root用户运行,这在NAS场景下是有风险的。如果容器被攻破,攻击者就拿到了宿主机的root权限。好在不少镜像支持通过user参数指定运行用户:
services: myapp: image: someimage:latest user: "1000:1000"1000:1000通常是NAS上第一个普通用户的UID和GID。指定之后容器以普通用户身份运行,即使出问题影响范围也有限。当然,不是所有容器都支持非root运行,有些需要写入系统目录的容器必须用root,这种就要靠其他手段来加固,比如限制网络访问、挂载只读文件系统等。
4.3 定期更新与备份策略
NAS上的容器不是部署完就一劳永逸了。我的做法是每个月检查一次镜像更新,用docker compose pull拉取新版本,然后docker compose up -d重建容器。重建之前先备份数据卷,万一新版本有问题可以快速回滚。
备份方面,我写了一个简单的脚本,每周日凌晨自动打包所有容器的数据卷:
#!/bin/bash BACKUP_DIR=/volume1/backup/docker DATE=$(date +%Y%m%d) for dir in /opt/docker/*/; do name=$(basename "$dir") tar -czf "$BACKUP_DIR/${name}_${DATE}.tar.gz" -C "$dir" . done这个脚本配合群晖的计划任务或者Linux的crontab,就能实现自动化备份。备份文件保留最近四周的,更早的自动删除,避免占满存储空间。
5. 常见问题排查与避坑经验汇总
5.1 容器启动失败排查思路
容器起不来,第一步永远是看日志:
docker logs container_name --tail 100日志里通常会告诉你具体原因。常见的几类问题:端口被占用(port is already allocated),换一个端口或者停掉占用端口的服务;数据卷权限不对(Permission denied),检查宿主机目录的权限和所有者;环境变量缺失(environment variable not set),对照文档补上。
群晖NAS上还有一个特有的问题:Docker服务偶尔会卡死,表现为所有容器都无响应。这时候在套件中心停用再启用Container Manager通常能恢复,但更稳妥的做法是SSH进去重启Docker服务:
sudo systemctl restart docker5.2 网络连接问题速查表
| 问题现象 | 可能原因 | 解决方法 |
|---|---|---|
| 容器内无法访问外网 | DNS配置错误 | 在daemon.json里配置dns |
| 容器之间无法通信 | 不在同一网络 | 创建自定义bridge网络 |
| 宿主机无法访问容器端口 | 端口未映射或防火墙拦截 | 检查ports配置和防火墙规则 |
| 容器访问宿主机服务失败 | 用了127.0.0.1 | 改用宿主机局域网IP |
5.3 资源占用优化技巧
NAS的CPU和内存资源有限,容器跑多了容易卡。我的优化经验:给每个容器设置资源限制,避免单个容器吃光所有资源:
services: myapp: image: someimage:latest deploy: resources: limits: cpus: '0.5' memory: 512Mcpus: '0.5'表示最多使用半个CPU核心,memory: 512M表示最多使用512MB内存。这两个限制根据应用的实际需求调整,比如青龙面板给1GB内存足够,MySQL给2GB,Ollama根据模型大小给4-8GB。
另外,定期清理不用的镜像和停止的容器:
docker system prune -a这个命令会删除所有未被使用的镜像、容器、网络和构建缓存。执行前确认没有正在运行的重要容器,避免误删。
5.4 数据安全最后一道防线
Docker的数据卷虽然方便,但不要把唯一的数据副本放在数据卷里。我的做法是:重要数据在NAS的RAID存储池里存一份,再通过Cloud Sync或者rsync同步到另一台设备或者外置硬盘上。Docker数据卷只是运行时的数据,真正的备份要在Docker之外做。
注意:群晖NAS的Docker数据卷默认在
/volume1/@docker,这个目录在File Station里是看不到的,需要通过SSH访问。备份的时候别漏了这个目录。
6. 关于NAS上跑Docker的一些个人体会
折腾了这么久,我最大的感受是:NAS上的Docker项目,少即是多。刚开始的时候什么都想装,青龙、Redis、MySQL、Ollama、AdGuard Home、Nextcloud、各种下载器,恨不得把NAS变成万能服务器。结果就是内存天天告警,CPU温度居高不下,系统响应越来越慢。后来砍掉了一半不常用的容器,只保留真正每天在用的那几个,NAS反而稳定多了。
另一个体会是,文档和社区比项目本身更重要。一个项目再强大,如果文档写得稀烂、社区没人维护,出了问题只能自己啃源码,那就不适合放在NAS上。我选择项目的标准很简单:GitHub上有清晰的README,Issues里有人回复,最近半年有更新。这三个条件满足,基本不会踩大坑。
最后分享一个小技巧:给每个容器在NAS的桌面上建一个书签,记录它的访问地址和管理端口。时间长了容器多了,很容易忘记哪个端口对应哪个服务。我是在群晖的Web Station里建了一个导航页,把所有自托管服务的入口都列在上面,点一下就能跳转,比翻笔记快多了。