本文档说明如何使用 run_docker_in_server.sh 脚本通过 Docker 部署 CodeBuddy2API。
脚本直接驱动 docker 命令,不依赖 docker-compose;镜像 tag 取自 git commit hash 前缀,便于版本追溯与回滚。
- Docker(或 Podman,命令兼容)
- curl(脚本在清理旧镜像前用它探测服务就绪)
- git(用于解析 commit hash 打 tag;非 git 仓库需设
ALLOW_UNVERSIONED_BUILD=1)
用 Podman 时:shell alias(如
docker=podman)不会被脚本子进程继承。若系统只有 podman 而无真实docker二进制,请设置DOCKER_BIN=podman:export DOCKER_BIN=podman ./run_docker_in_server.sh deploy否则脚本会报
找不到 Docker 可执行文件: docker。
确认(用 podman 时把 docker 换成 podman,或设 DOCKER_BIN=podman):
docker version && curl --version && git --version在仓库根目录执行:
# 1. 构建并启动(自动按 git hash 打 tag、生成配置、等待 /health 就绪、清理旧镜像)
./run_docker_in_server.sh deploy首次启动会自动生成一个随机密码并写入 data/config/config.yaml,同时在启动日志中打印:
Generated random password: <xxxx>
This password is stored in config/config.yaml. Change it as needed.
查看密码:
# 从启动日志获取(podman 用户用 podman logs,或设 DOCKER_BIN=podman)
docker logs codebuddy2api 2>&1 | grep "Generated random password"
# 或直接看配置文件
grep "password:" data/config/config.yaml如需自定义密码,编辑配置后再次 deploy:
vim data/config/config.yaml # 修改 server.password
./run_docker_in_server.sh deploy # 重新部署使新配置生效若只想构建镜像(如 CI 场景,不启动容器),用
./run_docker_in_server.sh build,详见命令详解。
部署成功后,服务监听在 http://127.0.0.1:8111(默认端口):
| 接口 | 地址 |
|---|---|
| OpenAI 兼容 | http://127.0.0.1:8111/codebuddy/v1 |
| Anthropic 兼容 | http://127.0.0.1:8111/codebuddy |
| 健康检查 | http://127.0.0.1:8111/health |
| Web 管理后台 | http://127.0.0.1:8111/ |
脚本提供 8 个子命令:
按 git hash 打 tag 构建镜像,不启动容器、不清理旧镜像。适合 CI 构建推送、或先 build 再单独 start 的场景。
./run_docker_in_server.sh build构建流程:
- 解析 git commit hash(环境变量显式指定 >
git HEAD>local) - 检查工作区是否干净(有未提交修改需
ALLOW_DIRTY_BUILD=1) - 以
<hash 前 N 位>作为镜像 tag(默认 N=8) - 若同名 tag 镜像已存在且 revision 一致:跳过构建(no-op),除非设
FORCE_REBUILD=1 - 否则执行
docker build(podman 用户脚本自动用podman build)构建,注入 hash 到org.opencontainers.image.revisionlabel
即:同一 commit 重复
build不会重建,避免重复劳动。改了代码提交新 commit 后 hash 变化,自然会构建新 tag。
等同于 build + start + 清理旧镜像,一步到位:
./run_docker_in_server.sh deploy完整流程:构建(同 build)→ 预检 HOST_PORT 是否被占用(优先 lsof,回退查容器端口)→ 启动容器(bind mount 持久化目录)→ 轮询 /health 等待就绪(超时 60s)→ 清理旧镜像(保留最近 IMAGE_RETENTION_COUNT 个,默认 5;受保护镜像一律跳过且不占名额)。
重复 deploy 同一 commit:若镜像已存在且 revision 一致,跳过构建,直接复用该镜像重启容器。这适合"只改了配置、没改代码"的场景——编辑 data/config/config.yaml 后直接 deploy 即可让新配置生效,无需重建镜像。设 FORCE_REBUILD=1 可强制重建。
用已构建好的镜像启动容器,不构建、不清理镜像。必须指定 IMAGE_TAG 或 IMAGE_NAME:
IMAGE_TAG=f68f2225 ./run_docker_in_server.sh start
# 或
IMAGE_NAME=codebuddy2api:f68f2225 ./run_docker_in_server.sh start列出指定 repository 下的本地镜像,包含保护状态与 git revision,便于追溯版本:
./run_docker_in_server.sh images输出示例:
IMAGE IMAGE ID CREATED PROTECTED REVISION
codebuddy2api:f68f2225 a1b2c3d4e5f6 2026-06-17 15:00:00 +0800 yes f68f2225867708ef0d57d8732c3e07334079ebf3
codebuddy2api:e0f5725a b2c3d4e5f6a7 2026-06-17 16:00:00 +0800 - e0f5725a3c...
PROTECTED 列为 yes 表示该 tag 已被 protect 标记,不会被自动清理。
删除指定 tag 的镜像:
IMAGE_TAG=f68f2225 ./run_docker_in_server.sh rm-image若该 tag 受保护(见 保护版本),删除会被拒绝,需加 --force:
IMAGE_TAG=f68f2225 ./run_docker_in_server.sh rm-image --force--force 删除后会自动从保护清单移除该 tag。
将指定 tag 标记为受保护,使其不被 deploy 自动清理,且 rm-image 删除时需 --force:
./run_docker_in_server.sh protect f68f2225 # 保护
./run_docker_in_server.sh unprotect f68f2225 # 取消保护保护清单存于仓库根的 .protected-images(每行一个 tag,跨实例共享)。images 命令会显示每个镜像的保护状态(PROTECTED 列)。
./run_docker_in_server.sh help # 总览(含命令列表与环境变量)
./run_docker_in_server.sh help deploy # 查看 deploy 的详细帮助支持 help <command> 只打印指定命令的用法、行为、副作用及相关环境变量。可用命令:build、deploy、start、images、rm-image、protect、unprotect。
所有服务配置集中在 data/config/config.yaml(首次 deploy 时从 config.example.yaml 自动生成)。完整字段说明见 README 配置选项。
密码:首次生成配置时会自动产生一个随机密码(见快速开始),无需手动设置即可使用。如需自定义:
server:
password: "你的访问密码" # API 调用时作为 Bearer Token注意:密码为空时服务端会拒绝所有需认证的请求(返回 500),管理后台也无法登录。首次启动已自动生成随机密码,避免空密码运行。
容器自动适配:脚本首次生成配置时会自动把 server.host 改为 0.0.0.0(容器需监听全部接口,否则外部无法访问)。若你手动维护配置不希望被改,设 SKIP_CONFIG_ADAPT=1。
注意:
creds_dir保持默认.codebuddy_creds即可,它会解析到挂载的/app/.codebuddy_creds,无需修改。
所有变量均可通过环境覆盖,在命令前加 VAR=value 即可:
| 变量 | 默认值 | 说明 |
|---|---|---|
REPO_ROOT |
脚本所在目录 | 仓库根目录 |
DOCKER_BIN |
docker |
Docker/Podman 可执行文件;用 podman 时设为 podman |
CONTAINER_NAME |
codebuddy2api |
容器名 |
HOST_PORT |
8111 |
宿主机暴露端口 |
CONTAINER_PORT |
8111 |
容器内监听端口 |
DATA_ROOT |
$REPO_ROOT/data |
持久化根目录(下设 config/ creds/ logs/) |
IMAGE_REPOSITORY |
codebuddy2api |
镜像仓库名 |
IMAGE_TAG |
- | 显式指定镜像 tag |
IMAGE_NAME |
- | 显式指定完整镜像名(含 tag,优先级最高) |
IMAGE_TAG_LENGTH |
8 |
git hash 截取长度(6 ~ hash 长度) |
IMAGE_RETENTION_COUNT |
5 |
本地保留镜像数,0 表示不清理 |
STARTUP_READY_URL |
http://127.0.0.1:$HOST_PORT/health |
就绪探测 URL |
STARTUP_READY_TIMEOUT_SECONDS |
60 |
就绪探测总超时 |
STARTUP_READY_INTERVAL_SECONDS |
2 |
就绪探测轮询间隔 |
STARTUP_READY_REQUEST_TIMEOUT_SECONDS |
2 |
单次 curl 超时 |
构建行为控制:
| 变量 | 作用 |
|---|---|
CODEBUDDY2API_GIT_COMMIT_HASH |
显式指定构建用的完整 git hash(40/64 位十六进制) |
ALLOW_DIRTY_BUILD=1 |
工作区有未提交修改时仍允许构建 |
ALLOW_UNVERSIONED_BUILD=1 |
非 git 仓库时允许构建(tag=local) |
FORCE_REBUILD=1 |
同 tag 且 revision 相同时强制重建 |
SKIP_CONFIG_ADAPT=1 |
跳过自动把 server.host 改为 0.0.0.0 |
# 使用非默认端口(避免与本机已占用的 8111 冲突)
HOST_PORT=8112 ./run_docker_in_server.sh deploy
# 自定义数据目录
DATA_ROOT=/data/codebuddy2api ./run_docker_in_server.sh deploy
# 工作区有未提交修改时强制构建
ALLOW_DIRTY_BUILD=1 ./run_docker_in_server.sh deploy
# 同一 commit 强制重新构建
FORCE_REBUILD=1 ./run_docker_in_server.sh deploy
# 指定完整镜像名启动
IMAGE_NAME=codebuddy2api:f68f2225 ./run_docker_in_server.sh start脚本通过 bind mount 把宿主机目录映射到容器内,容器可随意删除重建而数据不丢:
| 宿主机路径 | 容器路径 | 用途 |
|---|---|---|
$DATA_ROOT/config |
/app/config |
配置文件 config.yaml |
$DATA_ROOT/creds |
/app/.codebuddy_creds |
CodeBuddy 认证凭证(JSON) |
$DATA_ROOT/logs |
/app/logs |
运行日志(自动轮转压缩) |
默认 DATA_ROOT=$REPO_ROOT/data。建议生产环境指向独立的持久化磁盘:
DATA_ROOT=/data/codebuddy2api ./run_docker_in_server.sh deploy首次部署后,凭证需通过 Web 管理后台(
http://<host>:<port>/)或直接放入$DATA_ROOT/creds/添加。
配置文件可以直接挂载单文件(如 -v ./config.yaml:/app/config/config.yaml),但本脚本选择挂载整个 config/ 目录,原因:
- 首次部署更稳:当宿主机单文件不存在时,Docker 会把它当作目录创建,导致容器内
open()一个目录报IsADirectoryError。挂载目录则没有这个问题——Docker 正常创建空目录,脚本再往里生成config.yaml。 - 脚本可自动生成/适配配置:首次 deploy 从
config.example.yaml拷贝并改写server.host,依赖目录可写。
如果你的 config.yaml 由外部工具(CI / 配置管理中心)生成并维护、不希望脚本干预,可设 SKIP_CONFIG_ADAPT=1 跳过自动改写;此时仍挂载目录,但脚本不会触碰文件内容。
每次 deploy 用 git commit hash 前 8 位作 tag,完整 hash 写入镜像 label:
# 查看所有本地镜像及其对应 commit
./run_docker_in_server.sh images
# 查看某镜像对应的完整 commit(podman 用户把 docker 换成 podman)
docker image inspect --format '{{ index .Config.Labels "org.opencontainers.image.revision" }}' codebuddy2api:<tag># 1. 查看可用版本
./run_docker_in_server.sh images
# 2. 用旧版本镜像启动(不重新构建)
IMAGE_TAG=<旧版本tag> ./run_docker_in_server.sh startdeploy 完成后会自动清理旧镜像,仅保留最近 IMAGE_RETENTION_COUNT(默认 5)个。设为 0 关闭自动清理:
IMAGE_RETENTION_COUNT=10 ./run_docker_in_server.sh deploy # 保留 10 个
IMAGE_RETENTION_COUNT=0 ./run_docker_in_server.sh deploy # 不清理有些版本(如稳定发布版)希望长期保留、不被自动清理。用 protect 标记:
# 构建并保护一个版本
./run_docker_in_server.sh build
./run_docker_in_server.sh protect <tag>
# 查看保护状态
./run_docker_in_server.sh images
# IMAGE IMAGE ID CREATED PROTECTED REVISION
# codebuddy2api:f68f2225 a1b2c3d4e5f6 2026-06-17 15:00:00 yes f68f2225...受保护镜像:
- 不会被
deploy的自动清理删除(且不占用IMAGE_RETENTION_COUNT名额,保护多了也不会挤掉别的)。 rm-image删除时会被拒绝,需--force(删除后自动从清单移除)。
取消保护:
./run_docker_in_server.sh unprotect <tag>保护清单存于仓库根 .protected-images(每行一个 tag),跨实例共享——不论哪个 DATA_ROOT/CONTAINER_NAME 的实例执行 deploy,受保护镜像都不会被清。
手动删除指定镜像(受保护的需 --force):
IMAGE_TAG=<tag> ./run_docker_in_server.sh rm-imageDockerfile 采用多阶段构建:
- builder 阶段:用
uv安装依赖到/app/.venv,安装项目本身,运行 import 冒烟测试。 - runtime 阶段:仅拷贝
.venv+ 轻量应用文件,预创建三个挂载点,配置 healthcheck。
构建参数(脚本 build/deploy 已封装,以下为底层等价命令;podman 用户把 docker 换成 podman):
docker build \
-f Dockerfile \
--build-arg CODEBUDDY2API_GIT_COMMIT_HASH=<完整hash> \
-t codebuddy2api:<hash前8位> .镜像源可通过 REGISTRY build-arg 覆盖(默认 docker.xuanyuan.run):
docker build --build-arg REGISTRY=docker.io ...start/deploy 启动前会预检 HOST_PORT(优先用 lsof,回退查 docker ps),若被占用会直接报错并提示占用者,例如:
端口 8111 已被占用: python3.1 (PID 1642)
如需多版本并存,请用不同的 HOST_PORT 和 CONTAINER_NAME,例如:
HOST_PORT=8112 CONTAINER_NAME=codebuddy2api-v2 ./run_docker_in_server.sh start
端口 8111 被占用,无法启动容器 codebuddy2api。
换端口即可:
HOST_PORT=8112 ./run_docker_in_server.sh deploy多版本并存:要同时跑多个版本,给每个实例不同的
HOST_PORT、CONTAINER_NAME和DATA_ROOT(否则配置/凭证会串):HOST_PORT=8111 CONTAINER_NAME=cb2api-v1 DATA_ROOT=./data-v1 IMAGE_TAG=<旧hash> ./run_docker_in_server.sh start HOST_PORT=8112 CONTAINER_NAME=cb2api-v2 DATA_ROOT=./data-v2 IMAGE_TAG=<新hash> ./run_docker_in_server.sh start脚本按
CONTAINER_NAME精确匹配删除旧容器,不会影响其他实例。
当前 git 工作区存在未提交修改。请先提交/清理,或显式设置 ALLOW_DIRTY_BUILD=1。
提交修改后重试,或显式允许:
ALLOW_DIRTY_BUILD=1 ./run_docker_in_server.sh deploy脚本会打印容器最近 80 条日志。常见原因:
config.yaml缺少server.password:首次启动会自动生成随机密码(日志可见),正常不会出现空密码。若手动删过密码导致为空,服务端对需认证的请求返回 500、管理后台无法登录。编辑data/config/config.yaml设置密码后重新 deploy。- 忘记密码:从启动日志
docker logs codebuddy2api 2>&1 | grep "Generated random password"获取(podman 用户用podman logs,或设DOCKER_BIN=podman),或直接查看data/config/config.yaml中的server.password。 - 凭证未添加:服务能启动但 API 调用会报无凭证。通过 Web 后台添加凭证。
- 端口冲突 / 配置错误:查看日志
docker logs codebuddy2api(podman 用户用podman logs)。
延长探测超时:
STARTUP_READY_TIMEOUT_SECONDS=120 ./run_docker_in_server.sh deployserver.host 仍为 127.0.0.1,容器只监听 loopback。检查 data/config/config.yaml:
grep "host:" data/config/config.yaml应为 host: 0.0.0.0。若不是,手动改后重新 deploy,或确认未设 SKIP_CONFIG_ADAPT=1。
docker logs -f codebuddy2api # podman 用户: podman logs -f codebuddy2api
# 或查看挂载的日志文件
ls data/logs/docker exec -it codebuddy2api bash # podman 用户: podman exec -it codebuddy2api bash# 停止并删除容器(podman 用户把 docker 换成 podman)
docker rm -f codebuddy2api
# 删除所有本项目镜像
./run_docker_in_server.sh images # 查看有哪些
IMAGE_TAG=<tag> ./run_docker_in_server.sh rm-image # 逐个删除./run_docker_in_server.sh help # 查看用法(支持 help <command> 看详情)
./run_docker_in_server.sh build # 仅构建镜像(不启动)
./run_docker_in_server.sh deploy # 构建+启动+就绪检查+清理
./run_docker_in_server.sh images # 列出镜像(含 revision/保护状态)
IMAGE_TAG=<tag> ./run_docker_in_server.sh start # 启动已有镜像
IMAGE_TAG=<tag> ./run_docker_in_server.sh rm-image # 删除镜像
IMAGE_TAG=<tag> ./run_docker_in_server.sh rm-image --force # 强制删除受保护镜像
./run_docker_in_server.sh protect <tag> # 保护版本(防自动清理)
./run_docker_in_server.sh unprotect <tag> # 取消保护
HOST_PORT=8112 ./run_docker_in_server.sh deploy # 指定端口
DATA_ROOT=/data/cb2api ./run_docker_in_server.sh deploy # 指定数据目录
DOCKER_BIN=podman ./run_docker_in_server.sh deploy # 用 podman 代替 docker
docker logs -f codebuddy2api # 查看日志 (podman: podman logs)
docker exec -it codebuddy2api bash # 进入容器 (podman: podman exec)