Docker 单机跑通
本章带你从零跑通 GateLLM:装好网关 → 登录控制台 → 配一个上游 → 签发访问密钥 → 发出第一个模型请求。全程约 5 分钟。
你将完成什么
- 一个运行中的网关(
http://localhost:7890) - 一个已配置的上游(以 OpenAI 为例)和一个模型
- 一把访问密钥,能从任意 OpenAI 兼容客户端调用网关
前置
已有至少一个上游的 API key(如 OpenAI 的
sk-...)已装 Docker
先生成一把加密密钥
ENCRYPTION_KEY(第 1 步启动容器就要用):bashopenssl rand -hex 32 # 输出 64 位十六进制;存进密码管理器,务必别丢上游 API key 与访问密钥都在落库前加密,没有这把密钥,第 4 步保存上游、第 7 步签发密钥都会失败。在第 1 步就带上它能避免装到一半再重启容器。
支持的架构
官方镜像是 multi-arch(linux/amd64 + linux/arm64),docker pull 自动按宿主架构解析,无需任何额外参数——ARM 服务器(鲲鹏 / 飞腾 / AWS Graviton / Ampere、Apple Silicon)和 x86 服务器开箱即用。
未发布 armv7 / ppc64le / s390x / loongarch64 等架构。要显式钉架构、或遇到 exec format error / no matching manifest,见 镜像 tag 与架构。
Agent 辅助安装(可选)
下面 8 步是给人看的。如果你手边有 coding agent(Claude Code、Cursor、Codex 等),建议直接让它替你装 —— 配上游、签发密钥这些步骤涉及若干控制台字段和先后顺序(密钥组必须先于访问密钥),人手动填容易错序;agent 读完整篇再逐步执行更稳。
给人类
把下面这段提示词粘给你的 agent:
安装并配置 GateLLM 网关。请先用 curl 取回这份安装指南的原始 Markdown,从头到尾读完后逐步执行(步骤间有先后顺序依赖,例如密钥组必须先于访问密钥建立,不要跳读):
curl -fsSL https://docs.gatellm.io/zh-CN/quickstart/docker-single-node.md给 Agent
如果你就是那个 agent,先取回本文的原始 Markdown,然后逐步执行:
$ curl -fsSL https://docs.gatellm.io/zh-CN/quickstart/docker-single-node.md这份文档覆盖:启动容器(含为什么必须显式 -e STORAGE_MODE=sqlite)、健康检查、控制台首次登录与密码找回、配上游 → 配模型 → 建密钥组 → 签发访问密钥的固定顺序、发出第一个请求、常用环境变量、以及 401 / 403 / 404 / 502 的对症排查。
不要摘要,从头读到尾:步骤之间有顺序依赖,跳读会漏掉先建密钥组这类前置条件。本站每个页面都有对应的原始 Markdown —— 把 clean URL 加上 .md 后缀即可。完整文档索引见 /zh-CN/llms.txt。
1. 启动网关
镜像默认开控制台、开远程访问,首次启动且控制台用户表为空时自动创建管理员账号 protoflux。密码有两种来法,任选一条。
路径 A:启动时自己指定密码(推荐)
启动命令里有两个占位值,复制后先替换再运行:
CONSOLE_PASSWORD=change-me-on-first-login→ 换成你要用的控制台密码(≥8 字符)ENCRYPTION_KEY=REPLACE_WITH_YOUR_ENCRYPTION_KEY→ 换成前置里openssl rand -hex 32生成的那串
$ docker run -d \
--name gatellm \
-p 7890:7890 \
-e CONSOLE_PASSWORD=change-me-on-first-login \
-e ENCRYPTION_KEY=REPLACE_WITH_YOUR_ENCRYPTION_KEY \
-e STORAGE_MODE=sqlite \
-v gatellm-data:/var/lib/protoflux \
ghcr.io/gatellm-io/gatellm:latest⚠️ 别用占位值原样启动。
change-me-on-first-login是文档里的公开字符串,照抄等于给控制台设了一个人尽可知的弱密码;REPLACE_WITH_YOUR_ENCRYPTION_KEY原样留下则会让加密数据用公开密钥加密、形同未加密。两个都必须换成你自己的值。
启动后日志里只有一行 bootstrap: created default admin user 'protoflux'——不含明文密码(你已经知道它)。不用翻日志,直接进第 2 步。
⚠️
CONSOLE_PASSWORD只在第一次启动生效。网关只在控制台用户表为空(也就是首次启动)时读它,用来创建管理员protoflux。此后它被完全忽略:改这个变量再重启既不会改密、也不会报错。
- 改密码 → 控制台 →「控制台用户」页(唯一入口)
- 忘了密码 →
RESET_ADMIN,见第 3 步- 值必须 ≥8 字符,否则启动被配置校验拒掉,报
web_console init_login_password is too short (< 8 chars)
路径 B:不指定密码,让网关生成随机密码
不带 CONSOLE_PASSWORD 启动,网关在首次启动生成一把 24 位随机密码并把明文打印一次到日志(ENCRYPTION_KEY 仍要替换成你自己的值):
$ docker run -d \
--name gatellm \
-p 7890:7890 \
-e ENCRYPTION_KEY=REPLACE_WITH_YOUR_ENCRYPTION_KEY \
-e STORAGE_MODE=sqlite \
-v gatellm-data:/var/lib/protoflux \
ghcr.io/gatellm-io/gatellm:latest抓密码:
$ docker logs gatellm 2>&1 | grep "auto-generated"这行日志只在首启打印一次,重启不会重打;日志被轮转或丢弃后就只能靠 RESET_ADMIN 重置。所以生产环境更推荐路径 A。
两条命令都显式带
-e STORAGE_MODE=sqlite(命令里没写也会由模板自动补上):镜像默认storage_mode = "postgresql"+ 空postgres_url(见Dockerfile)。未注入LICENSE_KEY时,license 层会把这个惰性默认自动降级为 sqlite 让网关能启动,所以不加这条也能起;但显式带上能让行为不依赖授权状态——一旦注入有效授权,license 层不再降级,此时若POSTGRES_URL仍为空就会拒启,报server.postgres_url is required for postgresql storage mode。完整原因见下方「常用环境变量(可选)」一节的「存储后端与授权」。
2. 健康检查
curl http://localhost:7890/health
# 200 即存活就绪探针 /ready 在数据库不可达、网关排空(drain 模式)或入口落盘卷挂死时返回 503,可用于负载均衡器判断是否分发流量。
3. 登录控制台
浏览器打开 http://localhost:7890/console。
- 用户名
protoflux,密码取决于第 1 步走的哪条路径:走路径 A 就是你在-e CONSOLE_PASSWORD=里填的值;走路径 B 就是首启日志里那把随机密码。 - 忘记密码时,设环境变量
RESET_ADMIN=<新密码>后重启即重置(仅生效一次)。需再次重置:重新设RESET_ADMIN为新值再重启。重置成功后去掉该环境变量再重启,可避免每次重启告警。 - 登录后立即在「控制台用户」页改密(≥8 字符)。
CONSOLE_PASSWORD首启之后不再生效,控制台是唯一的改密入口(忘密码的兜底是RESET_ADMIN,见上)。
镜像默认
CONSOLE_ALLOW_REMOTE=true,控制台可从宿主机访问。若设为false,Docker Desktop / 桥接网络下从宿主机访问会 403——只在CONSOLE_ALLOW_REMOTE=true、或经反向代理与访问控制时才放行远程。
ℹ️ 登录后你可能在控制台顶部看到一条琥珀色「免费版 — 内存上限为 512MB…」横幅。这是未授权状态的正常提示,不影响本教程跑通(单机模式下功能完整)。关于这条横幅、512MB 上限与如何激活授权,见 许可与授权。
4. 配第一个上游
若你在第 1 步已按前置带上
ENCRYPTION_KEY,这里直接保存即可。只有跳过了ENCRYPTION_KEY时,这一步保存才会报encryption key: encryption_key not set in config——补法是生成一把(openssl rand -hex 32)并在第 1 步命令上加-e ENCRYPTION_KEY=<刚生成的那串>重启(复用同一数据卷,已有配置不丢)。务必备份这把密钥:丢了已加密的数据永久不可读。 详见环境变量配置参考 → ENCRYPTION_KEY。
控制台左侧 → 上游服务 → 新建,填:
| 字段 | 值(示例) | 说明 |
|---|---|---|
| 名称 | openai | 自定义,唯一即可 |
| 协议 | openai | 选对应上游的协议 |
| 基础 URL | https://api.openai.com/v1 | 上游真实地址,结尾不带 / |
| API Key | sk-... | 上游给你的密钥,点「添加」可加多把,按权重分布 |
| 启用 | ✓ |
保存。上游列表出现 openai,状态为活跃。
5. 配第一个模型
在上游 openai 这一行点 展开 → 模型子表 → 新建模型,填:
| 字段 | 值(示例) | 说明 |
|---|---|---|
| 名称 | gpt-4o | 客户端调用时填的模型名,可自定义 |
| 上游 | openai | 选刚建的上游 |
| 上游模型 ID | gpt-4o | 上游真实模型名 |
| 启用 | ✓ |
保存。客户端用 gpt-4o 这个名字调用时,网关会路由到上游 openai 的 gpt-4o。
「名称」是你对外暴露给客户端的名字,「上游模型 ID」是发往上游的真实名字。两者可以不同——这是网关做模型别名/映射的基础。
6. 建密钥组
访问密钥的权限由其所属密钥组决定,所以先建密钥组、再签发访问密钥(顺序不能反,否则建密钥时「分组」字段无项可选)。
控制台 → 访问密钥 → 切到「密钥组」页签 → 新建,填:
| 字段 | 值 | 说明 |
|---|---|---|
| 名称 | default | 分组名 |
| 模型 | 选 gpt-4o,或 *(全部) | 决定该组密钥能调哪些模型 |
| 启用 | ✓ |
保存。这个密钥组决定了挂在它下面的访问密钥能调哪些模型。
关键:访问密钥的权限由其所属密钥组的「模型」列表决定。模型列表为空 = 无权访问任何模型;填
["*"]= 可访问所有模型;填具体模型名 = 只能访问列出的。
7. 签发访问密钥
控制台 → 访问密钥 → 切回「访问密钥」页签 → 新建,填:
| 字段 | 值 | 说明 |
|---|---|---|
| 名称 | my-app-key | 密钥的标识,用于管理与审计 |
| API 密钥 | 点「生成」 | 自动生成一串,这是客户端要带的凭证 |
| 分组 | 选 default(上一步建) | 决定这把密钥能访问哪些模型 |
| 启用 | ✓ |
保存。客户端调用时带这把访问密钥(不是上游的 sk-...)。
8. 发第一个请求
curl http://localhost:7890/v1/chat/completions \
-H "Authorization: Bearer <你的访问密钥>" \
-H "Content-Type: application/json" \
-d '{
"model": "gpt-4o",
"messages": [{"role":"user","content":"你好"}]
}'收到模型回复即跑通。
常用环境变量(可选)
第 1 步只带了跑通教程所必需的变量。其余项按需加 -e 即可,不需要挂任何配置文件。例如切到 PostgreSQL + Redis 多实例:
$ docker run -d \
--name gatellm \
-p 7890:7890 \
-e STORAGE_MODE=postgresql \
-e POSTGRES_URL=postgres://gw:secret@db:5432/protoflux \
-e REDIS_URL=redis://redis:6379 \
-e ENCRYPTION_KEY=the-same-64-hex-key-on-every-instance \
-e LICENSE_KEY=your-license-key \
-v gatellm-data:/var/lib/protoflux \
ghcr.io/gatellm-io/gatellm:latestPOSTGRES_URL 的 TLS 用 sslmode 查询参数控制(disable / require / verify-full)。多实例部署时所有实例必须用同一把 ENCRYPTION_KEY——上面写成字面占位值而不是 $(openssl rand -hex 32),正是因为后者在每台机器上会生成不同的密钥。
常用项一览(完整清单见 环境变量配置参考):
| 变量 | 作用 |
|---|---|
CONSOLE_PASSWORD | 控制台管理员 protoflux 的初始密码,仅首次启动生效(≥8 字符);不设则首启自动生成随机密码并打印一次到日志 |
ENCRYPTION_KEY | 敏感数据加密密钥;保存上游 API key 或签发访问密钥前必设,否则保存报 encryption_key not set in config。openssl rand -hex 32 生成,务必备份 |
STORAGE_MODE | sqlite(单机)/ postgresql(多实例);多实例需配 POSTGRES_URL + REDIS_URL |
LICENSE_KEY | 激活授权,解锁多实例与更高内存上限 |
RESET_ADMIN | 忘记控制台密码时的一次性重置 |
⚠️ 存储后端与授权:镜像默认
storage_mode = "postgresql"+ 空postgres_url(见Dockerfile)。未注入LICENSE_KEY时,license 层会把这个惰性默认自动降级为 sqlite 让网关能启动;但注入有效授权后 license 层不再降级,此时若STORAGE_MODE仍是postgresql而POSTGRES_URL为空,启动会被配置校验拒掉,报server.postgres_url is required for postgresql storage mode。因此第 1 步显式带
-e STORAGE_MODE=sqlite,让单机命令在是否授权两种状态下都能跑通。要多实例用 PostgreSQL:填POSTGRES_URL+REDIS_URL(多实例属分布式能力,授权要求见 许可与授权)。
生产部署:抬高 fd 上限(强烈建议)
fd(file descriptor,文件描述符)是操作系统给每个"打开的东西"(网络连接、文件)分配的编号,总数有上限(ulimit -n)。网关每个排队中的请求会一直握着约 3 个 fd(客户端连接 + 断开检测句柄 + 落盘临时文件);一旦上游故障导致大量请求排队,fd 会线性增长。若部署环境的 fd 上限偏低(如 1024),排队请求可把 fd 耗尽,之后网关连新连接都接不进来(accept 报 Too many open files),控制台也随之不可达。
fd 上限在内核眼里只是编号空间,调大几乎零成本,没有理由不调。网关推荐软、硬上限都设 1048576。各部署形态的设置方法:
| 部署形态 | 设置方法 |
|---|---|
docker run | 命令加 --ulimit nofile=1048576:1048576 |
| docker-compose | service 下加 ulimits: { nofile: { soft: 1048576, hard: 1048576 } } |
| systemd(裸机) | unit 文件加 LimitNOFILE=1048576 |
| 裸 shell 手工运行 | 先 ulimit -n 1048576 再启动 |
| Kubernetes | Pod spec 无原生 ulimit 字段,依赖宿主机运行时默认值(Docker ≥ 20.10 默认即 1048576,各 K8s 运行时不一);以启动日志打印的实际生效值验收 |
# docker-compose.yml 片段
services:
protoflux:
image: protoflux:latest
ulimits:
nofile:
soft: 1048576
hard: 1048576注意两点:
- Dockerfile 里设不了——ulimit 是容器运行期属性,Dockerfile 规范没有该指令,只能靠上面的运行时参数。
- K8s 的 initContainer 改不了——rlimit 是进程属性,init 容器退出后不留痕迹,应用容器的限制来自运行时配置。
代码侧有兜底:网关启动时会尽力把软上限抬到硬上限(setrlimit),并在启动日志打印实际生效的 fd 上限与据此算出的连接预算(运维可据此验收);抬高后仍低于 65536 会记 WARN 提醒调整。同时数据面有连接预算(
DATA_PLANE_MAX_CONNECTIONS,默认从 fd 上限自动推导),无论上限多小都保证 fd 用不到顶、超限快速 429 而非停车攒连接——见 环境变量配置参考 → 监听与请求限制。
如何清理(可选)
教程跑完想彻底移除这套环境时,删掉容器和它的数据卷:
docker rm -f gatellm # 容器,即第 1 步 --name 的值
docker volume rm gatellm-data # 数据卷(上游、模型、密钥、日志都在这里)删数据卷会永久清除网关的全部配置与请求日志,不可恢复。只是想重启用
docker restart gatellm;想重建容器但保留数据,只rm容器、保留数据卷即可。仅删除容器后再次docker run挂回同名数据卷,配置会自动恢复。
常见问题
Q:登录控制台提示密码错误 / 忘记密码? 用 RESET_ADMIN=<新密码> 环境变量重置(仅生效一次,完整步骤见第 3 步)。
Q:改了 CONSOLE_PASSWORD 重启,密码没变? 预期行为。CONSOLE_PASSWORD 只在控制台用户表为空(首次启动)时被读取;表里已有用户后它被完全忽略,且不会报错。改密走控制台 →「控制台用户」;忘了密码走 RESET_ADMIN(见上)。
Q:启动失败,报 web_console init_login_password is too short (< 8 chars)?CONSOLE_PASSWORD 少于 8 字符会被配置校验拒启。换一个 ≥8 字符的值,或干脆去掉这个变量走路径 B。
Q:控制台打不开 / 返回 403? 零配置主路径不会遇到(镜像默认 CONSOLE_ALLOW_REMOTE=true)。若你把它设成了 false,Docker Desktop / 桥接网络下从宿主机访问会 403——见第 3 步的 CONSOLE_ALLOW_REMOTE 说明。
Q:日志里没看到 "auto-generated" 密码行? 最常见原因:你走的是第 1 步的路径 A,带了 -e CONSOLE_PASSWORD= 启动。这时网关用你设的密码建管理员,并刻意不打印明文,日志里只有 bootstrap: created default admin user 'protoflux' 这一行——直接用你设的值登录即可。 其次:该行只在首次启动且控制台用户表为空时打印一次,重启不会重打。若已登录过又忘了密码,用 RESET_ADMIN 重置(见上)。也可能是控制台被 CONSOLE_ENABLED=false 关掉,此时根本不会建管理员。
Q:请求返回 401? 访问密钥没带对。检查 Authorization: Bearer 后面跟的是你签发的访问密钥(不是上游的 sk-...)。
Q:请求返回 403 model_access_denied? 密钥所在的密钥组「模型」列表没包括你要调的模型。回到密钥组把该模型加进去(或用 *)。
Q:请求返回 404 model_not_found? 模型名拼错,或该模型被设了 hide_name(只能用别名访问)。
Q:请求返回 502 bad_gateway? 上游不通。检查上游的 base_url 和 API key 是否正确、上游是否可访问。
Q:在控制台保存上游 / 签发访问密钥时报 encryption key: encryption_key not set in config?ENCRYPTION_KEY 没设。见第 4 步开头的警示。
Q:容器启动就退出,日志报 exec format error? 镜像架构与宿主不符。通常因为显式带了 --platform 钉到了错误架构(如 ARM 宿主上钉了 linux/amd64)。去掉 --platform 即可——镜像本就按宿主架构自动解析。详见镜像 tag 与架构。
Q:docker pull 报 no matching manifest for linux/xxx? 该架构未发布,官方只发 linux/amd64 与 linux/arm64。详见镜像 tag 与架构。
下一步:接入方看 端点 · 认证 · 协议互通 了解全部端点和认证方式;管理员看 控制台登录与角色 开始管理。也可以继续 接入 OpenAI 上游 看更完整的 OpenAI 接入示例。
