本文目录导读:

实现灰度发布策略的Shell脚本通常需要结合负载均衡器(如Nginx、HAProxy)或服务注册中心进行操作,以下是几种常见的实现方案及示例代码。
基于Nginx的灰度发布
通过修改upstream配置,动态调整灰度服务器权重。
脚本示例
#!/bin/bash
# 灰度发布脚本 - Nginx版本
NGINX_CONF="/etc/nginx/conf.d/upstream.conf"
NGINX_BINARY="/usr/sbin/nginx"
BACKUP_DIR="/tmp/nginx_backup"
# 服务节点配置
PRODUCTION_SERVERS=("192.168.1.10:8080" "192.168.1.11:8080" "192.168.1.12:8080")
GRAY_SERVERS=("192.168.1.100:8080")
# 颜色输出
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
# 函数:生成upstream配置
generate_upstream_conf() {
local gray_weight=$1
cat > "$NGINX_CONF" << EOF
upstream backend {
# 生产服务器
EOF
for server in "${PRODUCTION_SERVERS[@]}"; do
echo " server $server weight=10;" >> "$NGINX_CONF"
done
cat >> "$NGINX_CONF" << EOF
# 灰度服务器
EOF
for server in "${GRAY_SERVERS[@]}"; do
if [ "$gray_weight" -gt 0 ] 2>/dev/null; then
echo " server $server weight=$gray_weight;" >> "$NGINX_CONF"
fi
done
echo "}" >> "$NGINX_CONF"
}
# 函数:备份配置
backup_config() {
mkdir -p "$BACKUP_DIR"
local backup_file="$BACKUP_DIR/upstream.conf.$(date +%Y%m%d_%H%M%S)"
cp "$NGINX_CONF" "$backup_file"
echo -e "${GREEN}配置已备份到: $backup_file${NC}"
}
# 函数:检查Nginx配置
check_nginx() {
$NGINX_BINARY -t 2>&1
return $?
}
# 函数:重载Nginx
reload_nginx() {
$NGINX_BINARY -s reload 2>&1
return $?
}
# 函数:执行灰度发布
gray_deploy() {
local weight=$1
echo -e "${YELLOW}开始灰度发布...${NC}"
# 备份当前配置
backup_config
# 生成新配置
generate_upstream_conf "$weight"
# 检查配置
if check_nginx; then
echo -e "${GREEN}Nginx配置检查通过${NC}"
# 重载Nginx
if reload_nginx; then
echo -e "${GREEN}灰度发布完成${NC}"
echo -e "灰度权重: ${weight}/10"
return 0
else
echo -e "${RED}Nginx重载失败,回滚配置...${NC}"
# 回滚配置
latest_backup=$(ls -t "$BACKUP_DIR"/upstream.conf.* | head -1)
if [ -n "$latest_backup" ]; then
cp "$latest_backup" "$NGINX_CONF"
reload_nginx
fi
return 1
fi
else
echo -e "${RED}Nginx配置检查失败,保留配置供调试${NC}"
return 1
fi
}
# 主菜单
show_menu() {
echo "=== 灰度发布管理脚本 ==="
echo "1. 开始灰度发布 (10%流量)"
echo "2. 增加灰度流量 (20%)"
echo "3. 增加灰度流量 (50%)"
echo "4. 全量发布"
echo "5. 回滚到生产版本"
echo "6. 查看当前状态"
echo "0. 退出"
echo "======================"
}
# 主逻辑
while true; do
show_menu
read -p "请选择操作: " choice
case $choice in
1) gray_deploy 1 ;;
2) gray_deploy 2 ;;
3) gray_deploy 5 ;;
4) gray_deploy 10 ;;
5) gray_deploy 0 ;;
6)
echo -e "${YELLOW}当前配置状态:${NC}"
cat "$NGINX_CONF"
;;
0)
echo -e "${GREEN}退出脚本${NC}"
exit 0
;;
*) echo -e "${RED}无效选择${NC}" ;;
esac
echo ""
done
基于Consul的服务发现灰度
使用Consul的服务注册与健康检查实现灰度发布。
#!/bin/bash
# 灰度发布脚本 - Consul版本
CONSUL_BIN="/usr/local/bin/consul"
CONSUL_ADDR="http://localhost:8500"
# 服务配置
SERVICE_NAME="myapp"
TAG_PRODUCTION="production"
TAG_GRAY="gray"
REGION="us-east-1"
# 颜色输出
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'
# 函数:获取服务实例
get_service_instances() {
local tag=$1
$CONSUL_BIN catalog services \
-http-addr="$CONSUL_ADDR" \
-filter="ServiceName == \"$SERVICE_NAME\" and ServiceTags contains \"$tag\"" \
| jq -r '.[] | .Address + ":" + (.ServicePort|tostring)'
}
# 函数:注册灰度服务
register_gray_service() {
local instance_id=$1
local address=$2
local port=$3
cat > /tmp/gray_service.json << EOF
{
"ID": "$instance_id-gray",
"Name": "$SERVICE_NAME",
"Address": "$address",
"Port": $port,
"Tags": ["$TAG_GRAY", "$REGION"],
"Check": {
"HTTP": "http://$address:$port/health",
"Interval": "10s",
"Timeout": "5s",
"DeregisterCriticalServiceAfter": "30s"
}
}
EOF
$CONSUL_BIN services register \
-http-addr="$CONSUL_ADDR" \
/tmp/gray_service.json
echo -e "${GREEN}灰度服务 $instance_id-gray 注册成功${NC}"
}
# 函数:切换标签
switch_tags() {
local instance_id=$1
local to_tag=$2
# 获取当前服务信息
local service_info=$($CONSUL_BIN catalog service \
-http-addr="$CONSUL_ADDR" \
"$SERVICE_NAME-$instance_id" 2>/dev/null)
if [ -n "$service_info" ]; then
$CONSUL_BIN services deregister \
-http-addr="$CONSUL_ADDR" \
"id=$SERVICE_NAME-$instance_id"
fi
# 重新注册为新的tag
cat > /tmp/service_update.json << EOF
{
"ID": "$SERVICE_NAME-$instance_id",
"Name": "$SERVICE_NAME",
"Tags": ["$to_tag", "$REGION"]
}
EOF
$CONSUL_BIN services register \
-http-addr="$CONSUL_ADDR" \
/tmp/service_update.json
echo -e "${GREEN}服务 $instance_id 切换到 $to_tag${NC}"
}
# 函数:灰度策略 - 按比例
gray_deploy_by_percent() {
local percent=$1
local total_instances=$2
local gray_instances=$(( total_instances * percent / 100 ))
echo -e "${YELLOW}灰度发布 $percent% 流量...${NC}"
# 获取所有生产实例
local production_instances=($(get_service_instances "$TAG_PRODUCTION"))
local count=0
for instance in "${production_instances[@]}"; do
if [ $count -lt $gray_instances ]; then
# 切换到灰度标签
switch_tags "$instance" "$TAG_GRAY"
((count++))
else
break
fi
done
echo -e "${GREEN}灰度发布完成: $gray_instances/$total_instances 实例${NC}"
}
# 函数:灰度策略 - 按用户ID
gray_deploy_by_user_id() {
local user_id_range=$1
echo -e "${YELLOW}为特定用户ID范围 ($user_id_range%) 启用灰度...${NC}"
# 注册灰度版本并设置用户ID过滤
$CONSUL_BIN kv put \
-http-addr="$CONSUL_ADDR" \
"config/$SERVICE_NAME/gray/user_id_range" "$user_id_range"
echo -e "${GREEN}灰度范围已设置: $user_id_range%${NC}"
}
# 函数:全量发布
full_release() {
echo -e "${YELLOW}执行全量发布...${NC}"
# 将所有灰度实例切回生产
local gray_instances=($(get_service_instances "$TAG_GRAY"))
for instance in "${gray_instances[@]}"; do
switch_tags "$instance" "$TAG_PRODUCTION"
done
# 清除灰度配置
$CONSUL_BIN kv delete \
-http-addr="$CONSUL_ADDR" \
"config/$SERVICE_NAME/gray/user_id_range" 2>/dev/null
echo -e "${GREEN}全量发布完成${NC}"
}
# 函数:回滚
rollback() {
echo -e "${YELLOW}回滚到上一个生产版本...${NC}"
# 将灰度实例切回生产
local gray_instances=($(get_service_instances "$TAG_GRAY"))
for instance in "${gray_instances[@]}"; do
switch_tags "$instance" "$TAG_PRODUCTION"
done
# 清除灰度配置
$CONSUL_BIN kv delete \
-http-addr="$CONSUL_ADDR" \
"config/$SERVICE_NAME/gray/user_id_range" 2>/dev/null
# 重新部署上一版本
echo -e "${GREEN}回滚完成,重新部署上一版本...${NC}"
# 这里可以调用部署脚本重新部署旧版本
}
# 主菜单
show_menu() {
echo "=== 基于Consul的灰度发布管理 ==="
echo "1. 灰度发布 (10% 实例)"
echo "2. 灰度发布 (50% 实例)"
echo "3. 按用户ID灰度 (10%)"
echo "4. 按用户ID灰度 (50%)"
echo "5. 全量发布"
echo "6. 回滚"
echo "7. 查看当前灰度状态"
echo "0. 退出"
}
# 主逻辑
while true; do
show_menu
read -p "请选择操作: " choice
case $choice in
1) gray_deploy_by_percent 10 10 ;;
2) gray_deploy_by_percent 50 10 ;;
3) gray_deploy_by_user_id 10 ;;
4) gray_deploy_by_user_id 50 ;;
5) full_release ;;
6) rollback ;;
7)
echo -e "${YELLOW}当前灰度状态:${NC}"
echo "生产实例:"
get_service_instances "$TAG_PRODUCTION" | while read line; do
echo " - $line"
done
echo "灰度实例:"
get_service_instances "$TAG_GRAY" | while read line; do
echo " - $line"
done
echo "灰度配置:"
$CONSUL_BIN kv get \
-http-addr="$CONSUL_ADDR" \
"config/$SERVICE_NAME/gray/user_id_range" 2>/dev/null || echo " 未设置"
;;
0) exit 0 ;;
*) echo -e "${RED}无效选择${NC}" ;;
esac
echo ""
done
基于Kubernetes的灰度发布
使用Kubernetes的Deployment和Service实现灰度发布。
#!/bin/bash
# 灰度发布脚本 - Kubernetes版本
NAMESPACE="production"
DEPLOYMENT_NAME="myapp"
SERVICE_NAME="myapp-svc"
IMAGE_NEW="myapp:2.0.0"
IMAGE_OLD="myapp:1.0.0"
# 颜色输出
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'
# 函数:创建灰度Deployment
create_gray_deployment() {
local replicas=$1
kubectl create deployment "$DEPLOYMENT_NAME-gray" \
--namespace="$NAMESPACE" \
--image="$IMAGE_NEW" \
--replicas="$replicas" \
--dry-run=client -o yaml > /tmp/gray-deploy.yaml
# 添加标签和选择器
sed -i 's/app: myapp/app: myapp-gray/g' /tmp/gray-deploy.yaml
kubectl apply -f /tmp/gray-deploy.yaml
echo -e "${GREEN}灰度Deployment已创建: $replicas 副本${NC}"
}
# 函数:更新Service选择器
update_service_selector() {
local gray_weight=$1
# 备份原Service
kubectl get svc "$SERVICE_NAME" \
--namespace="$NAMESPACE" \
-o yaml > /tmp/svc-backup.yaml
# 修改Service选择器包含灰度标签
kubectl patch svc "$SERVICE_NAME" \
--namespace="$NAMESPACE" \
-p "{\"spec\":{\"selector\":{\"app\":\"myapp\",\"version\":\"${gray_weight}\"}}}"
echo -e "${GREEN}Service选择器已更新${NC}"
}
# 函数:灰度发布
gray_deploy() {
local percent=$1
echo -e "${YELLOW}开始灰度发布 $percent%...${NC}"
# 计算副本数
local total_replicas=$(kubectl get deployment "$DEPLOYMENT_NAME" \
--namespace="$NAMESPACE" \
-o jsonpath='{.spec.replicas}')
local gray_replicas=$(( total_replicas * percent / 100 ))
# 创建灰度Deployment
create_gray_deployment "$gray_replicas"
# 等待灰度Deployment就绪
kubectl rollout status deployment "$DEPLOYMENT_NAME-gray" \
--namespace="$NAMESPACE" \
--timeout=300s
# 更新Service指向灰度版本
update_service_selector "gray"
echo -e "${GREEN}灰度发布完成: $percent% 流量${NC}"
}
# 函数:全量发布
full_release() {
echo -e "${YELLOW}执行全量发布...${NC}"
# 更新主Deployment
kubectl set image deployment/"$DEPLOYMENT_NAME" \
--namespace="$NAMESPACE" \
"$DEPLOYMENT_NAME=$IMAGE_NEW"
# 等待更新完成
kubectl rollout status deployment/"$DEPLOYMENT_NAME" \
--namespace="$NAMESPACE" \
--timeout=300s
# 删除灰度Deployment
kubectl delete deployment "$DEPLOYMENT_NAME-gray" \
--namespace="$NAMESPACE"
# 恢复Service选择器
kubectl patch svc "$SERVICE_NAME" \
--namespace="$NAMESPACE" \
-p "{\"spec\":{\"selector\":{\"app\":\"myapp\"}}}"
echo -e "${GREEN}全量发布完成${NC}"
}
# 函数:回滚
rollback() {
echo -e "${YELLOW}执行回滚...${NC}"
# 回滚主Deployment
kubectl rollout undo deployment/"$DEPLOYMENT_NAME" \
--namespace="$NAMESPACE"
# 删除灰度Deployment
kubectl delete deployment "$DEPLOYMENT_NAME-gray" \
--namespace="$NAMESPACE" 2>/dev/null
# 恢复Service选择器
kubectl patch svc "$SERVICE_NAME" \
--namespace="$NAMESPACE" \
-p "{\"spec\":{\"selector\":{\"app\":\"myapp\"}}}"
echo -e "${GREEN}回滚完成${NC}"
}
# 主菜单
show_menu() {
echo "=== Kubernetes灰度发布管理 ==="
echo "1. 灰度发布 (10%)"
echo "2. 灰度发布 (50%)"
echo "3. 全量发布"
echo "4. 回滚"
echo "5. 查看当前状态"
echo "0. 退出"
}
# 主逻辑
while true; do
show_menu
read -p "请选择操作: " choice
case $choice in
1) gray_deploy 10 ;;
2) gray_deploy 50 ;;
3) full_release ;;
4) rollback ;;
5)
echo -e "${YELLOW}当前状态:${NC}"
echo "主Deployment:"
kubectl get deployment "$DEPLOYMENT_NAME" \
--namespace="$NAMESPACE" \
-o wide
echo ""
echo "灰度Deployment:"
kubectl get deployment "$DEPLOYMENT_NAME-gray" \
--namespace="$NAMESPACE" 2>/dev/null || echo " 未创建"
echo ""
echo "Service:"
kubectl get svc "$SERVICE_NAME" \
--namespace="$NAMESPACE" \
-o wide
;;
0) exit 0 ;;
*) echo -e "${RED}无效选择${NC}" ;;
esac
echo ""
done
使用注意事项
- 安全性:所有脚本需要在具有适当权限的环境中运行
- 备份:每次操作前备份配置,保证可回滚
- 监控:灰度发布期间密切监控指标
- 自动化:考虑集成到CI/CD流水线中
- 超时处理:设置合理的超时时间,避免长时间等待
这些脚本提供了基础的灰度发布功能,可以根据实际场景进行调整和扩展。