本文目录导读:

下面我来介绍如何使用Shell脚本实现容器应用的金丝雀发布(Canary Release)。
核心思路
金丝雀发布的核心是逐步替换实例,通过流量控制实现风险控制,以下是完整的Shell脚本实现方案。
基础架构假设
- 容器编排:Docker / Docker Compose
- 负载均衡:Nginx / HAProxy
- 服务发现:Consul / 静态配置
Shell脚本实现
#!/bin/bash
set -euo pipefail
# ============ 全局配置 ============
APP_NAME="myapp"
NAMESPACE="production"
REGISTRY="registry.example.com"
IMAGE_TAG="${1:-latest}" # 新版本镜像标签
CANARY_PERCENT="${2:-10}" # 金丝雀流量比例(默认10%)
ROLLOUT_INTERVAL="${3:-30}" # 每步等待时间(秒)
# ============ 颜色输出 ============
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[0;33m'
NC='\033[0m' # No Color
info() { echo -e "${GREEN}[INFO]${NC} $1"; }
warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
error() { echo -e "${RED}[ERROR]${NC} $1"; exit 1; }
# ============ 函数定义 ============
# 检查必选命令是否安装
check_dependencies() {
local deps=("docker" "curl" "jq")
for cmd in "${deps[@]}"; do
if ! command -v "$cmd" &> /dev/null; then
error "Missing required command: $cmd"
fi
done
}
# 拉取新版本镜像
pull_image() {
info "Pulling new image: ${REGISTRY}/${APP_NAME}:${IMAGE_TAG}"
docker pull "${REGISTRY}/${APP_NAME}:${IMAGE_TAG}" || error "Failed to pull image"
}
# 获取稳定版本健康实例数
get_stable_instances() {
docker ps --filter "name=${APP_NAME}-stable" --filter "status=running" --format "{{.Names}}" | wc -l
}
# 启动金丝雀实例
start_canary() {
local canary_count=$(( $(get_stable_instances) * CANARY_PERCENT / 100 ))
canary_count=$(( canary_count < 1 ? 1 : canary_count ))
info "Starting ${canary_count} canary instance(s)..."
for i in $(seq 1 $canary_count); do
docker run -d \
--name "${APP_NAME}-canary-${i}" \
--label "version=canary" \
--label "canary=true" \
--network "app-network" \
-e "APP_VERSION=${IMAGE_TAG}" \
"${REGISTRY}/${APP_NAME}:${IMAGE_TAG}"
done
info "Canary instances started"
}
# 金丝雀健康检查
health_check() {
local retries=5
local delay=3
for i in $(seq 1 $retries); do
local healthy_instances=0
local total_instances=0
for container in $(docker ps --filter "label=canary=true" --format "{{.Names}}"); do
total_instances=$((total_instances + 1))
# 假设应用有健康检查端点
if curl -sf "http://${container}:8080/health" &> /dev/null; then
healthy_instances=$((healthy_instances + 1))
fi
done
if [ "$healthy_instances" -ge "$total_instances" ]; then
info "All canary instances healthy"
return 0
fi
warn "Health check attempt $i: ${healthy_instances}/${total_instances} healthy"
sleep "$delay"
done
return 1
}
# 更新负载均衡配置(Nginx示例)
update_load_balancer() {
local strategy="${1:-canary}" # canary / full
# 生成新的Nginx配置
local nginx_conf="/etc/nginx/conf.d/${APP_NAME}.conf"
cat > "$nginx_conf" << EOF
upstream app_backend {
# 稳定版本实例
EOF
for container in $(docker ps --filter "label=version=stable" --format "{{.Names}}"); do
local ip=$(docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' "$container")
echo " server ${ip}:8080 weight=100;" >> "$nginx_conf"
done
if [ "$strategy" = "canary" ]; then
echo "" >> "$nginx_conf"
echo " # 金丝雀实例(低权重)" >> "$nginx_conf"
for container in $(docker ps --filter "label=canary=true" --format "{{.Names}}"); do
local ip=$(docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' "$container")
echo " server ${ip}:8080 weight=${CANARY_PERCENT};" >> "$nginx_conf"
done
fi
echo "}" >> "$nginx_conf"
# 重载Nginx配置
nginx -s reload && info "Nginx configuration reloaded"
}
# 监控金丝雀指标
monitor_canary() {
local monitor_interval=10
local max_attempts=60 # 最多监控10分钟
info "Monitoring canary for ${max_attempts} intervals..."
for i in $(seq 1 $max_attempts); do
local error_rate=0
local response_time=0
# 这里可以集成Prometheus查询或日志分析
# 示例:检查错误日志
error_rate=$(docker logs "${APP_NAME}-canary-1" --since "10s" 2>&1 | grep -c "ERROR")
if [ "$error_rate" -gt 5 ]; then
warn "High error rate detected in canary: ${error_rate}"
return 1
fi
sleep "$monitor_interval"
done
return 0
}
# 回滚金丝雀
rollback_canary() {
warn "Rolling back canary deployment..."
# 停止并删除金丝雀实例
for container in $(docker ps -a --filter "label=canary=true" --format "{{.Names}}"); do
docker stop "$container" && docker rm "$container"
done
# 恢复Nginx配置(只保留稳定实例)
update_load_balancer "stable"
info "Canary rollback completed"
}
# 全量发布
full_rollout() {
info "Starting full rollout..."
local stable_count=$(get_stable_instances)
# 逐步替换稳定实例为金丝雀镜像
for i in $(seq 1 $stable_count); do
local old_container="${APP_NAME}-stable-${i}"
local new_container="${APP_NAME}-stable-${i}"
info "Upgrading instance ${i}/${stable_count}"
# 停止旧实例
docker stop "$old_container" > /dev/null 2>&1 || true
# 用新镜像启动
docker run -d \
--name "$new_container" \
--label "version=stable" \
--network "app-network" \
-e "APP_VERSION=${IMAGE_TAG}" \
"${REGISTRY}/${APP_NAME}:${IMAGE_TAG}"
# 健康检查
sleep 5
if ! docker ps | grep -q "$new_container"; then
error "Failed to start new instance"
fi
# 更新负载均衡
update_load_balancer "full"
# 等待稳定
sleep "$ROLLOUT_INTERVAL"
done
info "Full rollout completed"
}
# 清理旧版本
cleanup_old_versions() {
info "Cleaning up old version images..."
docker image prune -a --force --filter "until=24h"
}
# ============ 主流程 ============
main() {
echo "========================================="
echo " Canary Deployment Script"
echo " App: ${APP_NAME}"
echo " Image: ${IMAGE_TAG}"
echo " Canary: ${CANARY_PERCENT}%"
echo "========================================="
check_dependencies
# Step 1: 拉取新镜像
pull_image
# Step 2: 启动金丝雀实例
start_canary
# Step 3: 更新负载均衡(添加金丝雀)
update_load_balancer "canary"
# Step 4: 健康检查
if ! health_check; then
error "Health check failed for canary"
fi
# Step 5: 监控金丝雀
if ! monitor_canary; then
rollback_canary
error "Canary monitoring failed, rolled back"
fi
# Step 6: 全量发布
full_rollout
# Step 7: 清理
cleanup_old_versions
# Step 8: 移除金丝雀标签
for container in $(docker ps -a --filter "label=canary=true" --format "{{.Names}}"); do
docker rm "$container" 2> /dev/null || true
done
info "Canary deployment completed successfully!"
}
# 执行主函数
main "$@"
使用示例
# 基础用法 - 10%金丝雀流量部署v2.1.0 ./canary-deploy.sh v2.1.0 # 自定义金丝雀比例(20%) ./canary-deploy.sh v2.1.0 20 # 自定义间隔时间(60秒) ./canary-deploy.sh v2.1.0 10 60
Docker Compose版本
# docker-compose.canary.yml
version: '3.8'
services:
app-stable:
image: registry.example.com/myapp:${STABLE_TAG:-latest}
deploy:
replicas: 5
labels:
- "version=stable"
networks:
- app-network
app-canary:
image: registry.example.com/myapp:${CANARY_TAG:-latest}
deploy:
replicas: ${CANARY_REPLICAS:-1}
labels:
- "version=canary"
- "canary=true"
networks:
- app-network
networks:
app-network:
driver: bridge
高级特性(可选扩展)
灰度比例动态调整
# 动态调整金丝雀权重
update_canary_weight() {
local weight=$1
update_load_balancer "custom" "$weight"
info "Updated canary weight to ${weight}%"
}
集成监控告警
# 集成Prometheus指标查询
check_prometheus_metrics() {
local query='rate(app_errors_total[5m])'
local result=$(curl -s "http://prometheus:9090/api/v1/query?query=${query}")
local error_rate=$(echo "$result" | jq '.data.result[0].value[1]')
if (( $(echo "$error_rate > 0.01" | bc -l) )); then
return 1
fi
return 0
}
最佳实践建议
- 渐进式发布:从1% → 5% → 10% → 50% → 100%
- 自动回滚:设置错误率阈值自动触发回滚
- A/B测试:可以结合请求头/用户ID进行精细化分流
- 日志对比:收集金丝雀和稳定版本的日志对比分析
- 系统资源监控:监控CPU、内存、网络等指标
这个脚本提供了一个完整的金丝雀发布框架,你可以根据实际业务需求调整配置和监控逻辑。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播