Skip to content

Latest commit

 

History

History
479 lines (330 loc) · 17.9 KB

File metadata and controls

479 lines (330 loc) · 17.9 KB

CodeBuddy2API Docker 部署指南

本文档说明如何使用 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 个子命令:

build — 仅构建镜像

按 git hash 打 tag 构建镜像,不启动容器、不清理旧镜像。适合 CI 构建推送、或先 build 再单独 start 的场景。

./run_docker_in_server.sh build

构建流程:

  1. 解析 git commit hash(环境变量显式指定 > git HEAD > local
  2. 检查工作区是否干净(有未提交修改需 ALLOW_DIRTY_BUILD=1
  3. <hash 前 N 位> 作为镜像 tag(默认 N=8)
  4. 若同名 tag 镜像已存在且 revision 一致:跳过构建(no-op),除非设 FORCE_REBUILD=1
  5. 否则执行 docker build(podman 用户脚本自动用 podman build)构建,注入 hash 到 org.opencontainers.image.revision label

即:同一 commit 重复 build 不会重建,避免重复劳动。改了代码提交新 commit 后 hash 变化,自然会构建新 tag。

deploy — 构建并启动

等同于 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 可强制重建。

start — 启动已有镜像

用已构建好的镜像启动容器,不构建、不清理镜像。必须指定 IMAGE_TAGIMAGE_NAME

IMAGE_TAG=f68f2225 ./run_docker_in_server.sh start
#
IMAGE_NAME=codebuddy2api:f68f2225 ./run_docker_in_server.sh start

images — 列出本地镜像

列出指定 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 标记,不会被自动清理。

rm-image — 删除镜像

删除指定 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。

protect / unprotect — 保护/取消保护版本

将指定 tag 标记为受保护,使其不被 deploy 自动清理,且 rm-image 删除时需 --force

./run_docker_in_server.sh protect f68f2225     # 保护
./run_docker_in_server.sh unprotect f68f2225   # 取消保护

保护清单存于仓库根的 .protected-images(每行一个 tag,跨实例共享)。images 命令会显示每个镜像的保护状态(PROTECTED 列)。

help — 查看用法

./run_docker_in_server.sh help              # 总览(含命令列表与环境变量)
./run_docker_in_server.sh help deploy       # 查看 deploy 的详细帮助

支持 help <command> 只打印指定命令的用法、行为、副作用及相关环境变量。可用命令:builddeploystartimagesrm-imageprotectunprotect


配置

配置文件

所有服务配置集中在 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 start

镜像清理策略

deploy 完成后会自动清理旧镜像,仅保留最近 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-image

镜像构建细节

Dockerfile 采用多阶段构建:

  1. builder 阶段:用 uv 安装依赖到 /app/.venv,安装项目本身,运行 import 冒烟测试。
  2. 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 ...

故障排查

deploy 失败:端口被占用

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_PORTCONTAINER_NAMEDATA_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 精确匹配删除旧容器,不会影响其他实例。

deploy 失败:工作区有未提交修改

当前 git 工作区存在未提交修改。请先提交/清理,或显式设置 ALLOW_DIRTY_BUILD=1。

提交修改后重试,或显式允许:

ALLOW_DIRTY_BUILD=1 ./run_docker_in_server.sh deploy

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 deploy

容器外无法访问,但 healthcheck 显示健康

server.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)