跳到正文

Sub2API 部署实战:Fedora + Podman,把多个模型渠道整合成一个 API

约 8 分钟VibeCoding#API#Sub2API
目录起因:我不想再写一个中转脚本关于 Sub2API 的一个认知纠正架构第一步:装 Podman第二步:装 podman-compose(重点)路径 A:podman compose(原生插件)路径 B:podman-compose(Python 第三方包)podman compose vs podman-compose 的区别第三步:创建项目目录和配置文件创建 .env 文件创建 docker-compose.yml第四步:SELinux 处理(Fedora 必做)第五步:启动第六步:设置向导第七步:添加渠道第八步:验证踩坑记录坑 1:podman-compose restart 不重新加载配置坑 2:up -d 会重建容器,我的数据是不是丢了?坑 3:改了 compose 文件后,如何确认改动生效?坑 4:podman-compose 对 container_name 的处理运维速查开机自启内存占用总结

写于部署过程中,记录真实的踩坑与解决方案。


起因:我不想再写一个中转脚本

我有好几个不同模型服务商的 API key——DeepSeek、智谱、阿里通义、OpenAI……每个渠道接口格式、base URL 都不一样。想在自己的应用里用,就得针对每个渠道写一套调用代码,改个模型还要到处改。

所以我想:能不能把这些渠道整合成一个统一的 API 入口,前端只对接一个端点,内部自动分发?

查了一圈发现两个候选:

  • New API(QuantumNous/new-api):Go + PostgreSQL + Redis,需要搭三个组件,架构偏重
  • Sub2API(Wei-Shaw/sub2api):Go 单二进制 + PostgreSQL + Redis,但 Go 进程本身很轻

我的服务器是 2 核 2G 的 Fedora,New API 需要 PostgreSQL + Redis + Go 三者同时在跑,2G 内存实在局促。Sub2API 是 Go 二进制,内存占用小得多,2G 绰绰有余,所以最终选了 Sub2API。


关于 Sub2API 的一个认知纠正

一开始我以为 Sub2API 只支持境外模型(Claude、OpenAI、Gemini),国产渠道不行。后来发现这个判断完全是错的。

Sub2API 添加上游渠道时,只需要填 base URL + API key,它走的是标准的 OpenAI 兼容协议。只要是 OpenAI 格式的接口——DeepSeek、智谱、阿里通义、火山引擎,全都可以接。它不挑服务商,是一个通用的 API 网关。


架构

Sub2API 需要 PostgreSQL 15+ 和 Redis 7+ 两个依赖,通过容器来跑:

                     ┌──────────────┐
                     │  你的应用     │
                     └──────┬───────┘
                            │ OpenAI 格式
                            ▼
                  ┌─────────────────┐
                  │   Sub2API       │  统一入口
                  │   (端口 8080)   │  http://服务器IP:8080
                  └────────┬────────┘
                           │
        ┌──────────────────┼──────────────────┐
        ▼                  ▼                  ▼
   DeepSeek           智谱/通义          OpenAI/Claude

第一步:装 Podman

Fedora 自带 Podman,直接确认版本:

podman --version

第二步:装 podman-compose(重点)

这是最容易踩坑的一步。Podman 想用 Docker Compose 语法来管理多容器,有两条路:

路径 A:podman compose(原生插件)

通过 dnf 装 podman-plugins:

sudo dnf install -y podman-plugins
podman compose version   # 命令是 "podman compose",没有连字符

这是 Podman 官方内置的 compose 支持,是 Podman 子命令,性能更好,与 Podman 内核集成更紧密。

路径 B:podman-compose(Python 第三方包)

通过 pip 装:

pip install podman-compose
podman-compose --version   # 命令是 "podman-compose",有连字符

这是一个独立的 Python 程序,不是 Podman 的子命令。

我选的是路径 B(pip 安装),因为 dnf install podman-plugins 在我的系统上折腾了半天没装好。


podman compose vs podman-compose 的区别

虽然名字只差一个连字符,但它们是两个不同的东西:

对比项podman composepodman-compose
命令写法podman compose(子命令)podman-compose(独立命令)
安装方式dnf install podman-pluginspip install podman-compose
实现语言Go(Podman 原生)Python
本质Podman 内置功能第三方封装,调用 Podman 底层 API
性能更快,直接和 Podman 内核对接稍慢,经过 Python 中间层
功能覆盖原生支持所有 Podman 特性对 docker-compose.yml 语法兼容度高,但部分高级特性可能缺支持
SELinux 处理原生集成,自动处理可能需要手动设置 SELinux 上下文
container_name完全支持部分版本有兼容性差异
--compatibility 模式默认更"Podman 化"默认更"Docker 兼容"

最大的实际差异是命令写法。你看到教程写 docker compose up -d,换成 Podman 可能是 podman compose up -d(路径 A)或 podman-compose up -d(路径 B)。搜教程时经常被命令格式搞混。

如果你用 pip 装的,记住你的命令是 podman-compose(带连字符),不是 podman compose。


第三步:创建项目目录和配置文件

mkdir -p ~/sub2api-deploy && cd ~/sub2api-deploy
mkdir -p data postgres_data redis_data

创建 .env 文件

# 生成安全密钥
openssl rand -hex 32   # 输出一段随机字符串,复制下来

# 写入 .env
cat > .env << 'EOF'
POSTGRES_PASSWORD=你自己设的复杂密码
JWT_SECRET=刚才 openssl 生成的字符串
TOTP_ENCRYPTION_KEY=再用 openssl 生成一段
ADMIN_EMAIL=你的邮箱
ADMIN_PASSWORD=你的管理员密码
EOF

创建 docker-compose.yml

version: "3.9"

services:
  postgres:
    image: docker.io/postgres:15-alpine
    container_name: sub2api-postgres
    restart: always
    environment:
      POSTGRES_DB: sub2api
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
    ports:
      - "127.0.0.1:5432:5432"
    volumes:
      - ./postgres_data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres"]
      interval: 10s
      timeout: 5s
      retries: 5

  redis:
    image: docker.io/redis:7-alpine
    container_name: sub2api-redis
    restart: always
    ports:
      - "127.0.0.1:6379:6379"
    volumes:
      - ./redis_data:/data
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 10s
      timeout: 5s
      retries: 5

  sub2api:
    image: ghcr.io/wei-shaw/sub2api:latest
    container_name: sub2api
    restart: always
    ports:
      - "0.0.0.0:8080:8080"
    environment:
      - POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
      - JWT_SECRET=${JWT_SECRET}
      - TOTP_ENCRYPTION_KEY=${TOTP_ENCRYPTION_KEY}
      - ADMIN_EMAIL=${ADMIN_EMAIL}
      - ADMIN_PASSWORD=${ADMIN_PASSWORD}
      - RUN_MODE=simple
      - SIMPLE_MODE_CONFIRM=true
      - SECURITY_URL_ALLOWLIST_ALLOW_INSECURE_HTTP=false
    volumes:
      - ./data:/data
    depends_on:
      postgres:
        condition: service_healthy
      redis:
        condition: service_healthy

关于 RUN_MODE=simple:这是 Sub2API 的简易模式,隐藏 SaaS 计费功能,适合个人使用。如果后面想恢复标准模式(启用支付/计费),删掉 RUN_MODE=simple 和 SIMPLE_MODE_CONFIRM=true 这两行,然后 podman-compose up -d 重新拉起就行。


第四步:SELinux 处理(Fedora 必做)

Fedora 默认 SELinux 是 Enforcing 模式,容器挂载宿主机目录时会被拦截。先检查:

getenforce
# 如果输出 Enforcing,就需要处理

给三个数据目录打标签:

sudo semanage fcontext -a -t container_file_t "$PWD/postgres_data(/.*)?"
sudo restorecon -Rv $PWD/postgres_data
sudo semanage fcontext -a -t container_file_t "$PWD/redis_data(/.*)?"
sudo restorecon -Rv $PWD/redis_data
sudo semanage fcontext -a -t container_file_t "$PWD/data(/.*)?"
sudo restorecon -Rv $PWD/data

如果你用的是 podman-compose(pip 版),这一步尤其重要,因为它的 SELinux 自动处理能力不如原生 podman compose。


第五步:启动

# 记住你的命令带连字符
podman-compose up -d

# 查看状态
podman-compose ps

# 查看日志
podman-compose logs -f sub2api

第一次启动会拉取镜像,可能需要几分钟。


第六步:设置向导

浏览器访问 http://你的服务器IP:8080,进入设置向导:

  1. 数据库:主机 postgres,端口 5432,用户 postgres,密码填 .env 里设的
  2. Redis:主机 redis,端口 6379
  3. 管理员:.env 里的邮箱和密码会自动填入

完成向导后就进入后台了。


第七步:添加渠道

进入后台 → Accounts → Add Account,选 API Key 类型。

各大渠道的 base URL:

渠道Base URL
DeepSeekhttps://api.deepseek.com/v1
智谱https://open.bigmodel.cn/api/paas/v4
阿里通义https://dashscope.aliyuncs.com/compatible-mode/v1
火山引擎https://ark.cn-beijing.volces.com/api/v3
OpenAIhttps://api.openai.com/v1
Anthropichttps://api.anthropic.com

填好 base URL 和 API key,保存就行。多个渠道会自动做负载均衡和故障转移。


第八步:验证

curl http://你的服务器IP:8080/v1/chat/completions \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer 你在后台生成的API Key" \
  -d '{
    "model": "deepseek-chat",
    "messages": [{"role": "user", "content": "Hello"}]
  }'

能返回结果就说明整合成功了。


踩坑记录

坑 1:podman-compose restart 不重新加载配置

我发现改了 docker-compose.yml(比如删掉 RUN_MODE=simple),然后用 podman-compose restart sub2api,改动不生效。

原因是 restart 只是重启容器进程,不会重新读取 compose 文件。要加载新配置必须用 up -d:

# 错的:restart 只重启进程,不读新配置
podman-compose restart sub2api

# 对的:up -d 会重新加载 compose.yml 并重建容器
podman-compose up -d

坑 2:up -d 会重建容器,我的数据是不是丢了?

第一次看到 up -d 把容器重建了,以为数据库和配置全没了。其实数据还在——PostgreSQL 和 Redis 的数据都在本地目录里:

~/sub2api-deploy/postgres_data/   ← 数据库
~/sub2api-deploy/redis_data/       ← 缓存
~/sub2api-deploy/data/             ← 应用数据

容器重建 ≠ 数据重建。up -d 只会重建改了配置的容器,没改的 PostgreSQL 和 Redis 容器不会动,数据目录也不动。

真正会把数据清掉的命令是 podman-compose down -v(带 -v 会删卷),平时只 down 不会。

坑 3:改了 compose 文件后,如何确认改动生效?

# 确认环境变量
podman exec sub2api env | grep -i "RUN_MODE\|SIMPLE"
# 如果输出为空,说明 simple 模式已经关了

# 确认日志
podman-compose logs sub2api | grep -i "mode"

坑 4:podman-compose 对 container_name 的处理

podman-compose(pip 版)在启动带 container_name 的容器时,行为和原生 podman compose 有差异。如果看到容器名重复报错,检查一下是否有残留的旧容器:

podman ps -a --filter name=sub2api
# 有残留就删掉
podman rm -f sub2api sub2api-postgres sub2api-redis

运维速查

cd ~/sub2api-deploy

# 查看状态
podman-compose ps

# 查看日志
podman-compose logs -f sub2api

# 重新加载配置并重建容器(改 compose 后必须用这个)
podman-compose up -d

# 仅重启容器进程(不改配置时用)
podman-compose restart sub2api

# 停止(不删数据)
podman-compose down

# 升级
podman-compose pull
podman-compose up -d

# 备份
podman-compose down
tar czf sub2api-backup-$(date +%Y%m%d).tar.gz .
podman-compose up -d

开机自启

# 为每个容器生成 systemd 服务
for container in $(podman ps -a --format "{{.Names}}"); do
  podman generate systemd --name $container --new --files
done

sudo mv container-*.service /etc/systemd/system/
sudo systemctl daemon-reload
sudo systemctl enable --now container-sub2api-postgres
sudo systemctl enable --now container-sub2api-redis
sudo systemctl enable --now container-sub2api

内存占用

组件内存
Sub2API(Go 二进制)~50-100 MB
PostgreSQL 15 Alpine~200-400 MB
Redis 7 Alpine~50-100 MB
系统开销~200 MB
总计~500-800 MB

2G 内存完全够用,还有余量跑其他东西。


总结

Sub2API 的核心优势就是轻——Go 单二进制,2G 内存跑三个容器毫无压力。加上它对任意 OpenAI 兼容渠道的支持,把多个 API 渠道整合成一个端点这件事,确实省掉了自己写中转脚本的活儿。

唯一的小代价是需要搭 PostgreSQL + Redis 两个依赖,但用 Podman Compose 一行命令搞定,管理成本很低。