anxin-ruoyi/docker/deploy.sh
2026-01-08 20:47:24 +08:00

1086 lines
32 KiB
Bash
Executable File

#!/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 "$@"