ds2api 容器升级指南:镜像怎么换,配置凭什么还在
【免费下载链接】ds2apiDeepSeek-Compatible Middleware Interface: A technical exploration project in Go, focusing on high-concurrency protocol adaptation. It serves as a reference implementation for converting diverse web protocols into standardized formats.项目地址: https://gitcode.com/GitHub_Trending/ds/ds2api
如果你在用 Docker 跑 ds2api——这个把 DeepSeek Web 对话能力转换成 OpenAI、Claude、Gemini 兼容 API 的中间件——升级通常就是一条docker-compose命令。但先回答一个反直觉的问题:镜像整个被换掉、容器被删掉重建,为什么你的 DeepSeek 账号和 API 密钥一条不少?
答案在"数据与镜像分离"这个设计里。读完这篇,你能独立完成一次 ds2api 升级、讲清每一步发生了什么,出问题也知道往哪里查。
升级不丢配置:数据与镜像分离的三行关键
仓库根目录的 docker-compose.yml 里,和升级强相关的字段就这几个:
services: ds2api: image: ghcr.io/cjackhwang/ds2api:latest ports: - "${DS2API_HOST_PORT:-6011}:5001" volumes: - ./config.json:/data/config.json environment: - DS2API_CONFIG_PATH=/data/config.jsonimage:程序本体(二进制 + 管理台静态文件),升级时被替换的只有它;volumes:把宿主机的config.json挂进容器/data/config.json,你写入其中的账号、密钥、行为设置都存在宿主机上,不在镜像里;DS2API_CONFIG_PATH:显式告诉程序去哪里读写配置,避免写到镜像内只读的/app目录。
打个比方:镜像是一张只读光盘,挂载卷是随身 U 盘。换光盘等于重装系统,U 盘却跟着你走。技术上说:配置经 bind mount 注入,新容器启动后第一次读的就是这个挂载文件。
💡 镜像内已预建
/data目录并授权给非 root 的ds2api用户。单文件挂载时宿主机config.json的权限必须让该用户可读可写,否则升级后会出现permission denied。
另外,即使你没设置DS2API_CONFIG_PATH,新版本也会优先读/data/config.json;该文件不存在但检测到历史/app/config.json时会自动回退旧路径。所以升级后"配置没丢"不是运气。
先看容器重建:up -d实际做了什么
倒着拆这条命令,容器侧在先。新镜像就位(无论是拉取还是构建出来的)后,docker-compose up -d依次:
- 对比现有容器与 compose 文件的差异;
- 发现镜像 ID 变了,停止并移除旧的
ds2api容器; - 用新镜像创建同名容器:重新挂载
./config.json、重新注入.env里的DS2API_ADMIN_KEY等变量; - 按
restart: always策略后台启动。
端口映射也同步重建:宿主机默认6011(DS2API_HOST_PORT可覆盖),容器内固定5001。改过宿主机端口的话,访问地址要跟着改。
再回头看镜像重建:--build与层缓存
默认 compose 文件只声明了image:,没有build:段,本地没有源码时--build会直接报错。普通用户的"升级"因此是拉取 + 重启:
docker pull ghcr.io/cjackhwang/ds2api:latest docker-compose up -d--build留给改过源码、要验证未发布改动的人:在带构建上下文的 compose 配置里,它会先按 Dockerfile 用本地源码构建新镜像(执行前先git pull拿到最新代码;docker-compose.dev.yml 这类调试配置就带了 build 段)。
构建链路是多阶段的,一条线走完:
node:24 编译 WebUI(npm ci + npm run build)→ golang:1.26 静态交叉编译(CGO_ENABLED=0,ldflags 注入版本)→ debian:bookworm-slim 拷贝产物、以 ds2api 用户运行、监听 5001
版本号取自仓库根目录VERSION文件(当前为4.6.1),注入逻辑见 internal/version/version.go。Release 流水线另有一条runtime-from-dist路径直接消费预编译产物,保证镜像与 Release 包同源。
构建忽快忽慢由层缓存决定:Docker 逐层记录构建结果,指令输入没变就原样复用。只改 Go 源码,WebUI 构建那层直接跳过;一改webui/package.json,npm ci之后所有层全部重建。
ds2api 升级前检查清单
cp config.json config.json.bak chmod 644 config.json三件事:备份config.json(出问题时直接还原);确认宿主机文件权限;确认端口与密钥——宿主机端口默认6011,管理台登录用的是.env中的DS2API_ADMIN_KEY。
ds2api 升级路径取舍与版本回退
| 场景 | 做法 | 适合谁 |
|---|---|---|
| 常规升级 | docker pull官方 latest +docker-compose up -d | 绝大多数用户,产物与 Release 完全一致 |
| 源码改动升级 | 带 build 上下文的 compose 配置执行docker-compose up -d --build | 开发调试、验证未发布改动 |
| 回退旧版本 | 拉固定 tag 镜像,改 compose 的image字段后重启 | 新版本出问题需要退回 |
⚠️latest是移动标签,覆盖后无法"再拉回来",回退必须依赖版本 tag:
docker pull ghcr.io/cjackhwang/ds2api:v3.0.0把docker-compose.yml的image改成该 tag 再执行docker-compose up -d。配置仍在./config.json里,回退同样不丢数据。
升级后验证 ds2api 服务
curl -s http://127.0.0.1:6011/healthz # 预期 {"status":"ok"} curl -s http://127.0.0.1:6011/readyz # 预期 {"status":"ready"} curl -s http://127.0.0.1:6011/v1/models浏览器打开http://localhost:6011/admin,用DS2API_ADMIN_KEY登录,确认管理台正常加载、账号列表齐全。完整验证清单见 docs/DEPLOY.md 的"部署后检查"章节。
ds2api 升级常见问题排查表
| 现象 | 原因 | 解决办法 |
|---|---|---|
open /data/config.json: permission denied | 单文件 bind mount 对容器用户不可读写 | 宿主机执行chmod 644 config.json后重启容器 |
| 升级后管理台打不开 | 访问地址与映射端口不一致 | 检查DS2API_HOST_PORT(默认 6011),访问对应端口的/admin |
--build报错 service has no build | 默认 compose 只声明image | 改用拉取官方镜像方式,或给 compose 补 build 上下文 |
| 配置看似丢失 | 旧版本把配置写在只读的/app路径 | 显式设置DS2API_CONFIG_PATH=/data/config.json并保留./config.json挂载 |
版本号显示dev | 构建时既没传版本也没读到VERSION文件 | 带VERSION文件的完整源码重新构建,或改用官方镜像 |
收尾
ds2api 容器升级本质是两件独立的事:docker pull或--build决定新容器用哪个镜像,up -d决定怎么停旧起新,而账号与密钥完全由挂载的config.json保护。记住"镜像可换、挂载不动"这条线,升级就是固定流程:备份配置 → 查权限和端口 → 拉镜像重建容器 → 依次请求/healthz、/readyz、/v1/models→ 登录管理台收尾。
【免费下载链接】ds2apiDeepSeek-Compatible Middleware Interface: A technical exploration project in Go, focusing on high-concurrency protocol adaptation. It serves as a reference implementation for converting diverse web protocols into standardized formats.项目地址: https://gitcode.com/GitHub_Trending/ds/ds2api
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考