跳到正文

Docker 单机跑通

本章带你从零跑通 GateLLM:装好网关 → 登录控制台 → 配一个上游 → 签发访问密钥 → 发出第一个模型请求。全程约 5 分钟。

你将完成什么

  • 一个运行中的网关(http://localhost:7890
  • 一个已配置的上游(以 OpenAI 为例)和一个模型
  • 一把访问密钥,能从任意 OpenAI 兼容客户端调用网关

前置

  • 已有至少一个上游的 API key(如 OpenAI 的 sk-...

  • 已装 Docker

  • 先生成一把加密密钥 ENCRYPTION_KEY(第 1 步启动容器就要用):

    bash
    openssl 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:

text
安装并配置 GateLLM 网关。请先用 curl 取回这份安装指南的原始 Markdown,从头到尾读完后逐步执行(步骤间有先后顺序依赖,例如密钥组必须先于访问密钥建立,不要跳读):

curl -fsSL https://docs.gatellm.io/zh-CN/quickstart/docker-single-node.md

给 Agent

如果你就是那个 agent,先取回本文的原始 Markdown,然后逐步执行:

bash
$ 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 生成的那串
bash
$ 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 仍要替换成你自己的值):

bash
$ 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

抓密码:

bash
$ 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. 健康检查

bash
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选对应上游的协议
基础 URLhttps://api.openai.com/v1上游真实地址,结尾不带 /
API Keysk-...上游给你的密钥,点「添加」可加多把,按权重分布
启用

保存。上游列表出现 openai,状态为活跃。

5. 配第一个模型

在上游 openai 这一行点 展开 → 模型子表 → 新建模型,填:

字段值(示例)说明
名称gpt-4o客户端调用时填的模型名,可自定义
上游openai选刚建的上游
上游模型 IDgpt-4o上游真实模型名
启用

保存。客户端用 gpt-4o 这个名字调用时,网关会路由到上游 openaigpt-4o

「名称」是你对外暴露给客户端的名字,「上游模型 ID」是发往上游的真实名字。两者可以不同——这是网关做模型别名/映射的基础。

6. 建密钥组

访问密钥的权限由其所属密钥组决定,所以先建密钥组、再签发访问密钥(顺序不能反,否则建密钥时「分组」字段无项可选)。

控制台 → 访问密钥 → 切到「密钥组」页签 → 新建,填:

字段说明
名称default分组名
模型gpt-4o,或 *(全部)决定该组密钥能调哪些模型
启用

保存。这个密钥组决定了挂在它下面的访问密钥能调哪些模型。

关键:访问密钥的权限由其所属密钥组的「模型」列表决定。模型列表为空 = 无权访问任何模型;填 ["*"] = 可访问所有模型;填具体模型名 = 只能访问列出的。

7. 签发访问密钥

控制台 → 访问密钥 → 切回「访问密钥」页签 → 新建,填:

字段说明
名称my-app-key密钥的标识,用于管理与审计
API 密钥点「生成」自动生成一串,这是客户端要带的凭证
分组default(上一步建)决定这把密钥能访问哪些模型
启用

保存。客户端调用时带这把访问密钥(不是上游的 sk-...)。

8. 发第一个请求

bash
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 多实例:

bash
$ 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:latest

POSTGRES_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 configopenssl rand -hex 32 生成,务必备份
STORAGE_MODEsqlite(单机)/ postgresql(多实例);多实例需配 POSTGRES_URL + REDIS_URL
LICENSE_KEY激活授权,解锁多实例与更高内存上限
RESET_ADMIN忘记控制台密码时的一次性重置

⚠️ 存储后端与授权:镜像默认 storage_mode = "postgresql" + 空 postgres_url(见 Dockerfile)。未注入 LICENSE_KEY,license 层会把这个惰性默认自动降级为 sqlite 让网关能启动;但注入有效授权后 license 层不再降级,此时若 STORAGE_MODE 仍是 postgresqlPOSTGRES_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 耗尽,之后网关连新连接都接不进来acceptToo many open files),控制台也随之不可达。

fd 上限在内核眼里只是编号空间,调大几乎零成本,没有理由不调。网关推荐软、硬上限都设 1048576。各部署形态的设置方法:

部署形态设置方法
docker run命令加 --ulimit nofile=1048576:1048576
docker-composeservice 下加 ulimits: { nofile: { soft: 1048576, hard: 1048576 } }
systemd(裸机)unit 文件加 LimitNOFILE=1048576
裸 shell 手工运行ulimit -n 1048576 再启动
KubernetesPod spec 无原生 ulimit 字段,依赖宿主机运行时默认值(Docker ≥ 20.10 默认即 1048576,各 K8s 运行时不一);以启动日志打印的实际生效值验收
yaml
# 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 而非停车攒连接——见 环境变量配置参考 → 监听与请求限制

如何清理(可选)

教程跑完想彻底移除这套环境时,删掉容器和它的数据卷:

bash
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 configENCRYPTION_KEY 没设。见第 4 步开头的警示。

Q:容器启动就退出,日志报 exec format error 镜像架构与宿主不符。通常因为显式带了 --platform 钉到了错误架构(如 ARM 宿主上钉了 linux/amd64)。去掉 --platform 即可——镜像本就按宿主架构自动解析。详见镜像 tag 与架构

Q:docker pullno matching manifest for linux/xxx 该架构未发布,官方只发 linux/amd64linux/arm64。详见镜像 tag 与架构

下一步:接入方看 端点 · 认证 · 协议互通 了解全部端点和认证方式;管理员看 控制台登录与角色 开始管理。也可以继续 接入 OpenAI 上游 看更完整的 OpenAI 接入示例。