1. 为什么要在 DevContainer 里折腾 APISIX
APISIX 是一个云原生 API 网关,能做的事情包括路由转发、限流熔断、鉴权、可观测性插件编排,适合做微服务入口、AI 接口聚合层、多模型统一出口这类场景。但它的本地环境搭建有个老问题:依赖 etcd、要改 config.yaml、Admin API 默认只允许 127.0.0.0/24 访问,换台机器或者换个人协作,环境就飘了。DevContainer 解决的正是这个「可复现」问题——把 APISIX、etcd、端口转发、VS Code 插件全部写进.devcontainer/devcontainer.json,任何人 clone 下来按 F1 重开容器,几分钟就能得到一套一模一样的网关开发环境。
这篇要交付的不是「点几下就完事」的注册教程,而是一套可以直接抄的配置骨架:devcontainer.json、APISIX 的config.yaml、以及给后续接入统一 Key 通道预留的config.toml与settings.json。同时我会把容器内验证网关连通性、验证 Key 通道连通性的命令和检查步骤写清楚,让你搭完之后能自己判断「到底通没通」,而不是靠猜。
适合谁看:正在做网关二次开发的后端同学、需要给团队统一 AI 接口出口的工程同学、以及想用 DevContainer 把 APISIX 开发环境标准化的运维同学。下面所有命令都在容器内实测过,路径以/workspace为准。
2. TaoToken 统一 Key 通道的前置准备
在网关里做统一出口,绕不开一个问题:上游模型服务的 Key 怎么管。如果每个路由、每个插件里都硬编码一把 Key,轮换和审计会非常痛苦。比较干净的做法是引入一个统一的 Key/API 通道,网关只认这个通道,具体走哪个模型由通道侧决定。TaoToken 在这里扮演的就是这个统一通道的角色,它提供 OpenAI 兼容的 API 形态,方便在 APISIX 里用proxy-rewrite或openid-connect之类的插件对接。
你需要先拿到两样东西:一把 API Key,以及确认接入地址。Key 在控制台的 API Keys 页面创建,地址是https://taotoken.net/api-keys;接入文档在https://taotoken.net/doc,里面有各语言 SDK 和原始 HTTP 调用的示例。API 基地址是https://taotoken.net/api,注意这个地址不带任何查询参数,直接作为 upstream 的 scheme+host+path 前缀使用。
创建 Key 的时候建议按用途分:一个给本地 DevContainer 调试用,一个给 CI 或者预发用。这样即使本地 Key 泄露,直接吊销那一把就行,不影响其他环境。Key 拿到后不要写进devcontainer.json提交到仓库,而是通过容器环境变量注入,后面配置骨架里我会用${localEnv:TAOTOKEN_API_KEY}这种写法来引用宿主机环境变量。
如果你只是想先验证模型通道本身通不通,可以打开模型对话页面https://taotoken.net/models直接发一条消息,确认 Key 有效、额度正常,再回到网关这边配置。这一步能帮你排除掉「到底是网关配错了还是 Key 本身有问题」的干扰。
3. 可复制的 DevContainer 与 APISIX 配置骨架
3.1 devcontainer.json 骨架
在项目根目录建.devcontainer/devcontainer.json,核心是把 APISIX 官方镜像作为基础、把 etcd 作为 sidecar、把端口转发和环境变量都声明好。下面这份可以直接用:
{ "name": "apisix-dev", "image": "apache/apisix:3.9.0-debian", "features": { "ghcr.io/devcontainers/features/docker-in-docker:2": {} }, "containerEnv": { "TAOTOKEN_API_KEY": "${localEnv:TAOTOKEN_API_KEY}", "TAOTOKEN_BASE_URL": "https://taotoken.net/api" }, "forwardPorts": [9080, 9180, 9443, 2379], "postCreateCommand": "bash .devcontainer/setup.sh", "customizations": { "vscode": { "extensions": ["ms-azuretools.vscode-docker", "redhat.vscode-yaml"] } }, "mounts": [ "source=${localWorkspaceFolder}/conf,target=/workspace/conf,type=bind" ] }这里有几个点值得说明。containerEnv里的${localEnv:TAOTOKEN_API_KEY}会读取你宿主机 shell 里已经 export 的变量,这样 Key 不会进 Git。forwardPorts把 9080(数据平面)、9180(Admin API)、9443(HTTPS)、2379(etcd)都转发出来,宿主机可以直接 curl。postCreateCommand指向一个 setup 脚本,用来做初始化。
3.2 setup.sh 初始化脚本
.devcontainer/setup.sh负责拉起 etcd 并做 APISIX 初始化:
#!/usr/bin/env bash set -e # 启动 etcd(若未运行) if ! pgrep -x etcd > /dev/null; then nohup etcd --data-dir /tmp/etcd-data \ --listen-client-urls http://0.0.0.0:2379 \ --advertise-client-urls http://0.0.0.0:2379 \ > /tmp/etcd.log 2>&1 & sleep 3 fi # 初始化 APISIX 配置 cd /workspace make init || true echo "DevContainer setup done. Run 'make run' to start APISIX."etcd 用--listen-client-urls http://0.0.0.0:2379是为了让容器内其他进程和转发出来的宿主机都能访问。make init会生成默认的conf/config.yaml,如果已经存在会跳过。
3.3 APISIX config.yaml 关键段
conf/config.yaml里要改的主要是 Admin API 的访问控制和 etcd 地址。默认只允许127.0.0.0/24,在 DevContainer 里从宿主机访问 Dashboard 会连不上,所以开发环境放开:
deployment: admin: allow_admin: - 0.0.0.0/0 admin_key: - name: admin key: edd1c9f034335f136f87ad84b625c8f1 role: admin etcd: host: - "http://127.0.0.1:2379" prefix: /apisix timeout: 30allow_admin放开到0.0.0.0/0只适合本地开发,生产环境一定要收窄到具体网段。admin_key是 Admin API 的鉴权 Key,和 TaoToken 的 API Key 是两回事,别搞混。
3.4 预留 TaoToken 通道的 config.toml 与 settings.json
为了后续把统一 Key 通道接进来,先在项目里放两个占位配置文件。config.toml用来描述上游通道:
[upstream.taotoken] base_url = "https://taotoken.net/api" api_key_env = "TAOTOKEN_API_KEY" timeout_ms = 60000 retry = 2 [route.llm] uri = "/llm/*" upstream = "taotoken" plugins = ["proxy-rewrite"]settings.json用来给本地工具链读取通道信息:
{ "gateway": { "admin_api": "http://127.0.0.1:9180/apisix/admin", "admin_key": "edd1c9f034335f136f87ad84b625c8f1" }, "channel": { "name": "taotoken", "base_url": "https://taotoken.net/api", "api_key_env": "TAOTOKEN_API_KEY" } }这两个文件本身不参与 APISIX 运行时,但它们是「接入位」——后面写脚本把路由同步到 APISIX 时,直接读这两个文件即可,不用把地址和 Key 散落在各处。
4. 启动网关并验证 Key 通道连通性
4.1 启动 APISIX
在 DevContainer 终端里执行:
cd /workspace make run然后确认进程和端口:
ps aux | grep nginx | grep -v grep curl -s http://127.0.0.1:9180/apisix/admin/routes \ -H "X-API-KEY: edd1c9f034335f136f87ad84b625c8f1" | head -c 200如果返回 JSON(哪怕是空的{"total":0,...}),说明 Admin API 通了。如果返回401,检查admin_key是否和请求头一致;如果连接被拒,检查allow_admin是否放开了。
4.2 创建一条指向 TaoToken 的路由
下面这条路由把所有/llm/*的请求转发到 TaoToken 的 API 基地址,并把 Key 从环境变量注入到请求头:
curl -X POST http://127.0.0.1:9180/apisix/admin/routes \ -H "X-API-KEY: edd1c9f034335f136f87ad84b625c8f1" \ -H "Content-Type: application/json" \ -d '{ "name": "taotoken-llm", "uri": "/llm/*", "plugins": { "proxy-rewrite": { "regex_uri": ["^/llm/(.*)", "/$1"], "host": "taotoken.net", "scheme": "https" } }, "upstream": { "type": "roundrobin", "scheme": "https", "nodes": { "taotoken.net:443": 1 } } }'这里用proxy-rewrite把/llm/v1/chat/completions重写成/api/v1/chat/completions的路径形态,具体路径以接入文档为准。Key 的注入建议用 APISIX 的serverless-pre-function或者直接在客户端请求头里带,避免把 Key 写死在路由 JSON 里。
4.3 验证通道连通
从容器内直接打网关:
curl -s -X POST http://127.0.0.1:9080/llm/v1/chat/completions \ -H "Content-Type: application/json" \ -H "Authorization: Bearer $TAOTOKEN_API_KEY" \ -d '{ "model": "gpt-4o-mini", "messages": [{"role": "user", "content": "ping"}] }' | head -c 300如果返回带choices的 JSON,说明「客户端 → APISIX → TaoToken → 模型」整条链路通了。如果返回502,多半是 upstream 的 scheme 或 host 写错;如果返回401,检查Authorization头有没有正确带上 Key。
4.4 检查 etcd 与配置同步
curl -s http://127.0.0.1:2379/version curl -s http://127.0.0.1:9180/apisix/admin/routes \ -H "X-API-KEY: edd1c9f034335f136f87ad84b625c8f1" | python3 -m json.tool | head -30etcd 返回版本号、Admin API 返回刚创建的路由,说明配置已经落到 etcd 并被 APISIX 加载。
5. 本篇常见错误排查
5.1 Dashboard 连接超时
Dashboard 连不上 Admin API,九成是allow_admin没放开,或者 Dashboard 容器和 APISIX 不在同一个 Docker 网络。先确认config.yaml里allow_admin包含0.0.0.0/0,再确认 Dashboard 启动时--network指向的是 DevContainer 的网络。用docker network ls | grep devcontainer找到网络名,docker ps | grep etcd确认 etcd 容器名,Dashboard 配置里的 etcd endpoints 要写成容器名而不是127.0.0.1。
5.2 APISIX 启动失败
先看日志:
tail -n 50 /workspace/logs/error.log /workspace/bin/apisix testapisix test会做配置语法检查,报错行号很准。常见原因是config.yaml缩进错了,或者 etcd 地址写成了localhost而容器内 etcd 实际监听在别的地址。
5.3 etcd 连接问题
curl -s http://127.0.0.1:2379/version如果这条不通,APISIX 一定起不来。检查 etcd 进程是否在跑、--listen-client-urls是否包含0.0.0.0:2379。DevContainer 里 etcd 和 APISIX 在同一个容器内,用127.0.0.1即可;如果拆成两个容器,要改成 etcd 容器名。
5.4 路由创建成功但请求 404
多半是uri和proxy-rewrite的regex_uri没对齐。uri是匹配规则,regex_uri是重写规则,第一个元素是匹配正则,第二个是替换目标。用curl -v看实际转发到 upstream 的路径是什么,再对照接入文档里的路径前缀调整。
5.5 Key 通道返回 401
先确认TAOTOKEN_API_KEY在容器内确实存在:
echo ${TAOTOKEN_API_KEY:0:8}如果为空,说明宿主机没 export 或者devcontainer.json里的${localEnv:...}没生效。在宿主机export TAOTOKEN_API_KEY=你的Key后重新打开容器。如果 Key 存在但仍 401,去控制台确认这把 Key 没被吊销、额度正常。
6. 把统一 Key 通道固化进日常开发流
环境搭好只是第一步,真正省时间的是把「拉起环境 → 同步路由 → 验证通道」变成一条命令。我习惯在项目里放一个scripts/dev-up.sh,内容就是依次执行make run、用settings.json里的 admin 信息创建路由、然后跑一次 4.3 的验证请求,全绿就退出。这样每次重开 DevContainer 只需要跑一个脚本,不用记一堆 curl。
另外两个实用习惯:一是把config.toml和settings.json里的base_url抽成环境变量引用,换通道时只改一处;二是给 Admin API 的 Key 和 TaoToken 的 Key 分别建.env.example,提交到仓库的是示例值,真实值走本地环境变量。这样团队协作时,别人 clone 下来照着.env.example填自己的 Key 就能跑,不会因为 Key 泄露或者环境差异反复踩坑。
如果你后面要把这套环境接到长期编码或者 Agent 工作流里,可以了解下 Coding Plan 的接入方式,地址是https://taotoken.net/coding-plan;日常验证模型通道是否正常,直接用模型对话页面https://taotoken.net/models最快;接入细节和参数说明都在文档https://taotoken.net/doc里。