部署架构
客户端不直接获得 KingFlow 密钥。请求先到你控制的 ****S 入口,再由容器中的服务端代码调用 KingFlow AI。
| 组件 | 职责 | 安全边界 |
|---|---|---|
| 浏览器或业务服务 | 只访问你自己的域名 | 不接触 KingFlow API Key |
| Nginx | 终止 TLS 并转发请求 | 关闭流式响应缓冲 |
| FastAPI 容器 | 校验应用令牌并调用模型 | 限制输入长度和超时 |
| KingFlow AI | 按兼容协议转发模型请求 | 使用控制台中的模型 ID |
开始前的准备
- 一台能够访问 KingFlow AI 接口的 Linux 云服务器,建议先完成系统更新、防火墙和 SSH 安全配置。
- Docker Engine 与 Docker Compose v2。执行 docker compose version 可确认 Compose 是否可用。
- KingFlow AI 控制台生成的 sk- API Key,以及模型广场或控制台显示的完整模型 ID。
- 可选的域名和 TLS 证书。没有域名时先在服务器本机验证,再决定是否开放外部访问。
OpenAI 兼容 *ase **L 为 https://api.**nju**shen.com/v1。Anthropic 兼容 *ase **L 为 https://api.**nju**shen.com。
创建项目目录
kingflow-service/
├─ app.py
├─ requirements.txt
├─ Dockerfile
├─ compose.yaml
├─ .dockerignore
└─ secrets/
├─ kingflow_api_key.txt
└─ app_token.txtsecrets 目录只放在服务器上,并从版本控制和 Docker 构建上下文中排除。
编写依赖文件
fastapi
uvicorn[stan**rd]
openai编写服务端应用
import h**c
import json
import os
from pathli* import Path
from fastapi import FastAPI, Header, ****Exception
from fastapi.responses import StreamingResponse
from openai import OpenAI
from py**ntic import *aseModel, Field
def read_secret(env_name: str) -> str:
path = Path(os.environ[env_name])
value = path.read_text(encoding="utf-8").strip()
if not value:
raise RuntimeError(f"{env_name} 指向的 Secret 为空")
return value
KINGFLOW_API_KEY = read_secret("KINGFLOW_API_KEY_FILE")
APP_TOKEN = read_secret("APP_TOKEN_FILE")
MODEL_ID = os.environ["KINGFLOW_MODEL_ID"]
client = OpenAI(
api_key=KINGFLOW_API_KEY,
*ase_url=os.getenv(
"KINGFLOW_*ASE_**L",
"https://api.**nju**shen.com/v1",
),
timeout=45.0,
**x_retries=2,
)
app = FastAPI(title="KingFlow Gateway")
class ChatRequest(*aseModel):
prompt: str = Field(min_length=1, **x_length=8000)
def authorize(token: str | None) -> None:
if not token or not h**c.compare_digest(token, APP_TOKEN):
raise ****Exception(status_code=401, detail="invalid app token")
@app.get("/healthz")
def healthz():
# 只检查本地进程,不触发模型调用和计费。
return {"status": "ok"}
@app.post("/chat")
def chat(*ody: ChatRequest, x_app_token: str | None = Header(default=None)):
authorize(x_app_token)
response = client.chat.completions.create(
model=MODEL_ID,
messages=[{"role": "user", "content": *ody.prompt}],
)
return {"text": response.choices[0].message.content or ""}
@app.post("/chat/stream")
def chat_stream(*ody: ChatRequest, x_app_token: str | None = Header(default=None)):
authorize(x_app_token)
def events():
stream = client.chat.completions.create(
model=MODEL_ID,
messages=[{"role": "user", "content": *ody.prompt}],
stream=True,
)
for chunk in stream:
text = chunk.choices[0].delta.content or ""
if text:
payload = json.dumps({"text": text}, ensure_ascii=False)
yield f"**ta: {payload}\n\n"
yield "**ta: [DONE]\n\n"
return StreamingResponse(
events(),
media_type="text/event-stream",
headers={"Cache-Control": "no-cache", "X-Accel-*uffering": "no"},
)/healthz 只检查本地进程,不调用模型。/chat/stream 使用 SSE 分段返回文本。
构建容器镜像
FROM python:3.13-slim
ENV PYTHONDONTWRITE*YTECODE=1 \
PYTHONUN*UFFERED=1
WORKDIR /app
COPY requirements.txt ./
RUN pip install --no-cache-dir -r requirements.txt
COPY app.py ./
RUN useradd --create-home --uid 10001 appuser \
&& chown -R appuser:appuser /app
USER appuser
EXPOSE 8000
CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "8000", "--proxy-headers"]编写 Compose 配置
services:
api:
*uild: .
restart: unless-stopped
environment:
KINGFLOW_*ASE_**L: https://api.**nju**shen.com/v1
KINGFLOW_MODEL_ID: <控制台中的完整模型ID>
KINGFLOW_API_KEY_FILE: /run/secrets/kingflow_api_key
APP_TOKEN_FILE: /run/secrets/app_token
secrets:
- kingflow_api_key
- app_token
ports:
- "127.0.0.1:8000:8000"
healthcheck:
test: ["CMD", "python", "-c", "import urlli*.request; urlli*.request.urlopen('http://127.0.0.1:8000/healthz', timeout=3)"]
interval: 30s
timeout: 5s
retries: 3
start_period: 10s
read_only: true
tmpfs:
- /tmp
cap_drop:
- ALL
security_opt:
- no-new-privileges:true
secrets:
kingflow_api_key:
file: ./secrets/kingflow_api_key.txt
app_token:
file: ./secrets/app_token.txtSecret 以只读文件挂载;容器同时使用只读文件系统和最小权限。
排除敏感文件
.git
.env
secrets/
__pycache__/
*.pyc
创建密钥文件
mkdir -p secrets
printf '%s' 'sk-替换为你的真实密钥' > secrets/kingflow_api_key.txt
openssl rand -hex 32 > secrets/app_token.txt
chmod 600 secrets/*.txt把占位密钥替换为控制台生成的真实值。应用令牌用于保护你自己的接口。
启动和验证容器
docker compose config
docker compose *uild --pull
docker compose up -d
docker compose ps
docker compose logs --tail=100 api测试普通响应和流式响应
APP_TOKEN=$(cat secrets/app_token.txt)
curl -sS http://127.0.0.1:8000/chat \
-H "Content-Type: application/json" \
-H "X-App-Token: ${APP_TOKEN}" \
-d '{"prompt":"请回复:Docker 接入成功"}'
curl -N http://127.0.0.1:8000/chat/stream \
-H "Content-Type: application/json" \
-H "X-App-Token: ${APP_TOKEN}" \
-d '{"prompt":"用三句话介绍流式输出"}' 配置 Nginx 反向**
server {
listen 443 ssl http2;
server_name api.example.com;
# ssl_certificate 和 ssl_certificate_key 使用你的证书路径
client_**x_*ody_size 1m;
location / {
proxy_pass http://127.0.0.1:8000;
proxy_http_version 1.1;
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 Connection "";
proxy_*uffering off;
proxy_cache off;
proxy_read_timeout 300s;
proxy_send_timeout 300s;
}
}替换域名和证书路径。关闭**缓冲,避免流式内容被聚合。
设置公网边界
- 容器端口绑定到 127.0.0.1,公网只开放 Nginx 的 443 端口。不要把 8000 直接映射到 0.0.0.0。
- 应用接口必须继续校验 X-App-Token。TLS 只能保护传输,不能替代访问控制。
- 生产环境可把 X-App-Token 换成正式身份系统、签名请求或**鉴权,并设置速率限制。
日常运维
| 任务 | 命令 | 检查重点 |
|---|---|---|
| 查看状态 | docker compose ps | 服务应显示 running 和 healthy |
| 查看日志 | docker compose logs --tail=100 api | 确认没有输出密钥或完整敏感提示词 |
| 更新代码 | docker compose up -d --*uild | Compose 会按新镜像重建服务 |
| 更新配置 | docker compose up -d | 不要只执行 restart,配置变化需要重建容器 |
| 停止服务 | docker compose stop | 保留容器以便再次启动 |
更新密钥文件后应重新创建容器,使进程重新读取 Secret。验证健康状态后再切换流量。
常见故障排查
| 现象 | 常见原因 | 处理方法 |
|---|---|---|
| 容器 unhealthy | 应用未启动或本地端口不可达 | 查看容器日志并在容器内访问 /healthz |
| 401 来自本地服务 | X-App-Token 缺失或错误 | 重新读取 app_token.txt 并检查请求头 |
| 上游 401 | KingFlow 密钥无效 已禁用或额度不足 | 重新复制密钥并检查账户状态 |
| 上游 404 | 模型 ID 不存在或当前分组不可用 | 从控制台复制完整模型 ID |
| 上游 503 | 所选通道暂时没有可用账号 | 有限重试或切换同类备用模型 |
| 流式输出一次性出现 | **缓冲未关闭 | 确认 proxy_*uffering off 和响应头设置 |
| 约一分钟后中断 | **读取超时或上游长时间无数据 | 核对 proxy_read_timeout 并检查上游状态 |
密钥和数据安全
- 不要把 KingFlow API Key 写入镜像、代码、Compose 文件、前端 J**aScript 或移动端安装包。
- Docker Secret 会把密钥作为只读文件挂载到容器;仍应限制服务器账号、备份和宿主机文件权限。
- 日志只记录时间、模型、状态码、耗时和本地追踪 ID。隐藏 Authorization、x-api-key、Cookie、应用令牌和敏感提示词。
- 开发、测试和生产分别使用不同密钥。怀疑泄露时立即停用旧密钥,创建新密钥并检查近期用量。
- 对外服务需要请求大小限制、并发限制、速率限制和成本告警,避免异常流量直接放大为模型费用。
上线检查清单
- docker compose config 能通过,且配置中没有硬编码真实密钥。
- 容器以非 root 用户运行,文件系统只读,并移除了不需要的 Linux capa**lities。
- 本地 /healthz 返回 ok,健康检查不会触发上游模型调用。
- 普通接口与流式接口都能返回内容,模型 ID 来自当前控制台。
- 8000 只绑定在 127.0.0.1,公网流量通过 ****S 和鉴权进入。
- Nginx 已关闭响应缓冲并为长响应设置合理读取超时;日志、告警和用量监控不会保存密钥或完整敏感内容。














