#!/bin/bash # 部署脚本 - 若依框架Docker部署方案 # 实现服务启动和停止功能、服务健康检查、日志查看功能 # Requirements: 5.4, 5.5 set -e # =========================================== # 脚本配置 # =========================================== SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" PROJECT_ROOT="$(dirname "$SCRIPT_DIR")" DOCKER_DIR="$SCRIPT_DIR" # 默认配置 DEFAULT_ENVIRONMENT="development" DEFAULT_ACTION="status" FOLLOW_LOGS=false VERBOSE=false FORCE_RECREATE=false TIMEOUT=300 AUTO_CLEAN=false IMAGE_DIR="" # 服务配置 SERVICES=("anxin-mysql" "anxin-backend" "anxin-frontend") ALL_SERVICES="all" # =========================================== # 颜色定义 # =========================================== RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' PURPLE='\033[0;35m' CYAN='\033[0;36m' NC='\033[0m' # No Color # =========================================== # 日志函数 # =========================================== log_info() { echo -e "${BLUE}[INFO]${NC} $(date '+%Y-%m-%d %H:%M:%S') - $1" } log_success() { echo -e "${GREEN}[SUCCESS]${NC} $(date '+%Y-%m-%d %H:%M:%S') - $1" } log_warn() { echo -e "${YELLOW}[WARN]${NC} $(date '+%Y-%m-%d %H:%M:%S') - $1" } log_error() { echo -e "${RED}[ERROR]${NC} $(date '+%Y-%m-%d %H:%M:%S') - $1" } log_debug() { if [[ "$VERBOSE" == "true" ]]; then echo -e "${PURPLE}[DEBUG]${NC} $(date '+%Y-%m-%d %H:%M:%S') - $1" fi } log_step() { echo -e "${CYAN}[STEP]${NC} $(date '+%Y-%m-%d %H:%M:%S') - $1" } # =========================================== # 帮助信息 # =========================================== show_help() { cat << EOF 若依框架Docker部署 - 部署管理脚本 用法: $0 [选项] [动作] [服务] 动作: start 启动服务 (默认: 所有服务) stop 停止服务 (默认: 所有服务) restart 重启服务 (默认: 所有服务) status 查看服务状态 (默认动作) health 执行健康检查 logs 查看服务日志 ps 查看运行中的容器 down 停止并删除所有容器、网络和卷 up 启动所有服务 (等同于start all) load-images 加载Docker镜像文件 (从tar文件) 服务: all 所有服务 (默认) mysql 数据库服务 backend 后端服务 frontend 前端服务 anxin-mysql 数据库服务 (完整名称) anxin-backend 后端服务 (完整名称) anxin-frontend 前端服务 (完整名称) 选项: -e, --env ENV 指定环境 (development|staging|production) [默认: development] -f, --follow 跟踪日志输出 (仅用于logs动作) -t, --timeout SECONDS 设置操作超时时间 [默认: 300秒] --force 强制重新创建容器 --verbose 显示详细日志 -h, --help 显示此帮助信息 健康检查选项: --wait 等待所有服务健康后退出 --retry COUNT 健康检查重试次数 [默认: 10] --interval SECONDS 健康检查间隔 [默认: 30秒] 日志选项: --tail LINES 显示最后N行日志 [默认: 100] --since TIME 显示指定时间后的日志 (如: 2h, 30m) 镜像加载选项: --image-dir DIR 指定镜像文件目录 [默认: 当前目录] --auto-clean 加载后自动删除镜像文件 示例: $0 # 查看服务状态 (开发环境) $0 start # 启动所有服务 $0 start mysql # 仅启动数据库服务 $0 stop backend frontend # 停止后端和前端服务 $0 restart -e production # 重启生产环境所有服务 $0 health --wait # 等待所有服务健康 $0 logs backend -f # 跟踪后端服务日志 $0 logs --tail 50 --since 1h # 查看最近1小时的50行日志 $0 status -e staging # 查看测试环境状态 $0 load-images -e production # 加载生产环境镜像 $0 load-images --image-dir /tmp # 从指定目录加载镜像 Requirements Coverage: 5.4 - 日志输出用于问题排查 5.5 - 验证服务可用性 (健康检查) EOF } # =========================================== # 参数解析 # =========================================== parse_args() { SERVICES_TO_MANAGE=() while [[ $# -gt 0 ]]; do case $1 in start|stop|restart|status|health|logs|ps|down|up|load-images) ACTION="$1" shift ;; -e|--env) ENVIRONMENT="$2" shift 2 ;; --image-dir) IMAGE_DIR="$2" shift 2 ;; --auto-clean) AUTO_CLEAN=true shift ;; -f|--follow) FOLLOW_LOGS=true shift ;; -t|--timeout) TIMEOUT="$2" shift 2 ;; --force) FORCE_RECREATE=true shift ;; --verbose) VERBOSE=true shift ;; --wait) WAIT_FOR_HEALTH=true shift ;; --retry) HEALTH_RETRY_COUNT="$2" shift 2 ;; --interval) HEALTH_CHECK_INTERVAL="$2" shift 2 ;; --tail) LOG_TAIL_LINES="$2" shift 2 ;; --since) LOG_SINCE="$2" shift 2 ;; -h|--help) show_help exit 0 ;; all|mysql|backend|frontend|anxin-mysql|anxin-backend|anxin-frontend) # 标准化服务名称 case $1 in mysql) SERVICES_TO_MANAGE+=("anxin-mysql") ;; backend) SERVICES_TO_MANAGE+=("anxin-backend") ;; frontend) SERVICES_TO_MANAGE+=("anxin-frontend") ;; all) SERVICES_TO_MANAGE=("${SERVICES[@]}") ;; *) SERVICES_TO_MANAGE+=("$1") ;; esac shift ;; *) log_error "未知参数: $1" show_help exit 1 ;; esac done # 设置默认值 ACTION=${ACTION:-$DEFAULT_ACTION} ENVIRONMENT=${ENVIRONMENT:-$DEFAULT_ENVIRONMENT} IMAGE_DIR=${IMAGE_DIR:-"."} HEALTH_RETRY_COUNT=${HEALTH_RETRY_COUNT:-10} HEALTH_CHECK_INTERVAL=${HEALTH_CHECK_INTERVAL:-30} LOG_TAIL_LINES=${LOG_TAIL_LINES:-100} # 如果没有指定服务,默认为所有服务 if [[ ${#SERVICES_TO_MANAGE[@]} -eq 0 ]]; then SERVICES_TO_MANAGE=("${SERVICES[@]}") fi } # =========================================== # 环境验证和配置 # =========================================== validate_environment() { case $ENVIRONMENT in development|dev) ENVIRONMENT="development" ;; staging|stage) ENVIRONMENT="staging" ;; production|prod) ENVIRONMENT="production" ;; *) log_error "无效的环境: $ENVIRONMENT" log_info "支持的环境: development, staging, production" exit 1 ;; esac log_debug "环境验证通过: $ENVIRONMENT" } # 加载环境配置 load_environment_config() { local env_file="${DOCKER_DIR}/environments/.env.${ENVIRONMENT}" if [[ ! -f "$env_file" ]]; then log_error "环境配置文件不存在: $env_file" exit 1 fi log_debug "加载环境配置: $ENVIRONMENT" source "$env_file" # 设置Docker Compose文件路径 COMPOSE_FILE="${DOCKER_DIR}/docker-compose.${ENVIRONMENT}.yml" ENV_FILE="$env_file" if [[ ! -f "$COMPOSE_FILE" ]]; then log_error "Docker Compose文件不存在: $COMPOSE_FILE" exit 1 fi # 导出关键环境变量 export ENVIRONMENT export COMPOSE_PROJECT_NAME=${COMPOSE_PROJECT_NAME:-"anxin-${ENVIRONMENT}"} log_debug "环境配置加载完成" log_debug "Compose文件: $COMPOSE_FILE" log_debug "环境文件: $ENV_FILE" log_debug "项目名称: $COMPOSE_PROJECT_NAME" } # =========================================== # 系统检查 # =========================================== check_prerequisites() { log_debug "检查系统依赖..." # 检查Docker if ! command -v docker &> /dev/null; then log_error "Docker未安装或不在PATH中" exit 1 fi # 检查Docker Compose if ! command -v docker-compose &> /dev/null; then log_error "Docker Compose未安装或不在PATH中" exit 1 fi # 检查Docker守护进程 if ! docker info &> /dev/null; then log_error "Docker守护进程未运行" exit 1 fi log_debug "系统依赖检查通过" } # =========================================== # Docker Compose操作封装 # =========================================== compose_cmd() { local cmd="$1" shift cd "$DOCKER_DIR" log_debug "执行: docker-compose -f $COMPOSE_FILE --env-file $ENV_FILE $cmd $*" docker-compose -f "$COMPOSE_FILE" --env-file "$ENV_FILE" "$cmd" "$@" } # =========================================== # 服务启动功能 (Requirement 5.5) # =========================================== start_services() { local services_list=("$@") if [[ ${#services_list[@]} -eq 0 ]] || [[ "${services_list[*]}" == "${SERVICES[*]}" ]]; then log_step "启动所有服务..." services_list=() # 空数组表示所有服务 else log_step "启动指定服务: ${services_list[*]}" fi # 创建必要的数据目录 create_data_directories # 检查必需的镜像是否存在 check_required_images # 构建启动命令 local compose_args=("up" "-d" "--no-build") # 添加 --no-build 参数 if [[ "$FORCE_RECREATE" == "true" ]]; then compose_args+=("--force-recreate") fi # 添加服务列表 if [[ ${#services_list[@]} -gt 0 ]]; then compose_args+=("${services_list[@]}") fi # 执行启动命令 log_info "正在启动服务..." if compose_cmd "${compose_args[@]}"; then log_success "服务启动命令执行成功" # 等待服务启动 log_info "等待服务启动完成..." sleep 10 # 检查服务状态 check_services_status "${services_list[@]}" # 显示访问信息 show_access_info else log_error "服务启动失败" log_info "查看详细错误信息:" compose_cmd logs --tail 50 return 1 fi } # 创建数据目录 create_data_directories() { log_debug "创建必要的数据目录..." # 从环境变量获取路径,如果没有则使用默认值 local mysql_data_path=${MYSQL_DATA_PATH:-"./data/${ENVIRONMENT}/mysql"} local mysql_log_path=${MYSQL_LOG_PATH:-"./data/${ENVIRONMENT}/mysql-logs"} local backend_log_path=${BACKEND_LOG_PATH:-"./data/${ENVIRONMENT}/backend-logs"} local backend_upload_path=${BACKEND_UPLOAD_PATH:-"./data/${ENVIRONMENT}/uploads"} local frontend_log_path=${FRONTEND_LOG_PATH:-"./data/${ENVIRONMENT}/nginx-logs"} local data_dirs=( "$mysql_data_path" "$mysql_log_path" "$backend_log_path" "$backend_upload_path" "$frontend_log_path" ) for dir in "${data_dirs[@]}"; do # 转换相对路径为绝对路径 local abs_dir="${DOCKER_DIR}/${dir#./}" if [[ ! -d "$abs_dir" ]]; then log_debug "创建目录: $abs_dir" mkdir -p "$abs_dir" fi done # 设置目录权限 local base_data_dir="${DOCKER_DIR}/data/${ENVIRONMENT}" if [[ -d "$base_data_dir" ]]; then chmod -R 755 "$base_data_dir" fi } # 检查必需的镜像是否存在 check_required_images() { log_debug "检查必需的Docker镜像..." # 根据环境确定镜像标签 local env_tag case $ENVIRONMENT in production) env_tag="prod" ;; staging) env_tag="staging" ;; development) env_tag="dev" ;; esac local required_images=( "anxin-frontend:${env_tag}" "anxin-backend:${env_tag}" "mysql:8.0.36" ) local missing_images=() for image in "${required_images[@]}"; do if ! docker images --format "{{.Repository}}:{{.Tag}}" | grep -q "^$image$"; then missing_images+=("$image") else log_debug "✓ 镜像存在: $image" fi done if [[ ${#missing_images[@]} -gt 0 ]]; then log_error "以下必需的镜像不存在:" for image in "${missing_images[@]}"; do log_error " ✗ $image" done log_info "解决方案:" log_info "1. 运行构建脚本: ./build.sh -e $ENVIRONMENT" log_info "2. 或者从远程服务器拉取镜像" log_info "3. 或者使用推送脚本从其他机器传输镜像" exit 1 else log_success "所有必需的镜像都已存在" fi } # =========================================== # 服务停止功能 # =========================================== stop_services() { local services_list=("$@") if [[ ${#services_list[@]} -eq 0 ]] || [[ "${services_list[*]}" == "${SERVICES[*]}" ]]; then log_step "停止所有服务..." services_list=() # 空数组表示所有服务 else log_step "停止指定服务: ${services_list[*]}" fi # 构建停止命令 local compose_args=("stop") # 添加超时参数 compose_args+=("-t" "$TIMEOUT") # 添加服务列表 if [[ ${#services_list[@]} -gt 0 ]]; then compose_args+=("${services_list[@]}") fi # 执行停止命令 log_info "正在停止服务..." if compose_cmd "${compose_args[@]}"; then log_success "服务停止成功" # 显示当前状态 show_services_status else log_error "服务停止失败" return 1 fi } # =========================================== # 服务重启功能 # =========================================== restart_services() { local services_list=("$@") log_step "重启服务: ${services_list[*]:-所有服务}" # 先停止服务 if stop_services "${services_list[@]}"; then # 等待一段时间确保服务完全停止 log_info "等待服务完全停止..." sleep 5 # 再启动服务 start_services "${services_list[@]}" else log_error "重启失败: 服务停止阶段出错" return 1 fi } # =========================================== # 服务健康检查功能 (Requirement 5.5) # =========================================== check_services_health() { local services_list=("$@") local wait_for_health=${WAIT_FOR_HEALTH:-false} if [[ ${#services_list[@]} -eq 0 ]]; then services_list=("${SERVICES[@]}") fi log_step "执行服务健康检查..." local healthy_services=() local unhealthy_services=() local retry_count=0 local max_retries=$HEALTH_RETRY_COUNT while [[ $retry_count -lt $max_retries ]]; do healthy_services=() unhealthy_services=() log_info "健康检查 (第 $((retry_count + 1))/$max_retries 次)..." for service in "${services_list[@]}"; do local container_name="${service}-${ENVIRONMENT}" local health_status=$(get_service_health_status "$service") case $health_status in "healthy") log_success "✓ $service: 健康" healthy_services+=("$service") ;; "starting") log_warn "⚠ $service: 启动中..." unhealthy_services+=("$service") ;; "unhealthy") log_error "✗ $service: 不健康" unhealthy_services+=("$service") ;; "no-healthcheck") # 对于没有健康检查的服务,检查容器是否运行 if is_service_running "$service"; then log_success "✓ $service: 运行中 (无健康检查)" healthy_services+=("$service") else log_error "✗ $service: 未运行" unhealthy_services+=("$service") fi ;; "not-found") log_error "✗ $service: 容器未找到" unhealthy_services+=("$service") ;; *) log_error "✗ $service: 状态未知 ($health_status)" unhealthy_services+=("$service") ;; esac done # 如果所有服务都健康,或者不需要等待,则退出循环 if [[ ${#unhealthy_services[@]} -eq 0 ]] || [[ "$wait_for_health" != "true" ]]; then break fi # 等待下次检查 if [[ $retry_count -lt $((max_retries - 1)) ]]; then log_info "等待 ${HEALTH_CHECK_INTERVAL} 秒后重试..." sleep "$HEALTH_CHECK_INTERVAL" fi ((retry_count++)) done # 显示最终结果 echo "========================================" log_info "健康检查结果:" log_success "健康服务 (${#healthy_services[@]}): ${healthy_services[*]:-无}" if [[ ${#unhealthy_services[@]} -gt 0 ]]; then log_error "不健康服务 (${#unhealthy_services[@]}): ${unhealthy_services[*]}" # 显示不健康服务的详细信息 for service in "${unhealthy_services[@]}"; do show_service_details "$service" done return 1 else log_success "所有服务健康检查通过!" return 0 fi } # 获取服务健康状态 get_service_health_status() { local service="$1" local container_name="${service}-${ENVIRONMENT}" # 检查容器是否存在 if ! docker ps -a --format "{{.Names}}" | grep -q "^${container_name}$"; then echo "not-found" return fi # 检查容器是否运行 if ! docker ps --format "{{.Names}}" | grep -q "^${container_name}$"; then echo "stopped" return fi # 获取健康检查状态 local health_status=$(docker inspect --format='{{.State.Health.Status}}' "$container_name" 2>/dev/null || echo "no-healthcheck") echo "$health_status" } # 检查服务是否运行 is_service_running() { local service="$1" local container_name="${service}-${ENVIRONMENT}" docker ps --format "{{.Names}}" | grep -q "^${container_name}$" } # 显示服务详细信息 show_service_details() { local service="$1" local container_name="${service}-${ENVIRONMENT}" log_info "服务详细信息: $service" echo "----------------------------------------" # 容器状态 local container_status=$(docker inspect --format='{{.State.Status}}' "$container_name" 2>/dev/null || echo "not-found") echo "容器状态: $container_status" # 如果容器存在,显示更多信息 if [[ "$container_status" != "not-found" ]]; then # 启动时间 local started_at=$(docker inspect --format='{{.State.StartedAt}}' "$container_name" 2>/dev/null) echo "启动时间: $started_at" # 重启次数 local restart_count=$(docker inspect --format='{{.RestartCount}}' "$container_name" 2>/dev/null) echo "重启次数: $restart_count" # 最近日志 echo "最近日志:" docker logs --tail 10 "$container_name" 2>&1 | sed 's/^/ /' fi echo "----------------------------------------" } # =========================================== # 服务状态查看功能 # =========================================== show_services_status() { log_step "查看服务状态..." # 显示Docker Compose服务状态 log_info "Docker Compose服务状态:" compose_cmd ps echo "" # 显示详细的容器信息 log_info "容器详细信息:" echo "----------------------------------------" printf "%-20s %-15s %-15s %-20s %-10s\n" "服务名称" "容器状态" "健康状态" "端口映射" "重启次数" echo "----------------------------------------" for service in "${SERVICES[@]}"; do local container_name="${service}-${ENVIRONMENT}" # 获取容器信息 if docker ps -a --format "{{.Names}}" | grep -q "^${container_name}$"; then local status=$(docker inspect --format='{{.State.Status}}' "$container_name" 2>/dev/null) local health=$(get_service_health_status "$service") local ports=$(docker port "$container_name" 2>/dev/null | tr '\n' ' ' | sed 's/[[:space:]]*$//') local restart_count=$(docker inspect --format='{{.RestartCount}}' "$container_name" 2>/dev/null) # 格式化健康状态显示 case $health in "healthy") health="✓ 健康" ;; "starting") health="⚠ 启动中" ;; "unhealthy") health="✗ 不健康" ;; "no-healthcheck") health="- 无检查" ;; *) health="? 未知" ;; esac printf "%-20s %-15s %-15s %-20s %-10s\n" "$service" "$status" "$health" "${ports:-无}" "$restart_count" else printf "%-20s %-15s %-15s %-20s %-10s\n" "$service" "未创建" "-" "-" "-" fi done echo "----------------------------------------" # 显示网络信息 show_network_info # 显示卷信息 show_volume_info } # 显示网络信息 show_network_info() { log_info "网络信息:" local network_name="${COMPOSE_PROJECT_NAME}_default" if docker network ls --format "{{.Name}}" | grep -q "^${network_name}$"; then docker network inspect "$network_name" --format "网络名称: {{.Name}}, 驱动: {{.Driver}}, 子网: {{range .IPAM.Config}}{{.Subnet}}{{end}}" else echo "网络未创建: $network_name" fi } # 显示卷信息 show_volume_info() { log_info "数据卷信息:" echo "----------------------------------------" local volumes=( "mysql-data-${ENVIRONMENT}" "mysql-logs-${ENVIRONMENT}" "backend-logs-${ENVIRONMENT}" "backend-uploads-${ENVIRONMENT}" "frontend-logs-${ENVIRONMENT}" ) for volume in "${volumes[@]}"; do local full_volume_name="${COMPOSE_PROJECT_NAME}_${volume}" if docker volume ls --format "{{.Name}}" | grep -q "^${full_volume_name}$"; then local mount_point=$(docker volume inspect "$full_volume_name" --format "{{.Mountpoint}}" 2>/dev/null) echo "✓ $volume -> $mount_point" else echo "✗ $volume (未创建)" fi done echo "----------------------------------------" } # =========================================== # 日志查看功能 (Requirement 5.4) # =========================================== show_services_logs() { local services_list=("$@") if [[ ${#services_list[@]} -eq 0 ]]; then services_list=("${SERVICES[@]}") fi log_step "查看服务日志: ${services_list[*]}" # 构建日志命令 local compose_args=("logs") # 添加日志选项 if [[ "$FOLLOW_LOGS" == "true" ]]; then compose_args+=("-f") fi if [[ -n "$LOG_TAIL_LINES" ]]; then compose_args+=("--tail" "$LOG_TAIL_LINES") fi if [[ -n "$LOG_SINCE" ]]; then compose_args+=("--since" "$LOG_SINCE") fi # 添加时间戳 compose_args+=("-t") # 添加服务列表 compose_args+=("${services_list[@]}") # 执行日志命令 log_info "日志查看选项: tail=${LOG_TAIL_LINES:-all}, since=${LOG_SINCE:-all}, follow=${FOLLOW_LOGS}" if [[ "$FOLLOW_LOGS" == "true" ]]; then log_info "开始跟踪日志 (按 Ctrl+C 退出)..." fi compose_cmd "${compose_args[@]}" } # =========================================== # 镜像加载功能 # =========================================== load_docker_images() { log_step "加载Docker镜像..." # 检查镜像目录是否存在 if [[ ! -d "$IMAGE_DIR" ]]; then log_error "镜像目录不存在: $IMAGE_DIR" exit 1 fi log_info "搜索镜像目录: $IMAGE_DIR" # 根据环境确定镜像标签 local env_tag case $ENVIRONMENT in production) env_tag="prod" ;; staging) env_tag="staging" ;; development) env_tag="dev" ;; esac # 查找镜像文件 local image_files=() local expected_images=( "anxin-frontend_${env_tag}.tar" "anxin-backend_${env_tag}.tar" ) # 检查预期的镜像文件 for expected_image in "${expected_images[@]}"; do local image_path="${IMAGE_DIR}/${expected_image}" if [[ -f "$image_path" ]]; then image_files+=("$image_path") log_info "找到镜像文件: $expected_image" else log_warn "未找到预期镜像文件: $expected_image" fi done # 查找其他可能的镜像文件 while IFS= read -r -d '' file; do local filename=$(basename "$file") if [[ "$filename" == anxin-*.tar && ! " ${image_files[*]} " =~ " ${file} " ]]; then image_files+=("$file") log_info "找到额外镜像文件: $filename" fi done < <(find "$IMAGE_DIR" -name "anxin-*.tar" -type f -print0 2>/dev/null) # 检查是否找到镜像文件 if [[ ${#image_files[@]} -eq 0 ]]; then log_error "在目录 $IMAGE_DIR 中未找到任何镜像文件" log_info "预期的镜像文件格式: anxin-frontend_${env_tag}.tar, anxin-backend_${env_tag}.tar" exit 1 fi log_info "找到 ${#image_files[@]} 个镜像文件" # 加载镜像 local loaded_count=0 local failed_count=0 for image_file in "${image_files[@]}"; do local filename=$(basename "$image_file") log_info "正在加载镜像: $filename" # 检查文件大小 local file_size=$(stat -f%z "$image_file" 2>/dev/null || stat -c%s "$image_file" 2>/dev/null || echo "0") local size_mb=$((file_size / 1024 / 1024)) log_info "文件大小: ${size_mb}MB" # 加载镜像 if docker load -i "$image_file"; then log_success "镜像加载成功: $filename" ((loaded_count++)) # 如果启用自动清理,删除镜像文件 if [[ "$AUTO_CLEAN" == "true" ]]; then log_info "删除镜像文件: $filename" rm -f "$image_file" log_success "镜像文件已删除: $filename" fi else log_error "镜像加载失败: $filename" ((failed_count++)) fi echo "" done # 显示加载结果 echo "========================================" log_info "镜像加载结果:" log_success "成功加载: $loaded_count 个镜像" if [[ $failed_count -gt 0 ]]; then log_error "加载失败: $failed_count 个镜像" fi # 显示当前镜像列表 log_info "当前Docker镜像列表:" echo "----------------------------------------" docker images --format "table {{.Repository}}\t{{.Tag}}\t{{.ID}}\t{{.CreatedAt}}\t{{.Size}}" | grep -E "(anxin-|REPOSITORY)" || echo "未找到相关镜像" echo "----------------------------------------" if [[ $failed_count -gt 0 ]]; then return 1 else log_success "所有镜像加载完成!" return 0 fi } # =========================================== # 完整清理功能 # =========================================== down_all_services() { log_step "停止并清理所有服务..." log_warn "这将停止所有容器并删除网络,但保留数据卷" read -p "确认继续? (y/N): " -n 1 -r echo if [[ ! $REPLY =~ ^[Yy]$ ]]; then log_info "操作已取消" return 0 fi # 执行down命令 if compose_cmd down --remove-orphans; then log_success "服务清理完成" # 显示清理后的状态 log_info "清理后状态:" compose_cmd ps else log_error "服务清理失败" return 1 fi } # =========================================== # 显示访问信息 # =========================================== show_access_info() { log_info "服务访问信息:" echo "========================================" echo "环境: $ENVIRONMENT" echo "----------------------------------------" echo "前端应用: http://localhost:${FRONTEND_PORT:-80}" echo "后端API: http://localhost:${BACKEND_PORT:-8080}" echo "数据库: localhost:${DB_PORT:-3306}" echo "----------------------------------------" echo "管理命令:" echo " 查看状态: $0 status -e $ENVIRONMENT" echo " 查看日志: $0 logs -e $ENVIRONMENT" echo " 健康检查: $0 health -e $ENVIRONMENT" echo " 停止服务: $0 stop -e $ENVIRONMENT" echo "========================================" } # =========================================== # 检查服务状态 (内部使用) # =========================================== check_services_status() { local services_list=("$@") if [[ ${#services_list[@]} -eq 0 ]]; then services_list=("${SERVICES[@]}") fi log_info "检查服务启动状态..." local running_services=() local failed_services=() for service in "${services_list[@]}"; do if is_service_running "$service"; then running_services+=("$service") log_success "✓ $service: 运行中" else failed_services+=("$service") log_error "✗ $service: 未运行" fi done if [[ ${#failed_services[@]} -gt 0 ]]; then log_warn "部分服务启动失败: ${failed_services[*]}" log_info "建议执行健康检查: $0 health -e $ENVIRONMENT" fi } # =========================================== # 主函数 # =========================================== main() { # 显示脚本信息 log_info "若依框架Docker部署 - 部署管理脚本" log_info "脚本版本: 1.0.0" log_info "执行时间: $(date '+%Y-%m-%d %H:%M:%S')" # 解析参数 parse_args "$@" # 验证环境 validate_environment # 加载环境配置 load_environment_config # 检查系统依赖 check_prerequisites # 显示当前配置 log_debug "当前配置:" log_debug " 动作: $ACTION" log_debug " 环境: $ENVIRONMENT" log_debug " 服务: ${SERVICES_TO_MANAGE[*]}" log_debug " 超时: ${TIMEOUT}秒" # 根据动作执行相应操作 case $ACTION in start|up) start_services "${SERVICES_TO_MANAGE[@]}" ;; stop) stop_services "${SERVICES_TO_MANAGE[@]}" ;; restart) restart_services "${SERVICES_TO_MANAGE[@]}" ;; status|ps) show_services_status ;; health) check_services_health "${SERVICES_TO_MANAGE[@]}" ;; logs) show_services_logs "${SERVICES_TO_MANAGE[@]}" ;; down) down_all_services ;; load-images) load_docker_images ;; *) log_error "未知动作: $ACTION" show_help exit 1 ;; esac local exit_code=$? if [[ $exit_code -eq 0 ]]; then log_success "操作完成!" else log_error "操作失败 (退出代码: $exit_code)" fi exit $exit_code } # =========================================== # 错误处理 # =========================================== trap 'log_error "脚本执行被中断"; exit 1' INT TERM # 执行主函数 main "$@"