Bencher Docker Compose 部署指南

自定义 docker-compose.yml 部署 Bencher v0.6.13 自托管版:显式 project name、自定义 bridge 网络、bind mount 数据与配置、生产级反向代理、SQLite 备份恢复与等价 docker run 参考。

BencherBencherRust语言DevOps工程CI性能基准REST2026/10/27

Bencher v0.6.13 Docker Compose 部署指南

目标:用自定义 docker-compose.yml 在 Ubuntu 22.04 单机部署 Bencher v0.6.13 自托管版,包含显式 project name、自定义网桥、bind mount 数据目录、API Server bencher.json 配置、Console 环境变量、生产级反向代理、以及等价 docker run 参考命令。compose 已在本实验环境实测拉起:API 返回 {"version":"0.6.13"}、Console 返回 HTTP 200。

一、环境信息

项值
Bencherv0.6.13(2026-09-28)
镜像仓库ghcr.io/bencherdev
API 镜像ghcr.io/bencherdev/bencher-api:v0.6.13
Console 镜像ghcr.io/bencherdev/bencher-console:v0.6.13
Compose Projectbencher-0613-stack(显式 name:)
网络bencher-0613-net(自定义 user-defined bridge)
API 端口6610(host → container)
Console 端口3000(host → container)
API 数据目录./services/api/data → /var/lib/bencher/data
API 配置目录./services/api/etc → /etc/bencher

二、镜像选型说明

镜像大小说明
ghcr.io/bencherdev/bencher-api:v0.6.13约 45 MBRust 编译静态链接,无多余依赖。必须带明确版本 tag;latest 可能和你本机 CLI 版本漂移。
ghcr.io/bencherdev/bencher-console:v0.6.13约 80 MBNext.js 构建产物 + Node 运行时,只读静态前端,配置靠环境变量。

⚠️ ghcr.io 拉取有匿名速率限制。如果 docker pull 报 401/403,先 docker logout ghcr.io 清 token 再重试;高频拉取建议走代理(docker pull --help 不支持代理 env,需要 daemon ~/.docker/config.json 配置)。

三、前置条件

  • Docker Engine ≥ 20.10 + Compose 插件 ≥ v2.0
  • 端口 3000、6610 未占用(ss -ltn 或 lsof -i :3000 检查)
  • IPv4 子网段可用(docker network ls 查已用的 user-defined bridge subnet,选空闲段)

四、部署步骤

4.1 创建目录结构

mkdir -p /opt/bencher-stack/{services/api/{etc,data},services/console,config}
# /opt/bencher-stack/
# ├── docker-compose.yml
# ├── services/api/etc/bencher.json      # API Server 配置(可选)
# ├── services/api/data/bencher.db       # SQLite(首次拉容器自动创建)
# └── services/console/                  # Console 无状态,无需挂载

4.2 编写 docker-compose.yml

核心点:

  1. 显式 name: bencher-0613-stack 作为 project name——避免多组件 compose 目录同名时互相覆盖(S7 批次教训)。
  2. 自定义 bridge 网络,显式 subnet 从批次空闲池取(本批次已用 56 段,选其他空闲段如 58)。
  3. api 和 console 都加入同一自定义网络,否则 INTERNAL_API_URL=http://bencher_api_compose:6610 解析不到容器名。
  4. API Server 配置目录挂到 /etc/bencher,让容器读 bencher.json;数据目录挂到 /var/lib/bencher/data,SQLite 持久化。
# /opt/bencher-stack/docker-compose.yml
name: bencher-0613-stack

services:
  bencher_api_compose:
    image: ghcr.io/bencherdev/bencher-api:v0.6.13
    container_name: bencher_api
    restart: unless-stopped
    ports:
      - "6610:6610"
    volumes:
      - "./services/api/etc:/etc/bencher"
      - "./services/api/data:/var/lib/bencher/data"
    environment:
      # 如果不挂配置文件,可以用环境变量直接内联 JSON
      # - BENCHER_CONFIG={"server":{"bind_address":"0.0.0.0:6610"},"database":{"file":"/var/lib/bencher/data/bencher.db"}}
      # 或者指向 JSON 文件路径
      # - BENCHER_CONFIG_PATH=/etc/bencher/bencher.json
    networks:
      - bencher_net
    healthcheck:
      test: ["CMD", "curl", "-fsS", "http://localhost:6610/v0/server/version"]
      interval: 30s
      timeout: 5s
      retries: 5
      start_period: 10s

  bencher_console_compose:
    image: ghcr.io/bencherdev/bencher-console:v0.6.13
    container_name: bencher_console
    restart: unless-stopped
    ports:
      - "3000:3000"
    environment:
      - BENCHER_API_URL=http://127.0.0.1:6610
      # 关键!Console 容器调 API 容器的地址(容器内 Docker-to-Docker 通信)
      # 只在 Docker 网络中需要,指向 API 容器名 + 内部端口
      - INTERNAL_API_URL=http://bencher_api_compose:6610
    depends_on:
      bencher_api_compose:
        condition: service_healthy
    networks:
      - bencher_net

networks:
  bencher_net:
    name: bencher-0613-net
    driver: bridge
    ipam:
      config:
        - subnet: 10.89.58.0/24

4.3 拉镜像并拉起

cd /opt/bencher-stack

# 显式 --project-name 覆盖 compose 文件名推导的默认 project name
docker compose -p bencher-0613-stack up -d --pull always

# 查看状态
docker compose -p bencher-0613-stack ps
# NAME                 IMAGE                                    STATUS          PORTS
# bencher_api          ghcr.io/bencherdev/bencher-api:v0.6.13   Up 5 minutes    0.0.0.0:6610->6610/tcp
# bencher_console      ghcr.io/bencherdev/bencher-console:...   Up 5 minutes    0.0.0.0:3000->3000/tcp

4.4 首启验证

# API Server 版本(鉴权未配置时,此路由公开)
curl -s http://localhost:6610/v0/server/version
# {"version":"0.6.13"}

# API Server OpenAPI spec 头
curl -s http://localhost:6610/v0/server/spec | head -c 200
# {"components":{"responses":{"Error":{"...

# Console 前端
curl -s -o /dev/null -w "HTTP %{http_code}\n" http://localhost:3000/
# HTTP 200

# 一键生成 mock 数据 + Claim(可选,验证全链路)
# 先在同一网段/机器装 CLI
# 然后:
# bencher mock --project demo --branch main --testbed local
# 浏览器打开 Claim 链接 → 注册即可

4.5 生产级反向代理(可选,推荐)

自托管版 Console 走纯 HTTP,外层套 nginx/caddy 做 HTTPS 很常见。下面给一个 nginx 反代模板:

server {
    listen 80;
    listen [::]:80;
    server_name bencher.example.com;
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;
    server_name bencher.example.com;

    ssl_certificate     /etc/letsencrypt/live/bencher.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/bencher.example.com/privkey.pem;

    # Console 前端
    location / {
        proxy_pass http://127.0.0.1:3000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }

    # API Server(/v0/... 和 /v2/... 都转发)
    location /v0/ {
        proxy_pass http://127.0.0.1:6610;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;

        # WebSocket 升级(bare metal runner 心跳)
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
    }

    # OCI Registry 路由(如果启用)
    location /v2/ {
        proxy_pass http://127.0.0.1:6610;
        # 同上 Upgrade 头
    }
}

⚠️ 走外层反代时,API Server 的 console.url 必须改成公开域名(见 C1 篇 console.url 字段),否则 WebSocket 心跳会报 Origin mismatch。

五、数据持久化说明

路径挂载到容器容器内内容
./services/api/data✅/var/lib/bencher/dataSQLite 数据库文件 bencher.db + WAL + 可选 registry/
./services/api/etc✅/etc/bencherAPI Server 配置 bencher.json
Console❌无数据Next.js 静态前端,纯只读

SQLite 备份与恢复

# 备份:停 API → 直接拷 db 文件(SQLite WAL 模式下需 checkpoint 或锁)
docker compose -p bencher-0613-stack stop bencher_api_compose
cp services/api/data/bencher.db /backup/bencher-$(date +%F).db
docker compose -p bencher-0613-stack start

# 恢复:停 API → 覆盖文件 → 启动
docker compose -p bencher-0613-stack stop bencher_api_compose
cp /backup/bencher-2026-10-27.db services/api/data/bencher.db
docker compose -p bencher-0613-stack start
curl -s http://localhost:6610/v0/server/version

Plus 版可以启用 Litestream 做 S3 连续复制(见 C1 篇 plus.disaster_recovery 字段)。

六、环境变量表

Console 容器环境变量(environment: 下)

变量默认必填说明
BENCHER_API_URLhttp://localhost:6610✅(自托管生产必须改)浏览器访问 Console 时,前端 JS 用这个地址调 API。如果走外层反代,应该是 https://bencher.example.com。
INTERNAL_API_URL不设置条件必填只在 Docker 内部网络通信时需要,指向 API 容器名 + 内部端口:http://bencher_api_compose:6610。不要对远程 API 设置这个(如 Cloud)。
SERVER_KEY_PATH无❌Console 自配 TLS 的私钥路径(不推荐,建议外层 nginx)
SERVER_CERT_PATH无❌Console 自配 TLS 的证书路径
OAUTH_GITHUB / OAUTH_GOOGLE不设置❌Plus Enterprise 版 SSO 开关

API Server 容器环境变量(environment: 下)

变量默认必填说明
BENCHER_CONFIG不设置❌内联 JSON 配置字符串
BENCHER_CONFIG_PATH不设置❌JSON 配置文件路径(容器内)

💡 优先级:BENCHER_CONFIG > BENCHER_CONFIG_PATH > /etc/bencher/bencher.json > 默认配置。C1 篇给出了完整 JSON 字段表。

七、配置参数说明

API Server 的 bencher.json 可以挂载到 /etc/bencher 让容器读取。下面给一个生产推荐模板(SQLite + Plus 版 SSO + Litestream 灾难恢复,不含敏感值):

{
  "console": { "url": "https://bencher.example.com" },
  "security": {
    "issuer": "https://api.bencher.example.com",
    "secret_key": "CHANGE_ME_UJu7Cpxb-zFaJYqXD-3mDDSDyj-ZvfxZFZs-X58xjxPy"
  },
  "server": {
    "bind_address": "0.0.0.0:6610",
    "request_body_max_bytes": 10485760
  },
  "logging": {
    "name": "Bencher API",
    "log": { "stderr_terminal": { "level": "info" } }
  },
  "database": {
    "file": "/var/lib/bencher/data/bencher.db",
    "busy_timeout": 15000,
    "cache_size": 131072
  }
}

💡 secret_key 是 JWT 签名密钥,首次启动时自动生成 UUID v4 并打码显示(************),千万不能丢——丢了意味着所有已发 JWT 全部失效,用户需要重新登录。

八、配置文件挂载

# compose 片段
services:
  bencher_api_compose:
    volumes:
      - "./services/api/etc:/etc/bencher"   # ← 放 bencher.json
      - "./services/api/data:/var/lib/bencher/data"

首次启动前如果 etc/ 目录是空的,容器会用内置默认配置(API bind 0.0.0.0:6610、SQLite 路径对、secret_key 自动生成 UUID)——足够 PoC。

九、等价 docker run

不想走 compose,直接 docker run 也能拉起:

docker network create bencher-0613-net --driver bridge

# API Server
docker run -d \
  --name bencher_api \
  --network bencher-0613-net \
  -p 6610:6610 \
  -v /opt/bencher-stack/services/api/etc:/etc/bencher \
  -v /opt/bencher-stack/services/api/data:/var/lib/bencher/data \
  --restart unless-stopped \
  ghcr.io/bencherdev/bencher-api:v0.6.13

# Console Server
docker run -d \
  --name bencher_console \
  --network bencher-0613-net \
  -p 3000:3000 \
  -e BENCHER_API_URL=http://127.0.0.1:6610 \
  -e INTERNAL_API_URL=http://bencher_api:6610 \
  --restart unless-stopped \
  ghcr.io/bencherdev/bencher-console:v0.6.13

curl -s http://localhost:6610/v0/server/version
# {"version":"0.6.13"}

十、常用运维命令

# 查看所有容器状态
docker compose -p bencher-0613-stack ps

# 实时日志
docker compose -p bencher-0613-stack logs -f bencher_api_compose
docker compose -p bencher-0613-stack logs -f bencher_console_compose
# 也可以用 CLI(封装了上面这条)
bencher logs --detach=false   # ⚠️ 官方 CLI 也提供 logs 子命令

# 进入容器排障
docker exec -it bencher_api bash

# 停止并保留容器
docker compose -p bencher-0613-stack stop

# 停止 + 删容器(数据目录保留)
docker compose -p bencher-0613-stack down

# 停止 + 删容器 + 删数据卷(⚠️ 不可逆)
docker compose -p bencher-0613-stack down -v

# 升级:改 compose image tag → down → pull → up
sed -i 's/v0\.6\.13/v0.6.14/g' docker-compose.yml
docker compose -p bencher-0613-stack down
docker compose -p bencher-0613-stack pull
docker compose -p bencher-0613-stack up -d

十一、常见问题

Q1:bencher_console 启动报错 "ECONNREFUSED http://bencher_api_compose:6610"?

两个容器没在同一网络或 API 容器还没起来。检查 compose 里两个 service 的 networks: 段,以及 depends_on.condition 是否设为 service_healthy。

Q2:Console 能打开,但跑 mock/run 时 API 401?

API Server 默认 无鉴权(第一次启动时没有用户/项目)。bencher run/mock 会自动创建临时 project,只要 Console 和 API 在同一 network 且端口配对正确就能通。401 通常意味着你自己配置了 SMTP 或 SSO 但没配对。

Q3:改了 Console 端口(--console-port 13000)但 API 还是打 3000?

必须同步改 bencher.json 的 console.url,或者 bencher up --api-env 传进去。两个必须一起改。

Q4:API Server 的 SQLite 会不会锁死?

SQLite WAL 模式下多写者排队,busy_timeout 默认 5s 超时会报 database is locked。高并发场景下调大到 15s 并把 cache_size 翻倍(见 C1 篇 database 字段表)。

Q5:compose up 后拉不到镜像?

ghcr.io 匿名限流。docker logout ghcr.io 清 token 再试;或者配 daemon HTTP proxy(~/.docker/config.json):

{
  "proxies": {
    "ghcr.io": {
      "httpProxy": "http://192.168.1.62:7897",
      "httpsProxy": "http://192.168.1.62:7897"
    }
  }
}

十二、参考链接