尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

NewAPI+Sub2API 手把手部署搭建教程:TaoToken 统一 Key 接入配置

NewAPI+Sub2API 手把手部署搭建教程:TaoToken 统一 Key 接入配置 1. 为什么要把 NewAPI 和 Sub2API 放在一起部署如果你手上有多个模型渠道又想让团队或自己的几个客户端统一走一个入口NewAPI 负责的是「统一网关 令牌分发 用量统计」Sub2API 负责的是「订阅式额度管理 用户套餐」两者拼起来才是一套完整的自用中转站。单独跑 NewAPI你能拿到一个 OpenAI 兼容的/v1/chat/completions端点单独跑 Sub2API你能做用户和套餐但缺少上游渠道聚合。合在一起前端用户拿 Sub2API 发的订阅 Key后端实际请求打到 NewAPI 聚合的渠道上链路才闭环。这篇教程面向的是需要统一管理多模型 API 通道的开发者交付物很具体一份可复制的docker-compose.yml、一份.env.secret骨架、TaoToken 统一 Key 的接入步骤以及部署完成后的连通性验证命令。整套环境我在 Ubuntu 24.04 上跑通过内存 2G 的机器也能起来关键是几个参数必须显式限制否则 MySQL 和 Postgres 会互相抢内存。先说清楚整体拓扑不然后面配置容易迷路。NewAPI 自带 MySQL 和 RedisSub2API 单独用 PostgreSQL但复用 NewAPI 的 Redis用 DB1 区分。两个应用都只监听本地回环公网暴露交给 cloudflared 隧道。这样做的原因是数据库端口一个都不对外攻击面小隧道重启会换临时域名所以要把域名写回 NewAPI 的ServerAddress令牌详情页才能生成完整调用链接。TaoToken 在这里的角色是上游统一 Key 提供方。你在 NewAPI 里新建渠道时把 TaoToken 的 API 地址和 Key 填进去就能把它的模型能力挂到自己的网关下再由 NewAPI 分发给 Sub2API 的用户。下面从环境准备开始一步步来。2. 环境准备与 TaoToken 前置配置2.1 系统确认与基础工具先确认系统版本命令输出里能看到VERSION_CODENAME就行cat /etc/os-release | head -5装基础工具包sudo apt-get update -y sudo apt-get install -y ca-certificates curl gnupg lsb-release vim2.2 安装 Docker 与 Docker Compose加 Docker 官方 GPG 密钥国内网络优先走清华镜像sudo install -m 0755 -d /etc/apt/keyrings sudo curl -fsSL --connect-timeout 8 https://mirrors.tuna.tsinghua.edu.cn/docker-ce/linux/ubuntu/gpg \ -o /etc/apt/keyrings/docker.asc sudo chmod ar /etc/apt/keyrings/docker.asc写 apt 源先探测清华镜像是否可达不通再回退官方CODENAME$(. /etc/os-release echo $VERSION_CODENAME) ARCH$(dpkg --print-architecture) REPOhttps://mirrors.tuna.tsinghua.edu.cn/docker-ce/linux/ubuntu curl -fsSI --connect-timeout 5 $REPO/dists/$CODENAME/Release /dev/null \ || REPOhttps://download.docker.com/linux/ubuntu echo deb [arch$ARCH signed-by/etc/apt/keyrings/docker.asc] $REPO $CODENAME stable \ | sudo tee /etc/apt/sources.list.d/docker.list sudo apt-get update -y安装 Docker 组件sudo apt-get install -y docker-ce docker-ce-cli containerd.io \ docker-buildx-plugin docker-compose-plugin sudo systemctl enable --now docker sudo usermod -aG docker $USER验证docker --version docker compose version注意usermod -aG docker加组之后要重新登录 SSH 才不用 sudo 跑 docker。本教程后面命令仍带sudo兼容。配置镜像加速同时限制容器日志大小避免长跑把磁盘写满sudo tee /etc/docker/daemon.json /dev/null EOF { registry-mirrors: [ https://docker.m.daocloud.io, https://dockerproxy.com, https://docker.1panel.live ], log-driver: json-file, log-opts: { max-size: 10m, max-file: 3 } } EOF sudo systemctl restart docker docker info | grep -A3 Registry Mirrorslog-opts限制单容器日志最多 30 MB10×3这个在小盘 VPS 上很关键。2.3 获取 TaoToken 统一 Key打开 TaoToken 控制台在 API Keys 页面创建一个新 Key复制保存。这个 Key 就是后面填进 NewAPI 渠道的凭证。同时记下 API 地址https://taotoken.net/apiNewAPI 新建渠道时「代理地址」填这个「密钥」填刚复制的 Key。如果你还没注册先走官网入口完成账号创建再进控制台建 Key。整个流程不需要额外配置建完 Key 就能直接用。3. 部署 NewAPI 并接入 TaoToken 渠道3.1 创建目录与生成密码sudo mkdir -p /opt/new-api/{data,logs,mysql,redis} sudo chown -R $USER:$USER /opt/new-api cd /opt/new-api一次性生成 4 个随机密码写入.env.secret只生成一次丢了就要全部重置cat /opt/new-api/.env.secret EOF MYSQL_ROOT_PASSWORD$(openssl rand -base64 24 | tr -d / | cut -c1-24) MYSQL_USERnewapi MYSQL_PASSWORD$(openssl rand -base64 24 | tr -d / | cut -c1-24) SESSION_SECRET$(openssl rand -hex 32) EOF chmod 600 /opt/new-api/.env.secret cat /opt/new-api/.env.secret把输出的 4 行抄到密码管理器。3.2 编写 docker-compose.ymlset -a; source /opt/new-api/.env.secret; set a cat /opt/new-api/docker-compose.yml EOF services: new-api: image: calciumion/new-api:latest container_name: new-api restart: unless-stopped depends_on: mysql: condition: service_healthy redis: condition: service_started ports: - 3000:3000 environment: TZ: Asia/Shanghai SQL_DSN: newapi:${MYSQL_PASSWORD}tcp(mysql:3306)/new-api?charsetutf8mb4parseTimeTruelocLocal REDIS_CONN_STRING: redis://redis:6379/0 SESSION_SECRET: ${SESSION_SECRET} CRYPTO_SECRET: ${SESSION_SECRET} SYSTEM_NAME: WeskyApi GIN_MODE: release ERROR_LOG_ENABLED: true volumes: - ./data:/data - ./logs:/app/logs healthcheck: test: [CMD-SHELL, wget -qO- http://127.0.0.1:3000/api/status /dev/null || exit 1] interval: 30s timeout: 5s retries: 10 mysql: image: mysql:8.4 container_name: new-api-mysql restart: unless-stopped environment: MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD} MYSQL_DATABASE: new-api MYSQL_USER: newapi MYSQL_PASSWORD: ${MYSQL_PASSWORD} TZ: Asia/Shanghai command: - --character-set-serverutf8mb4 - --collation-serverutf8mb4_unicode_ci - --innodb-buffer-pool-size128M - --max-connections200 - --performance-schemaOFF volumes: - ./mysql:/var/lib/mysql healthcheck: test: [CMD, mysqladmin, ping, -h, 127.0.0.1, -uroot, -p${MYSQL_ROOT_PASSWORD}] interval: 10s timeout: 5s retries: 20 redis: image: redis:7-alpine container_name: new-api-redis restart: unless-stopped command: [redis-server, --maxmemory, 128mb, --maxmemory-policy, allkeys-lru, --save, 900, 1] volumes: - ./redis:/data EOF关键调优小内存机器必须MySQL 的--innodb-buffer-pool-size128M限制缓冲池默认会自适应到 25% 内存--performance-schemaOFF省约 50MBRedis 的--maxmemory 128mb加 LRU 策略防止无限增长。3.3 启动并初始化管理员cd /opt/new-api sudo docker compose pull sudo docker compose up -d sudo docker compose ps等服务就绪for i in $(seq 1 36); do if curl -fsS http://127.0.0.1:3000/api/status /dev/null; then echo ok; break; fi echo 等待中 $i/36; sleep 5 done当前版本 NewAPI 不再预置默认账户必须主动调/api/setup接口创建 rootROOT_USERroot ROOT_PASSWORDWesky-$(openssl rand -base64 16 | tr -d / | cut -c1-16) echo ROOT_USERNAME$ROOT_USER | sudo tee -a /opt/new-api/.env.secret echo ROOT_PASSWORD$ROOT_PASSWORD | sudo tee -a /opt/new-api/.env.secret echo 务必记下: $ROOT_USER / $ROOT_PASSWORD curl -sS -X POST http://127.0.0.1:3000/api/setup \ -H Content-Type: application/json \ -d {\Username\:\$ROOT_USER\,\Password\:\$ROOT_PASSWORD\,\ConfirmPassword\:\$ROOT_PASSWORD\,\SelfUseModeEnabled\:false,\DemoSiteEnabled\:false} echo期望返回{message:系统初始化成功,success:true}。登录拿 cookie 和 uidLOGIN_RESP$(curl -sS -c /tmp/newapi_cookie.txt -X POST http://127.0.0.1:3000/api/user/login \ -H Content-Type: application/json \ -d {\username\:\$ROOT_USER\,\password\:\$ROOT_PASSWORD\}) echo $LOGIN_RESP UID$(echo $LOGIN_RESP | python3 -c import sys,json;print(json.load(sys.stdin)[data][id])) echo UID$UID关键坑调任何管理员接口都要带New-Api-User: $UID这个 HTTP 头仅 cookie 不够。否则会得到Unauthorized, New-Api-User header not provided。3.4 在 NewAPI 里新建 TaoToken 渠道登录 NewAPI 后台进入「渠道」→「添加渠道」。关键字段这样填字段值类型OpenAI名称TaoToken代理地址https://taotoken.net/api密钥你在 TaoToken 控制台创建的 Key模型按需勾选或填gpt-4o,gpt-4o-mini,claude-3-5-sonnet等保存后点「测试」按钮返回绿色即渠道可用。这一步做完NewAPI 就已经能通过 TaoToken 转发请求了。4. 部署 Sub2API 并复用 NewAPI 的 Redis4.1 创建目录与生成机密sudo mkdir -p /opt/sub2api/{data,postgres} sudo chown -R $USER:$USER /opt/sub2api cd /opt/sub2api cat /opt/sub2api/.env.secret EOF POSTGRES_PASSWORD$(openssl rand -base64 24 | tr -d / | cut -c1-24) JWT_SECRET$(openssl rand -hex 32) TOTP_ENCRYPTION_KEY$(openssl rand -hex 32) ADMIN_EMAILadminweskyapi.local ADMIN_PASSWORDWesky-$(openssl rand -base64 16 | tr -d / | cut -c1-16) EOF chmod 600 /opt/sub2api/.env.secret cat /opt/sub2api/.env.secretJWT_SECRET必须固定变了所有用户被踢下线TOTP_ENCRYPTION_KEY必须固定变了所有 2FA 失效ADMIN_EMAIL是登录用户名。4.2 编写 docker-compose.ymlset -a; source /opt/sub2api/.env.secret; set a cat /opt/sub2api/docker-compose.yml EOF services: sub2api: image: weishaw/sub2api:latest container_name: sub2api restart: unless-stopped ulimits: nofile: { soft: 65535, hard: 65535 } ports: - 127.0.0.1:8080:8080 depends_on: postgres: condition: service_healthy volumes: - ./data:/app/data environment: AUTO_SETUP: true SERVER_HOST: 0.0.0.0 SERVER_PORT: 8080 SERVER_MODE: release RUN_MODE: standard DATABASE_HOST: postgres DATABASE_PORT: 5432 DATABASE_USER: sub2api DATABASE_PASSWORD: ${POSTGRES_PASSWORD} DATABASE_DBNAME: sub2api DATABASE_SSLMODE: disable DATABASE_MAX_OPEN_CONNS: 30 DATABASE_MAX_IDLE_CONNS: 5 REDIS_HOST: new-api-redis REDIS_PORT: 6379 REDIS_DB: 1 REDIS_POOL_SIZE: 128 REDIS_MIN_IDLE_CONNS: 4 JWT_SECRET: ${JWT_SECRET} JWT_EXPIRE_HOUR: 168 TOTP_ENCRYPTION_KEY: ${TOTP_ENCRYPTION_KEY} ADMIN_EMAIL: ${ADMIN_EMAIL} ADMIN_PASSWORD: ${ADMIN_PASSWORD} TZ: Asia/Shanghai SECURITY_URL_ALLOWLIST_ENABLED: false SECURITY_URL_ALLOWLIST_ALLOW_INSECURE_HTTP: true SECURITY_URL_ALLOWLIST_ALLOW_PRIVATE_HOSTS: true networks: - sub2api-network - newapi-shared healthcheck: test: [CMD, wget, -q, -T, 5, -O, /dev/null, http://localhost:8080/health] interval: 30s timeout: 10s retries: 5 start_period: 60s postgres: image: postgres:18-alpine container_name: sub2api-postgres restart: unless-stopped shm_size: 128mb environment: PGDATA: /var/lib/postgresql/data POSTGRES_USER: sub2api POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} POSTGRES_DB: sub2api TZ: Asia/Shanghai command: - postgres - -c - shared_buffers64MB - -c - effective_cache_size192MB - -c - max_connections80 volumes: - ./postgres:/var/lib/postgresql/data networks: - sub2api-network healthcheck: test: [CMD-SHELL, pg_isready -U sub2api -d sub2api] interval: 10s timeout: 5s retries: 10 start_period: 30s networks: sub2api-network: driver: bridge newapi-shared: external: true name: new-api_default EOF三个不能省的细节PGDATA: /var/lib/postgresql/data必须显式设置。postgres:18-alpine默认PGDATA/var/lib/postgresql/18/docker不显式设的话挂载到./postgres的卷里不会落数据重启就 initdb账号订阅全丢。shm_size: 128mbPG18 默认 64MB 太小复杂查询会报共享内存不足。networks.newapi-shared.external: true name: new-api_default把 sub2api 接入 new-api 的网络才能用容器名new-api-redis解析到那个 redis 容器。4.3 启动并验证cd /opt/sub2api sudo docker compose pull sudo docker compose up -d sudo docker compose ps期望两个容器都Up (healthy)且sub2api的 PORTS 列必须是127.0.0.1:8080不是0.0.0.0。for i in $(seq 1 36); do if curl -fsS http://127.0.0.1:8080/health /dev/null; then echo ok; break; fi echo 等待中 $i; sleep 5 done curl -sS http://127.0.0.1:8080/health期望返回{status:ok}。5. 公网暴露与调用验证5.1 安装 cloudflared 并起两个隧道sudo mkdir -p --mode0755 /usr/share/keyrings curl -fsSL https://pkg.cloudflare.com/cloudflare-main.gpg \ | sudo tee /usr/share/keyrings/cloudflare-main.gpg /dev/null echo deb [signed-by/usr/share/keyrings/cloudflare-main.gpg] https://pkg.cloudflare.com/cloudflared any main \ | sudo tee /etc/apt/sources.list.d/cloudflared.list sudo apt-get update -y sudo apt-get install -y cloudflared cloudflared --version写 NewAPI 的 systemd 单元sudo tee /etc/systemd/system/cloudflared-newapi.service /dev/null EOF [Unit] Descriptioncloudflared quick tunnel for new-api Afternetwork-online.target docker.service Wantsnetwork-online.target [Service] Typesimple ExecStart/usr/local/bin/cloudflared tunnel --url http://127.0.0.1:3000 --no-autoupdate --metrics 127.0.0.1:20241 Restarton-failure RestartSec5 Userroot StandardOutputappend:/var/log/cloudflared-newapi.log StandardErrorappend:/var/log/cloudflared-newapi.log ExecStartPre/bin/sh -c command -v cloudflared | xargs -I{} ln -sf {} /usr/local/bin/cloudflared [Install] WantedBymulti-user.target EOF sudo touch /var/log/cloudflared-newapi.log sudo chmod 640 /var/log/cloudflared-newapi.log sudo systemctl daemon-reload sudo systemctl enable --now cloudflared-newapi.serviceSub2API 的单元三处差异--url指向 8080、--metrics用 20242、日志路径不同sudo tee /etc/systemd/system/cloudflared-sub2api.service /dev/null EOF [Unit] Descriptioncloudflared quick tunnel for Sub2API Afternetwork-online.target docker.service Wantsnetwork-online.target [Service] Typesimple ExecStart/usr/local/bin/cloudflared tunnel --url http://127.0.0.1:8080 --no-autoupdate --metrics 127.0.0.1:20242 Restarton-failure RestartSec5 Userroot StandardOutputappend:/var/log/cloudflared-sub2api.log StandardErrorappend:/var/log/cloudflared-sub2api.log ExecStartPre/bin/sh -c command -v cloudflared | xargs -I{} ln -sf {} /usr/local/bin/cloudflared [Install] WantedBymulti-user.target EOF sudo touch /var/log/cloudflared-sub2api.log sudo chmod 640 /var/log/cloudflared-sub2api.log sudo systemctl daemon-reload sudo systemctl enable --now cloudflared-sub2api.service拿两个临时域名for i in $(seq 1 30); do URL$(sudo grep -oE https://[a-z0-9-]\.trycloudflare\.com /var/log/cloudflared-newapi.log | head -1) [ -n $URL ] { echo NewAPI URL: $URL; break; } sleep 3 done echo $URL | sudo tee /opt/new-api/tunnel_url.txt for i in $(seq 1 30); do URL2$(sudo grep -oE https://[a-z0-9-]\.trycloudflare\.com /var/log/cloudflared-sub2api.log | head -1) [ -n $URL2 ] { echo Sub2API URL: $URL2; break; } sleep 3 done echo $URL2 | sudo tee /opt/sub2api/tunnel_url.txt把 NewAPI 的公网地址写回ServerAddress令牌详情页才能生成完整链接curl -sS -b /tmp/newapi_cookie.txt -X PUT http://127.0.0.1:3000/api/option/ \ -H Content-Type: application/json -H New-Api-User: $UID \ -d {\key\:\ServerAddress\,\value\:\$URL\} echo5.2 从外部验证连通性从你自己的电脑不是服务器执行curl -sS https://你的tunnel域名.trycloudflare.com/api/status | head -c 200 curl -sS https://你的sub2api域名.trycloudflare.com/health期望分别返回 NewAPI 状态 JSON 和{status:ok}。5.3 用 TaoToken Key 跑一次真实请求在 NewAPI 后台「令牌」页面创建一个令牌复制sk-开头的 Key。然后从外部发起一次对话请求curl -sS https://你的tunnel域名.trycloudflare.com/v1/chat/completions \ -H Authorization: Bearer sk-你的NewAPI令牌 \ -H Content-Type: application/json \ -d { model: gpt-4o-mini, messages: [{role: user, content: 用一句话说明什么是统一 API 网关}], max_tokens: 100 }返回里能看到choices[0].message.content就说明整条链路通了客户端 → NewAPI → TaoToken → 模型 → 原路返回。这一步是整个部署的验收动作跑通它比看任何日志都直观。6. 常见报错排查症状可能原因处置docker compose pull卡死镜像源不可达加加速器后sudo systemctl restart dockerMySQL OOM 自动重启buffer-pool 没限制确认 compose 里有--innodb-buffer-pool-size128M加 swapUnauthorized, New-Api-User header not provided调管理员接口忘带 header所有/api/option、/api/user/...必须带-H New-Api-User: $UID第二次跑 setup 报「用户名/密码不正确」DB 已有 root密码不对用.env.secret里的 ROOT_PASSWORD不要重新生成Sub2API 启动报POSTGRES_PASSWORD is requiredenv 没带过去检查set -a; source .env.secret; set a后再执行 composeSub2API 重启数据全丢PGDATA没显式设见 4.2 节关键细节 1Sub2API 连不上 redisexternal 网络名错docker network ls看 new-api 的网络名是不是new-api_defaultTunnel URL 突然不通cloudflared 重启换了 URLcat /opt/*/tunnel_url.txt拿新地址或看日志服务器自己 curl trycloudflare 失败国内 DNS 屏蔽不影响外部从本机/手机访问验证两个 tunnel 同时只起来一个metrics 端口撞了第二个改成 20242排查时优先看容器日志sudo docker logs --tail 100 new-api和sudo docker logs --tail 100 sub2api能覆盖八成问题。隧道问题看sudo journalctl -u cloudflared-newapi.service -n 100 --no-pager。7. 后续接入与运维建议部署完成后日常运维主要盯三件事容器健康状态、隧道 URL 变化、磁盘占用。sudo docker ps --format table {{.Names}}\t{{.Status}}\t{{.Ports}}一眼能看完所有容器free -h和df -h各看一眼内存和磁盘。如果你打算长期跑编码类或 Agent 类任务建议把 NewAPI 的令牌换成 Coding Plan 对应的额度类型这样用量统计更清晰也方便按项目拆分。模型验证阶段可以直接用模型对话页面快速试通不用每次都写 curl。备份脚本建议接 cron每天凌晨跑一次sudo mkdir -p /opt/backup sudo docker exec new-api-mysql sh -c \ MYSQL_PWD$(grep ^MYSQL_PASSWORD /opt/new-api/.env.secret | cut -d -f2) \ mysqldump -unewapi --single-transaction --quick --lock-tablesfalse new-api \ | sudo tee /opt/backup/new-api-$(date %F).sql /dev/null sudo docker exec sub2api-postgres pg_dump -U sub2api sub2api \ | gzip | sudo tee /opt/backup/sub2api-$(date %F).sql.gz /dev/null安全清单里最容易被忽略的是 SSH部署完把PasswordAuthentication no打开只留密钥登录同时用 ufw 或安全组只放 22/80/443封掉 3000 和 8080。数据库端口一个都不要对外这是底线。最后提醒一点trycloudflare.com的临时域名每次重启 cloudflared 都会变只适合测试。要稳定用自有域名走 Named Tunnel 把api.yourdomain.com和pincc.yourdomain.com分别指向 3000 和 8080然后把ServerAddress同步更新成正式域名。这样令牌详情页生成的链接才不会隔天就失效。
返回列表