Appearance
部署(实际落地版)
本文档记录实际部署的架构与步骤。架构决策见 DECISIONS.md(当前架构:决策 12 自建 Go 网关)。 客户端接入见 clients.md。
架构总览(双服务器分层)
服务器 A(女朋友账号,2核2G)— 数据层(隐藏)
115.29.241.36
└── postgres :5432 gateway 库(5 表)
安全组:5432 → 只放行 B 的 IP(121.40.184.111/32)
服务器 B(毛庆辉账号,2核2G)— 服务层 + 入口层
121.40.184.111
├── gateway :8080(systemd gateway.service,单二进制 ~10MB)
│ ├── /v1/* → 自建 Go 网关(透传)
│ ├── /api/* → 自建 Go 网关(管理 API)
│ └── / → 静态面板(navpage/)
└── caddy :80/:443(唯一公网入口,TLS + 反代 /v1, /api 到 :8080)客户端统一指向 http://121.40.184.111(Caddy 入口),不再用 4000 端口(那是 LiteLLM 时代)。
服务器 A:纯 PostgreSQL(数据层)
只装 PostgreSQL,不跑任何应用。网关 binary 不在 A 机部署。
安装 PG(Ubuntu/Debian)
bash
sudo apt update
sudo apt install -y postgresql postgresql-contrib
sudo systemctl enable --now postgresql创建库与角色
bash
sudo -u postgres psql <<'SQL'
CREATE USER gateway WITH PASSWORD '你的密码';
CREATE DATABASE gateway OWNER gateway;
GRANT ALL PRIVILEGES ON DATABASE gateway TO gateway;
SQL导入 schema
bash
psql "postgresql://gateway:你的密码@127.0.0.1:5432/gateway" \
-f gateway/schema.sql # 仓库内:5 张表(upstream_keys/keys/usage_logs/channels/models)安全组 / pg_hba.conf
- 公网安全组:
5432/tcp→ 只放行121.40.184.111/32(B 机),其他全拒。 pg_hba.conf加一行限制 B 机访问(可选,双重防御):host gateway gateway 121.40.184.111/32 scram-sha-25622→ 你的 IP(SSH,密钥登录);删 RDP / ICMP 等默认规则。
服务器 B:Go 网关 + Caddy(服务层 + 入口层)
目录结构
/opt/gateway/
├── gateway # 编译产物(gateway/bin/gateway)
├── config.yaml # 渠道配置(首次启动会种子导入到 PG)
├── navpage/ # 静态面板(仓库 navpage/,含 index.html / panel.html)
├── migrate # cmd/migrate 编译产物(手动跑迁移时用)
└── dbclean # cmd/dbclean 编译产物(清理用量日志)注:仓库根目录的
config.yaml(LiteLLM 风格的model_list)是历史遗留,不再使用——当前实际配置是gateway/config.yaml(channels 格式)。
编译网关
bash
cd gateway
CGO_ENABLED=0 go build -o bin/gateway ./cmd/gateway
CGO_ENABLED=0 go build -o bin/migrate ./cmd/migrate # 可选
CGO_ENABLED=0 go build -o bin/dbclean ./cmd/dbclean # 可选
# 产物 ~10MB 单二进制,无运行时依赖或者直接用 gateway/bin/ 里已经编译好的产物(git 不跟踪,部署时自行构建)。
systemd 单元(/etc/systemd/system/gateway.service)
ini
[Unit]
Description=云端模型路由 Go 网关
After=network.target
[Service]
Type=simple
WorkingDirectory=/opt/gateway
ExecStart=/opt/gateway/gateway
Restart=on-failure
RestartSec=2
# 环境变量(建议放在 /etc/gateway.env,chmod 600)
EnvironmentFile=/etc/gateway.env
# 安全加固
NoNewPrivileges=true
ProtectSystem=strict
ProtectHome=true
ReadWritePaths=/opt/gateway
[Install]
WantedBy=multi-user.target环境变量(/etc/gateway.env,chmod 600)
bash
# PostgreSQL(A 机)
DATABASE_URL=postgresql://gateway:你的密码@115.29.241.36:5432/gateway
# master key(32 字节 hex,openssl rand -hex 32 生成)
# 用于 AES-256-GCM 解密 PG 里加密的上游 key
GATEWAY_MASTER_KEY=你的32字节hex
# 管理 API 口令(Bearer 认证,浏览器登录面板用)
PANEL_PASSWORD=你的面板口令
# 上游 key(PG 录入后这里就不用了,留作 fallback / 本地开发)
DEEPSEEK_API_KEY=sk-xxx
MINIMAX_API_KEY=sk-cp-xxx
# 静态面板目录(默认 ../navpage,相对 WorkingDirectory)
# STATIC_DIR=/opt/gateway/navpage录入上游 key(AES 加密存 PG)
bash
cd /opt/gateway
sudo -E ./gateway keys set-upstream --provider deepseek # 交互输入 key(不回显)
sudo -E ./gateway keys set-upstream --provider minimax也可在面板「渠道」页填
POST /api/channels/{provider}/key完成录入。
Caddyfile(/etc/caddy/Caddyfile)
:80 {
encode zstd gzip
reverse_proxy /v1/* 127.0.0.1:8080
reverse_proxy /api/* 127.0.0.1:8080
# 根路径 / 落到网关内置的 http.FileServer(serve /opt/gateway/navpage)
reverse_proxy / 127.0.0.1:8080
}备案通过后:把
:80换成域名(maomaokingdom.top等),Caddy 自动 HTTPS。
也可以让 Caddy 直接 serve 静态面板(减少一次反代):
:80 {
encode zstd gzip
reverse_proxy /v1/* 127.0.0.1:8080
reverse_proxy /api/* 127.0.0.1:8080
root * /opt/gateway/navpage
file_server
}启动
bash
sudo systemctl daemon-reload
sudo systemctl enable --now gateway
sudo systemctl status gateway
# 启动日志里应看到:loaded N channels / using PostgreSQL store / gateway listening on :8080
# Caddy
sudo systemctl enable --now caddy # 已装则跳过
sudo systemctl reload caddy # 改完 Caddyfile 后渠道配置(/opt/gateway/config.yaml)
渠道为中心:每个渠道(provider)挂多个模型,每个模型挂多条协议路由(纯透传)+ 价格。
首次启动时网关会从 config.yaml 种子导入 channels/models 到 PG;之后面板增删改都走 PG,config.yaml 不再被读取(除了再次手动导入覆盖)。
yaml
channels:
- provider: deepseek
name: DeepSeek
balance_type: balance # 余额型(金额) | quota(百分比)
balance_url: https://api.deepseek.com/user/balance
models_url: https://api.deepseek.com/models
models:
- name: deepseek-v4-pro
routes:
"/v1/messages": { upstream: "https://api.deepseek.com/anthropic/v1/messages", model: "deepseek-v4-pro", usage: anthropic }
"/v1/responses": { upstream: "https://api.deepseek.com/responses", model: "deepseek-v4-pro", usage: responses }
"/v1/chat/completions":{ upstream: "https://api.deepseek.com/v1/chat/completions", model: "deepseek-v4-pro", usage: chat_completions }
pricing: { input_per_m: 3, output_per_m: 6, cache_read_per_m: 0.025, cache_write_per_m: 3 }
- name: deepseek-v4-flash
routes: { ... 同上 ... }
pricing: { input_per_m: 1, output_per_m: 2, cache_read_per_m: 0.02, cache_write_per_m: 1 }
- provider: minimax
name: MiniMax
balance_type: quota
balance_url: https://api.minimaxi.com/v1/api/openplatform/coding_plan/remains
models_url: https://api.minimaxi.com/v1/models
models:
- name: minimax-m3 # 客户端 model 名(直白)
routes:
"/v1/messages": { upstream: "https://api.minimaxi.com/anthropic/v1/messages", model: "MiniMax-M3", usage: anthropic }
"/v1/responses": { upstream: "https://api.minimaxi.com/v1/responses", model: "MiniMax-M3", usage: responses }
"/v1/chat/completions":{ upstream: "https://api.minimaxi.com/v1/chat/completions", model: "MiniMax-M3", usage: chat_completions }
pricing: { input_per_m: 2.1, output_per_m: 8.4, cache_read_per_m: 0.42, cache_write_per_m: 2.625 }注意:
minimax-m3是客户端可用的名字(直白),上游 model 名是MiniMax-M3(MiniMax 官方大小写)。网关转发时会自动替换(replaceModel(body, route.Model))。
关键坑(踩过)
- 改完 Caddyfile 必
systemctl reload caddy——Caddy 不监听 SIGUSR1 自动 reload。 - 改完 .env 必
systemctl restart gateway——systemd 不会自动重读 env 文件。 GATEWAY_MASTER_KEY必须 32 字节 hex(64 个字符)——keys.MasterKeyFromHex启动校验,错就 fatal。PANEL_PASSWORD留空 = 管理 API 无认证(仅开发用),生产务必设置。DATABASE_URL留空 = 内存模式——自动签发一条 bootstrap key(首次启动日志可见,仅此一次)。- 渠道配置改完
config.yaml不会自动 reload 到 PG——要么重启 gateway(启动时会重新种子导入,仅 PG 里没数据才导入),要么在面板里改(写 PG,reloadChannels()立刻生效)。 - Caddy 反代
/时网关会 serveSTATIC_DIR(默认../navpage)——确认WorkingDirectory=/opt/gateway,静态面板就在/opt/gateway/navpage/。 - anthropic 协议缺
anthropic-versionheader 会被上游 401——网关applyProtocolHeaders缺失时补2023-06-01默认值,但透传优先(客户端传了就用客户端的)。
安全
- 客户端用带预算的 virtual key(每个 100 元/月),不用 master key。
- 上游 key 在 PG 里以 AES-256-GCM 密文 存,
GATEWAY_MASTER_KEY仅 B 机 env 持有。 - A 机安全组只放行 5432 给 B;B 机只暴露 80/443。
- 面板登录用
PANEL_PASSWORD(Bearer 对称口令,subtle.ConstantTimeCompare 防时序攻击)。 - 虚拟 key SHA-256 哈希存 PG,明文只在签发时返回一次。
待办
- [ ] 备案通过 → 域名解析 + Caddy HTTPS
- [ ] 公开内容(博客/文档/简历)上线,替换导航页占位卡片
- [ ]
cmd/migrate/cmd/dbclean文档化(当前是裸二进制,按需使用)