docker-compose 与多容器编排
为什么要用 compose
用向导建容器,一两个还轻松,五个以上就开始难受:每个容器的端口、目录、环境变量都散在向导里,改一处要点半天,换台 NAS 还得从头配一遍。docker-compose 把这些全部写进一个 YAML 文件,一个命令或者一次点击就能把整套服务拉起来,删掉重建也只要几秒。配置文件本身就是最好的文档,什么端口、什么目录、什么环境变量,全在明处。
DSM 7.2 的 Container Manager 把 compose 做进了图形界面,这就是「项目」标签页。它不需要你登录 SSH 敲命令,粘贴 YAML、点下一步、等拉取完成,一套多容器服务就跑起来了。习惯命令行的玩家照样可以 SSH 上去用 docker compose 命令管理,两者操作的是同一套项目。
一个典型场景:带数据库的应用。向导方式要分别建两个容器,还要手动建网络让它们互通;compose 方式则是把网络声明进文件,声明依赖关系,启动顺序自动处理。
项目标签页:三步起来一套服务
在「项目」标签页里点「新建」,向导只有三步:
- 填项目名称和保存路径。路径建议放在
/volume1/docker/项目名/下面,YAML 文件和容器配置集中管理。 - 来源选「创建 docker-compose.yml」,在编辑器里粘贴配置。
- 点下一步,Container Manager 会先校验 YAML 语法,再拉取镜像、创建容器、启动服务。
| 步骤 | 填什么 | 注意事项 |
|---|---|---|
| 常规 | 项目名称、路径 | 路径选 docker 共享文件夹下 |
| 来源 | 粘贴 compose 文件 | 可选本地已有文件 |
| 部署 | 自动校验并启动 | 超时失败看日志标签 |
项目建好之后,列表里会显示运行状态、容器数量和资源占用。停止、启动、重建都是一键操作,「删除」会清掉整套服务和网络,但映射到 NAS 的文件目录不会被动,数据还在原地。

一个可照抄的示例
下面是一套常见的应用加数据库组合,结构足够典型,换成别的应用照着改就行:
services:
app:
image: linuxserver/openspeedtest:latest
container_name: speedtest
restart: unless-stopped
ports:
- "3080:3000"
volumes:
- /volume1/docker/speedtest/config:/config
environment:
- TZ=Asia/Shanghai
- PUID=1026
- PGID=100
depends_on:
- db
db:
image: mariadb:10.11
container_name: speedtest-db
restart: unless-stopped
volumes:
- /volume1/docker/speedtest/db:/var/lib/mysql
environment:
- MYSQL_ROOT_PASSWORD=换成你自己的强密码
- TZ=Asia/Shanghai
照抄时要改三处:PUID 和 PGID 换成你 NAS 上实际用户的 ID,root 密码换成强密码,端口确认没被占用。depends_on 保证 app 在 db 之后启动。两个容器都在默认网络里,app 用主机名 db 就能连到数据库,这正是 compose 相比向导最大的便利。
常用字段速查
compose 的字段很多,日常 80% 的场景用下面这些就够:
| 字段 | 作用 | 示例 |
|---|---|---|
| image | 镜像名和版本 | mariadb:10.11 |
| ports | 本地端口比容器端口 | 「3080:3000」 |
| volumes | 本地路径比容器路径 | /volume1/docker/a:/config |
| environment | 环境变量 | TZ=Asia/Shanghai |
| restart | 重启策略 | unless-stopped |
| depends_on | 启动依赖 | 另一个服务名 |
| networks | 加入自定义网络 | 见下一节 |
| mem_limit | 内存上限 | 1024m |
写 YAML 最容易犯两个错:一是缩进,必须用空格不能用 Tab,层级错一个空格整个文件就报错;二是冒号后面要有空格,image:mariadb 是无效的。项目标签页的编辑器有语法校验,哪里错了会直接指出来,比命令行友好不少。
自定义网络与跨项目通信
默认情况下,每个项目有自己的网络,项目里的容器互通,跨项目则不行。如果两个项目里的容器需要互访,比如反代容器要转发到另一个项目的服务,可以建一个自定义网桥,让相关容器都加进来。
在「网络」标签页里新增一个网桥网络,名字比如叫 synobridge,然后在 compose 里引用:
services:
web:
image: nginx:latest
networks:
- synobridge
networks:
synobridge:
external: true
external: true 表示这个网络已经存在,不要新建。加好之后,两个项目里的容器就能用容器名互相访问。比起端口映射再绕回来,容器间直连既快又不占主机端口。对网络原理不太熟的读者,可以先补一下桥接和 NAT 的概念,参考 计算机网络基础。

写 compose 的三个技巧
技巧之一是把可变的东西放环境变量。数据库密码、域名、端口前缀这类值,可以单独放一个 .env 文件里,compose 用 ${变量名} 引用。好处是换环境只改一个文件,YAML 本身可以放心分享和备份,不会把密码泄出去。Container Manager 的项目编辑器支持这种写法,.env 和 docker-compose.yml 放同一目录即可。
技巧之二是加健康检查。给关键服务写 healthcheck,Docker 会周期性探活,挂掉的服务在容器列表里一眼就能看出来,而不是等到你自己访问才发现。网络类应用探一个 HTTP 端口、数据库探端口连通就行。
技巧之三善用 restart: unless-stopped。它和 always 的区别在于:always 在你手动停止后,重启 NAS 时仍会拉起来;unless-stopped 尊重你的手动停止。绝大多数自用场景后者更合适。
| 重启策略 | 手动停止后 | NAS 重启后 | 适用场景 |
|---|---|---|---|
| no | 不启动 | 不启动 | 临时任务 |
| always | 不启动 | 启动 | 核心常驻服务 |
| unless-stopped | 不启动 | 启动 | 多数自用场景 |
| on-failure | 不启动 | 仅异常退出时启动 | 一次性脚本 |
更新与重建
更新项目里的服务有两种情况。只改配置(端口、目录、环境变量),在项目详情里编辑 YAML 保存,Container Manager 会自动重建受影响的容器;要更新镜像版本,把 image 后面的 tag 改掉,保存后同样自动重建。latest 标签不会自动追新,停掉再启动项目也不会拉新镜像,必须改 tag 或者先在镜像标签页下载新版本。
重建过程中有个细节值得知道:项目删除容器时不会删除映射目录,但会删除未映射的数据以及容器本身的网络配置。所以重建前扫一眼 volumes 列表,确认该映射的都映射了。另外,数据库类服务重建前最好停掉项目再改,避免容器运行中文件被改坏。
还有一种常见需求是迁移:把整个 docker 目录打包复制到新 NAS,装好 Container Manager,用相同的路径新建项目,所有服务就回来了。前提是新 NAS 的卷编号相同、用户 ID 相同。卷编号不同要全局替换路径,用户 ID 不同要改 PUID——这也是上一篇权限篇强调统一目录结构的原因。
养成一个习惯:改配置前先把 YAML 复制到本地备份一份。项目配置文件就在项目路径下,重装系统后只要数据还在,把 docker 目录拷回去、重建项目,几分钟就能恢复全部服务。
延伸阅读
- Container Manager 上手:从镜像到容器:向导与标签页的基础操作
- 目录映射与权限:坑最多的地方:PUID、PGID 与目录归属的完整方案
