正在加载文档...
文档内容较大,正在处理中,请稍候
正在加载文档...
文档内容较大,正在处理中,请稍候
本文档详细说明了前后端版本管理系统的实现原理和使用方法,涵盖前端软链接机制、后端 Docker 镜像版本管理、版本部署、自动备份、版本回滚、版本清理等核心功能,确保前后端部署的可靠性和可追溯性。
版本管理系统提供了前后端统一的版本管理方案:
前端版本管理系统是一个基于**软链接(Symbolic Link)**的版本管理方案,它提供了:
后端版本管理系统基于 Docker 镜像标签进行版本管理,它提供了:
在生产环境中,前后端版本管理都至关重要:
# 在服务器上执行
cd /opt/docker_project
./scripts/auto-deploy.sh deploy v1.0.0# 查看部署状态(包含前后端版本信息)
./scripts/auto-deploy.sh status
# 查看前端软链接
ls -la frontend/current
# 输出: current -> versions/v1.0.0
# 查看后端版本(.env 文件)
grep BACKEND_VERSION .env
# 输出: BACKEND_VERSION=v1.0.0
# 查看后端 Docker 镜像
docker images | grep project-root-backend# 回滚到最新备份(前后端同时回滚)
./scripts/auto-deploy.sh rollback
# 回滚到指定版本
./scripts/auto-deploy.sh rollback v1.0.0恭喜! 你已经掌握了版本管理的基本使用。接下来让我们深入了解各个概念。
软链接是 Linux/Unix 系统中的一种特殊文件,它指向另一个文件或目录。在前端版本管理中,我们使用软链接来实现零停机部署。
软链接类似于 Windows 系统中的"快捷方式",它不存储实际数据,而是存储指向目标路径的引用。
# 创建软链接
ln -sfn versions/v1.0.0 current
# 查看软链接
ls -la current
# 输出: lrwxrwxrwx 1 root root 18 Dec 24 10:00 current -> versions/v1.0.0
# 访问软链接
cd current # 实际访问的是 versions/v1.0.0 目录frontend/
├── versions/ # 版本存储目录
│ ├── v1.0.0/ # 版本 1.0.0 的完整文件
│ ├── v1.0.1/ # 版本 1.0.1 的完整文件
│ └── v1.0.2/ # 版本 1.0.2 的完整文件
├── backups/ # 备份存储目录
│ ├── v1.0.0/ # 版本 1.0.0 的备份
│ └── v1.0.1/ # 版本 1.0.1 的备份
└── current -> versions/v1.0.2 # 软链接,指向当前版本工作流程:
frontend/currentcurrent 软链接指向 versions/v1.0.2versions/v1.0.2 目录current 软链接指向 versions/v1.0.3前端版本管理的目录结构如下:
/opt/docker_project/frontend/
├── versions/ # 版本存储目录
│ ├── v1.0.0/ # 版本 1.0.0 的完整文件
│ │ ├── index.html
│ │ ├── js/
│ │ ├── css/
│ │ └── static/
│ ├── v1.0.1/ # 版本 1.0.1 的完整文件
│ └── v1.0.2/ # 版本 1.0.2 的完整文件
├── backups/ # 备份存储目录
│ ├── v1.0.0/ # 版本 1.0.0 的备份
│ ├── v1.0.1/ # 版本 1.0.1 的备份
│ └── v1.0.2/ # 版本 1.0.2 的备份
├── protect/ # 受保护目录(SSL证书、统计页面等)
│ └── demo.html
├── current -> versions/v1.0.2 # 软链接,指向当前版本
└── version.conf # 版本配置文件| 目录/文件 | 说明 | 用途 |
|---|---|---|
versions/ |
版本存储目录 | 存储所有部署的版本,每个版本一个子目录 |
backups/ |
备份存储目录 | 存储部署前的自动备份,用于回滚 |
protect/ |
受保护目录 | 存储 SSL 证书、统计页面等需要保留的文件 |
current |
软链接 | 指向当前生效的版本目录 |
version.conf |
版本配置 | 记录当前版本号和部署时间 |
版本号采用**语义化版本(Semantic Versioning)**规范,前后端使用统一的版本号:
v主版本号.次版本号.修订号示例:
v1.0.0 - 主版本 1,次版本 0,修订号 0v1.0.1 - 主版本 1,次版本 0,修订号 1(修复 bug)v1.1.0 - 主版本 1,次版本 1,修订号 0(新增功能)v2.0.0 - 主版本 2,次版本 0,修订号 0(重大更新)版本号获取优先级:
./scripts/auto-deploy.sh deploy v1.0.0version-info.txt 读取package.json 的 version 字段读取deploy_YYYYMMDD_HHMMSS前后端版本同步:
frontend/version.conf 和软链接中.env 文件的 BACKEND_VERSION 变量中project-root-backend:v1.0.0后端使用 Docker 镜像标签进行版本管理:
# Docker 镜像命名规范
project-root-backend:v1.0.0镜像标签说明:
project-root-backend:镜像名称(固定)v1.0.0:版本标签(语义化版本号)latest:最新版本标签(自动指向最新版本)后端版本信息存储在 .env 文件中:
# .env 文件
BACKEND_VERSION=v1.0.0Docker Compose 配置:
# docker-compose.yml
services:
backend:
image: ${PROJECT_NAME:-project-root}-backend:${BACKEND_VERSION:-latest}工作原理:
.env 文件中的 BACKEND_VERSIONproject-root-backend:v1.0.0.env 文件并重启服务backup/
└── backend/
├── project-root-backend-v1.0.0.tar # 版本 1.0.0 的镜像文件
├── project-root-backend-v1.0.1.tar # 版本 1.0.1 的镜像文件
└── project-root-backend-v1.0.2.tar # 版本 1.0.2 的镜像文件版本文件说明:
.tar 格式的 Docker 镜像导出文件project-root-backend-版本号.tarbackup/backend/ 目录(部署前上传的文件)前端版本部署的完整流程如下:
1. 检查 backup 目录
↓
2. 备份当前版本(如果存在)
↓
3. 提取版本号(从 version-info.txt 或 package.json)
↓
4. 复制前端文件到 versions/版本号/
↓
5. 同步受保护目录内容
↓
6. 更新 current 软链接
↓
7. 更新版本配置文件
↓
8. 清理旧版本(保留最近 5 个)后端版本部署的完整流程如下:
1. 检查 backup/backend 目录
↓
2. 查找 Docker 镜像文件(*.tar)
↓
3. 停止当前运行的服务
↓
4. 删除旧版本镜像(如果存在)
↓
5. 加载新版本镜像(docker load)
↓
6. 提取版本号(从镜像文件名)
↓
7. 更新 .env 文件中的 BACKEND_VERSION
↓
8. 启动 Docker Compose 服务
↓
9. 健康检查
↓
10. 清理旧镜像(可选)执行 ./scripts/auto-deploy.sh deploy v1.0.0 时的完整流程:
1. 检查 backup 目录
↓
2. 备份当前前端版本
↓
3. 加载后端 Docker 镜像
├── 停止服务
├── 删除旧镜像
├── 加载新镜像
└── 更新 .env 文件
↓
4. 部署前端版本
├── 复制文件到 versions/
├── 更新软链接
└── 更新版本配置
↓
5. 启动 Docker Compose 服务
↓
6. 健康检查
↓
7. 清理旧版本(前后端)部署新版本前,系统会自动备份当前版本:
current 软链接# 1. 读取当前版本
current_version=$(readlink frontend/current)
# 结果: versions/v1.0.1
# 2. 提取版本号
version_name=$(basename "$current_version")
# 结果: v1.0.1
# 3. 备份到 backups 目录
rsync -av --no-links "versions/v1.0.1/" "backups/v1.0.1/"rsync --no-links 避免软链接嵌套# 如果版本已存在,先清除旧版本中的软链接
if [ -d "versions/v1.0.0" ]; then
find "versions/v1.0.0" -type l -delete # 清除软链接
rm -rf "versions/v1.0.0" # 删除旧版本
fi重要:如果版本相同,覆盖时需要清除旧目录中的所有软链接,避免软链接嵌套问题。
# 使用 rsync 排除软链接
rsync -av --no-links "backup/frontend/" "versions/v1.0.0/"
# 或使用备用方案
cp -r "backup/frontend/" "versions/v1.0.0/"
find "versions/v1.0.0" -type l -delete # 清除软链接# 同步 protect 目录内容到新版本
rsync -av --include=".*" --no-links "protect/" "versions/v1.0.0/"受保护目录包含:
# 删除旧的软链接
rm -rf frontend/current
# 创建新的软链接
cd frontend
ln -sfn "versions/v1.0.0" current# 更新 version.conf
echo "version=v1.0.0" > frontend/version.conf
echo "deploy_time=$(date '+%Y-%m-%d %H:%M:%S')" >> frontend/version.conf# 查找 backup/backend 目录中的镜像文件
latest_image=$(find backup/backend -name "*.tar" -type f | sort -r | head -1)
# 提取镜像名称
image_name=$(basename "$latest_image" .tar)
# 结果: project-root-backend-v1.0.0# 停止可能使用该镜像的服务
docker compose down
# 确保没有容器在使用该镜像
using_containers=$(docker ps -a --filter "ancestor=$image_name" --format "{{.ID}}")
if [ -n "$using_containers" ]; then
echo "$using_containers" | xargs docker rm -f
fi# 检查镜像是否已存在
if docker images --format "{{.Repository}}:{{.Tag}}" | grep -q "^${image_name}$"; then
# 删除旧镜像
docker rmi "$image_name" --force
# 清理悬空镜像
docker image prune -f
fi# 加载 Docker 镜像
docker load -i "$latest_image"
# 验证镜像是否正确加载
docker images | grep "$image_name"# 从镜像名称中提取语义版本号
semantic_version=$(echo "$image_name" | grep -o 'v[0-9]\+\.[0-9]\+\.[0-9]\+')
# 结果: v1.0.0
# 更新 .env 文件中的 BACKEND_VERSION
if grep -q "^BACKEND_VERSION=" .env; then
sed -i "s/^BACKEND_VERSION=.*/BACKEND_VERSION=$semantic_version/" .env
else
echo "BACKEND_VERSION=$semantic_version" >> .env
fi# 启动 Docker Compose 服务
docker compose up -d
# 等待服务启动
sleep 10
# 验证服务状态
docker compose ps# 检查 API 是否响应
curl -f http://localhost:8888/health
# 检查容器日志
docker compose logs backend当新版本与旧版本相同时,如果直接覆盖,可能会导致软链接嵌套问题。系统通过以下方式避免:
versions/v1.0.0/
├── index.html
└── static/ -> ../v1.0.0/static # 软链接嵌套(错误)覆盖前清除软链接:
find "versions/v1.0.0" -type l -delete
rm -rf "versions/v1.0.0"复制时排除软链接:
rsync -av --no-links "backup/frontend/" "versions/v1.0.0/"复制后清理软链接:
cp -r "backup/frontend/" "versions/v1.0.0/"
find "versions/v1.0.0" -type l -delete版本回滚是指将当前版本切换回之前的某个版本。系统支持:
1. 检查备份目录
↓
2. 选择回滚目标版本(最新或指定)
↓
3. 删除当前软链接
↓
4. 将备份复制到 versions 目录(如果不存在)
↓
5. 创建新的软链接指向目标版本
↓
6. 更新版本配置文件1. 更新 .env 文件中的 BACKEND_VERSION
↓
2. 停止当前服务
↓
3. 启动服务(使用新版本镜像)
↓
4. 健康检查执行 ./scripts/auto-deploy.sh rollback v1.0.0 时的完整流程:
1. 检查前端备份目录
↓
2. 选择回滚目标版本
↓
3. 前端回滚
├── 删除当前软链接
├── 复制备份到 versions/
└── 创建新软链接
↓
4. 后端回滚
├── 更新 .env 文件中的 BACKEND_VERSION
└── 重启 Docker Compose 服务
↓
5. 健康检查cd /opt/docker_project
./scripts/auto-deploy.sh rollback# 回滚到 v1.0.0
./scripts/auto-deploy.sh rollback v1.0.0# 查看所有备份版本
ls -la frontend/backups/
# 查看备份版本列表(仅显示语义化版本)
ls frontend/backups/ | grep -E "^v[0-9]+\.[0-9]+\.[0-9]+" | sort -V# 自动选择最新备份
backup_to_use=$(ls -t frontend/backups/ | grep -E "^v[0-9]+\.[0-9]+\.[0-9]+" | head -1)
# 或使用指定版本
backup_to_use="v1.0.0"# 如果版本在 versions 目录已存在,先清除软链接
if [ -d "versions/$backup_to_use" ]; then
find "versions/$backup_to_use" -type l -delete
rm -rf "versions/$backup_to_use"
fi
# 复制备份到 versions 目录(排除软链接)
rsync -av --no-links "backups/$backup_to_use/" "versions/$backup_to_use/"# 删除当前软链接
rm -rf frontend/current
# 创建新的软链接
ln -sfn "versions/$backup_to_use" frontend/current# 更新 .env 文件中的 BACKEND_VERSION
sed -i "s/^BACKEND_VERSION=.*/BACKEND_VERSION=$backup_to_use/" .env
# 验证版本号已更新
grep BACKEND_VERSION .env
# 输出: BACKEND_VERSION=v1.0.0重要说明:
backup/backend/ 目录中# 停止服务
docker compose down
# 启动服务(会读取 .env 文件中的 BACKEND_VERSION)
docker compose up -d
# 等待服务启动
sleep 10
# 验证服务状态
docker compose ps
# 健康检查
curl -f http://localhost:8888/health# 检查目标版本的镜像是否已加载
target_version="v1.0.0"
image_name="project-root-backend:${target_version}"
if ! docker images --format "{{.Repository}}:{{.Tag}}" | grep -q "^${image_name}$"; then
# 镜像不存在,需要先加载
log "镜像不存在,需要从 backup/backend 目录加载"
# 查找镜像文件
image_file=$(find backup/backend -name "*${target_version}.tar" | head -1)
if [ -n "$image_file" ]; then
# 加载镜像
docker load -i "$image_file"
else
error "找不到版本 ${target_version} 的镜像文件"
exit 1
fi
fi# 备份当前 .env 文件
cp .env .env.bak
# 更新 BACKEND_VERSION
if grep -q "^BACKEND_VERSION=" .env; then
sed -i "s/^BACKEND_VERSION=.*/BACKEND_VERSION=$target_version/" .env
else
echo "BACKEND_VERSION=$target_version" >> .env
fi# 停止服务
docker compose down
# 启动服务(使用新版本)
docker compose up -d
# 等待服务完全启动
sleep 15
# 验证服务状态
docker compose ps
# 检查容器日志
docker compose logs backend --tail 50⚠️ 重要提示:
系统会自动清理旧版本,避免磁盘空间浪费:
versions/ 目录保留最近 5 个版本backups/ 目录保留最近 5 个备份ls -t 按修改时间排序,删除最旧的版本<none>:<none> 的悬空镜像部署脚本会在部署完成后自动执行清理:
# 部署时会自动清理
./scripts/auto-deploy.sh deploy v1.0.0
# 部署完成后自动清理旧版本# 清理旧版本(保留最近 5 个)
./scripts/auto-deploy.sh cleanup
# 清理旧版本并清空 backup 目录
./scripts/auto-deploy.sh cleanup --cleanup-backupcd frontend/versions
# 按修改时间排序,删除第 6 个及以后的版本
old_versions=$(ls -t | tail -n +6)
if [ -n "$old_versions" ]; then
echo "$old_versions" | xargs rm -rf
ficd frontend/backups
# 按修改时间排序,删除第 6 个及以后的备份
old_backups=$(ls -t | tail -n +6)
if [ -n "$old_backups" ]; then
echo "$old_backups" | xargs rm -rf
fi# 清理悬空镜像(<none>:<none>)
docker image prune -f
# 查看所有后端镜像
docker images | grep project-root-backend
# 手动删除指定版本的镜像
docker rmi project-root-backend:v1.0.0
# 删除所有未使用的镜像(谨慎使用)
docker image prune -a -f# 清理 backup/backend 目录中的旧镜像文件
cd backup/backend
# 按修改时间排序,删除第 6 个及以后的镜像文件
old_images=$(ls -t *.tar 2>/dev/null | tail -n +6)
if [ -n "$old_images" ]; then
echo "$old_images" | xargs rm -f
fi⚠️ 重要提示:
Nginx 配置中,root 指令指向软链接:
server {
listen 80;
server_name your-domain.com;
# 指向软链接,自动跟随到实际版本目录
root /opt/docker_project/frontend/current;
index index.html;
location / {
try_files $uri $uri/ /index.html;
}
}使用软链接的优势:
# 查看软链接
ls -la /opt/docker_project/frontend/current
# 输出: lrwxrwxrwx 1 root root 18 Dec 24 10:00 current -> versions/v1.0.2
# 查看软链接指向的实际路径
readlink -f /opt/docker_project/frontend/current
# 输出: /opt/docker_project/frontend/versions/v1.0.2
# 验证文件是否存在
ls /opt/docker_project/frontend/current/index.html
# 应该能正常访问# 查看完整的部署状态
cd /opt/docker_project
./scripts/auto-deploy.sh status输出内容:
=== 部署状态 ===
最后后端镜像: project-root-backend-v1.0.0
最后前端版本: v1.0.2
=== 前端版本信息 ===
当前前端版本: versions/v1.0.2
前端文件数量: 1234
=== 后端版本信息 ===
当前后端版本: v1.0.0
后端镜像: project-root-backend:v1.0.0
镜像大小: 450MB
容器状态: Up 2 hours
=== backup 目录状态 ===
后端镜像文件: 1 个 (/opt/docker_project/backup/backend)
前端文件: 567 个 (/opt/docker_project/backup/frontend)
=== 服务状态 ===
NAME STATUS PORTS
project-root-backend Up 2 hours 0.0.0.0:8888->8888/tcp
=== API 状态 ===
✓ API 正常
=== 前端状态 ===
✓ 前端正常# 查看所有前端版本
ls -la frontend/versions/
# 查看所有前端备份
ls -la frontend/backups/
# 查看当前前端版本
cat frontend/version.conf
# 输出:
# version=v1.0.2
# deploy_time=2025-12-24 10:00:00# 查看所有后端镜像
docker images | grep project-root-backend
# 查看当前后端版本(.env 文件)
grep BACKEND_VERSION .env
# 输出: BACKEND_VERSION=v1.0.0
# 查看 backup 目录中的镜像文件
ls -lh backup/backend/*.tar
# 查看当前运行的容器使用的镜像
docker ps --format "table {{.Names}}\t{{.Image}}\t{{.Status}}"# 查看软链接
ls -la frontend/current
# 查看软链接指向
readlink frontend/current
# 输出: versions/v1.0.2
# 查看软链接指向的实际路径
readlink -f frontend/current
# 输出: /opt/docker_project/frontend/versions/v1.0.2# 查看镜像详细信息
docker inspect project-root-backend:v1.0.0
# 查看镜像历史(构建步骤)
docker history project-root-backend:v1.0.0
# 查看镜像大小
docker images project-root-backend --format "table {{.Repository}}\t{{.Tag}}\t{{.Size}}\t{{.CreatedAt}}"
# 查看容器使用的镜像版本
docker inspect project-root-backend | grep Imagev主版本.次版本.修订号 规范.env 文件中的 BACKEND_VERSION 与实际使用的镜像版本一致v1.0、v1 等不规范版本号v1.0、v1 等不规范版本号docker compose down 停止服务问题:部署相同版本时,出现软链接嵌套,导致文件访问异常。
原因:
解决:
覆盖前清除软链接:
find "versions/v1.0.0" -type l -delete
rm -rf "versions/v1.0.0"复制时排除软链接:
rsync -av --no-links "backup/frontend/" "versions/v1.0.0/"系统已自动处理:部署脚本已包含软链接清理逻辑
问题:回滚后,前端版本正确,但后端版本号没有更新。
原因:
.env 文件中的 BACKEND_VERSION 没有更新解决:
检查 .env 文件:
grep BACKEND_VERSION .env手动更新版本号:
sed -i "s/^BACKEND_VERSION=.*/BACKEND_VERSION=v1.0.0/" .env重启服务:
docker compose restart问题:回滚时提示备份目录不存在。
原因:
解决:
检查备份目录:
ls -la frontend/backups/如果没有备份,无法回滚,需要重新部署
重要版本建议手动备份到其他位置
问题:部署时提示磁盘空间不足。
原因:
解决:
清理旧版本:
./scripts/auto-deploy.sh cleanup清理 backup 目录:
./scripts/auto-deploy.sh cleanup --cleanup-backup检查磁盘空间:
df -h手动删除不需要的版本:
rm -rf frontend/versions/v1.0.0
rm -rf frontend/backups/v1.0.0问题:current 软链接指向的版本不存在。
原因:
解决:
检查软链接:
ls -la frontend/current
readlink frontend/current检查目标目录:
ls -la frontend/versions/v1.0.0重新创建软链接:
rm -f frontend/current
ln -sfn "versions/v1.0.0" frontend/current或回滚到可用版本:
./scripts/auto-deploy.sh rollback问题:部署时无法获取版本号,使用时间戳作为版本号。
原因:
version-info.txt 文件不存在或格式错误package.json 中版本号格式不正确解决:
检查版本信息文件:
cat backup/version-info.txt手动指定版本号:
./scripts/auto-deploy.sh deploy v1.0.0修复构建脚本,确保生成版本信息文件
问题:部署时 Docker 镜像加载失败。
原因:
解决:
检查 Docker 服务状态:
docker info
systemctl status docker检查镜像文件:
ls -lh backup/backend/*.tar
file backup/backend/*.tar检查磁盘空间:
df -h
docker system df手动加载镜像:
docker load -i backup/backend/project-root-backend-v1.0.0.tar验证镜像是否加载成功:
docker images | grep project-root-backend问题:回滚后端版本时,容器无法启动。
原因:
.env 文件中的 BACKEND_VERSION 未正确更新.env 文件中的版本不一致解决:
检查目标版本的镜像是否存在:
docker images | grep project-root-backend
grep BACKEND_VERSION .env如果镜像不存在,需要先加载镜像:
# 查找镜像文件
find backup/backend -name "*v1.0.0.tar"
# 加载镜像
docker load -i backup/backend/project-root-backend-v1.0.0.tar检查 .env 文件中的版本号:
grep BACKEND_VERSION .env手动更新版本号并重启:
sed -i "s/^BACKEND_VERSION=.*/BACKEND_VERSION=v1.0.0/" .env
docker compose down
docker compose up -d问题:后端容器启动后立即退出。
原因:
解决:
检查容器日志:
docker compose logs backend
docker logs project-root-backend检查镜像是否存在:
docker images | grep project-root-backend
grep BACKEND_VERSION .env检查端口是否被占用:
netstat -tlnp | grep 8888检查依赖服务状态:
docker compose ps
docker compose logs mysql
docker compose logs redis重启所有服务:
docker compose down
docker compose up -d问题:Docker 镜像占用大量磁盘空间。
原因:
解决:
查看镜像占用空间:
docker images
docker system df清理悬空镜像:
docker image prune -f删除不需要的旧镜像:
docker rmi project-root-backend:v1.0.0清理构建缓存:
docker builder prune -f全面清理(谨慎使用):
docker system prune -a -f# 完整部署(前后端,自动检测版本号)
./scripts/auto-deploy.sh deploy
# 部署指定版本(前后端)
./scripts/auto-deploy.sh deploy v1.0.0
# 仅部署前端(自动检测版本号)
./scripts/auto-deploy.sh deploy-frontend
# 仅部署前端(指定版本)
./scripts/auto-deploy.sh deploy-frontend v1.0.1说明:
deploy:完整部署,包含前后端deploy-frontend:仅部署前端,不更新后端# 回滚到最新备份
./scripts/auto-deploy.sh rollback
# 回滚到指定版本
./scripts/auto-deploy.sh rollback v1.0.0# 清理旧版本(保留最近 5 个)
./scripts/auto-deploy.sh cleanup
# 清理旧版本并清空 backup 目录
./scripts/auto-deploy.sh cleanup --cleanup-backup# 查看部署状态
./scripts/auto-deploy.sh status
# 查看部署日志
./scripts/auto-deploy.sh logs
# 查看当前版本
cat frontend/version.conf
# 查看软链接
ls -la frontend/current
readlink frontend/current# 手动创建软链接
cd /opt/docker_project/frontend
ln -sfn "versions/v1.0.0" current
# 手动删除软链接
rm -f current
# 手动查看软链接指向
readlink current
readlink -f current# 手动加载 Docker 镜像
docker load -i backup/backend/project-root-backend-v1.0.0.tar
# 手动更新 .env 文件中的版本号
sed -i "s/^BACKEND_VERSION=.*/BACKEND_VERSION=v1.0.0/" .env
# 手动重启服务
docker compose down
docker compose up -d
# 手动查看镜像列表
docker images | grep project-root-backend
# 手动删除指定版本的镜像
docker rmi project-root-backend:v1.0.0
# 手动查看容器使用的镜像
docker inspect project-root-backend | grep Image# 进入项目根目录
cd /Volumes/Work/Template/project_root
# 构建指定版本
./scripts/build-for-deploy.sh v1.0.3
# 构建产物会生成在 build-history/日期/时间/ 目录
# 例如: build-history/20251224/143000/# 找到最新构建目录
LATEST_BUILD=$(find build-history -name "version-info.txt" -exec dirname {} \; | sort | tail -1)
# 上传到服务器 backup 目录
scp -r "$LATEST_BUILD"/* root@your-server:/opt/docker_project/backup/# 登录服务器
ssh root@your-server
# 进入项目目录
cd /opt/docker_project
# 执行自动部署
./scripts/auto-deploy.sh deploy v1.0.3# 查看部署状态
./scripts/auto-deploy.sh status
# 查看当前版本
cat frontend/version.conf
# 查看软链接
ls -la frontend/current构建脚本会自动生成 version-info.txt 文件,格式如下:
版本: v1.0.3
构建时间: Mon Dec 24 14:30:00 CST 2025
构建模式: production
Git提交: a1b2c3d4
Git分支: main
Git消息: feat: 新增版本管理功能部署脚本会按以下优先级读取版本号:
./scripts/auto-deploy.sh deploy v1.0.3版本: 或 VERSION= 字段读取package.json 的 version 字段读取deploy_YYYYMMDD_HHMMSS前端构建时会生成 version.json 文件,包含详细的版本信息:
{
"version": "v1.0.3",
"name": "react-antd-webpack",
"description": "React + Ant Design前端应用",
"buildTime": "2025-12-24T14:30:00.000Z",
"buildTimeFormatted": "2025/12/24 14:30:00",
"buildEnvironment": "production",
"git": {
"commit": "a1b2c3d4",
"branch": "main"
},
"nodeVersion": "v20.19.0",
"platform": "darwin",
"features": {
"typescript": true,
"antd": true,
"react": true
}
}前端可以通过 /version.json 接口访问版本信息。
| 特性 | 软链接(Symbolic Link) | 硬链接(Hard Link) |
|---|---|---|
| 存储方式 | 存储目标路径的引用 | 指向同一个 inode |
| 跨文件系统 | ✅ 支持 | ❌ 不支持 |
| 目录支持 | ✅ 支持 | ❌ 不支持 |
| 删除目标 | 软链接失效(悬空链接) | 硬链接仍然有效 |
| 文件大小 | 很小(仅存储路径) | 与目标文件相同 |
在前端版本管理中,我们使用软链接,因为:
# 基本语法
ln -s 目标路径 链接名称
# 常用选项
ln -sfn "versions/v1.0.0" current
# -s: 创建软链接(symbolic link)
# -f: 强制创建(如果链接已存在,先删除)
# -n: 如果目标是符号链接,跟随到实际目标# 查看软链接详细信息
ls -la current
# 输出: lrwxrwxrwx 1 root root 18 Dec 24 10:00 current -> versions/v1.0.0
# 查看软链接指向的相对路径
readlink current
# 输出: versions/v1.0.0
# 查看软链接指向的绝对路径
readlink -f current
# 输出: /opt/docker_project/frontend/versions/v1.0.0# 方法1:使用 rm(推荐)
rm -f current
# 方法2:使用 unlink
unlink current
# ⚠️ 注意:不要使用 rm -rf,可能会删除目标目录软链接的更新是原子操作,这意味着:
工作原理:
# 1. 创建新版本目录
mkdir -p versions/v1.0.3
cp -r backup/frontend/* versions/v1.0.3/
# 2. 原子性更新软链接(使用 -f 选项)
ln -sfn "versions/v1.0.3" current
# 这一步是原子操作,瞬间完成
# 3. Nginx 自动读取新版本(无需重启)
# Nginx 的 root 指令指向 current,会自动跟随到新版本当新版本与旧版本相同时,如果直接覆盖,可能会出现软链接嵌套:
versions/v1.0.0/
├── index.html
├── static/
│ └── images/ -> ../v1.0.0/static/images # 软链接嵌套(错误)
└── js/
└── app.js问题影响:
系统通过三层防护避免软链接嵌套:
覆盖前清除:
# 清除旧版本目录中的所有软链接
find "versions/v1.0.0" -type l -delete
rm -rf "versions/v1.0.0"复制时排除:
# 使用 rsync 排除软链接
rsync -av --no-links "backup/frontend/" "versions/v1.0.0/"复制后清理:
# 如果使用 cp,复制后立即清理
cp -r "backup/frontend/" "versions/v1.0.0/"
find "versions/v1.0.0" -type l -delete/opt/docker_project/
├── backup/ # 临时备份目录(部署前上传的文件)
│ ├── backend/ # 后端 Docker 镜像
│ │ └── *.tar
│ └── frontend/ # 前端静态文件
│ ├── index.html
│ ├── js/
│ └── css/
├── frontend/ # 前端版本管理目录
│ ├── versions/ # 版本存储(所有部署的版本)
│ │ ├── v1.0.0/ # 版本 1.0.0
│ │ ├── v1.0.1/ # 版本 1.0.1
│ │ └── v1.0.2/ # 版本 1.0.2(当前)
│ ├── backups/ # 备份存储(部署前的备份)
│ │ ├── v1.0.0/ # 版本 1.0.0 的备份
│ │ ├── v1.0.1/ # 版本 1.0.1 的备份
│ │ └── v1.0.2/ # 版本 1.0.2 的备份
│ ├── protect/ # 受保护目录
│ │ └── demo.html # SSL 证书、统计页面等
│ ├── current -> versions/v1.0.2 # 软链接(当前版本)
│ └── version.conf # 版本配置文件
└── logs/ # 日志目录
├── deploy.log # 部署日志
├── last-backend-image.txt
└── last-frontend-version.txt┌─────────────────────────────────────────────────────────┐
│ 本地构建环境 │
│ ./scripts/build-for-deploy.sh v1.0.3 │
│ └──> build-history/20251224/143000/ │
│ ├── backend/*.tar │
│ ├── frontend/ │
│ └── version-info.txt │
└──────────────────┬───────────────────────────────────────┘
│ scp 上传
▼
┌─────────────────────────────────────────────────────────┐
│ 服务器 backup 目录 │
│ /opt/docker_project/backup/ │
│ ├── backend/*.tar │
│ └── frontend/ │
└──────────────────┬───────────────────────────────────────┘
│ 执行部署脚本
▼
┌─────────────────────────────────────────────────────────┐
│ 自动部署脚本处理流程 │
│ 1. 备份当前前端版本 -> frontend/backups/v1.0.2/ │
│ 2. 加载后端 Docker 镜像 │
│ - docker load -i backup/backend/*.tar │
│ - 镜像: project-root-backend:v1.0.3 │
│ 3. 更新 .env 文件: BACKEND_VERSION=v1.0.3 │
│ 4. 复制前端文件 -> frontend/versions/v1.0.3/ │
│ 5. 同步受保护目录 │
│ 6. 更新前端软链接: current -> versions/v1.0.3 │
│ 7. 更新版本配置 │
│ 8. 启动 Docker Compose 服务 │
│ 9. 清理旧版本(保留最近 5 个) │
└──────────────────┬───────────────────────────────────────┘
│
├──> Nginx 自动读取
│ frontend/current -> versions/v1.0.3
│
└──> Docker Compose 启动
image: project-root-backend:v1.0.3
┌─────────────────────────────────────────────────────────┐
│ 服务运行 │
│ Nginx: root /opt/docker_project/frontend/current │
│ Backend: project-root-backend:v1.0.3 (Docker 容器) │
└─────────────────────────────────────────────────────────┘┌─────────────────────────────────────────────────────────┐
│ 回滚操作 │
│ ./scripts/auto-deploy.sh rollback v1.0.2 │
└──────────────────┬───────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ 1. 检查备份目录 │
│ frontend/backups/v1.0.2/ 存在? │
└──────────────────┬───────────────────────────────────────┘
│ 是
▼
┌─────────────────────────────────────────────────────────┐
│ 2. 删除当前软链接 │
│ rm -rf frontend/current │
└──────────────────┬───────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ 3. 复制备份到 versions 目录 │
│ rsync backups/v1.0.2/ -> versions/v1.0.2/ │
│ (排除软链接) │
└──────────────────┬───────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ 4. 创建新软链接 │
│ ln -sfn versions/v1.0.2 current │
└──────────────────┬───────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ 5. 更新后端版本号 │
│ .env: BACKEND_VERSION=v1.0.2 │
└──────────────────┬───────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ 6. 重启服务 │
│ docker compose down && docker compose up -d │
└─────────────────────────────────────────────────────────┘# 1. 本地构建新版本
./scripts/build-for-deploy.sh v1.0.3
# 2. 上传到服务器
scp -r build-history/20251224/143000/* root@server:/opt/docker_project/backup/
# 3. 服务器部署
ssh root@server "cd /opt/docker_project && ./scripts/auto-deploy.sh deploy v1.0.3"
# 4. 验证部署
ssh root@server "./scripts/auto-deploy.sh status"结果:
v1.0.2 → v1.0.3v1.0.2 已备份到 backups/current -> versions/v1.0.3# 1. 发现新版本有问题
# 2. 立即回滚到上一版本
ssh root@server "cd /opt/docker_project && ./scripts/auto-deploy.sh rollback"
# 3. 验证回滚
ssh root@server "./scripts/auto-deploy.sh status"结果:
v1.0.3 → v1.0.2current -> versions/v1.0.2# 1. 修复了 v1.0.3 的问题,重新部署相同版本
./scripts/build-for-deploy.sh v1.0.3
# 2. 上传并部署
scp -r build-history/20251224/150000/* root@server:/opt/docker_project/backup/
ssh root@server "cd /opt/docker_project && ./scripts/auto-deploy.sh deploy v1.0.3"系统处理:
v1.0.3 已存在# 1. 只更新前端,不更新后端
./scripts/build-for-deploy.sh v1.0.3
# 2. 上传前端文件
scp -r build-history/20251224/143000/frontend root@server:/opt/docker_project/backup/
# 3. 仅部署前端
ssh root@server "cd /opt/docker_project && ./scripts/auto-deploy.sh deploy-frontend v1.0.3"结果:
v1.0.2 → v1.0.3v主版本.次版本.修订号 规范.env 文件中的版本号与实际使用的镜像版本一致v1.0、v1 等不规范版本号docker compose down 停止服务版本管理是生产环境稳定性的重要保障