AI 网关的 docker compose 配置,难点通常不在 compose 语法,而在先确认三件事:网关镜像是否需要配套数据库、对外端口开放到什么范围、模型供应商的密钥放在哪里。把这三件事定下来,compose 文件本身一般只有几十行:一个网关服务、一个可选的数据库服务、把密钥交给 .env 注入、数据走 named volume、加一个 healthcheck。镜像名、端口、健康检查路径都要按你选的那款网关说明替换,不要照抄别人的文件。
建议先把网关拆成“网关容器 + 可选数据库 + .env 密钥 + named volume 数据”四块再写 compose。对外端口默认只绑 127.0.0.1 或内网地址,镜像固定 tag 不用 latest,密钥不写进 compose 明文。写完后先跑 docker compose config 校验,再起容器看日志和健康检查,最后发一个最小的对话请求确认链路通。数据库持久化、容器权限、端口暴露范围是三个最容易忽略的边界。
先确认当前场景和目标
同样是“装个 AI 网关”,单机自己试和内网多人共用,compose 写法差别不小。开始写文件前建议先确认下面几项,任何一项没定,后面都要返工:
- 运行范围:只在本机调用,还是给同网段的其他服务用。这决定端口写
127.0.0.1:4000:4000还是0.0.0.0,也决定要不要额外做鉴权。 - 是否带数据库:不少网关支持无数据库的单机模式(配置写文件),也支持接 Postgres 存密钥、模型清单和用量记录。带数据库就要多一个服务和一次初始化。
- 密钥来源:上游模型供应商的 key、网关自身的访问 key,建议全部放
.env,compose 里只引用变量名。不要把真实 key 写进environment后提交到仓库。 - 配置文件形式:有的网关用环境变量描述模型,有的要求挂载一个 yaml 配置文件。确认清楚是“挂文件”还是“配环境变量”,写错方向会一直起不来。
按这个顺序处理
建议按“目录 → .env → compose → 启动”的顺序推进,每一步都能单独检查,出错时容易定位。
第一步:建目录和 .env
在部署目录下放三个东西:docker-compose.yml、.env、可选的 config/ 目录。先写 .env,变量名按实际网关的要求替换:

# .env 示例,变量名以所选网关说明为准
GATEWAY_IMAGE_TAG=1.0.0
GATEWAY_MASTER_KEY=换成一个足够长的随机串
PG_PASSWORD=换成一个独立密码
UPSTREAM_API_KEY=填上游模型服务的密钥
.env 建议加进 .gitignore,同时把权限收紧到只有部署用户可读。
第二步:写 compose 骨架
下面是一个通用骨架,以单网关 + Postgres 为例。镜像名、端口、健康检查路径、环境变量名都要换成你实际使用的那款网关的写法:
services:
ai-gateway:
image: your-gateway:${GATEWAY_IMAGE_TAG}
restart: unless-stopped
ports:
- "127.0.0.1:4000:4000"
env_file:
- .env
environment:
- DATABASE_URL=postgresql://gateway:${PG_PASSWORD}@db:5432/gateway
volumes:
- ./config:/app/config:ro
- gateway-data:/app/data
depends_on:
db:
condition: service_healthy
healthcheck:
test: ['CMD', 'curl', '-f', 'http://127.0.0.1:4000/health']
interval: 30s
timeout: 5s
retries: 3
db:
image: postgres:16
restart: unless-stopped
environment:
- POSTGRES_USER=gateway
- POSTGRES_PASSWORD=${PG_PASSWORD}
- POSTGRES_DB=gateway
volumes:
- pg-data:/var/lib/postgresql/data
healthcheck:
test: ['CMD-SHELL', 'pg_isready -U gateway']
interval: 10s
timeout: 5s
retries: 5
volumes:
gateway-data:
pg-data:
几个替换点说明:不需要数据库的网关,删掉 db 服务和 DATABASE_URL,同时去掉 depends_on;没有配置文件机制的网关,去掉 ./config 那行挂载;如果镜像里没有 curl,healthcheck 要换成容器内确实存在的命令,否则会一直显示 unhealthy。数据库端口不要映射到宿主机,容器之间走 compose 默认网络即可。

第三步:启动
docker compose config # 先渲染一遍,检查变量是否都替换成功
sudo docker compose up -d
docker compose ps
docker compose logs -f ai-gateway
docker compose config 会把 .env 代入后的最终配置打印出来,密钥为空或变量名拼错在这一步就能看出来,比直接起容器再翻日志省事。
改完后怎么验证
验证建议分三层,从容器状态到业务链路逐层确认,不要只看 up -d 没报错就当作完成。
- 容器层:
docker compose ps里网关和数据库都应处于 running,健康状态不是 unhealthy。如果一直是 starting 后转 unhealthy,先看docker compose logs里有没有连接数据库失败或配置文件解析失败的报错。 - 服务层:从宿主机请求健康检查接口,确认返回正常:
curl -s http://127.0.0.1:4000/health。路径按实际网关替换,有的网关用/healthz或管理页根路径。 - 业务层:发一个最小对话请求,确认从网关到上游模型这一段也通。下面是通用骨架,模型名和路径要按网关文档替换:
curl -s http://127.0.0.1:4000/v1/chat/completions \ -H "Authorization: Bearer 你的网关访问密钥" \ -H "Content-Type: application/json" \ -d '{"model":"你在网关里配置的模型名","messages":[{"role":"user","content":"ping"}]}'
预期信号是:返回结构里带有模型回复内容而不是鉴权错误或 502。如果返回鉴权失败,先查网关访问密钥;如果返回上游错误,查 UPSTREAM_API_KEY 和上游服务地址是否可达。用量记录是否落库,可以重启一次容器后回管理页看数据是否还在,能保留说明 volume 生效。
容易忽略的边界
- 镜像 tag:用
latest时,某次docker compose pull可能把行为不一致的版本拉下来。建议固定到具体 tag,升级时先改 tag 再重启,出问题能改回去。 - 数据库版本兼容:网关与 Postgres 大版本之间有兼容要求时,
pg-datavolume 里的数据不会跟着镜像自动升级。升级前先备份,不要直接换postgres大版本镜像。 - 目录和文件权限:容器内以非 root 用户运行时,挂载进去的
config/读写权限、以及 named volume 的属主可能不匹配,表现为配置读不到或写入失败。先看日志里的 permission denied,再调整宿主机目录权限或容器用户。 - 端口暴露:网关本身往往只有一层 key 校验,直接监听
0.0.0.0且没有前置鉴权时,同网络内任何人都能调用并消耗你的上游额度。只在需要时放开,并考虑在前面加一层带鉴权的服务。 - 回退方式:改配置前保留一份可用的
docker-compose.yml副本。回退时用docker compose down(不要加-v)后换回旧文件重启,named volume 里的数据不会被删除;加-v会连同数据库一起清掉,这一步要格外注意。