CLIProxyAPI Docker 部署排错全攻略

摘要:本文针对 Docker Toolbox (VirtualBox) + Mac Intel 环境下部署 CLIProxyAPI 的常见问题,提供了完整的排错指南。涵盖 CGO 线程创建失败、config.yaml 挂载冲突、服务无法访问等五大核心问题,并给出最终正确的启动命令、配置示例和验证方法。重点解决 seccomp 系统调用拦截、文件/目录挂载冲突、Docker Toolbox 网络访问等典型痛点,帮助用户快速定位并解决问题。

适用环境:Docker Toolbox (VirtualBox) + Mac Intel + eceasy/cli-proxy-api:latest
版本:CLIProxyAPI v7.2.128


一、环境背景

项目
操作系统macOS (Intel)
Docker 环境Docker Toolbox + VirtualBox(非 Docker Desktop)
Docker 版本v19.03.12
镜像eceasy/cli-proxy-api:latest
Docker Machine 名称master(不是 default)
虚拟机 IP192.168.99.100
宿主机 Host-Only 网卡vboxnet5 / 192.168.99.1

重要前提:Docker Toolbox 运行在 VirtualBox 虚拟机内,Mac 本机 127.0.0.1 不能直接访问容器端口,必须使用虚拟机 IP 192.168.99.100


二、问题 1:CGO 线程创建失败

现象

runtime/cgo: pthread_create failed: Operation not permitted
SIGABRT: abort

根因

镜像内部新版 glibc 调用 clone3 系统调用,Docker Toolbox 虚拟机的 seccomp 安全策略拦截了该系统调用,CGO 代码创建线程直接崩溃。

解决方案

启动命令增加 --security-opt seccomp=unconfined 参数,临时放开 seccomp 系统调用限制。

docker run --rm \
  --security-opt seccomp=unconfined \
  ...

不推荐使用 --privileged,权限过大。


三、问题 2:config.yaml is a directory

现象

failed to read config file: read /CLIProxyAPI/config.yaml: is a directory

根因

宿主机的 config.yaml 文件不存在。Docker Desktop / Toolbox 在挂载时,如果源文件不存在,会自动把容器内目标路径创建成文件夹,于是程序读取配置文件时报错。

排查命令

ls -la /path/to/config.yaml
# 如果输出是 d 开头(directory),说明是文件夹,不是文件

解决方案

  1. 删除错误生成的目录
rm -rf /path/to/config.yaml
  1. 创建真实的配置文件
touch /path/to/config.yaml
# 写入配置内容

四、问题 3:not a directory(挂载冲突)

现象

mounting ...config.yaml ... caused "not a directory"
Are you trying to mount a directory onto a file (or vice-versa)?

根因

镜像构建时,/CLIProxyAPI/config.yaml 已经被创建成了一个目录(镜像层的坑)。当你尝试把宿主机文件挂载到这个已存在的目录上时,Docker 直接拒绝。

验证

docker run --rm --entrypoint ls eceasy/cli-proxy-api:latest -ld /CLIProxyAPI/config.yaml
# 输出开头是 d → 确认是目录

解决方案

不要直接挂载单个文件到 /CLIProxyAPI/config.yaml,改为:

  • 将配置文件挂载到容器内全新的无关路径
  • 通过 --config 启动参数指定配置文件位置

五、问题 4:程序二进制文件丢失

现象

exec: "./CLIProxyAPI": stat ./CLIProxyAPI: no such file or directory

根因

为了绕过上一个问题,把整个 /CLIProxyAPI 目录用宿主机目录覆盖挂载,结果把容器里的程序二进制文件 ./CLIProxyAPI 给覆盖没了,容器启动找不到可执行程序。

解决方案

配置文件挂载到独立路径,不要覆盖 /CLIProxyAPI 整个目录


六、问题 5:服务启动但访问不了

现象

容器日志显示 API server started successfully on: 0.0.0.0:8317,但浏览器 / curl 访问 http://127.0.0.1:8317 连接失败。

根因

Docker Toolbox 运行在 VirtualBox 虚拟机内,-p 8317:8317 映射的是虚拟机的 8317 端口,不是 Mac 主机的 8317 端口。

排查步骤

第一步:确认 docker-machine 名称
docker-machine ls
# 注意 ACTIVE 带 * 的那台机器名

坑:默认机器名不一定是 default,可能是 master 或其他名字。docker-machine ip 不带参数会去找 default,如果你的机器名不同就会报 Host is not running

第二步:获取虚拟机 IP
docker-machine ip master   # 替换成你的机器名
# 预期输出:192.168.99.100
第三步:虚拟机内部验证服务
docker-machine ssh master
# 在虚拟机内执行
curl -v http://127.0.0.1:8317
  • 虚拟机内部通 → 容器服务正常,问题在 Mac 到虚拟机的网络层
  • 虚拟机内部不通 → 容器程序本身有问题,看容器日志
第四步:Mac 主机访问虚拟机 IP
curl -v http://192.168.99.100:8317

如果 Mac 访问虚拟机 IP 超时

检查 VirtualBox 网络配置:

  1. 打开 VirtualBox → 选中 master 虚拟机 → 设置 → 网络
  2. 确认有一张 Host-Only Adapter 网卡,绑定 vboxnet5
  3. Mac 端 ifconfig 能看到 vboxnet5 且 IP 为 192.168.99.1

想要 Mac 本机 127.0.0.1 直接访问

用 socat 做端口转发:

brew install socat
socat TCP-LISTEN:8317,fork,reuseaddr TCP:192.168.99.100:8317

该命令前台运行,关闭终端转发失效。


七、最终正确启动命令

目录结构

cliproxy-local/
├── conf/
│   └── config.yaml     # 配置文件
├── auth-dir/           # 认证数据目录
└── plugins-dir/        # 插件目录

启动命令

cd /path/to/cliproxy-local

docker run --rm \
  --security-opt seccomp=unconfined \
  -p 8317:8317 \
  -v $(pwd)/conf:/opt/clip-conf \
  -v $(pwd)/auth-dir:/root/.cli-proxy-api \
  -v $(pwd)/plugins-dir:/CLIProxyAPI/plugins \
  eceasy/cli-proxy-api:latest \
  ./CLIProxyAPI --config /opt/clip-conf/config.yaml

关键点说明

参数作用
--security-opt seccomp=unconfined解决 CGO clone3 系统调用被拦截
-v $(pwd)/conf:/opt/clip-conf配置文件挂载到独立路径,不覆盖程序目录
./CLIProxyAPI --config /opt/clip-conf/config.yaml通过启动参数指定配置文件路径,绕开镜像内 /CLIProxyAPI/config.yaml 目录冲突

八、config.yaml 最简配置示例

host: ""
port: 8317
auth-dir: "/root/.cli-proxy-api"

remote-management:
  allow-remote: true
  secret-key: "Clip@Admin2026"
  disable-control-panel: false

api-keys:
  - "sk-clip-local-789abc01"

plugins:
  enabled: false
  dir: "/CLIProxyAPI/plugins"

debug: true

# 上游 OpenAI 兼容接口示例
openai-compatibility:
  - name: glm52-gateway
    base-url: "https://your-upstream-gateway/v1"
    api-key-entries:
      - api-key: "sk-real-upstream-api-key-here"
    models:
      - name: "glm-5.2"
        alias: "glm-5.2"
        # 请求参数预处理:自动钳位 thinking.budget_tokens 到 1024 以内
        request-modifier: |
          function modify(req) {
            if (req.body && req.body.thinking && req.body.thinking.budget_tokens) {
              req.body.thinking.budget_tokens = Math.min(req.body.thinking.budget_tokens, 1024);
            }
            return req;
          }

YAML 必须使用 2 空格缩进,绝对不能用 Tab。


九、验证方法

1. 检查容器状态

docker ps
# 确认 STATUS 为 Up,PORTS 有 0.0.0.0:8317->8317/tcp

2. 查看容器日志

docker logs -f <容器ID>
# 正常日志末尾应包含:
# API server started successfully on: 0.0.0.0:8317

3. 测试 API 连通性

# 虚拟机 IP 访问(Docker Toolbox 必须用这个)
curl http://192.168.99.100:8317/

# 正常返回:
# {
#   "endpoints": [...],
#   "message": "CLI Proxy API Server"
# }

4. 测试模型列表

curl http://192.168.99.100:8317/v1/models \
  -H "Authorization: Bearer sk-clip-local-789abc01"

5. 管理后台

浏览器访问:http://192.168.99.100:8317/management.html
登录密码:Clip@Admin2026


十、排错速查表

报错原因解决方案
pthread_create failed: Operation not permittedseccomp 拦截 clone3--security-opt seccomp=unconfined
config.yaml: is a directory宿主机文件不存在,Docker 自动建了目录删除目录,创建真实文件
not a directory容器内目标路径已是目录,文件挂不进去挂载到新路径,用 --config 指定
stat ./CLIProxyAPI: no such file or directory挂载整个目录覆盖了程序二进制不要覆盖 /CLIProxyAPI 整个目录
127.0.0.1 访问不通Docker Toolbox 端口映射在虚拟机里用虚拟机 IP 192.168.99.100 访问
Host is not runningdocker-machine 机器名不对docker-machine ls 看正确名字
YAML 解析失败用了 Tab 或缩进不对全部换成 2 空格缩进

十一、相关报错补充(API 层面)

Thinking.BudgetTokens 超限

field Thinking.BudgetTokens invalid, should be in (-∞, 1024]

上游网关限制 thinking.budget_tokens 最大 1024,在 config.yaml 的模型配置中加 request-modifier 钩子自动钳位(见第八节配置示例)。

TPM 耗尽

inference tpm exhausted

每分钟 token 配额用完,等几十秒滑动窗口释放,或降低并发 / 缩短上下文。

更多推荐