本文目录导读:

我来为你详细介绍Shell脚本实现蓝绿部署策略的方法。
蓝绿部署的基本原理
蓝绿部署维护两套完全相同的生产环境:蓝色(当前运行)和绿色(待部署),流量在两者之间切换,实现零停机部署。
完整实现脚本
基础配置脚本
#!/bin/bash
# 配置参数
APP_NAME="myapp"
BLUE_DIR="/www/${APP_NAME}/blue"
GREEN_DIR="/www/${APP_NAME}/green"
NGINX_CONF="/etc/nginx/conf.d/${APP_NAME}.conf"
BACKUP_DIR="/backup/${APP_NAME}"
# 日志函数
log() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1"
}
# 错误处理
error_exit() {
log "ERROR: $1"
exit 1
}
部署核心函数
# 获取当前活跃环境
get_active_env() {
# 检查nginx指向哪个目录
if grep -q "$BLUE_DIR" "$NGINX_CONF" 2>/dev/null; then
echo "blue"
elif grep -q "$GREEN_DIR" "$NGINX_CONF" 2>/dev/null; then
echo "green"
else
echo "unknown"
fi
}
# 构建新版本
build_new_version() {
local target_dir=$1
local version_tag=$2
log "开始构建新版本到 $target_dir"
# 创建部署目录
mkdir -p "$target_dir"
# 构建或复制应用代码
# 示例:从git拉取代码
# git clone -b $version_tag https://github.com/yourrepo/app.git "$target_dir"
# 或者复制本地构建的代码
cp -r /tmp/build/* "$target_dir/"
# 执行构建命令
cd "$target_dir" && npm install 2>&1 || {
log "构建失败"
return 1
}
log "版本构建完成"
return 0
}
# 部署到非活跃环境
deploy_to_inactive() {
local active_env=$(get_active_env)
local deploy_dir
if [ "$active_env" = "blue" ]; then
deploy_dir="$GREEN_DIR"
elif [ "$active_env" = "green" ]; then
deploy_dir="$BLUE_DIR"
else
# 首次部署,默认部署到blue
deploy_dir="$BLUE_DIR"
fi
log "部署到非活跃环境: $deploy_dir"
# 构建新版本
build_new_version "$deploy_dir" "v1.2.0" || {
error_exit "构建失败"
}
# 运行测试
run_health_check "$deploy_dir" || {
log "健康检查失败,回滚部署"
return 1
}
echo "$deploy_dir"
}
切换流量函数
# 切换Nginx配置
switch_nginx() {
local new_env=$1
local new_dir
if [ "$new_env" = "blue" ]; then
new_dir="$BLUE_DIR"
new_color="blue"
else
new_dir="$GREEN_DIR"
new_color="green"
fi
log "切换Nginx到 $new_color 环境"
# 备份当前配置
cp "$NGINX_CONF" "${NGINX_CONF}.backup.$(date +%Y%m%d%H%M%S)"
# 生成新配置
cat > "$NGINX_CONF" << EOF
upstream ${APP_NAME}_backend {
server 127.0.0.1:8000; # 修改为实际端口
}
server {
listen 80;
server_name ${APP_NAME}.example.com;
root ${new_dir};
index index.html index.php;
location / {
try_files \$uri \$uri/ /index.php?\$query_string;
}
location ~ \.php$ {
fastcgi_pass 127.0.0.1:9000;
fastcgi_param SCRIPT_FILENAME ${new_dir}\$fastcgi_script_name;
include fastcgi_params;
}
}
EOF
# 测试配置
nginx -t || {
log "Nginx配置测试失败,恢复备份"
cp "${NGINX_CONF}.backup"* "$NGINX_CONF"
return 1
}
# 重载Nginx
systemctl reload nginx || nginx -s reload || {
log "Nginx重载失败"
return 1
}
log "Nginx配置切换成功"
return 0
}
# 优雅切换(使用upstream)
graceful_switch() {
local target_env=$1
log "执行优雅切换到 $target_env"
# 创建或更新upstream配置
cat > "/etc/nginx/conf.d/${APP_NAME}_upstream.conf" << EOF
upstream ${APP_NAME}_backend {
server 127.0.0.1:8001; # 蓝环境
server 127.0.0.1:8002; # 绿环境
}
EOF
# 根据目标环境调整权重
if [ "$target_env" = "blue" ]; then
# 逐渐将流量导向蓝环境
for i in 10 30 50 70 90 100; do
sed -i "s/server 127.0.0.1:8001 weight=[0-9]*/server 127.0.0.1:8001 weight=$i/" "/etc/nginx/conf.d/${APP_NAME}_upstream.conf"
sed -i "s/server 127.0.0.1:8002 weight=[0-9]*/server 127.0.0.1:8002 weight=$((100-i))/" "/etc/nginx/conf.d/${APP_NAME}_upstream.conf"
nginx -s reload
sleep 5
done
fi
}
健康检查函数
# 健康检查
run_health_check() {
local check_dir=$1
local check_url="http://localhost:8080/health"
log "执行健康检查..."
# 检查进程是否运行
if ! pgrep -f "node.*server.js" >/dev/null; then
log "应用进程未运行"
return 1
fi
# HTTP健康检查
for i in {1..5}; do
if curl -sf "$check_url" >/dev/null 2>&1; then
log "健康检查通过"
return 0
fi
sleep 2
done
log "健康检查失败"
return 1
}
# 验证部署成功
verify_deployment() {
local verify_url=$1
local expected_content=$2
log "验证部署..."
# 发送测试请求
response=$(curl -s "$verify_url")
if echo "$response" | grep -q "$expected_content"; then
log "部署验证通过"
return 0
else
log "部署验证失败"
return 1
fi
}
回滚函数
# 回滚部署
rollback() {
local previous_env=$1
log "执行回滚到 $previous_env 环境"
# 直接切换回之前的活跃环境
switch_nginx "$previous_env" || {
error_exit "回滚失败"
}
# 停止新环境
stop_env "$(get_inactive_env)"
log "回滚完成"
}
# 停止环境
stop_env() {
local env_dir=$1
local env_name=$(basename "$env_dir")
log "停止 $env_name 环境"
# 停止应用服务
if [ -f "$env_dir/scripts/stop.sh" ]; then
bash "$env_dir/scripts/stop.sh"
fi
# 清理进程
pkill -f "$env_dir" 2>/dev/null || true
}
主部署流程
# 主部署函数
blue_green_deploy() {
log "开始蓝绿部署..."
# 1. 获取当前活跃环境
local active_env=$(get_active_env)
log "当前活跃环境: $active_env"
# 2. 备份当前版本
local backup_file="${BACKUP_DIR}/${APP_NAME}_${active_env}_$(date +%Y%m%d%H%M%S).tar.gz"
tar -czf "$backup_file" -C "/www/${APP_NAME}/" "$active_env" 2>/dev/null || {
log "备份失败,继续部署"
}
# 3. 部署到非活跃环境
local deploy_dir=$(deploy_to_inactive)
[ $? -eq 0 ] || {
log "部署到非活跃环境失败"
return 1
}
# 4. 预检查(可选)
if [ -f "pre_check.sh" ]; then
bash pre_check.sh "$deploy_dir" || {
log "预检查失败"
rollback "$active_env"
return 1
}
fi
# 5. 切换流量
local new_env=$(basename "$deploy_dir")
switch_nginx "$new_env" || {
log "流量切换失败"
rollback "$active_env"
return 1
}
# 6. 验证新环境
verify_deployment "http://test.example.com/health" "OK" || {
log "新环境验证失败,执行回滚"
rollback "$active_env"
return 1
}
# 7. 保持旧环境一段时间(便于快速回滚)
log "保持旧环境运行30分钟..."
(sleep 1800 && clean_old_env "$active_env") &
log "蓝绿部署成功完成!"
return 0
}
# 清理旧环境
clean_old_env() {
local old_env=$1
local old_dir="/www/${APP_NAME}/$old_env"
log "清理旧环境: $old_dir"
# 询问确认
read -p "确认删除旧环境? (y/n) " -n 1 -r
echo
if [[ $REPLY =~ ^[Yy]$ ]]; then
rm -rf "$old_dir"
log "旧环境已清理"
else
log "保留旧环境"
fi
}
使用示例
#!/bin/bash
# 加载部署脚本
source ./blue_green_deploy.sh
# 显示帮助信息
show_help() {
echo "蓝绿部署管理脚本"
echo "Usage:"
echo " ./deploy.sh deploy - 执行蓝绿部署"
echo " ./deploy.sh status - 查看部署状态"
echo " ./deploy.sh rollback - 回滚到上一版本"
echo " ./deploy.sh clean - 清理旧版本"
}
# 显示状态
show_status() {
local active_env=$(get_active_env)
echo "=================================="
echo "应用状态:"
echo " 活跃环境: $active_env"
echo " 蓝环境: $([ -d "$BLUE_DIR" ] && echo '运行中' || echo '未部署')"
echo " 绿环境: $([ -d "$GREEN_DIR" ] && echo '运行中' || echo '未部署')"
echo ""
echo "最后部署时间: $(stat -c %y "$BLUE_DIR" 2>/dev/null || echo 'N/A')"
echo "=================================="
}
# 主程序
case "${1:-help}" in
deploy)
blue_green_deploy
;;
status)
show_status
;;
rollback)
local prev_env=$(get_inactive_env)
rollback "$prev_env"
;;
clean)
local inactive_env=$(get_inactive_env)
clean_old_env "$inactive_env"
;;
help|*)
show_help
;;
esac
高级特性
灰度发布扩展
# 灰度发布(渐进式切换)
canary_deploy() {
local percentage=$1 # 10-90
# 逐步增加新环境的流量比例
for step in $(seq 10 $percentage 10); do
log "设置流量比例: 新环境 $step%"
set_traffic_weight $((100-step)) $step
sleep 10
# 监控错误率
check_error_rate || {
log "错误率异常,执行回滚"
return 1
}
done
return 0
}
数据库兼容性检查
# 数据库迁移检查
check_db_migration() {
local new_version=$1
# 检查数据库迁移脚本是否存在
if [ -f "migrations/${new_version}.sql" ]; then
log "发现数据库迁移,需先执行"
# 只读模式检查
mysql -e "SELECT 1" || {
error_exit "数据库连接失败"
}
return 0
fi
}
使用建议
- 测试环境验证:先在测试环境验证脚本
- 监控告警:配置关键指标的监控告警
- 回滚计划:始终准备好回滚方案
- 数据一致性:确保数据库迁移的向后兼容性
- 日志记录:详细记录部署过程中的所有操作
这个脚本提供了完整的蓝绿部署实现,你可以根据具体的技术栈和业务需求进行调整和扩展。