news 2026/9/23 11:02:33

手把手教你用 Docker+Nginx+Cloudflare 为 NEW API 与 CLI Proxy API 搭建 TaoToken 统一 Key 通道:从域名 SSL 到 config.toml

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
手把手教你用 Docker+Nginx+Cloudflare 为 NEW API 与 CLI Proxy API 搭建 TaoToken 统一 Key 通道:从域名 SSL 到 config.toml

1. 为什么要把 NEW API 和 CLI Proxy API 串成一条通道

如果你手里同时跑着 NEW API 和 CLI Proxy API(下面简称 CPA),大概率会遇到一个很现实的问题:两个服务各自有各自的 Key、各自的端口、各自的鉴权文件,客户端要接哪个、Key 放哪、额度怎么算,全靠脑子记。我试过最原始的做法——NEW API 开 3000 端口对外,CPA 开 8317 端口对外,结果就是两套密钥满天飞,日志分散在两个容器里,排查一次请求要来回切三次终端。

这篇要解决的就是这件事:用 Docker Compose 把 NEW API、CPA、Redis、Postgres、Nginx 编排在同一个 bridge 网络里,让 NEW API 作为唯一的对外入口和 Key 通道,CPA 退到内网只做认证文件管理和上游渠道,Nginx 负责 80/443 的 SSL 终结,Cloudflare 负责 DNS 解析和证书签发。最终你只需要记住一个域名、一个 Key,就能把请求打到整条链路上。

适合谁看:已经在云服务器上跑过 Docker、手里有 NEW API 或 CPA 镜像、想把它俩合并成一个统一入口的人。如果你还没装 Docker,先补一下docker --versiondocker compose version这两条命令能正常输出再往下走。整篇的验证动作都用 curl 完成,不需要额外装 Postman。

核心检索词先摆出来:NEW API 是渠道管理和 Key 分发的面板,CLI Proxy API 是管理认证文件并向上游转发请求的代理层,Docker 负责编排,Nginx 做反向代理和 SSL,Cloudflare 做 DNS 和证书。这五样东西各司其职,缺一个链路就不完整。

2. 前置准备:目录结构、TaoToken 通道与镜像版本

2.1 先把目录骨架搭出来

所有配置都放在/opt/proxy下,这样迁移和备份都方便。执行下面这条命令,一次性把 CPA、NEW API、Nginx 需要的目录和空文件建好:

mkdir -p /opt/proxy/{cpa/{logs,auths},newapi/{data,logs},nginx/{ssl,logs,conf.d}} \ && cd /opt/proxy \ && touch docker-compose.yml cpa/config.yaml nginx/nginx.conf nginx/conf.d/api.conf

建完之后目录树长这样,后面每一步往哪个文件里写内容,对照这棵树就不会乱:

/opt/proxy/ ├── docker-compose.yml ├── cpa/ │ ├── config.yaml │ ├── auths/ │ └── logs/ ├── newapi/ │ ├── data/ │ └── logs/ └── nginx/ ├── nginx.conf ├── conf.d/ │ └── api.conf ├── ssl/ └── logs/

2.2 TaoToken 统一 Key 通道的定位

整条链路里,TaoToken 扮演的是统一 Key 和 API 通道的角色。你可以在 NEW API 的渠道管理里把 CPA 配成一个上游渠道,也可以把 TaoToken 的 API 地址作为渠道填进去,让 NEW API 统一对外发 Key、统一计费。TaoToken 的 API 入口是https://taotoken.net/api,控制台和 Key 管理在https://taotoken.net/console,接入文档在https://taotoken.net/doc。这三个地址后面配渠道和验证时会反复用到。

需要提前拿到的东西:一个 TaoToken 的 API Key(在控制台里创建),以及 CPA 的config.yaml里要填的api-keys。这两个 Key 不要用同一个,CPA 的 Key 是给 NEW API 当上游凭证用的,TaoToken 的 Key 是给客户端调 NEW API 用的,分开管理出问题好定位。

2.3 版本参考

镜像版本不用锁死,用 latest 即可,但心里要有数。NEW API 用calciumion/new-api:latest,CPA 用eceasy/cli-proxy-api:latest,Nginx 用nginx:alpine,Postgres 用postgres:15,Redis 用redis:latest。云服务器 2C2G 起步就够跑这套,内存吃紧的话把 Postgres 换成 MySQL 或者直接上 SQLite 也行,但本文按 Postgres 写。

3. 可复制配置:config.yaml、docker-compose.yml 与 Nginx

3.1 CPA 的 config.yaml

这个文件控制 CPA 的监听端口、管理密钥、认证目录和重试策略。带的参数必须改,带的可以按需改,其余照抄:

host: "" port: 8317 tls: enable: false remote-management: allow-remote: true secret-key: "$2a$10$5dHykttqHWAU.WcYFg0qgOXhDoxC1P7wpZ7i2T8Kj9xqwvVRfTUm2" # 改成你自己的 disable-control-panel: false auth-dir: "~/.cli-proxy-api" api-keys: - sk-8SGCShq021BAFgpBE # 改成你自己的 debug: true logging-to-file: true logs-max-total-size-mb: 100 request-retry: 3 max-retry-interval: 30 routing: strategy: "round-robin" ws-auth: false usage-statistics-enabled: false # 走 NEW API 统计时保持 false

auth-dir里的~在 Linux 容器里就是/root,所以 docker-compose 里要把./cpa/auths挂到/root/.cli-proxy-api,认证文件放进去就能被 CPA 读到。secret-key是管理接口的密钥,所有/v0/management路由都要带它,留空则整个管理 API 返回 404。

3.2 docker-compose.yml 完整骨架

这份 compose 把五个服务放在同一个newapi-network里,NEW API 不再直接暴露 3000 端口,只通过 Nginx 对外。带 `` 的密码和域名记得改:

services: new-api: image: calciumion/new-api:latest container_name: new-api restart: always command: --log-dir /app/logs ports: - '3000' # 只在内网暴露,不映射到宿主机 volumes: - ./newapi/data:/data - ./newapi/logs:/app/logs environment: - SQL_DSN=postgresql://root:123456@postgres:5432/new-api # 改密码 - REDIS_CONN_STRING=redis://redis - TZ=Asia/Shanghai - ERROR_LOG_ENABLED=true - BATCH_UPDATE_ENABLED=true depends_on: - redis - postgres - cpa healthcheck: test: ['CMD-SHELL', "wget -q -O - http://localhost:3000/api/status | grep -o '\"success\":\\s*true' || exit 1"] interval: 30s timeout: 10s retries: 3 networks: - newapi-network redis: image: redis:latest container_name: redis restart: always networks: - newapi-network postgres: image: postgres:15 container_name: postgres restart: always environment: - POSTGRES_USER=root - POSTGRES_PASSWORD=123456 # 改密码 - POSTGRES_DB=new-api volumes: - pg_data:/var/lib/postgresql/data networks: - newapi-network cpa: image: eceasy/cli-proxy-api:latest container_name: cpa volumes: - ./cpa/config.yaml:/CLIProxyAPI/config.yaml - ./cpa/auths:/root/.cli-proxy-api - ./cpa/logs:/CLIProxyAPI/logs restart: always networks: - newapi-network nginx: image: nginx:alpine container_name: nginx restart: always ports: - '80:80' - '443:443' volumes: - ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro - ./nginx/conf.d:/etc/nginx/conf.d:ro - ./nginx/ssl:/etc/nginx/ssl:ro - ./nginx/logs:/var/log/nginx depends_on: - new-api networks: - newapi-network volumes: pg_data: networks: newapi-network: driver: bridge

注意new-apiports写的是'3000'而不是'3000:3000',前者只在内网暴露,后者会映射到宿主机。既然走 Nginx 反代,就没必要让 3000 直接对外。

3.3 Nginx 主配置与站点配置

nginx/nginx.conf直接照抄,不用改:

user nginx; worker_processes auto; error_log /var/log/nginx/error.log warn; pid /var/run/nginx.pid; events { worker_connections 1024; use epoll; multi_accept on; } http { include /etc/nginx/mime.types; default_type application/octet-stream; log_format main '$remote_addr - $remote_user [$time_local] "$request" ' '$status $body_bytes_sent "$http_referer" ' '"$http_user_agent" "$http_x_forwarded_for"'; access_log /var/log/nginx/access.log main; sendfile on; tcp_nopush on; tcp_nodelay on; keepalive_timeout 65; types_hash_max_size 2048; client_max_body_size 100m; gzip on; gzip_vary on; gzip_min_length 1024; gzip_comp_level 6; gzip_types text/plain text/css text/xml text/javascript application/json application/javascript application/xml+rss application/rss+xml font/truetype font/opentype application/vnd.ms-fontobject image/svg+xml; include /etc/nginx/conf.d/*.conf; }

nginx/conf.d/api.conf里把server_name和证书路径改成你自己的域名:

server { listen 80; server_name api.example.com; # 改成你的域名 return 301 https://$server_name$request_uri; } server { listen 443 ssl http2; server_name api.example.com; # 改成你的域名 ssl_certificate /etc/nginx/ssl/api.pem; ssl_certificate_key /etc/nginx/ssl/api.key; ssl_protocols TLSv1.2 TLSv1.3; ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384; ssl_prefer_server_ciphers off; ssl_session_cache shared:SSL:10m; ssl_session_timeout 10m; add_header Strict-Transport-Security "max-age=63072000" always; add_header X-Frame-Options "SAMEORIGIN" always; add_header X-Content-Type-Options "nosniff" always; add_header X-XSS-Protection "1; mode=block" always; access_log /var/log/nginx/api-access.log main; error_log /var/log/nginx/api-error.log; client_max_body_size 100m; location / { proxy_pass http://new-api:3000; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection 'upgrade'; 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; proxy_set_header X-Forwarded-Host $host; proxy_set_header X-Forwarded-Port $server_port; proxy_connect_timeout 300s; proxy_send_timeout 300s; proxy_read_timeout 300s; proxy_buffering off; proxy_cache off; proxy_cache_bypass $http_upgrade; } location /nginx-health { access_log off; return 200 "healthy\n"; add_header Content-Type text/plain; } }

proxy_buffering off这一行很关键,流式输出(SSE)如果开着缓冲,客户端会看到内容一段一段卡着出来,关掉之后才是逐字返回。

3.4 Cloudflare 证书与 DNS

在 Cloudflare 控制台进入 SSL/TLS 的 Origin Server 页面,创建一个 Origin Certificate,填你的二级域名(比如api.example.com),把生成的证书内容存成nginx/ssl/api.pem,私钥存成nginx/ssl/api.key,然后收紧权限:

chmod 600 /opt/proxy/nginx/ssl/api.key

DNS 那边加一条 A 记录,名称填api,内容填你服务器的公网 IP,代理状态打开(橙色云朵)。这样外部访问走 Cloudflare 边缘,回源走你签发的 Origin 证书,Nginx 只认 Cloudflare 的流量。如果你不想用 Cloudflare 代理,把云朵关掉、DNS 直接解析到 IP 也行,但那样 Nginx 和 SSL 就白配了,不如直接用IP:3000

4. 启动与验证:curl 打通每一段链路

4.1 启动编排

/opt/proxy下执行:

docker compose up -d

正常输出会看到五个容器依次 Started。如果之前起过又改了配置,先docker compose down再 up。启动后确认状态:

docker compose ps

五个服务都应该是runninghealthy。NEW API 的 healthcheck 要等 30 秒左右才会变 healthy,别急着下结论。

4.2 验证 NEW API 内网可达

从 Nginx 容器里打 NEW API 的 status 接口,确认内网链路通:

docker exec nginx wget -q -O - http://new-api:3000/api/status

返回里应该能看到"success":true。这一步不通,后面域名访问一定不通,先查docker compose logs new-api看数据库连上没有。

4.3 验证 CPA 内网可达

CPA 的端口是 8317,从 NEW API 容器里打它的根路径:

docker exec new-api wget -q -O - http://cpa:8317/ | head -c 200

能返回内容就说明 CPA 起来了。如果返回连接拒绝,检查cpa/config.yamlport是不是 8317,以及挂载路径有没有写错。

4.4 验证域名与 SSL

等 DNS 生效(一般 1 到 10 分钟),在本地执行:

curl -I https://api.example.com/nginx-health

期望看到HTTP/2 200healthy。如果卡住或报证书错误,先curl -I http://api.example.com看 80 端口有没有 301 跳转,再检查 Cloudflare 的 SSL 模式是不是 Full (strict),Flexible 模式会导致回源用 HTTP,和你的 443 配置对不上。

4.5 验证统一 Key 通道

在 NEW API 面板里登录管理员账号,进入渠道管理,新增一个渠道:类型选 OpenAI 兼容,API 地址填http://cpa:8317,密钥填config.yamlapi-keys那个值。保存后点测试,通过就说明 NEW API 能通过 CPA 打到上游。

然后用你在 NEW API 里生成的客户端 Key,从本地打一次对话接口:

curl https://api.example.com/v1/chat/completions \ -H "Authorization: Bearer sk-你的NEWAPI客户端Key" \ -H "Content-Type: application/json" \ -d '{"model":"gpt-4o-mini","messages":[{"role":"user","content":"ping"}]}'

返回里有choices字段就说明整条链路通了:客户端 → Cloudflare → Nginx → NEW API → CPA → 上游。如果返回 401,检查 Key 是不是 NEW API 里生成的;返回 502,检查 Nginx 的proxy_pass指向的服务名对不对。

5. 本篇常见错排查

5.1 docker compose 重启报 iptables 错误

docker compose down之后再 up,有时会撞上Failed to Setup IP tables: Unable to enable SKIP DNAT rule。这是 Docker 的 nat 链没清干净,重启 Docker 服务即可:

systemctl restart docker && docker compose up -d

5.2 防火墙放了端口但外部还是不通

CentOS 上firewall-cmd --list-ports显示 443 已放行,但外部访问超时,多半是云服务器安全组没开。安全组和系统防火墙是两层,都要放行。另外注意firewall-cmd的提示:连接实际走的是dockerzone,不是public,但外部入站走 public,所以 80/443 在 public 放行就够了。

5.3 Nginx 启动后 502 Bad Gateway

九成是proxy_pass里的服务名写错,或者 NEW API 还没 healthy。先docker compose logs nginx看错误日志,再docker exec nginx wget -q -O - http://new-api:3000/api/status确认后端活着。服务名必须和 compose 里的container_name或服务名一致,本文用的是new-api

5.4 流式输出卡顿或一次性返回

检查api.confproxy_buffering offproxy_cache off有没有漏。另外proxy_read_timeout建议设到 300s,默认 60s 在长回答场景下会提前断开。

5.5 CPA 管理接口返回 404

config.yamlsecret-key留空会导致所有/v0/management路由返回 404。填上密钥后重启 CPA 容器,请求时带上Authorization: Bearer <secret-key>即可。

6. 后续接入与 Key 管理

链路跑通之后,日常操作基本都在 NEW API 面板里完成:发 Key、看用量、调渠道权重。如果你要长期跑编码类或 Agent 类任务,建议把客户端直接指向 NEW API 的域名,Key 用 NEW API 生成的,这样额度统计和限流都在一个地方管。TaoToken 的 Coding Plan 适合这种长期编码场景,可以在https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_content=coding_plan&utm_campaign=rewrite看具体方案。

需要新建 Key 或者查用量的时候,控制台在https://taotoken.net/console?utm_source=taotoken_aicg_blog_end&utm_content=console&utm_campaign=rewrite,API Key 管理页在https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_content=api_keys&utm_campaign=rewrite。接入细节和参数说明看文档https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_content=doc&utm_campaign=rewrite,模型对话调试用https://taotoken.net/chat?utm_source=taotoken_aicg_blog_end&utm_content=model_chat&utm_campaign=rewrite。API 入口统一是https://taotoken.net/api,配置渠道时直接填这个地址就行。

最后提醒一句:config.yaml里的secret-keyapi-keys、compose 里的数据库密码、Cloudflare 的私钥,这三类东西别提交到 Git,也别贴到公开渠道。整套配置备份的时候把ssl/api.keycpa/auths单独加密存。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/23 11:02:28

2026最新居住证申请避坑指南:面试常考原理详解

2026最新居住证申请避坑指南:面试常考原理详解 面试被问“居住证申请原理”却答不上来?别慌,这不是玄学,是逻辑。很多运维或后端开发在面试中被问到家办业务自动化接口设计,往往卡在“为什么需要分步提交”或“状态机如何流转”上。今天用 2026最新…

作者头像 李华
网站建设 2026/9/23 11:02:19

2026最新国产拍偷精品网底层原理图解与面试避坑指南

2026最新国产拍偷精品网底层原理图解与面试避坑指南 面试被问原理答不上来,真的会瞬间露怯。别慌,2026最新的技术栈里,很多“国产拍偷精品网”相关的网络底层逻辑其实没那么玄乎。很多开发者只会在文档里复制粘贴配置,一旦面试官追问数据怎么在网卡和内核之间流转,直接卡壳。今天我们就把这套机制拆解透,让你…

作者头像 李华
网站建设 2026/9/23 11:02:12

3个坑解决uptime配置卡半天:运维面试最佳实践全解析

3个坑解决uptime配置卡半天:运维面试最佳实践全解析 配置环境就卡半天?别怪你手慢,是 uptime 这个看似简单的命令,在面试和实战中全是“坑”。很多人以为它只是看一眼服务器负载,结果一问负载计算原理、内核时间戳获取,直接哑火。今天把 uptime 在 Linux…

作者头像 李华
网站建设 2026/9/23 11:01:50

3个代码坑搞懂2013年法定节假日一文

3个代码坑搞懂2013年法定节假日一文 刚把网上抄的日历代码跑起来,报错 KeyError: '2013-01-01' ?别急着删库。这种 复制来的代码跑不通不知道怎么调 的情况,在老项目迁移时太常见了。2013年的节假日规则特殊,很多通用库默认处理不了。今天咱们 一文搞懂…

作者头像 李华
网站建设 2026/9/23 11:01:27

VMware 常用命令速查:从 esxcfg-vswitch 到 vmkiscsi-tool 的排障清单

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华