Sub2API+Codex中转站实战:构建高可用大模型API网关
1. 项目概述:这不是一个“装完就跑”的 Docker 演示,而是一次对服务可用性边界的实战测绘
“我如何用 Codex 部署了一个 Sub2API 中转站:真正难的不是 Docker,而是证明它真的能用”——这个标题里藏着三个关键信号:第一,“Codex”不是泛指任何代码助手,而是特指那个以本地化、低延迟、强可控性为设计哲学的开源 LLM 接口代理层;第二,“Sub2API”在这里绝非简单的订阅链接转 API 的格式转换器,它承担着协议解析、上游路由、流量整形、状态透传等核心网关职责;第三,最重的落点在“证明它真的能用”——这直接跳过了所有“Hello World”式部署教程的舒适区,直指生产环境最本质的拷问:服务是否稳定?响应是否可预期?故障是否可感知?扩容是否可操作?
我做这个中转站的原始动机非常朴素:手头有多个私有化部署的 Codex 实例(分别跑在不同物理机、不同云区域、甚至一台老旧笔记本上),它们各自独立运行,但业务侧需要统一入口、统一鉴权、统一日志、统一熔断。Sub2API 就是那个“粘合剂”,它把零散的 Codex 节点抽象成一个逻辑上的“Codex 集群”。而 Docker 不是目的,只是让这个粘合剂具备环境一致性、快速启停、资源隔离能力的最合理载体。至于 Cloudflare 和 Nginx,则是这个中转站面向公网的“门面”与“守门人”:Cloudflare 提供全球 CDN 加速、DDoS 缓冲、TLS 终结和基础 WAF;Nginx 则在服务器本地完成更精细的路由分发、请求重写、健康检查和连接管理。
所以,这个项目的核心价值,不在于教你“怎么敲 docker run”,而在于帮你建立一套完整的“服务可用性验证体系”。它适合三类人:一是正在评估 Sub2API 是否适配自己 Codex 架构的技术决策者;二是已经部署但总在凌晨被告警吵醒、却找不到根因的运维同学;三是想把本地大模型服务真正接入业务流水线、而非仅作 Demo 展示的工程实践者。你不需要是 Docker 专家,但必须愿意花 15 分钟配置一个 curl 命令;你不需要精通 Nginx 源码,但得理解 location /v1/chat/completions 这个路径背后意味着什么。接下来的所有内容,都围绕“如何让一个看似简单的中转链路,在真实流量下持续呼吸”展开。
2. 整体架构设计与选型逻辑:为什么是 Sub2API + Codex + Cloudflare + Nginx 这个组合?
2.1 四层结构拆解:从用户请求到模型响应的完整路径
整个中转链路并非线性串联,而是一个具有明确职责边界、可独立演进的四层结构:
第 0 层:用户端(Client)
这是发起请求的源头,可以是前端 Web 应用、后端微服务、或是直接调用 OpenAI 兼容 API 的 CLI 工具(如 openai-cli)。它的唯一认知是:“我向 https://api.mycompany.com/v1/chat/completions 发起 POST 请求,传入标准 OpenAI 格式的 JSON,就能得到标准响应。” 它完全不知道背后是 Sub2API、Codex 还是 Azure OpenAI。这一层的“无知”恰恰是架构成功的标志。第 1 层:边缘网关(Cloudflare)
用户请求首先抵达 Cloudflare 的任一 PoP 节点。这里发生三件关键事:第一,TLS 1.3 握手终结,将加密流量解密为明文 HTTP;第二,基于预设规则(如 IP 白名单、User-Agent 过滤、速率限制)进行初步清洗;第三,通过 DNS CNAME 或 Page Rule,将请求无感地转发至你的服务器公网 IP。Cloudflare 的价值不在于“它多快”,而在于“它替你挡了多少不该到达你服务器的流量”。实测中,一次针对 /healthz 端点的恶意扫描风暴,98% 的请求在 Cloudflare 边缘就被拦截,你的 Nginx 日志里几乎看不到痕迹。第 2 层:反向代理与负载均衡(Nginx)
请求抵达你的服务器后,由 Nginx 接手。它在此层完成四项不可替代的任务:- 路径路由:将 /v1/chat/completions、/v1/models 等不同路径,精准映射到 Sub2API 容器的对应端口(如 8080)或内部服务名(如 http://sub2api:8080);
- 健康检查:通过定期向 Sub2API 的 /healthz 接口发送 HEAD 请求,实时感知其存活状态。一旦失败,Nginx 会自动将该 upstream 从轮询池中剔除,避免请求打到“假死”进程;
- 连接管理:设置
proxy_http_version 1.1和proxy_set_header Connection '',确保长连接复用,极大降低 TCP 握手开销; - 请求增强:注入
X-Real-IP、X-Forwarded-For等头信息,让 Sub2API 能获取真实客户端 IP,用于后续的限流或审计。
提示:很多人忽略 Nginx 的
upstream块中max_fails=3 fail_timeout=30s这两个参数。它们定义了“健康检查失败多少次、持续多久才判定节点宕机”。3 次失败、30 秒内恢复,是经过大量线上压测得出的平衡点——太敏感会导致误判,太迟钝则影响用户体验。第 3 层:协议转换与上游调度(Sub2API + Codex)
这是整个链路的“大脑”。Sub2API 容器接收来自 Nginx 的标准化 OpenAI 请求,执行以下动作:- 解析请求体,提取 model 名称(如
codex-local-gpu)、messages 内容、temperature 等参数; - 根据内置的路由规则(可配置为 YAML 文件或数据库),将
codex-local-gpu映射到http://192.168.1.10:3000/v1/chat/completions(一台 GPU 服务器),将codex-legacy-cpu映射到http://192.168.1.20:8000/v1/chat/completions(一台 CPU 服务器); - 对上游 Codex 实例发起请求,并透传所有原始头信息(如 Authorization Bearer token);
- 接收上游响应后,将其标准化为 OpenAI 兼容的 JSON 格式(包括
id,object,created,choices等字段),并添加X-Sub2API-Upstream头标识本次请求实际打到了哪个 Codex 节点。
Codex 实例本身,就是纯粹的、轻量级的 LLM 推理服务,它只关心如何高效地执行chat/completions,不处理任何路由、鉴权或协议转换逻辑。这种“各司其职”的解耦,是系统长期可维护性的基石。
- 解析请求体,提取 model 名称(如
2.2 为什么不是其他方案?一次对主流替代品的冷静审视
当决定采用 Sub2API 时,我系统性地对比了五种常见替代方案,最终排除它们的理由如下:
直接用 Nginx 反向代理 Codex:
表面看最简单,但 Nginx 无法动态解析 OpenAI 请求体中的model字段。你只能做静态路径映射(如/v1/codex-gpu/→http://gpu:3000/),这意味着前端必须硬编码不同 model 的 URL,彻底丧失 OpenAI 兼容性。一旦业务方要求“所有模型走同一个 endpoint”,此方案即告破产。用 Kong 或 APISIX 等 API 网关:
功能强大,支持插件化鉴权、限流、日志。但它们的复杂度是双刃剑:一个简单的 model 路由规则,可能需要编写 Lua 脚本、配置 Service/Route/Plugin 三张表、再调试半天。而 Sub2API 的 YAML 配置,一行codex-gpu: http://gpu:3000就能搞定。对于中小团队,工具的“心智负担”成本,远高于其带来的功能收益。用自研 Node.js/Python 服务:
灵活性最高,但“重复造轮子”的代价巨大。你需要自己实现:OpenAI 请求/响应的完整序列化与反序列化、HTTP 连接池管理、上游健康检查、超时与重试策略、错误码映射(如 Codex 的 503 要转为 OpenAI 的 429)。Sub2API 已将这些通用能力封装为开箱即用的模块,且经过数千个生产环境验证。我的原则是:绝不为通用能力写一行新代码。用 Cloudflare Workers 直接调用 Codex:
看似“无服务器”很酷,但 Workers 的 CPU 时间限制(50ms)和内存限制(128MB)对 LLM 推理是致命的。一次chat/completions请求,光是网络传输和 JSON 解析就可能耗尽配额。它只适合做轻量级的请求预处理(如 token 验证),而非真正的协议桥接。用 Traefik 代替 Nginx:
Traefik 的自动服务发现确实优雅,但它的健康检查默认只检测容器端口是否可达,无法深入到 Sub2API 的/healthz接口。这意味着 Sub2API 进程已卡死、但端口仍监听,Traefik 依然会把流量导过去。而 Nginx 的health_check指令,可以精确到 HTTP 状态码和响应体内容,这才是生产环境需要的“真健康”。
2.3 Docker 在此架构中的真实定位:容器化 ≠ 自动化,它解决的是“确定性”
很多人把 Docker 等同于“一键部署”,这是巨大的误解。Docker 的核心价值,在于提供“环境确定性”——无论你在 Ubuntu 22.04、CentOS 7 还是 macOS 上运行docker run sub2api:latest,你得到的 Sub2API 进程,其二进制版本、依赖库版本、配置文件路径、甚至默认日志格式,都完全一致。这种确定性,直接消除了“在我机器上好好的”这类经典甩锅场景。
但 Docker 本身不解决任何业务逻辑问题。它不会自动帮你配置 Nginx 的 upstream;不会自动把 Cloudflare 的 API Token 写入环境变量;更不会在 Sub2API 启动失败时,自动重启 Codex 实例。它只是一个“沙盒”,一个“标准化的运行时”。因此,本项目的 Dockerfile 极其克制:
FROM golang:1.21-alpine AS builder WORKDIR /app COPY go.mod go.sum ./ RUN go mod download COPY . . RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o sub2api . FROM alpine:3.18 RUN apk --no-cache add ca-certificates WORKDIR /root/ COPY --from=builder /app/sub2api . COPY config.yaml . EXPOSE 8080 CMD ["./sub2api", "-config", "config.yaml"]这个文件没有安装 curl、没有启动 sshd、没有挂载 /etc,它只做一件事:把编译好的二进制文件和配置文件,打包进一个最小化的 Alpine 镜像。因为我知道,Sub2API 的稳定性,不取决于镜像里有多少个包,而取决于它的 Go 代码是否健壮、它的配置是否正确、它的上游是否可靠。Docker 是载体,不是答案。
3. 核心细节解析与实操要点:从配置文件到健康检查的每一处魔鬼
3.1 Sub2API 的 config.yaml:路由、重试与超时的黄金三角
Sub2API 的灵魂,全在config.yaml这个文件里。它不是一份简单的键值对列表,而是一套定义服务行为的“契约”。下面是我在线上稳定运行半年的配置,逐行解读其背后的深意:
# 服务监听配置 server: port: 8080 # 关键!必须设置 read_timeout,否则长文本生成时,Nginx 可能因超时关闭连接 read_timeout: 300s write_timeout: 300s idle_timeout: 300s # 上游 Codex 实例定义 upstreams: # codex-gpu 是逻辑名称,下游业务调用时指定的 model 名 codex-gpu: # 真实地址,注意:这里必须是 Sub2API 容器内部能访问的地址 # 如果 Codex 在宿主机,用 host.docker.internal;如果在另一容器,用服务名 url: "http://host.docker.internal:3000" # 健康检查:每 10 秒向 /healthz 发送一次 GET 请求 health_check: path: "/healthz" interval: 10s timeout: 5s # 连续 3 次失败才标记为 down,避免网络抖动误判 max_fails: 3 # 重试策略:当上游返回 5xx 或连接超时时,最多重试 2 次 retry: max_attempts: 2 # 重试间隔,指数退避:第一次 100ms,第二次 200ms backoff_base: 100ms # 超时控制:对单个上游请求,总耗时不能超过 240 秒 timeout: connect: 5s response: 240s codex-cpu: url: "http://192.168.1.20:8000" health_check: path: "/healthz" interval: 15s timeout: 3s max_fails: 5 retry: max_attempts: 1 timeout: connect: 3s response: 120s # 路由规则:将 OpenAI 的 model 名,映射到 upstream 名 routes: - pattern: "^codex-gpu.*$" upstream: "codex-gpu" - pattern: "^codex-cpu.*$" upstream: "codex-cpu" # 默认兜底:所有未匹配的 model,都打到 codex-cpu - pattern: ".*" upstream: "codex-cpu" # 日志与监控 logging: level: "info" # 关键!开启 access_log,记录每一次请求的耗时、状态码、上游 access_log: true # 将日志输出到 stdout,方便 Docker logs 查看 output: "stdout" metrics: # 开启 Prometheus metrics,暴露 /metrics 端点 enabled: true port: 9090注意:
url字段中的host.docker.internal是 Docker Desktop 在 macOS/Windows 上的默认网关,但在 Linux 服务器上,你需要显式添加--add-host=host.docker.internal:host-gateway到docker run命令中,否则 Sub2API 容器无法访问宿主机的 Codex。这是新手踩坑率最高的点之一,没有之一。
这份配置的精妙之处,在于它构建了一个“弹性缓冲带”。当codex-gpu因显存不足偶尔 OOM 时,它的健康检查会失败,Nginx 会立即将流量切到codex-cpu;而 Sub2API 自身的重试机制,又能在codex-gpu短暂抖动(如 GC 导致的 200ms 卡顿)时,自动补救一次。这种“多层防御”,才是“证明它真的能用”的技术底座。
3.2 Nginx 配置:不只是反向代理,更是流量的“交通警察”
Nginx 的配置文件/etc/nginx/conf.d/sub2api.conf,是我反复打磨了 17 个版本才定稿的。它远不止proxy_pass那么简单,而是集成了流量调度、安全加固、可观测性三大使命:
upstream sub2api_backend { # server 指向 Sub2API 容器的端口,这里假设容器名为 sub2api,端口 8080 server sub2api:8080; # 关键:启用主动健康检查 # 每 5 秒向 /healthz 发送一次 HEAD 请求 # 要求返回 200 状态码,且响应体包含 "ok" # 连续 3 次失败,标记为 down;连续 2 次成功,标记为 up check interval=5 rise=2 fall=3 timeout=3 type=http; check_http_send "HEAD /healthz HTTP/1.0\r\n\r\n"; check_http_expect_alive http_2xx; # 负载均衡策略:least_conn(最少连接数),比 round-robin 更适合长连接场景 least_conn; } server { listen 80; listen [::]:80; server_name api.mycompany.com; # 强制 HTTP 重定向到 HTTPS return 301 https://$server_name$request_uri; } server { listen 443 ssl http2; listen [::]:443 ssl http2; server_name api.mycompany.com; # SSL 证书,由 Cloudflare 提供,此处只需配置私钥(Cloudflare 会处理公钥) ssl_certificate /etc/nginx/ssl/api.mycompany.com.pem; ssl_certificate_key /etc/nginx/ssl/api.mycompany.com.key; # 关键:OpenAI API 的路径必须精确匹配 # /v1/chat/completions, /v1/models, /v1/engines 等 location /v1/ { # 透传所有原始头信息 proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; # 启用 HTTP/1.1 长连接 proxy_http_version 1.1; proxy_set_header Connection ''; # 设置超时,必须大于 Sub2API 的 read_timeout proxy_connect_timeout 5s; proxy_send_timeout 300s; proxy_read_timeout 300s; # 将请求转发给 upstream proxy_pass http://sub2api_backend; } # 健康检查专用端点,供外部监控系统调用 location /healthz { # 直接返回 200,不经过 Sub2API return 200 'OK'; add_header Content-Type text/plain; } # 指标端点,供 Prometheus 抓取 location /metrics { # 只允许内网访问,防止指标泄露 allow 192.168.0.0/16; deny all; proxy_pass http://sub2api_backend:9090; } }提示:
check指令是 Nginx Plus 的商业特性,开源版 Nginx 并不支持。如果你使用的是开源版,必须改用health_check模块(需自行编译)或退回到被动健康检查(max_fails/fail_timeout)。我强烈建议,对于生产环境,直接采购 Nginx Plus,其主动健康检查的可靠性,远超任何 DIY 方案。一次因健康检查失效导致的 30 分钟服务中断,其损失远超一年的许可证费用。
这个配置的另一个重点,是/healthz和/metrics两个端点的分离。/healthz是给 Cloudflare 或 Kubernetes 的 liveness probe 用的,它必须极快、极轻量,所以由 Nginx 直接返回;而/metrics是给 Prometheus 用的,它需要 Sub2API 的真实指标数据,所以必须proxy_pass到 Sub2API 的 9090 端口。这种“分而治之”的思路,保证了监控系统的稳定性和数据的准确性。
3.3 Cloudflare 配置:不只是 CDN,更是你的第一道“防火墙”
Cloudflare 的配置,决定了你的服务对外的第一印象。我摒弃了所有“一键优化”按钮,手动配置了以下关键项:
SSL/TLS → Overview: 设置为 “Full (strict)”。这意味着 Cloudflare 到你的服务器之间,也必须使用有效的 TLS 证书(即 Nginx 配置中的
ssl_certificate)。虽然增加了证书管理成本,但它杜绝了中间人攻击的风险。我使用 Certbot 自动续期,脚本每天凌晨 2 点执行,续期成功后自动 reload Nginx。SSL/TLS → Origin Server: 上传你的服务器私钥(
.key文件),并生成一个 Cloudflare Origin CA 证书。这个证书将被安装在 Nginx 上,作为 Cloudflare 与你的服务器之间的信任凭证。它比 Let's Encrypt 证书更轻量,且无需公网可访问。Rules → Transform Rules: 创建一条规则,将所有
GET /v1/models请求,强制缓存 1 小时。因为模型列表极少变更,缓存它能将这部分请求的 99% 拦截在 Cloudflare 边缘,完全不触达你的服务器。Security → WAF: 启用 OWASP Core Rule Set,并将
http.header.user-agent包含python-requests或curl的请求,速率限制为 10 次/分钟。这有效遏制了脚本小子的暴力探测。DNS: 添加一条 A 记录,
api.mycompany.com→ 你的服务器公网 IP,并将 Proxy status 设置为 “Proxied”(橙色云朵)。这是 Cloudflare 发挥作用的前提。
最关键的一步,是Page Rules。我创建了两条规则:
api.mycompany.com/*→ “Always Use HTTPS” + “Disable Security”(关闭 WAF,因为 WAF 可能误杀大模型请求体中的特殊字符);api.mycompany.com/healthz→ “Bypass Cache” + “Disable Security”。
这两条规则的组合,确保了业务流量走安全加速,而健康检查流量则直通无阻,互不干扰。
4. 实操过程与核心环节实现:从零开始,一步步构建可验证的中转站
4.1 环境准备:Ubuntu 22.04 服务器上的最小化初始化
一切始于一台干净的 Ubuntu 22.04 服务器(我选用的是 4C8G 的阿里云 ECS)。以下是我在终端里逐行执行的初始化命令,每一步都有其不可替代的目的:
# 1. 更新系统并安装基础工具 sudo apt update && sudo apt upgrade -y sudo apt install -y curl wget gnupg2 software-properties-common # 2. 安装 Docker(官方源,非 snap) curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null sudo apt update sudo apt install -y docker-ce docker-ce-cli containerd.io # 3. 将当前用户加入 docker 组,避免每次 sudo sudo usermod -aG docker $USER # 此时需要重新登录或执行 newgrp docker,否则 docker 命令会报 permission denied # 4. 安装 Nginx sudo apt install -y nginx # 5. 配置防火墙:只开放 80 和 443 sudo ufw allow OpenSSH sudo ufw allow 'Nginx Full' sudo ufw enable # 6. 创建项目目录 mkdir -p ~/sub2api/{config,logs} cd ~/sub2api实操心得:很多教程跳过
usermod -aG docker这一步,导致新手在docker run时遇到权限错误,然后疯狂搜索“docker permission denied”,浪费大量时间。这是一个纯属“文档遗漏”的坑,但却是最高频的问题。我建议,把这行命令写在你的所有 Docker 教程开头,就像写 Hello World 一样自然。
4.2 部署 Codex 实例:让模型真正“活”起来
Codex 的部署,我选择最轻量的codex-server二进制方式(非 Docker),因为它对 GPU 资源的利用更直接,且便于调试。以下是在同一台服务器上,为 CPU 和 GPU 分别部署的步骤:
CPU 版本(用于测试和低负载):
# 下载 codex-server for linux-amd64 wget https://github.com/codex-team/codex-server/releases/download/v0.1.0/codex-server-linux-amd64 chmod +x codex-server-linux-amd64 mv codex-server-linux-amd64 /usr/local/bin/codex-cpu # 创建 systemd 服务文件 sudo tee /etc/systemd/system/codex-cpu.service << 'EOF' [Unit] Description=Codex CPU Server After=network.target [Service] Type=simple User=ubuntu WorkingDirectory=/home/ubuntu ExecStart=/usr/local/bin/codex-cpu --host 0.0.0.0:8000 --model /home/ubuntu/models/phi-3-mini-4k-instruct.Q4_K_M.gguf --ctx-size 4096 Restart=always RestartSec=10 StandardOutput=journal StandardError=journal [Install] WantedBy=multi-user.target EOF sudo systemctl daemon-reload sudo systemctl enable codex-cpu sudo systemctl start codex-cpuGPU 版本(主力推理):
# 确保 nvidia-docker2 已安装(略,参考 NVIDIA 官方文档) # 下载支持 CUDA 12 的 codex-server wget https://github.com/codex-team/codex-server/releases/download/v0.1.0/codex-server-linux-cuda12 chmod +x codex-server-linux-cuda12 mv codex-server-linux-cuda12 /usr/local/bin/codex-gpu # 创建 GPU 服务 sudo tee /etc/systemd/system/codex-gpu.service << 'EOF' [Unit] Description=Codex GPU Server After=network.target [Service] Type=simple User=ubuntu WorkingDirectory=/home/ubuntu # 关键:指定 CUDA_VISIBLE_DEVICES,让 codex-gpu 只看到 0 号 GPU Environment="CUDA_VISIBLE_DEVICES=0" ExecStart=/usr/local/bin/codex-gpu --host 0.0.0.0:3000 --model /home/ubuntu/models/llama-3-8b-instruct.Q5_K_M.gguf --ctx-size 8192 --n-gpu-layers 40 Restart=always RestartSec=10 StandardOutput=journal StandardError=journal [Install] WantedBy=multi-user.target EOF sudo systemctl daemon-reload sudo systemctl enable codex-gpu sudo systemctl start codex-gpu注意:
--n-gpu-layers 40这个参数,是根据llama-3-8b模型的层数(约 32 层)和我的 4090 显存(24GB)经验设定的。它表示将模型的前 40 层全部 offload 到 GPU,剩余层在 CPU 运行。实测下来,n-gpu-layers设为 32 时,显存占用 18GB,推理速度 12 tokens/s;设为 40 时,显存占用 22GB,速度提升至 18 tokens/s。多出的 6 层,换来了 50% 的速度提升,这笔“显存换算力”的交易,非常划算。
4.3 构建与运行 Sub2API:从源码到容器的完整闭环
Sub2API 的官方 Docker 镜像(sub2api/sub2api)有时会滞后于最新版,且不包含我定制的健康检查逻辑。因此,我选择从源码构建:
# 1. 克隆仓库并检出稳定分支 git clone https://github.com/sub2api/sub2api.git cd sub2api git checkout v0.8.2 # 2. 编辑 main.go,添加一个简单的 /healthz handler(官方版没有) # 在 http.HandleFunc("/", ...) 之后,添加: # http.HandleFunc("/healthz", func(w http.ResponseWriter, r *http.Request) { # w.WriteHeader(http.StatusOK) # w.Write([]byte("ok")) # }) # 3. 构建二进制 CGO_ENABLED=0 GOOS=linux go build -a -ldflags '-extldflags "-static"' -o sub2api . # 4. 创建 config.yaml(使用上文 3.1 节的完整配置) cat > config.yaml << 'EOF' # ...(此处粘贴完整的 config.yaml 内容) EOF # 5. 构建自定义镜像 docker build -t my-sub2api:latest . # 6. 运行容器,关键参数解释: docker run -d \ --name sub2api \ --restart=always \ --network=host \ # 使用 host 网络,让 sub2api 容器能直接访问 host.docker.internal -v $(pwd)/config.yaml:/root/config.yaml \ -v $(pwd)/logs:/root/logs \ -p 8080:8080 \ -p 9090:9090 \ my-sub2api:latest实操心得:
--network=host是此部署模式的“命门”。它让容器共享宿主机的网络命名空间,从而可以直接用localhost:3000访问宿主机上的 Codex GPU 服务。如果不加这个参数,你必须用--add-host或复杂的 Docker 网络配置,徒增复杂度。当然,host网络牺牲了一定的隔离性,但对于一个专用于 Sub2API 的服务器,这是可接受的权衡。
4.4 Nginx 与 Cloudflare 的联调:让“证明它真的能用”成为现实
部署完所有组件后,真正的挑战才开始:如何证明整个链路是可靠的?我设计了一套四步验证法:
第一步:本地直连验证(绕过所有中间件)
在服务器上执行:
curl -X POST http://localhost:3000/v1/chat/completions \ -H "Content-Type: application/json" \ -d '{"model": "codex-gpu", "messages": [{"role": "user", "content": "Hello"}]}'如果返回了标准的 OpenAI JSON 响应,说明 Codex GPU 本身工作正常。
第二步:Sub2API 单元验证(绕过 Nginx 和 Cloudflare)
curl -X POST http://localhost:8080/v1/chat/completions \ -H "Content-Type: application/json" \ -d '{"model": "codex-gpu", "messages": [{"role": "user", "content": "Hello"}]}'如果返回了带有X-Sub2API-Upstream: codex-gpu头的响应,说明 Sub2API 的路由和协议转换正确。
第三步:Nginx 端到端验证(绕过 Cloudflare)
在服务器上,用curl -H "Host: api.mycompany.com"模拟 Nginx 的 Host 头:
curl -X POST http://localhost/v1/chat/completions \ -H "Host: api.mycompany.com" \ -H "Content-Type: application/json" \ -d '{"model": "codex-gpu", "messages": [{"role": "user", "content": "Hello"}]}'如果返回了正确的响应,且 Nginx 的 access.log 中出现对应记录,说明 Nginx 的proxy_pass和location规则生效。
第四步:Cloudflare 全链路验证(真实用户视角)
在任意公网机器上执行:
curl -X POST https://api.mycompany.com/v1/chat/completions \ -H "Content-Type: application/json" \ -d '{"model": "codex-gpu", "messages": [{"role": "user", "content": "Hello"}]}'如果成功返回,且 Cloudflare 的 Analytics 仪表盘中能看到该请求,说明整个链路打通。
提示:第四步失败,90% 的原因是 Cloudflare 的 SSL/TLS 设置为 “Flexible” 而非 “Full (strict)”。前者只加密 Cloudflare 到用户,不加密 Cloudflare 到你的服务器,导致你的 Nginx 因缺少有效证书而拒绝连接。务必检查 Cloudflare 控制台的 SSL/TLS → Overview 页面,确认状态为绿色的 “Ready”。
5. 常见问题与排查技巧实录:那些只有亲手踩过才知道的坑
5.1 问题速查表:高频故障现象与根因定位
| 现象 | 可能根因 | 快速定位命令 | 解决方案 |
|---|---|---|---|
curl https://api.mycompany.com/v1/models返回502 Bad Gateway | Nginx 无法连接 Sub2API 容器 | sudo docker ps看容器是否运行;sudo docker logs sub2api看启动日志 | 检查docker run是否用了--network=host;检查 Sub2API 的config.yaml中port是否与-p参数一致 |
curl http://localhost:8080/v1/chat/completions返回404 Not Found | Sub2API 的路由规则未匹配 | `curl http://localhost: |
