跳到主要内容

在现有服务旁增加新服务

假设 VPS 已运行一个 Discord Bot,现在想加入 RSSHub、个人 API 或网页。核心问题不是“Docker 能不能再跑一个容器”,而是怎样让每个服务的配置、生命周期、网络和数据边界保持清楚。

推荐:一项应用一个 Compose 项目

/srv/
├── discord-bot/
│ ├── compose.yaml
│ ├── .env # 不进 Git
│ └── data/
├── rsshub/
│ ├── compose.yaml
│ └── .env
├── infohub/
│ ├── compose.yaml
│ └── data/
└── caddy/
├── compose.yaml
└── Caddyfile

Compose 默认用目录名作为 project name,因此这些项目的容器、默认网络和资源名称会自然分组。也可以在 Compose 顶层明确写 name: discord-bot。独立项目的优点是:更新 Bot 不会顺手重建 RSSHub,日志和回滚范围也更清楚。

一个巨大的全局 compose.yaml并非永远错误,但会把不相关服务绑在同一生命周期里。单机个人服务器从独立项目开始,通常更容易理解。

先判断服务是否需要入站端口

Discord Gateway Bot 通常主动建立到 Discord 的持久 WebSocket 出站连接。仅使用 Gateway 的 Bot 一般不需要在 VPS 公网开放端口:

/srv/discord-bot/compose.yaml
name: discord-bot

services:
bot:
image: ghcr.io/example/discord-bot:<pinned-version>
restart: unless-stopped
env_file:
- .env
volumes:
- ./data:/app/data

如果 Bot 同时接收 HTTP interactions、webhook 或提供管理面板,它才可能需要入站 HTTP。此时优先让 Caddy 作为唯一公网入口,而不是给每个容器随意发布端口。

三种网络入口

服务类型建议入口示例
纯出站 worker/Bot不写 portsDiscord Gateway Bot、定时抓取器
仅管理员访问绑定 loopback 或 Tailscale127.0.0.1:3000:3000,或仅 tailnet
公网站点/APICaddy 反向代理,经 80/443不直接公开应用端口

需要跨 Compose 项目让 Caddy 访问应用时,可建立一个明确命名的外部 Docker 网络:

docker network create ingress

各项目声明使用它:

networks:
ingress:
external: true

只把需要被代理的服务接入 ingress。数据库和缓存应留在各自项目的内部网络,不发布数据库端口到公网。

密钥与配置

Discord bot token 是高价值秘密:

  • 放在权限受限的 .env或更合适的 secrets 管理中;
  • .env加入 .gitignore,不要提交到仓库;
  • 不在 docker compose config、截图或日志分享中暴露展开后的值;
  • 怀疑泄露时在 Discord Developer Portal 轮换,而不是仅从文件删除。

示例:

cd /srv/discord-bot
touch .env
chmod 600 .env

笔记只写变量名,例如 DISCORD_TOKEN,不写真实值。

新增服务的固定流程

1. 盘点资源和冲突

docker ps --format 'table {{.Names}}\t{{.Status}}\t{{.Ports}}'
docker network ls
docker volume ls
ss -lntup
free -h
df -h /

确认端口未占用、内存和磁盘有余量,并知道新服务的数据位置。

2. 创建独立目录

mkdir -p /srv/new-service/data
cd /srv/new-service

3. 审查 Compose 的最终配置

docker compose config --quiet
docker compose config --services
docker compose config --images

docker compose config可能展开环境变量;不要把完整输出复制到公开渠道。

4. 拉取并启动

docker compose pull
docker compose up -d
docker compose ps
docker compose logs --tail 100

生产环境优先固定明确版本或 digest,不盲目依赖 latest。镜像更新可能包含不兼容变化。

5. 验证服务自己的健康

容器显示 running只说明主进程没退出。还要验证业务行为:Bot 是否成功连接 Gateway 并响应测试命令,HTTP 服务是否通过 Caddy 返回预期内容,数据是否写入预期卷。

6. 记录回滚点

更新前记录当前镜像版本和 Compose diff。回滚通常是恢复旧版本、再次 docker compose up -d,并恢复兼容的数据;如果迁移改变了数据库格式,仅改回镜像可能不够。

单独操作每个项目

cd /srv/discord-bot
docker compose ps
docker compose logs -f --tail 100
docker compose pull
docker compose up -d

cd /srv/rsshub
docker compose ps

也可在任意目录使用:

docker compose -f /srv/discord-bot/compose.yaml ps

不要在 /srv顶层随手运行 docker compose down,也不要用 docker system prune -a解决普通磁盘或部署问题;前者可能针对错误项目,后者会做大范围清理。

更新一个服务而不碰其他服务

cd /srv/discord-bot
docker compose pull bot
docker compose up -d --no-deps bot
docker compose ps
docker compose logs --tail 100 bot

这会只重建指定 service。依赖、数据库迁移和版本说明仍需逐项检查,--no-deps不是通用安全开关。

什么时候再考虑合并

只有两个组件共享同一发布周期、必须一起启动/回滚,并且由同一套配置拥有时,才适合放入同一个 Compose 项目。例如应用与它专用的 worker 可以同项目;Discord Bot 与完全独立的 RSSHub 通常不需要合并。

服务器逐渐增长时,清晰的边界比少几个 YAML 文件更省事。容器很多并不可怕,没人知道哪个命令会重启谁才可怕。