夜雨聆风学习资料网

ARTICLE · 1349612

改过源码的开源项目怎么升级?一套不丢数据、可回滚的流程

改过源码的开源项目怎么升级?一套不丢数据、可回滚的流程

开源项目发了新版本,你很想升级。

可你改过它的代码,服务还在线上跑着,数据库里存的是真实数据。直接 git pull?要么冲突一大片,要么把自己的修改冲掉。

我最近在一台 4G 内存的小服务器上升级了一个自部署的开源服务,折腾了一整晚。踩完坑后,我把过程整理成一套通用流程,适用于大多数“用源码构建镜像 + Docker Compose 部署”的项目。

文中的 /data/app-src、myapp:custom、app、postgres 都是示例名称,请换成你自己的目录、镜像名和服务名。

一、先备份:源码和数据分开备份

cp -a /data/app-src /data/app-src.bak-$(date +%Y%m%d)

-a 会把 .git 目录、隐藏文件、文件权限和还没提交的修改一起复制。后面的 Git 操作就算改乱了,也能用备份恢复。

但备份了源码不等于备份了数据。数据库正在运行时直接复制它的文件,得到的副本很可能不完整。数据库要单独导出:

cd /data/app-src
mkdir -p backups
docker compose exec -T postgres sh -c \
'pg_dump -U "$POSTGRES_USER" -d "$POSTGRES_DB"' \
  > backups/db-$(date +%Y%m%d).sql
ls -lh backups/

导出文件大小明显不为 0,才算导出成功。MySQL 改用 mysqldump,做法相同。

二、把自己的修改提交到本地分支

很多人以为:这是别人的项目,我没权限,不能 commit。

其实 commit 只写入你本地的仓库,不需要任何权限;需要权限的是 push。整个升级过程都不需要 push。

git switch -c local/custom-$(date +%Y%m%d)
git status
git add 你改过的文件1 你改过的文件2
git commit -m "Preserve local customizations"

两个提醒:

  • 不要用 `git add .`
    。git status 里那些红色的数据目录、.env 和 Compose 配置文件,大多是部署时生成的未跟踪文件。全部添加的话,密钥和数据库文件都会被提交进去。
  • 如果提示 Author identity unknown,可以只给这一条命令临时指定身份,不必修改全局配置:
git -c user.name="你的名字" -c user.email="you@example.com" \
  commit -m "Preserve local customizations"

三、合并上游:冲突的地方两边都要保留

合并前,先看一眼上游的 Release Notes 或 CHANGELOG,重点留意三件事:数据库迁移、配置项变更、被废弃的接口。

git fetch origin
git merge --no-commit --no-ff origin/main
git status

--no-commit 会让合并停在提交之前,你可以先检查结果,再决定是否提交。

处理冲突只有两条原则:

  1. 只修改 <<<<<<<` 和 `>&gt;&gt;&gt;&gt;&gt;&gt; 之间的冲突块,不要让整个文件直接采用你的版本或上游的版本;
  2. 两边的有效内容都保留:你的定制要留着,上游新增的字段和参数也要留着。

改完后检查有没有残留的冲突标记,命令没有任何输出才算清理干净:

git grep -nE '^(<<<<<<<|=======|>>>>>>>)'

我在这里吃过亏:复制冲突内容时漏掉了半行,合并照样提交成功了,直到构建时才报语法错误。合并成功只说明冲突标记清理干净了,不代表代码能编译。 条件允许的话,提交前先在本地跑一次构建或类型检查。

四、改动线上之前,先准备好回滚

构建新镜像时,myapp:custom 这个标签会被挪到新镜像上。所以动手前,先给当前正在运行的镜像另外打一个回滚标签:

docker tag "$(docker inspect "$(docker compose ps -q app)" --format '{{.Image}}')" \
  myapp:rollback-$(date +%Y%m%d)

有两点需要知道:

  • 构建新镜像不会影响正在运行的容器。容器一直使用旧镜像,只有重建容器后才会换成新镜像。
  • 新版本启动时可能会自动迁移数据库。迁移之后,只换回旧镜像不一定能恢复原样,所以第一步的数据库导出必不可少。

五、构建镜像:别凭感觉判断是否成功

如果 docker compose build 提示 No services to build,说明 Compose 文件里只写了 image:,没有 build: 配置。这时直接用 docker build 构建。

构建耗时较长,建议放到后台运行,这样 SSH 断开也不会中断构建:

nohup sh -c 'docker build --pull --progress=plain -t myapp:custom . ; echo "BUILD_EXIT=$?"' \
  > build.log 2>&1 &
tail -f build.log

日志末尾出现 BUILD_EXIT=0 才算构建成功。按 Ctrl+C 只会退出日志查看,不会停止构建。

然后对比新旧镜像的 ID 和创建时间:

docker image inspect myapp:custom myapp:rollback-日期 \
  --format '{{.Id}} {{.Created}}'

两个 ID 不同,而且新镜像的创建时间是刚才,才说明新镜像真的生成了。我第一次构建时服务器卡死了,重启后一查,两个 ID 一模一样,说明那次根本没构建成功。

六、内存小的服务器,前端构建最容易出问题

带前端的项目,tsc、vite build 这类步骤非常吃内存。我那台 3.6G 内存的机器,Node 用到 1.8G 左右就报了 JavaScript heap out of memory(内存不足)。

只加 swap 还是不够,最后三招一起用才构建成功。

1. 暂停服务,腾出内存(先确认当前没人在用):

docker compose stop

只用 stop,不要用 down -v,-v 会删除数据卷。

2. 添加临时 swap,先用 df -h 确认磁盘空间足够:

dd if=/dev/zero of=/data/swap-build bs=1M count=4096
chmod 600 /data/swap-build
mkswap /data/swap-build && swapon /data/swap-build
free -h

3. 调高 Node 的堆内存上限,把 Dockerfile 里的前端构建命令改成:

RUN NODE_OPTIONS=--max-old-space-size=3072 npm run build

数值不要超过机器实际可用的内存加 swap。改完的 Dockerfile 也记得提交到本地分支。

七、替换容器,并确认真的换上了新镜像

如果上一步停掉了所有服务,要先启动数据库等依赖服务,再替换应用:

docker compose up -d --no-build --pull never postgres redis
docker compose up -d --no-deps --no-build --pull never --force-recreate app
参数
作用
--no-deps
只处理应用,不重建数据库等依赖
--no-build
不重新触发构建
--pull never
不拉取远程的同名镜像,避免覆盖本地自定义镜像
--force-recreate
强制用新镜像重建容器

替换后检查:

docker compose ps
docker compose logs --tail=100 app
docker image inspect myapp:custom --format '{{.Id}}'
docker inspect "$(docker compose ps -q app)" --format '{{.Image}}'

同时满足三点,才算替换完成:容器在运行、日志没有报错、最后两条命令输出的镜像 ID 一致。

八、升级后出问题,先分层定位

服务起来了,接入它的应用却报错,先别急着回滚,用 curl 一层一层测试:

  1. 服务层
    :健康检查接口能不能访问;
  2. 认证层
    :带上密钥访问一个只读接口,能不能正常返回;
  3. 业务层
    :发一个真实请求,看具体的错误信息。

我这次遇到的几个报错,最后发现都不是升级本身造成的:一个是后台没有开启对应的协议开关,一个是客户端还在用已经被上游下线的旧模型,还有一个是上游拦截了测试内容。

升级后出现的报错,不一定是升级导致的。 先看清报错出自哪一层,再对症处理。

另外,测试时别把密钥明文留在截图或命令历史里。一旦泄露,立即在后台作废,重新生成一个。

九、收尾,附一张可收藏的清单

确认运行稳定后,删除临时 swap:

swapoff /data/swap-build && rm /data/swap-build

回滚镜像和数据库备份再保留几天,确认没问题后再清理。

最后,把整套流程压缩成一张清单:

  • 用 cp -a 备份源码,并单独导出数据库
  • 把本地修改提交到本地分支,不用 git add .
  • 先看更新说明,再用 merge --no-commit 合并
  • 冲突块两边都保留,检查残留标记,并先编译验证
  • 给旧镜像打回滚标签
  • 后台构建,确认 BUILD_EXIT=0 和新旧镜像 ID 不同
  • 内存小:暂停服务、加临时 swap、调高 Node 堆内存上限
  • 用 --no-deps --force-recreate 替换,核对容器的镜像 ID
  • 升级后分层排查,别急着回滚

升级本身不难,难的是每一步都留好退路。

如果你也在自己的服务器上跑开源项目,不妨收藏这份清单,下次升级前对照着过一遍。觉得有用的话,欢迎点赞,或转发给同样在折腾自部署的朋友。

回首页,搜索你想要的文档

在首页搜索框输入关键词,快速定位资料

文库首页搜索指引 立即搜索

相关学习资料