Docker开发
概述
Docker 应用运行在 TOS 7 Docker 引擎管理的容器中。
前提条件: Docker Engine 并非 TOS 7 预装组件。它作为应用在 TOS 应用中心提供,需要用户自行安装并启用。当用户安装基于 Docker 的应用时,平台会自动检测 Docker Engine,若缺失或未启用则提示安装 — 开发者无需额外操作。
核心要求:
- 必须提供兼容 Compose Spec 3.8+ 的
docker-compose.yml - 数据必须通过卷挂载持久化到 NAS 可访问目录
- 严格禁止使用特权模式
- 严格禁止占用系统核心端口(22、80、443、8181、5050)
包结构(.tar.gz 压缩包)
Docker 应用以 .tar.gz 归档包提交。归档包根目录下必须恰好包含以下文件:
<appid>.tar.gz
├── config.ini
├── <appid>.lang
├── <appid>.svg
└── docker-compose.yml
文件说明:
| 文件 | 必填 | 说明 |
|---|---|---|
config.ini | ✅ 是 | 应用元数据配置 |
<appid>.lang | ✅ 是 | 多语言文件(14 种语言) |
<appid>.svg | ✅ 是 | 应用图标(SVG 格式) |
docker-compose.yml | ✅ 是 | 容器编排配置 |
重要说明:
config.ini.icon字段必须指向/images/icons/<appid>.svg。安装时由平台完成映射。- 包名遵循第 4 章 · 包规范中"Release 资源命名规范"所定义的格式:
<appid>.tar.gz。- 带 UI 的 Docker 应用,其
docker-compose.yml必须包含x-app-meta段(见第 3 节)。- 不带 UI 的 Docker 应用无需包含
x-app-meta段。
docker-compose.yml 规范
version: "3.8"
services:
<appid>:
image: <registry>/<image>:<tag> # 镜像仅限 Docker Hub
container_name: <appid>
restart: unless-stopped
Volumes:
- /Volume*/DockerAppData/<appid>/config:/config
- /Volume*/DockerAppData/<appid>/data:/data
ports:
- "<宿主端口>:<容器端口>"
environment:
- TZ=Asia/Shanghai
user: "1000:1000"
x-app-meta:
web:
port: <宿主端口>
protocol: http
说明:
/Volume*/中的*仅为文档记法,代表用户安装时选择的卷号(例如 Volume1、Volume2)。平台不会展开它 —— 严禁将/Volume*/…写入 docker-compose.yml、systemd unit、生命周期脚本或任何会被机器读取的配置。
规则:
-
版本:必须兼容 Compose Spec 3.8 及以上
-
x-app-meta:对于有 UI 的 Docker 应用,
x-app-meta标签必须是docker-compose.yml文件中的顶层键,包含web.port(Web UI 端口号)和web.protocol(请求协议,通常为http)。x-app-meta:
web:
port: 8080
protocol: http -
注释:可以在
docker-compose.yml文件中添加注释。YAML 中注释以#开头,且#前必须有一个空格(如port: 8080 # Web UI 端口)。注释也可以单独占一行。 -
数据持久化:所有数据目录必须挂载到宿主路径。仅存储在容器内的数据会在容器删除时丢失。
宿主机侧卷路径写法(受支持的形式): 平台不会展开
/Volume*/。每个绑定挂载的宿主机侧必须使用下列写法之一;数据统一落在应用数据根目录/Volume<N>/DockerAppData/<appid>/下。Compose 写法 类型 宿主机侧含义 数据位置 ./data:/dataBind Mount 宿主机相对路径 /Volume1/DockerAppData/<appid>/data/data:/dataBind Mount 宿主机绝对路径 /Volume1/DockerAppData/<appid>/data/Volume1/app/data:/dataBind Mount 宿主机绝对路径 /Volume1/DockerAppData/<appid>/dataapp-data:/dataNamed Volume Docker 卷名称 — /dataAnonymous Volume 无 source,由 Docker 自动创建 — type: tmpfstmpfs 内存文件系统 — ⛔ 严禁在 docker-compose.yml、systemd unit、生命周期脚本或任何会被机器读取的配置中书写
/Volume*/…。 平台不解析*:会真的创建一个名为/Volume*的字面量目录,数据写到那里,而不是应用数据根目录。相对路径基准(relative-path base): 在
./data:/data中,.相对于包含docker-compose.yml文件的目录解析 —— 这是 Compose 的标准行为,即宿主机相对路径以 Compose 项目目录为基准解析。平台随后将解析出的目录放到应用数据根目录下,因此无论用户安装时选择哪个卷,同一个 compose 文件都能正常工作。 -
端口映射:
- 禁用端口:22、80、443、8181、5050(系统服务)
- 推荐范围:8000-19999
- 提交前确认所选端口在 TNAS 上未被占用
-
特权模式:严格禁止。必须使用
user字段指定 UID/GID。 -
时区:默认配置
TZ=Asia/Shanghai。用户可自行修改。请勿将时区留空 — 时间戳不一致会导致时间敏感应用数据损坏。 -
容器名称:可以自定义名称,只要它在 Docker 守护进程上全局唯一即可。名称重复会导致运行时错误。
-
重启策略:普通服务使用
unless-stopped -
网络模式:
network_mode: host严格禁止,仅系统级网络工具除外。系统级网络工具需在提交时明确说明理由,经审核同意后方可使用。普通应用一律禁止。使用宿主机网络模式破坏容器隔离,存在安全风险。改用端口映射:
ports:
- "8080:8080"
- 共享文件夹:Docker 应用不参与 TNAS 共享文件夹机制。与 Deb 应用不同,平台不会为 Docker 应用创建共享文件夹,它不得调用
ter_share_add或声明share_folders,且docker-compose.yml中不得出现共享文件夹挂载占位符(<shared_folder_path>)。所有持久化业务数据都保存在容器内,并必须通过卷挂载存储 —— 平台会将其解析到应用数据根目录/Volume<N>/DockerAppData/<appid>/。
镜像与安全要求
-
镜像来源(仅限 Docker Hub):所有 Docker 镜像必须来自 Docker Hub,非 Docker Hub 镜像直接驳回。 镜像必须托管在 Docker Hub(hub.docker.com)。目前不支持其他镜像仓库(如 ghcr.io、quay.io、自建私有仓库等)。
优先级 来源 示例 1(首选) Docker Hub 官方项目镜像 nginx、postgres2 Docker Hub 已验证发布者 带 Verified badge 的 Docker Hub 镜像 3 Docker Hub 知名社区镜像 linuxserver/jellyfin(100M+ 拉取量、活跃维护)❌ 驳回 非 Docker Hub 来源的镜像 私有仓库、ghcr.io、quay.io 等 ❌ 驳回 Docker Hub 上未经验证的个人镜像 拉取量少、无文档的 Docker Hub 镜像
强制要求: 镜像必须托管在 Docker Hub 上。审核时将验证镜像来源。使用非 Docker Hub 镜像将被直接驳回。 来自非 Docker Hub 来源或未经验证的 Docker Hub 镜像将在安全审核中被驳回。
- 镜像体积:使用多阶段构建或 Alpine 基础镜像以减小体积。
- 敏感信息:禁止在镜像或 compose 文件中硬编码密码、Token 或密钥。使用环境变量或
.env文件。 - 安全扫描:提交前运行
docker scan或trivy检查已知漏洞。 - 用户权限:**严格禁止使用 root 用户,严格禁止使用
--privileged特权模式。**必须通过user字段指定非 root 用户。
完整示例
应用概览:
- ID:
myapp-docker - 类型:Docker 应用
- 镜像:
linuxserver/myapp:latest - 端口:8080
- 依赖:DockerEngine
config.ini
{
"id": "myapp-docker",
"icon": "/images/icons/myapp-docker.svg",
"publisher": "开发者名称",
"path": "http://${ip}:8080",
"exec": true,
"open_path": true,
"resize": true,
"maxmin": true,
"width": 0,
"height": 0,
"help": "https://github.com/example/myapp/wiki",
"version": "1.0.0",
"recommend": false,
"beta": false,
"low_version": "TOS7.0",
"category": ["Utilities"],
"depend": ["DockerEngine"],
"relation": ["docker", "DockerEngine"],
"platform": "x86_64",
"official": "https://example.com",
"application_type": "docker",
"system_id": "",
"package": "",
"compose_project": "myapp-docker",
"user": "myapp",
"all_user_display": true,
"allow_open_in_mobile": false
}
docker-compose.yml
version: "3.8"
services:
myapp-docker:
image: linuxserver/myapp:1.0.0
container_name: myapp-docker
restart: unless-stopped
Volumes:
- /Volume*/DockerAppData/myapp-docker/config:/config
- /Volume*/DockerAppData/myapp-docker/data:/data
ports:
- "8080:8080"
environment:
- TZ=Asia/Shanghai
- PUID=1000
- PGID=1000
x-app-meta:
web:
port: 8080
protocol: http
说明:
/Volume*/中的*仅为文档记法,代表用户安装时选择的卷号(例如 Volume1、Volume2)。平台不会展开它 —— 严禁将/Volume*/…写入 docker-compose.yml、systemd unit、生命周期脚本或任何会被机器读取的配置。该应用为 WebUI 外部打开,因此path使用http://${ip}:<端口>格式。
多容器服务启动顺序: 对于具有多个服务的应用(如 Web + 数据库):
services:
app-db:
image: postgres:16
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 10s
timeout: 5s
retries: 5
app-web:
image: myapp:1.0.0
depends_on:
app-db:
condition: service_healthy
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
interval: 30s
timeout: 10s
retries: 3
- 使用
depends_on并设置condition: service_healthy确保正确的启动顺序 - 每个服务必须定义健康检查
- 平台验证:所有服务必须健康后应用才显示为"运行中"
健康检查失败处理:
- 连续 3 次健康检查失败后,容器标记为
unhealthy。 - 应用中心会将应用状态改为"未启用",需要用户手动点击启用。
- TOS 7 没有内置的 Docker 重启策略;如需重启策略,开发者必须在
docker-compose.yml中自行配置。 - 如果容器遇到运行时错误,用户可以通过容器管理应用查看错误日志。
数据备份、迁移和重置:
下表中的路径为平台解析后的实际落盘位置(见第 3 节规则 4 与第 6.4 节),<N> 为用户安装时选择的卷号。
| 操作 | 路径 | 方法 |
|---|---|---|
| 备份配置 | /Volume<N>/DockerAppData/<appid>/config | tar 或 rsync 备份 |
| 备份数据 | /Volume<N>/DockerAppData/<appid>/data | tar 或 rsync 备份 |
| 迁移 | 全部 /Volume<N>/DockerAppData/<appid>/ | 复制到新设备,相同路径 |
| 重置为默认 | 停止容器 → 删除 /Volume<N>/DockerAppData/<appid>/config → 重启 | 容器创建全新配置 |
| 完全删除数据 | 停止容器 → 删除 /Volume<N>/DockerAppData/<appid>/ | 所有数据永久删除 |
注意:配置和数据分开存储,支持独立备份/恢复。卸载时勾选"同时删除数据"会永久删除上述目录,执行前请先备份。