Appearance
部署脚本
本节实现一个自动化应用部署脚本,支持版本管理、滚动发布、健康检查和一键回滚。
功能设计
- 从 Git 拉取指定版本代码
- 执行构建和依赖安装
- 优雅停止旧服务
- 启动新版本并验证健康状态
- 失败时自动回滚
完整部署脚本
bash
#!/usr/bin/env bash
#
# 应用自动化部署脚本
# 用法:./deploy.sh <版本/分支> [--env production|staging]
#
set -euo pipefail
# ============================================================
# 配置区
# ============================================================
APP_NAME="myapp"
APP_USER="www-data"
DEPLOY_BASE="/opt/${APP_NAME}"
RELEASES_DIR="${DEPLOY_BASE}/releases"
SHARED_DIR="${DEPLOY_BASE}/shared"
CURRENT_LINK="${DEPLOY_BASE}/current"
REPO_URL="git@github.com:company/myapp.git"
KEEP_RELEASES=5 # 保留最近 N 个版本
HEALTH_CHECK_URL="http://localhost:8080/health"
HEALTH_CHECK_RETRY=10
HEALTH_CHECK_INTERVAL=3
# ============================================================
# 工具函数
# ============================================================
log() { echo "[$(date '+%H:%M:%S')] $*"; }
success() { echo "[$(date '+%H:%M:%S')] ✅ $*"; }
warn() { echo "[$(date '+%H:%M:%S')] ⚠️ $*" >&2; }
error() { echo "[$(date '+%H:%M:%S')] ❌ $*" >&2; exit 1; }
# 执行命令(以指定用户)
run_as() {
sudo -u "$APP_USER" bash -c "$*"
}
# ============================================================
# 部署步骤
# ============================================================
# 1. 检查依赖
check_dependencies() {
log "检查依赖..."
for cmd in git curl systemctl; do
command -v "$cmd" >/dev/null 2>&1 || error "缺少依赖:$cmd"
done
success "依赖检查通过"
}
# 2. 拉取代码
pull_code() {
local version="$1"
local release_dir="${RELEASES_DIR}/${TIMESTAMP}"
log "拉取代码:版本 $version → $release_dir"
mkdir -p "$release_dir"
git clone --depth 1 --branch "$version" "$REPO_URL" "$release_dir" \
|| error "代码拉取失败"
success "代码拉取完成(commit: $(git -C "$release_dir" rev-parse --short HEAD))"
echo "$release_dir"
}
# 3. 安装依赖
install_dependencies() {
local release_dir="$1"
log "安装依赖..."
if [[ -f "${release_dir}/package.json" ]]; then
cd "$release_dir" && npm ci --production 2>&1 | tail -5
elif [[ -f "${release_dir}/requirements.txt" ]]; then
pip install -r "${release_dir}/requirements.txt" -q
elif [[ -f "${release_dir}/pom.xml" ]]; then
cd "$release_dir" && mvn package -q -DskipTests
fi
success "依赖安装完成"
}
# 4. 创建共享目录链接
link_shared() {
local release_dir="$1"
log "链接共享目录..."
mkdir -p "${SHARED_DIR}/logs" "${SHARED_DIR}/uploads" "${SHARED_DIR}/config"
ln -sf "${SHARED_DIR}/logs" "${release_dir}/logs"
ln -sf "${SHARED_DIR}/uploads" "${release_dir}/uploads"
ln -sf "${SHARED_DIR}/config" "${release_dir}/config"
success "共享目录链接完成"
}
# 5. 切换当前版本
switch_current() {
local release_dir="$1"
log "切换当前版本:$release_dir"
ln -snf "$release_dir" "$CURRENT_LINK"
success "版本切换完成"
}
# 6. 重启服务
restart_service() {
log "重启服务:$APP_NAME"
systemctl restart "$APP_NAME" || error "服务重启失败"
success "服务重启完成"
}
# 7. 健康检查
health_check() {
log "健康检查:$HEALTH_CHECK_URL"
local retry=0
while (( retry < HEALTH_CHECK_RETRY )); do
if curl -sf "$HEALTH_CHECK_URL" > /dev/null 2>&1; then
success "健康检查通过"
return 0
fi
(( retry++ ))
log "等待服务就绪... (${retry}/${HEALTH_CHECK_RETRY})"
sleep "$HEALTH_CHECK_INTERVAL"
done
error "健康检查失败,服务未在预期时间内就绪"
}
# 8. 清理旧版本
cleanup_releases() {
log "清理旧版本(保留最新 $KEEP_RELEASES 个)..."
local releases
releases=$(ls -1t "$RELEASES_DIR" | tail -n +$(( KEEP_RELEASES + 1 )))
if [[ -z "$releases" ]]; then
log "无需清理"
return 0
fi
echo "$releases" | while read -r rel; do
log "删除旧版本:$rel"
rm -rf "${RELEASES_DIR:?}/$rel"
done
success "旧版本清理完成"
}
# 回滚到上一个版本
rollback() {
warn "部署失败,开始回滚..."
local prev_release
prev_release=$(ls -1t "$RELEASES_DIR" | sed -n '2p')
if [[ -z "$prev_release" ]]; then
error "没有可回滚的版本"
fi
ln -snf "${RELEASES_DIR}/$prev_release" "$CURRENT_LINK"
systemctl restart "$APP_NAME" || true
warn "已回滚到版本:$prev_release"
}
# ============================================================
# 主流程
# ============================================================
VERSION="${1:?请提供部署版本或分支名}"
ENV="${2:-production}"
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
log "================================================"
log "开始部署 $APP_NAME"
log " 版本:$VERSION"
log " 环境:$ENV"
log " 时间:$TIMESTAMP"
log "================================================"
# 注册回滚钩子
trap rollback ERR
mkdir -p "$RELEASES_DIR" "$SHARED_DIR"
check_dependencies
release_dir=$(pull_code "$VERSION")
install_dependencies "$release_dir"
link_shared "$release_dir"
switch_current "$release_dir"
restart_service
health_check
cleanup_releases
# 取消回滚钩子
trap - ERR
success "================================================"
success "部署成功!"
success " 版本:$VERSION"
success " 路径:$release_dir"
success "================================================"使用方法
bash
chmod +x deploy.sh
# 部署指定版本
./deploy.sh v1.2.3
# 部署到 staging 环境
./deploy.sh main --env staging
# 查看当前版本
readlink /opt/myapp/current
# 手动回滚
ls /opt/myapp/releases/ # 查看所有版本
ln -snf /opt/myapp/releases/<旧版本> /opt/myapp/current
systemctl restart myapp