Skip to main content
Version: TOS 7

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、生命周期脚本或任何会被机器读取的配置。

规则:

  1. 版本:必须兼容 Compose Spec 3.8 及以上

  2. 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
  3. 注释:可以在 docker-compose.yml 文件中添加注释。YAML 中注释以 # 开头,且 # 前必须有一个空格(如 port: 8080 # Web UI 端口)。注释也可以单独占一行。

  4. 数据持久化:所有数据目录必须挂载到宿主路径。仅存储在容器内的数据会在容器删除时丢失。

    宿主机侧卷路径写法(受支持的形式): 平台不会展开 /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>/data
    app-data:/dataNamed VolumeDocker 卷名称—
    /dataAnonymous Volume无 source,由 Docker 自动创建—
    type: tmpfstmpfs内存文件系统—

    ⛔ 严禁在 docker-compose.yml、systemd unit、生命周期脚本或任何会被机器读取的配置中书写 /Volume*/…。 平台不解析 *:会真的创建一个名为 /Volume* 的字面量目录,数据写到那里,而不是应用数据根目录。

    相对路径基准(relative-path base): 在 ./data:/data 中,. 相对于包含 docker-compose.yml 文件的目录解析 —— 这是 Compose 的标准行为,即宿主机相对路径以 Compose 项目目录为基准解析。平台随后将解析出的目录放到应用数据根目录下,因此无论用户安装时选择哪个卷,同一个 compose 文件都能正常工作。

  5. 端口映射:

    • 禁用端口:22、80、443、8181、5050(系统服务)
    • 推荐范围:8000-19999
    • 提交前确认所选端口在 TNAS 上未被占用
  6. 特权模式:严格禁止。必须使用 user 字段指定 UID/GID。

  7. 时区:默认配置 TZ=Asia/Shanghai。用户可自行修改。请勿将时区留空 — 时间戳不一致会导致时间敏感应用数据损坏。

  8. 容器名称:可以自定义名称,只要它在 Docker 守护进程上全局唯一即可。名称重复会导致运行时错误。

  9. 重启策略:普通服务使用 unless-stopped

  10. 网络模式:network_mode: host 严格禁止,仅系统级网络工具除外。系统级网络工具需在提交时明确说明理由,经审核同意后方可使用。普通应用一律禁止。使用宿主机网络模式破坏容器隔离,存在安全风险。改用端口映射:

ports:
- "8080:8080"
  1. 共享文件夹:Docker 应用不参与 TNAS 共享文件夹机制。与 Deb 应用不同,平台不会为 Docker 应用创建共享文件夹,它不得调用 ter_share_add 或声明 share_folders,且 docker-compose.yml 中不得出现共享文件夹挂载占位符(<shared_folder_path>)。所有持久化业务数据都保存在容器内,并必须通过卷挂载存储 —— 平台会将其解析到应用数据根目录 /Volume<N>/DockerAppData/<appid>/。

镜像与安全要求​

  1. 镜像来源(仅限 Docker Hub):所有 Docker 镜像必须来自 Docker Hub,非 Docker Hub 镜像直接驳回。 镜像必须托管在 Docker Hub(hub.docker.com)。目前不支持其他镜像仓库(如 ghcr.io、quay.io、自建私有仓库等)。

    优先级来源示例
    1(首选)Docker Hub 官方项目镜像nginx、postgres
    2Docker Hub 已验证发布者带 Verified badge 的 Docker Hub 镜像
    3Docker Hub 知名社区镜像linuxserver/jellyfin(100M+ 拉取量、活跃维护)
    ❌ 驳回非 Docker Hub 来源的镜像私有仓库、ghcr.io、quay.io 等
    ❌ 驳回Docker Hub 上未经验证的个人镜像拉取量少、无文档的 Docker Hub 镜像

强制要求: 镜像必须托管在 Docker Hub 上。审核时将验证镜像来源。使用非 Docker Hub 镜像将被直接驳回。 来自非 Docker Hub 来源或未经验证的 Docker Hub 镜像将在安全审核中被驳回。

  1. 镜像体积:使用多阶段构建或 Alpine 基础镜像以减小体积。
  2. 敏感信息:禁止在镜像或 compose 文件中硬编码密码、Token 或密钥。使用环境变量或 .env 文件。
  3. 安全扫描:提交前运行 docker scan 或 trivy 检查已知漏洞。
  4. 用户权限:**严格禁止使用 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>/configtar 或 rsync 备份
备份数据/Volume<N>/DockerAppData/<appid>/datatar 或 rsync 备份
迁移全部 /Volume<N>/DockerAppData/<appid>/复制到新设备,相同路径
重置为默认停止容器 → 删除 /Volume<N>/DockerAppData/<appid>/config → 重启容器创建全新配置
完全删除数据停止容器 → 删除 /Volume<N>/DockerAppData/<appid>/所有数据永久删除

注意:配置和数据分开存储,支持独立备份/恢复。卸载时勾选"同时删除数据"会永久删除上述目录,执行前请先备份。