# if-else语句 check_file() { local file=$1 if [ -f "$file" ]; then echo"文件 $file 存在" if [ -r "$file" ]; then echo"文件可读" fi if [ -w "$file" ]; then echo"文件可写" fi if [ -x "$file" ]; then echo"文件可执行" fi elif [ -d "$file" ]; then echo"$file 是目录" else echo"$file 不存在" fi }
# 数值比较 compare_numbers() { local num1=$1 local num2=$2 if [ "$num1" -eq "$num2" ]; then echo"$num1 等于 $num2" elif [ "$num1" -gt "$num2" ]; then echo"$num1 大于 $num2" else echo"$num1 小于 $num2" fi }
# 字符串比较 compare_strings() { local str1=$1 local str2=$2 if [ "$str1" = "$str2" ]; then echo"字符串相等" elif [ "$str1" \< "$str2" ]; then echo"$str1 在字典序中小于 $str2" else echo"$str1 在字典序中大于 $str2" fi # 模式匹配 case"$str1"in *.txt) echo"这是一个文本文件" ;; *.log) echo"这是一个日志文件" ;; *) echo"未知文件类型" ;; esac }
# 逻辑运算 logical_operations() { local file=$1 local size_limit=1000000 # 1MB # AND运算 if [ -f "$file" ] && [ -s "$file" ]; then echo"文件存在且不为空" fi # OR运算 if [ ! -f "$file" ] || [ ! -r "$file" ]; then echo"文件不存在或不可读" return 1 fi # 复杂条件 file_size=$(stat -c%s "$file" 2>/dev/null || echo 0) if [ -f "$file" ] && [ "$file_size" -gt "$size_limit" ]; then echo"文件过大: $(numfmt --to=iec $file_size)B" fi }
# 循环控制 loop_control_demo() { echo -e "\n=== 循环控制示例 ===" for i in {1..10}; do if [ $i -eq 3 ]; then echo"跳过数字 $i" continue fi if [ $i -eq 8 ]; then echo"在数字 $i 处退出循环" break fi echo"处理数字: $i" done }
# 嵌套循环 nested_loop_demo() { echo -e "\n=== 嵌套循环示例 ===" for i in {1..3}; do echo"外层循环: $i" for j in {a..c}; do echo" 内层循环: $j" done done }
# 带返回值的函数 add_numbers() { local num1=$1 local num2=$2 local result=$((num1 + num2)) echo$result# 返回结果 }
# 复杂函数示例 backup_file() { local source_file=$1 local backup_dir=${2:-/backup} local timestamp=$(date +%Y%m%d_%H%M%S) # 参数验证 if [ $# -lt 1 ]; then echo"用法: backup_file <源文件> [备份目录]" >&2 return 1 fi if [ ! -f "$source_file" ]; then echo"错误: 源文件不存在: $source_file" >&2 return 1 fi # 创建备份目录 mkdir -p "$backup_dir" # 生成备份文件名 local filename=$(basename"$source_file") local backup_file="$backup_dir/${filename}.${timestamp}.bak" # 执行备份 ifcp"$source_file""$backup_file"; then echo"备份成功: $backup_file" return 0 else echo"备份失败" >&2 return 1 fi }
# 递归函数示例 factorial() { local n=$1 if [ $n -le 1 ]; then echo 1 else local prev=$(factorial $((n-1))) echo $((n * prev)) fi }
# 数组处理函数 process_array() { local -n arr_ref=$1# 数组引用 local operation=$2 case"$operation"in "sum") local total=0 for num in"${arr_ref[@]}"; do total=$((total + num)) done echo$total ;; "max") local max=${arr_ref[0]} for num in"${arr_ref[@]}"; do if [ $num -gt $max ]; then max=$num fi done echo$max ;; "sort") # 冒泡排序 local len=${#arr_ref[@]} for ((i=0; i<len-1; i++)); do for ((j=0; j<len-i-1; j++)); do if [ ${arr_ref[j]} -gt ${arr_ref[j+1]} ]; then # 交换元素 local temp=${arr_ref[j]} arr_ref[j]=${arr_ref[j+1]} arr_ref[j+1]=$temp fi done done ;; esac }
# 进度条函数 show_progress() { local current=$1 local total=$2 local width=50 local percentage=$((current * 100 / total)) local completed=$((current * width / total)) printf"\r[" printf"%*s"$completed | tr' ''=' printf"%*s" $((width - completed)) | tr' ''-' printf"] %d%% (%d/%d)"$percentage$current$total if [ $current -eq $total ]; then echo fi }
# 确认函数 confirm() { local message=${1:-"确认继续吗?"} local default=${2:-"n"} whiletrue; do if [ "$default" = "y" ]; then read -p "$message [Y/n]: " choice choice=${choice:-y} else read -p "$message [y/N]: " choice choice=${choice:-n} fi case"$choice"in [Yy]|[Yy][Ee][Ss]) return 0 ;; [Nn]|[Nn][Oo]) return 1 ;; *) echo"请输入 y 或 n" ;; esac done }
# 重试函数 retry() { local max_attempts=$1 local delay=$2 shift 2 localcommand="$@" local attempt=1 while [ $attempt -le $max_attempts ]; do print_info "尝试执行 (第 $attempt 次): $command" ifeval"$command"; then print_success "命令执行成功" return 0 else print_warning "命令执行失败 (第 $attempt 次)" if [ $attempt -lt $max_attempts ]; then print_info "等待 $delay 秒后重试..." sleep$delay fi fi ((attempt++)) done print_error "命令执行失败,已达到最大重试次数" return 1 }
# 文件操作函数 safe_copy() { localsource=$1 local destination=$2 local backup_suffix=".bak.$(date +%Y%m%d_%H%M%S)" if [ ! -f "$source" ]; then print_error "源文件不存在: $source" return 1 fi # 如果目标文件存在,先备份 if [ -f "$destination" ]; then print_info "备份现有文件: $destination$backup_suffix" cp"$destination""$destination$backup_suffix" fi # 复制文件 ifcp"$source""$destination"; then print_success "文件复制成功: $source -> $destination" return 0 else print_error "文件复制失败" return 1 fi }
# 检查依赖 check_dependencies() { local dependencies=("$@") local missing=() for cmd in"${dependencies[@]}"; do if ! command -v "$cmd" > /dev/null 2>&1; then missing+=("$cmd") fi done if [ ${#missing[@]} -gt 0 ]; then print_error "缺少以下依赖: ${missing[*]}" return 1 else print_success "所有依赖检查通过" return 0 fi }
# 临时文件管理 create_temp_file() { local prefix=${1:-"script"} local temp_file=$(mktemp"/tmp/${prefix}.XXXXXX") echo"$temp_file" }
# 批量文件重命名 batch_rename() { local directory=${1:-.} local pattern=$2 local replacement=$3 if [ $# -lt 3 ]; then echo"用法: batch_rename <目录> <模式> <替换>" return 1 fi local count=0 for file in"$directory"/*; do if [ -f "$file" ]; then localbasename=$(basename"$file") local new_name=$(echo"$basename" | sed "s/$pattern/$replacement/g") if [ "$basename" != "$new_name" ]; then local new_path="$directory/$new_name" ifmv"$file""$new_path"; then echo"重命名: $basename -> $new_name" ((count++)) else echo"重命名失败: $basename" >&2 fi fi fi done echo"共重命名 $count 个文件" }
# 文件内容替换 replace_in_files() { local directory=$1 local file_pattern=$2 local search_pattern=$3 local replacement=$4 local backup=${5:-true} if [ $# -lt 4 ]; then echo"用法: replace_in_files <目录> <文件模式> <搜索模式> <替换内容> [备份:true/false]" return 1 fi local backup_suffix="" if [ "$backup" = "true" ]; then backup_suffix=".bak.$(date +%Y%m%d_%H%M%S)" fi find "$directory" -name "$file_pattern" -type f | whileread -r file; do if grep -q "$search_pattern""$file"; then echo"处理文件: $file" # 备份原文件 if [ "$backup" = "true" ]; then cp"$file""$file$backup_suffix" fi # 执行替换 sed -i "s/$search_pattern/$replacement/g""$file" echo" 替换完成" fi done }
# 文件去重 deduplicate_files() { local directory=${1:-.} local dry_run=${2:-false} echo"在目录 $directory 中查找重复文件..." # 使用MD5计算文件哈希 declare -A file_hashes declare -A hash_files find "$directory" -type f | whileread -r file; do localhash=$(md5sum"$file" | cut -d' ' -f1) local size=$(stat -c%s "$file") local key="${hash}_${size}" if [ -n "${hash_files[$key]}" ]; then echo"发现重复文件:" echo" 原文件: ${hash_files[$key]}" echo" 重复文件: $file" if [ "$dry_run" = "false" ]; then if confirm "删除重复文件 $file"; then rm"$file" echo" 已删除: $file" fi fi else hash_files[$key]="$file" fi done }
# 文件同步 sync_directories() { local source_dir=$1 local target_dir=$2 local options=${3:-"--archive --verbose"} if [ ! -d "$source_dir" ]; then echo"源目录不存在: $source_dir" >&2 return 1 fi # 创建目标目录 mkdir -p "$target_dir" echo"同步目录: $source_dir -> $target_dir" # 使用rsync同步 ifcommand -v rsync > /dev/null; then rsync $options"$source_dir/""$target_dir/" else # 备用方案:使用cp cp -r "$source_dir"/* "$target_dir/" fi echo"同步完成" }
# 文件压缩和归档 archive_old_files() { local source_dir=$1 local archive_dir=$2 local days_old=${3:-30} local compress=${4:-true} if [ ! -d "$source_dir" ]; then echo"源目录不存在: $source_dir" >&2 return 1 fi mkdir -p "$archive_dir" local archive_name="archive_$(date +%Y%m%d_%H%M%S)" local temp_dir="/tmp/$archive_name" mkdir -p "$temp_dir" echo"查找 $days_old 天前的文件..." # 查找旧文件 local file_count=0 find "$source_dir" -type f -mtime +$days_old | whileread -r file; do local relative_path=${file#$source_dir/} local target_path="$temp_dir/$relative_path" # 创建目标目录结构 mkdir -p "$(dirname "$target_path")" # 移动文件 mv"$file""$target_path" ((file_count++)) done if [ $file_count -gt 0 ]; then echo"归档 $file_count 个文件" # 创建归档 if [ "$compress" = "true" ]; then tar -czf "$archive_dir/$archive_name.tar.gz" -C "/tmp""$archive_name" else tar -cf "$archive_dir/$archive_name.tar" -C "/tmp""$archive_name" fi # 清理临时目录 rm -rf "$temp_dir" echo"归档完成: $archive_dir/$archive_name.tar.gz" else echo"没有找到需要归档的文件" rm -rf "$temp_dir" fi }
# 加载配置 load_monitor_config() { if [ -f "$CONFIG_FILE" ]; then source"$CONFIG_FILE" log_message "INFO""配置文件已加载: $CONFIG_FILE" else log_message "WARN""配置文件不存在,使用默认配置" fi }
# 日志记录 log_message() { local level=$1 local message=$2 local timestamp=$(date'+%Y-%m-%d %H:%M:%S') echo"[$timestamp] [$level] $message" | tee -a "$LOG_FILE" if [ "$level" = "ALERT" ]; then echo"[$timestamp] $message" >> "$ALERT_LOG" fi }
# 发送告警 send_alert() { local alert_type=$1 local message=$2 local timestamp=$(date'+%Y-%m-%d %H:%M:%S') log_message "ALERT""[$alert_type] $message" # 邮件告警 if [ ! -z "$ALERT_EMAIL" ]; then echo"$message" | mail -s "System Alert: $alert_type - $(hostname)""$ALERT_EMAIL" fi # Slack告警 if [ ! -z "$SLACK_WEBHOOK" ]; then local payload=$(cat << EOF { "text": "🚨 System Alert", "attachments": [ { "color": "danger", "fields": [ { "title": "Alert Type", "value": "$alert_type", "short": true }, { "title": "Host", "value": "$(hostname)", "short": true }, { "title": "Time", "value": "$timestamp", "short": true }, { "title": "Message", "value": "$message", "short": false } ] } ] } EOF ) curl -X POST -H 'Content-type: application/json' \ --data "$payload""$SLACK_WEBHOOK" > /dev/null 2>&1 fi }
# CPU监控 monitor_cpu() { local cpu_usage=$(top -bn1 | grep "Cpu(s)" | awk '{print $2}' | cut -d'%' -f1) local cpu_int=${cpu_usage%.*} log_message "INFO""CPU使用率: ${cpu_usage}%" if [ "$cpu_int" -gt "$CPU_THRESHOLD" ]; then local top_processes=$(ps aux --sort=-%cpu | head -6 | tail -5 | awk '{print $11}' | tr'\n'', ') send_alert "HIGH_CPU""CPU使用率过高: ${cpu_usage}% (阈值: ${CPU_THRESHOLD}%). 高CPU进程: $top_processes" fi # 系统负载检查 local load_avg=$(uptime | awk -F'load average:''{print $2}' | awk '{print $1}' | tr -d ',') log_message "INFO""系统负载: $load_avg" if (( $(echo "$load_avg > $LOAD_THRESHOLD" | bc -l) )); then send_alert "HIGH_LOAD""系统负载过高: $load_avg (阈值: $LOAD_THRESHOLD)" fi }
# 内存监控 monitor_memory() { local mem_info=$(free | grep Mem) local total_mem=$(echo$mem_info | awk '{print $2}') local used_mem=$(echo$mem_info | awk '{print $3}') local mem_usage=$(echo"scale=1; $used_mem * 100 / $total_mem" | bc) local mem_usage_int=${mem_usage%.*} log_message "INFO""内存使用率: ${mem_usage}%" if [ "$mem_usage_int" -gt "$MEMORY_THRESHOLD" ]; then local top_processes=$(ps aux --sort=-%mem | head -6 | tail -5 | awk '{print $11}' | tr'\n'', ') send_alert "HIGH_MEMORY""内存使用率过高: ${mem_usage}% (阈值: ${MEMORY_THRESHOLD}%). 高内存进程: $top_processes" fi # Swap使用检查 local swap_info=$(free | grep Swap) if [ ! -z "$swap_info" ]; then local swap_used=$(echo$swap_info | awk '{print $3}') if [ "$swap_used" -gt 0 ]; then local swap_total=$(echo$swap_info | awk '{print $2}') local swap_usage=$(echo"scale=1; $swap_used * 100 / $swap_total" | bc) log_message "WARN""Swap正在使用: ${swap_usage}%" if [ "${swap_usage%.*}" -gt 50 ]; then send_alert "HIGH_SWAP""Swap使用率过高: ${swap_usage}%" fi fi fi }
# 磁盘监控 monitor_disk() { df -h | grep -E '^/dev/' | whileread line; do local usage=$(echo$line | awk '{print $5}' | cut -d'%' -f1) local mount_point=$(echo$line | awk '{print $6}') local filesystem=$(echo$line | awk '{print $1}') log_message "INFO""磁盘 $mount_point 使用率: ${usage}%" if [ "$usage" -gt "$DISK_THRESHOLD" ]; then send_alert "HIGH_DISK""磁盘 $mount_point ($filesystem) 使用率过高: ${usage}% (阈值: ${DISK_THRESHOLD}%)" fi done # 检查inode使用率 df -i | grep -E '^/dev/' | whileread line; do local inode_usage=$(echo$line | awk '{print $5}' | cut -d'%' -f1) local mount_point=$(echo$line | awk '{print $6}') if [ "$inode_usage" -gt 80 ]; then send_alert "HIGH_INODE""磁盘 $mount_point inode使用率过高: ${inode_usage}%" fi done }
# 网络监控 monitor_network() { # 检查网络连接数 local connections=$(netstat -tupln | grep ESTABLISHED | wc -l) log_message "INFO""当前网络连接数: $connections" if [ "$connections" -gt 1000 ]; then send_alert "HIGH_CONNECTIONS""网络连接数过多: $connections" fi # 检查网络接口状态 for interface in $(ls /sys/class/net/ | grep -v lo); do if [ -f "/sys/class/net/$interface/operstate" ]; then local state=$(cat /sys/class/net/$interface/operstate) if [ "$state" != "up" ]; then send_alert "NETWORK_DOWN""网络接口 $interface 状态异常: $state" fi fi done # 检查网络错误 for interface in $(ls /sys/class/net/ | grep -v lo); do if [ -f "/sys/class/net/$interface/statistics/rx_errors" ]; then local rx_errors=$(cat /sys/class/net/$interface/statistics/rx_errors) local tx_errors=$(cat /sys/class/net/$interface/statistics/tx_errors) if [ "$rx_errors" -gt 100 ] || [ "$tx_errors" -gt 100 ]; then send_alert "NETWORK_ERRORS""网络接口 $interface 错误过多: RX=$rx_errors, TX=$tx_errors" fi fi done }
# 服务监控 monitor_services() { local critical_services=("ssh""nginx""mysql""redis""docker") for service in"${critical_services[@]}"; do if systemctl list-unit-files | grep -q "^$service.service"; then if ! systemctl is-active --quiet "$service"; then send_alert "SERVICE_DOWN""关键服务 $service 未运行" else log_message "INFO""服务 $service 运行正常" fi fi done # 检查失败的服务 local failed_services=$(systemctl list-units --failed --no-legend | wc -l) if [ "$failed_services" -gt 0 ]; then local failed_list=$(systemctl list-units --failed --no-legend | awk '{print $1}' | tr'\n'', ') send_alert "SERVICES_FAILED""发现 $failed_services 个失败的服务: $failed_list" fi }
# 进程监控 monitor_processes() { # 检查僵尸进程 local zombie_count=$(ps aux | awk '$8 ~ /^Z/ { count++ } END { print count+0 }') if [ "$zombie_count" -gt 0 ]; then send_alert "ZOMBIE_PROCESSES""发现 $zombie_count 个僵尸进程" fi # 检查高CPU进程 local high_cpu_processes=$(ps aux --sort=-%cpu | awk 'NR>1 && $3>50 {print $11}' | head -5 | tr'\n'', ') if [ ! -z "$high_cpu_processes" ]; then log_message "WARN""高CPU使用进程: $high_cpu_processes" fi # 检查高内存进程 local high_mem_processes=$(ps aux --sort=-%mem | awk 'NR>1 && $4>20 {print $11}' | head -5 | tr'\n'', ') if [ ! -z "$high_mem_processes" ]; then log_message "WARN""高内存使用进程: $high_mem_processes" fi }
# 日志监控 monitor_logs() { # 检查系统错误日志 local error_count=$(journalctl --since "5 minutes ago" --priority=err | wc -l) if [ "$error_count" -gt 10 ]; then send_alert "HIGH_ERROR_RATE""最近5分钟内系统错误过多: $error_count 条" fi # 检查登录失败 local failed_logins=$(journalctl --since "5 minutes ago" | grep -i "failed\|failure" | wc -l) if [ "$failed_logins" -gt 5 ]; then send_alert "FAILED_LOGINS""最近5分钟内登录失败过多: $failed_logins 次" fi }
stop_daemon() { if [ -f "$PID_FILE" ]; then local pid=$(cat"$PID_FILE") ifkill -0 "$pid" 2>/dev/null; then kill"$pid" rm -f "$PID_FILE" echo"监控进程已停止" else echo"监控进程未运行" rm -f "$PID_FILE" fi else echo"PID文件不存在" fi }
status_daemon() { if [ -f "$PID_FILE" ] && kill -0 "$(cat "$PID_FILE")" 2>/dev/null; then echo"监控进程正在运行 (PID: $(cat "$PID_FILE"))" else echo"监控进程未运行" fi }
# 环境配置 declare -A ENVIRONMENTS=( ["dev"]="dev.example.com" ["staging"]="staging.example.com" ["production"]="prod.example.com" )
# 部署配置 declare -A DEPLOY_CONFIG=( ["git_repo"]="https://github.com/user/myapp.git" ["branch"]="main" ["build_command"]="npm run build" ["start_command"]="npm start" ["health_check_url"]="http://localhost:3000/health" ["health_check_timeout"]="30" )
# 日志记录 deploy_log() { local level=$1 local message=$2 local timestamp=$(date'+%Y-%m-%d %H:%M:%S') echo"[$timestamp] [$level] $message" | tee -a "$LOG_FILE" }
# 错误处理 handle_deploy_error() { local exit_code=$1 local step=$2 deploy_log "ERROR""部署失败在步骤: $step (退出码: $exit_code)" # 回滚 if [ "$AUTO_ROLLBACK" = "true" ]; then deploy_log "INFO""开始自动回滚" rollback_deployment fi # 发送告警 send_deploy_alert "DEPLOY_FAILED""部署失败: $step" exit$exit_code }
# 发送部署告警 send_deploy_alert() { local alert_type=$1 local message=$2 deploy_log "ALERT""[$alert_type] $message" # 这里可以集成邮件、Slack等告警 if [ ! -z "$SLACK_WEBHOOK" ]; then curl -X POST -H 'Content-type: application/json' \ --data "{\"text\":\"🚀 Deploy Alert: $message\"}" \ "$SLACK_WEBHOOK" > /dev/null 2>&1 fi }
# 环境检查 check_environment() { localenv=$1 deploy_log "INFO""检查部署环境: $env" # 检查必要的命令 local required_commands=("git""node""npm") for cmd in"${required_commands[@]}"; do if ! command -v "$cmd" > /dev/null; then deploy_log "ERROR""缺少必要命令: $cmd" return 1 fi done # 检查目录权限 if [ ! -w "$(dirname "$APP_DIR")" ]; then deploy_log "ERROR""没有写入权限: $(dirname "$APP_DIR")" return 1 fi # 检查磁盘空间 local available_space=$(df"$(dirname "$APP_DIR")" | tail -1 | awk '{print $4}') if [ "$available_space" -lt 1048576 ]; then# 1GB deploy_log "ERROR""磁盘空间不足: ${available_space}KB" return 1 fi deploy_log "INFO""环境检查通过" return 0 }
# 创建备份 create_backup() { local backup_name="backup_$(date +%Y%m%d_%H%M%S)" local backup_path="$BACKUP_DIR/$backup_name" deploy_log "INFO""创建备份: $backup_path" if [ -d "$APP_DIR" ]; then mkdir -p "$BACKUP_DIR" cp -r "$APP_DIR""$backup_path" # 保留最近10个备份 ls -t "$BACKUP_DIR" | tail -n +11 | xargs -I {} rm -rf "$BACKUP_DIR/{}" deploy_log "INFO""备份创建成功: $backup_path" echo"$backup_path" else deploy_log "INFO""应用目录不存在,跳过备份" fi }
# 下载代码 download_code() { local branch=${1:-${DEPLOY_CONFIG["branch"]}} local temp_dir="/tmp/deploy_$$" deploy_log "INFO""下载代码分支: $branch" # 克隆代码 if ! git clone -b "$branch""${DEPLOY_CONFIG["git_repo"]}""$temp_dir"; then deploy_log "ERROR""代码下载失败" return 1 fi # 移动到应用目录 mkdir -p "$APP_DIR" rsync -av --delete "$temp_dir/""$APP_DIR/" # 清理临时目录 rm -rf "$temp_dir" deploy_log "INFO""代码下载完成" return 0 }
# 构建应用 build_application() { deploy_log "INFO""开始构建应用" cd"$APP_DIR" || return 1 # 安装依赖 if [ -f "package.json" ]; then deploy_log "INFO""安装Node.js依赖" npm ci --production fi # 执行构建命令 if [ ! -z "${DEPLOY_CONFIG["build_command"]}" ]; then deploy_log "INFO""执行构建命令: ${DEPLOY_CONFIG["build_command"]}" eval"${DEPLOY_CONFIG["build_command"]}" fi deploy_log "INFO""应用构建完成" return 0 }
# 蓝绿部署 blue_green_deploy() { localenv=$1 local branch=${2:-${DEPLOY_CONFIG["branch"]}} deploy_log "INFO""开始蓝绿部署到环境: $env" # 确定当前活跃环境 local current_env="blue" local target_env="green" if [ -f "/tmp/current_env" ]; then current_env=$(cat /tmp/current_env) if [ "$current_env" = "blue" ]; then target_env="green" else target_env="blue" fi fi deploy_log "INFO""当前环境: $current_env, 目标环境: $target_env" # 部署到目标环境 local target_dir="${APP_DIR}_${target_env}" local original_app_dir="$APP_DIR" APP_DIR="$target_dir" # 执行部署 deploy_to_environment "$env""$branch" # 切换流量 deploy_log "INFO""切换流量到: $target_env" # 更新负载均衡器配置(这里需要根据实际情况实现) # update_load_balancer "$target_env" # 更新当前环境标记 echo"$target_env" > /tmp/current_env # 恢复原始APP_DIR APP_DIR="$original_app_dir" deploy_log "INFO""蓝绿部署完成" }
# 金丝雀部署 canary_deploy() { localenv=$1 local branch=${2:-${DEPLOY_CONFIG["branch"]}} local traffic_percentage=${3:-10} deploy_log "INFO""开始金丝雀部署: $traffic_percentage% 流量" # 部署金丝雀版本 local canary_dir="${APP_DIR}_canary" local original_app_dir="$APP_DIR" APP_DIR="$canary_dir" deploy_to_environment "$env""$branch" # 配置流量分流(需要根据实际负载均衡器实现) deploy_log "INFO""配置 $traffic_percentage% 流量到金丝雀版本" # 监控金丝雀版本 deploy_log "INFO""监控金丝雀版本性能" sleep 300 # 监控5分钟 # 检查错误率 local error_rate=$(check_error_rate "$canary_dir") if [ "$error_rate" -lt 5 ]; then deploy_log "INFO""金丝雀版本表现良好,准备全量部署" # 全量部署 APP_DIR="$original_app_dir" deploy_to_environment "$env""$branch" # 清理金丝雀版本 rm -rf "$canary_dir" else deploy_log "ERROR""金丝雀版本错误率过高: $error_rate%" # 回滚金丝雀版本 rm -rf "$canary_dir" return 1 fi APP_DIR="$original_app_dir" deploy_log "INFO""金丝雀部署完成" }
# 检查错误率 check_error_rate() { local app_dir=$1 local log_file="$app_dir/logs/app.log" if [ ! -f "$log_file" ]; then echo"0" return fi local total_requests=$(grep -c "REQUEST""$log_file" || echo"1") local error_requests=$(grep -c "ERROR""$log_file" || echo"0") local error_rate=$((error_requests * 100 / total_requests)) echo"$error_rate" }
# 主函数 main() { local action=${1:-help} localenv=$2 local branch=$3 # 加载配置 if [ -f "$CONFIG_FILE" ]; then source"$CONFIG_FILE" fi case"$action"in "deploy") if [ -z "$env" ]; then echo"请指定部署环境" >&2 exit 1 fi deploy_to_environment "$env""$branch" ;; "rollback") if [ -z "$env" ]; then echo"请指定回滚环境" >&2 exit 1 fi rollback_deployment ;; "blue-green") if [ -z "$env" ]; then echo"请指定部署环境" >&2 exit 1 fi blue_green_deploy "$env""$branch" ;; "canary") if [ -z "$env" ]; then echo"请指定部署环境" >&2 exit 1 fi local percentage=${4:-10} canary_deploy "$env""$branch""$percentage" ;; "health") health_check ;; "backup") create_backup ;; "help"|*) cat << EOF 自动化部署脚本 用法: $0 <action> <environment> [branch] [options] 操作: deploy <env> [branch] 标准部署 rollback <env> 回滚部署 blue-green <env> [branch] 蓝绿部署 canary <env> [branch] [percent] 金丝雀部署 health 健康检查 backup 创建备份 help 显示帮助 环境: dev, staging, production 示例: $0 deploy production main $0 rollback production $0 blue-green staging feature-branch $0 canary production main 20 EOF ;; esac }
# 参数验证函数 validate_parameters() { local required_params=("$@") for param in"${required_params[@]}"; do if [ -z "${!param:-}" ]; then echo"错误: 缺少必需参数 $param" >&2 return 1 fi done }
# 配置验证 validate_config() { local config_file=$1 if [ ! -f "$config_file" ]; then echo"配置文件不存在: $config_file" >&2 return 1 fi # 检查配置文件语法 if ! bash -n "$config_file"; then echo"配置文件语法错误: $config_file" >&2 return 1 fi echo"配置文件验证通过: $config_file" }