Skip to main content

最佳实践

应用目录布局

遵循一致的目录布局以确保可维护性和兼容性。所有非嵌入式应用(包括官方和第三方应用)均安装在存储卷上,路径为 /Volume*/@apps/<appid>/

标准目录布局:

/Volume*/@apps/<appid>/
├── <binary> # 应用可执行文件
├── config.ini # 应用配置文件
├── <appid>.lang # 语言文件
├── images/ # 图标资源
├── webui.bz2 # 前端页面压缩包(WebUI 应用)
├── nginx/ # Nginx 配置(外部打开应用)
├── init.d/ # Systemd 服务文件
├── data/ # 运行时数据(缓存、临时文件,可写)
└── logs/ # 应用日志

说明: /Volume*/ 中的 * 表示用户安装时选择的卷号(例如 Volume1、Volume2)。

数据存储建议:

  • 运行时数据/Volume*/@apps/<appid>/data/)— 应用生成的缓存、临时文件和运行时状态。此类数据可以安全地重新生成。
  • 日志/Volume*/@apps/<appid>/logs/)— 应用日志文件。确保配置日志轮转。
  • 用户业务数据 — 必须存储在 /Volume*/ 下的共享文件夹中(例如 /Volume*/<appid>/),以便用户通过 SMB/NFS 访问。

数据持久化

理解数据类型:

数据类型路径描述
运行时数据/Volume*/@apps/<appid>/data/缓存、临时文件、运行时状态(可重新生成)
用户数据/Volume*/<appid>/(共享文件夹)持久化业务数据(必须能在应用升级后保留)

Deb 应用:

  1. 运行时数据存储在 /Volume*/@apps/<appid>/data/
  2. 用户数据必须存储在应用创建的共享文件夹中:
    # 在 postinst 中 — 为用户数据创建共享文件夹
    ter_share_add -name <appid> -owner <appid>
  3. 为保持兼容性,应用可创建符号链接:
    ln -s /Volume*/<appid> /Volume*/@apps/<appid>/data
  4. 共享文件夹 /Volume*/<appid>/ 可供用户通过 SMB/NFS 访问

Docker 应用:

  1. 将配置和运行时数据挂载到 /Volume*/DockerAppData/<appid>/
    Volumes:
    - /Volume*/DockerAppData/<appid>/config:/config
    - /Volume*/DockerAppData/<appid>/cache:/cache
  2. 用户数据必须存储在共享文件夹中:
    Volumes:
    - /Volume*/<appid>:/data
  3. 禁止在容器文件系统中存储数据
  4. 配置和数据使用独立卷,以支持独立备份

说明: /Volume*/ 中的 * 表示用户安装时选择的卷号(例如 Volume1、Volume2)。

  • 运行时数据可以安全删除,不会丢失用户业务数据
  • 用户数据(共享文件夹)必须在应用升级前备份

日志

Deb 应用:

# 使用 systemd 日志(推荐)
# 服务的所有 stdout/stderr 自动被捕获
# 查看日志:journalctl -u <appid>

# 或写入文件
exec >> /Volume*/@apps/<appid>/logs/app.log 2>&1

Docker 应用:

services:
myapp:
logging:
driver: json-file
options:
max-size: "10m"
max-file: "3"

说明: 每个容器日志文件最大 10MB,保留 3 个文件,总日志大小上限为 30MB。

最佳实践:

  • 使用结构化日志(推荐 JSON 格式)
  • 每条日志包含时间戳、级别和上下文
  • 轮转日志防止磁盘耗尽
  • 禁止在日志中记录敏感信息(密码、Token、个人数据)

日志保留与清理:

日志类型最长保留时间清理方式
应用日志(文件)30 天Logrotate:每日轮转,保留 30 个文件
Systemd 日志平台管理通过 journald 限制自动管理
Docker 容器日志每文件 10MB,共 3 文件Docker 日志驱动配置

Logrotate 配置:

/Volume*/@apps/<appid>/logs/*.log {
daily
rotate 30
compress
delaycompress
missingok
notifempty
copytruncate
}

资源限制

资源Deb 应用(systemd)Docker 应用(compose)
内存MemoryMax=512Mmemory: 512M
CPUCPUQuota=200%cpus: '2.0'
文件描述符LimitNOFILE=65536N/A(容器级别)
进程数LimitNPROC=256N/A(容器级别)
磁盘N/A(使用配额)卷大小限制

指导原则:

  • 根据预期工作负载设置资源限制,而非最大可能用量
  • 在典型用量基础上预留 20-30% 的峰值缓冲
  • 在 README.md 中记录资源需求

健康检查

Deb 应用:

# 在 systemd 服务文件中
[Service]
StartLimitBurst=3
StartLimitIntervalSec=60

# 看门狗(如应用支持)
WatchdogSec=30

Docker 应用:

services:
myapp:
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 30s

升级与迁移

Deb 应用:

  1. postinst 中始终检查旧版本:
    if [ -n "$2" ]; then
    # 从 $2 升级 — 运行迁移
    /Volume*/@apps/<appid>/bin/migrate --from "$2"
    fi
  2. 升级过程中绝不删除用户数据
  3. 修改配置格式前先备份
  4. 迁移逻辑应可逆以支持回滚
  5. 建议用户将数据存储在 /Volume*/@apps/<appid>/data/ 内,或者 /Volume*/,确保升级后数据不丢失

Docker 应用:

  1. 使用入口脚本检测和迁移旧数据格式:
    #!/bin/bash
    if [ -f /config/version ]; then
    OLD_VERSION=$(cat /config/version)
    if [ "$OLD_VERSION" != "$NEW_VERSION" ]; then
    /app/migrate.sh "$OLD_VERSION" "$NEW_VERSION"
    fi
    fi
    echo "$NEW_VERSION" > /config/version
  2. 至少测试最近2个大版本的升级路径

安全加固

Deb 应用:

[Service]
# 丢弃所有能力,仅添加所需
AmbientCapabilities=CAP_NET_BIND_SERVICE
NoNewPrivileges=true

# 文件系统保护
ProtectSystem=strict
ProtectHome=true
ReadWritePaths=/Volume*/@apps/<appid>/data /Volume*/@apps/<appid>/logs

# 网络命名空间(可选)
# PrivateNetwork=true # 仅当不需要网络时

# 用户命名空间
# PrivateUsers=true

说明: 如果应用需要写入 /etc 下的配置文件,必须使用 ReadWritePaths 显式声明可写路径。

Docker 应用:

services:
myapp:
security_opt:
- no-new-privileges:true
cap_drop:
- ALL
cap_add:
- NET_BIND_SERVICE # 仅当需要绑定1024以下端口时
read_only: true
tmpfs:
- /tmp
- /run

必须(所有提交必须包含):

  • NoNewPrivileges=true
  • ProtectSystem=strict
  • ProtectHome=true
  • ReadWritePaths(仅显式路径)
  • 非 root User/Group

建议(强烈建议):

  • AmbientCapabilities(仅需的能力)
  • LimitNOFILELimitNPROC
  • PrivateTmp=true
  • PrivateDevices=true

可选(高级加固):

  • PrivateNetwork=true(仅在不需要网络时)
  • PrivateUsers=true
  • MemoryDenyWriteExecute=true

应用端口分配

规则:

  1. 优先在推荐范围 8000-19999 内选择端口(共 12000 个端口,大幅降低冲突概率)
  2. 若推荐范围端口被占用,可使用 49152-65535(动态端口范围) 作为备选。
  3. 选择前检查常用端口避免冲突,通过环境变量使端口可配置
  4. 在 README.md 中文档化端口使用

端口范围说明:

  • 8000-19999:为 TOS 7 应用推荐端口段,避开系统核心服务端口(如22/80/443/8181),且数量充足,可满足绝大多数应用的端口需求
  • 49152-65535:为 IANA 定义的动态/私有端口段,适合临时或备用场景使用

常用端口参考(避免使用):

端口应用
22SSH
80TOS Web(HTTP)
443TOS Web(HTTPS)
445SMB
3306MySQL
5050TOS 守护进程
5432PostgreSQL
6379Redis
8096Jellyfin
8181TOS Nginx
8443TOS HTTPS
9000Portainer
9090Prometheus