611 lines
16 KiB
Bash
Executable File
611 lines
16 KiB
Bash
Executable File
#!/bin/bash
|
||
# ============================================
|
||
# LJWX 统一构建、推送、部署脚本 v2.0
|
||
# ============================================
|
||
# 功能:
|
||
# - 统一从.env读取配置
|
||
# - 支持分阶段执行:build, push, deploy
|
||
# - 自动数据库迁移管理
|
||
# - 完整的错误处理和日志
|
||
# ============================================
|
||
|
||
set -e
|
||
|
||
# ============================================
|
||
# 配置加载
|
||
# ============================================
|
||
|
||
# 加载.env配置
|
||
if [ -f .env ]; then
|
||
echo "📋 加载配置文件: .env"
|
||
set -a
|
||
source .env
|
||
set +a
|
||
else
|
||
echo "❌ 错误: .env 文件不存在"
|
||
echo "💡 请先创建配置文件:"
|
||
echo " cp .env.unified .env"
|
||
echo " 然后编辑.env文件设置您的配置"
|
||
exit 1
|
||
fi
|
||
|
||
# 颜色定义
|
||
RED='\033[0;31m'
|
||
GREEN='\033[0;32m'
|
||
YELLOW='\033[1;33m'
|
||
BLUE='\033[0;34m'
|
||
MAGENTA='\033[0;35m'
|
||
CYAN='\033[0;36m'
|
||
NC='\033[0m' # No Color
|
||
|
||
# 默认值
|
||
COMPOSE_FILE=${COMPOSE_FILE:-docker-compose.prod.v2.yml}
|
||
BUILD_LOG_DIR="./logs/build"
|
||
DEPLOY_LOG_DIR="./logs/deploy"
|
||
|
||
# 创建日志目录
|
||
mkdir -p "$BUILD_LOG_DIR" "$DEPLOY_LOG_DIR"
|
||
|
||
# ============================================
|
||
# 工具函数
|
||
# ============================================
|
||
|
||
print_header() {
|
||
echo -e "\n${BLUE}========================================${NC}"
|
||
echo -e "${BLUE}$1${NC}"
|
||
echo -e "${BLUE}========================================${NC}\n"
|
||
}
|
||
|
||
print_success() {
|
||
echo -e "${GREEN}✅ $1${NC}"
|
||
}
|
||
|
||
print_error() {
|
||
echo -e "${RED}❌ $1${NC}"
|
||
}
|
||
|
||
print_warning() {
|
||
echo -e "${YELLOW}⚠️ $1${NC}"
|
||
}
|
||
|
||
print_info() {
|
||
echo -e "${CYAN}ℹ️ $1${NC}"
|
||
}
|
||
|
||
print_step() {
|
||
echo -e "${MAGENTA}🔨 $1${NC}"
|
||
}
|
||
|
||
# 检查必需命令
|
||
check_requirements() {
|
||
local missing_cmds=()
|
||
|
||
for cmd in docker mysql mysqldump; do
|
||
if ! command -v $cmd &> /dev/null; then
|
||
missing_cmds+=("$cmd")
|
||
fi
|
||
done
|
||
|
||
if [ ${#missing_cmds[@]} -gt 0 ]; then
|
||
print_error "缺少必需命令: ${missing_cmds[*]}"
|
||
exit 1
|
||
fi
|
||
}
|
||
|
||
# 检查Docker是否运行
|
||
check_docker() {
|
||
if ! docker info &> /dev/null; then
|
||
print_error "Docker未运行,请先启动Docker"
|
||
exit 1
|
||
fi
|
||
}
|
||
|
||
# 检查Registry是否可用
|
||
check_registry() {
|
||
local registry_host=${PRIVATE_REGISTRY_HOST:-localhost:15000}
|
||
|
||
if [ "${PRIVATE_REGISTRY_ENABLED}" = "true" ]; then
|
||
if curl -f -s "http://${registry_host}/v2/_catalog" > /dev/null; then
|
||
print_success "本地私有 Registry 可用: $registry_host"
|
||
return 0
|
||
else
|
||
print_warning "本地私有 Registry 不可用: $registry_host"
|
||
print_info "可以手动启动 Registry 或禁用 PRIVATE_REGISTRY_ENABLED"
|
||
return 1
|
||
fi
|
||
fi
|
||
return 0
|
||
}
|
||
|
||
# ============================================
|
||
# 数据库管理
|
||
# ============================================
|
||
|
||
export_database() {
|
||
print_header "导出数据库基线"
|
||
|
||
# 检查宿主MySQL连接
|
||
if ! mysql -h ${HOST_MYSQL_HOST} -P ${HOST_MYSQL_PORT} \
|
||
-u${HOST_MYSQL_USER} -p${HOST_MYSQL_PASSWORD} \
|
||
-e "SELECT 1" &>/dev/null; then
|
||
print_error "无法连接到宿主数据库 ${HOST_MYSQL_HOST}:${HOST_MYSQL_PORT}"
|
||
print_info "请检查.env中的 HOST_MYSQL_* 配置"
|
||
return 1
|
||
fi
|
||
|
||
# 使用db-migrate.sh导出
|
||
if [ -f ./db-migrate.sh ]; then
|
||
./db-migrate.sh export "${LJWX_VERSION}"
|
||
else
|
||
print_warning "db-migrate.sh 不存在,使用简单导出"
|
||
|
||
local data_file="./docker/mysql/data.sql"
|
||
mysqldump -h ${HOST_MYSQL_HOST} -P ${HOST_MYSQL_PORT} \
|
||
-u${HOST_MYSQL_USER} -p${HOST_MYSQL_PASSWORD} \
|
||
--single-transaction \
|
||
--quick \
|
||
--lock-tables=false \
|
||
--routines \
|
||
--triggers \
|
||
--events \
|
||
--complete-insert \
|
||
${HOST_MYSQL_DATABASE} > "$data_file" 2>/dev/null
|
||
|
||
print_success "数据库已导出: $data_file"
|
||
fi
|
||
}
|
||
|
||
# ============================================
|
||
# 构建镜像
|
||
# ============================================
|
||
|
||
build_image() {
|
||
local component=$1
|
||
local version=${2:-${LJWX_VERSION}}
|
||
|
||
print_step "构建 ${component} 镜像"
|
||
|
||
local image_tag="${REGISTRY}/${component}:${version}"
|
||
local latest_tag="${REGISTRY}/${component}:latest"
|
||
local log_file="${BUILD_LOG_DIR}/${component}_$(date +%Y%m%d_%H%M%S).log"
|
||
|
||
# 根据组件选择Dockerfile和构建上下文
|
||
local dockerfile=""
|
||
local context="."
|
||
local build_args=""
|
||
|
||
case "$component" in
|
||
ljwx-mysql)
|
||
dockerfile="docker/mysql/Dockerfile"
|
||
# MySQL构建前先导出数据库
|
||
if [ "${DB_MIGRATION_ENABLED}" = "true" ]; then
|
||
export_database || print_warning "数据库导出失败,使用现有data.sql"
|
||
fi
|
||
;;
|
||
ljwx-redis)
|
||
dockerfile="docker/redis/Dockerfile"
|
||
;;
|
||
ljwx-boot)
|
||
dockerfile="ljwx-boot/ljwx-boot-admin/Dockerfile.prod"
|
||
context="ljwx-boot"
|
||
;;
|
||
ljwx-bigscreen)
|
||
dockerfile="ljwx-bigscreen/bigscreen/Dockerfile.prod"
|
||
context="ljwx-bigscreen/bigscreen"
|
||
;;
|
||
ljwx-admin)
|
||
dockerfile="ljwx-admin/Dockerfile"
|
||
context="ljwx-admin"
|
||
;;
|
||
*)
|
||
print_error "未知组件: $component"
|
||
return 1
|
||
;;
|
||
esac
|
||
|
||
# 构建参数
|
||
build_args="--build-arg BUILD_DATE=${BUILD_DATE:-$(date -u +'%Y-%m-%dT%H:%M:%SZ')}"
|
||
build_args="$build_args --build-arg GIT_COMMIT=${GIT_COMMIT:-$(git rev-parse --short HEAD 2>/dev/null || echo unknown)}"
|
||
build_args="$build_args --build-arg VERSION=${version}"
|
||
|
||
print_info "镜像标签: $image_tag"
|
||
print_info "Dockerfile: $dockerfile"
|
||
print_info "构建上下文: $context"
|
||
|
||
# 执行构建
|
||
if [ "${LOCAL_BUILD}" = "true" ]; then
|
||
# 本地构建
|
||
docker build \
|
||
${build_args} \
|
||
--platform ${PLATFORMS:-linux/amd64} \
|
||
-t "$image_tag" \
|
||
-t "$latest_tag" \
|
||
-f "$dockerfile" \
|
||
"$context" 2>&1 | tee "$log_file"
|
||
else
|
||
# Buildx多架构构建
|
||
docker buildx build \
|
||
${build_args} \
|
||
--platform ${PLATFORMS:-linux/amd64,linux/arm64} \
|
||
-t "$image_tag" \
|
||
-t "$latest_tag" \
|
||
-f "$dockerfile" \
|
||
"$context" 2>&1 | tee "$log_file"
|
||
fi
|
||
|
||
if [ ${PIPESTATUS[0]} -eq 0 ]; then
|
||
print_success "${component} 构建成功"
|
||
print_info "日志文件: $log_file"
|
||
return 0
|
||
else
|
||
print_error "${component} 构建失败"
|
||
print_info "查看日志: $log_file"
|
||
return 1
|
||
fi
|
||
}
|
||
|
||
build_all() {
|
||
print_header "构建所有镜像 - 版本 ${LJWX_VERSION}"
|
||
|
||
local components=(
|
||
"ljwx-mysql"
|
||
"ljwx-redis"
|
||
"ljwx-boot"
|
||
"ljwx-bigscreen"
|
||
"ljwx-admin"
|
||
)
|
||
|
||
local failed_components=()
|
||
|
||
for component in "${components[@]}"; do
|
||
# 获取组件特定版本
|
||
local component_version_var="${component//-/_}_VERSION"
|
||
component_version_var=$(echo "$component_version_var" | tr '[:lower:]' '[:upper:]') # 转大写
|
||
local version="${!component_version_var:-${LJWX_VERSION}}"
|
||
|
||
if ! build_image "$component" "$version"; then
|
||
failed_components+=("$component")
|
||
fi
|
||
echo ""
|
||
done
|
||
|
||
if [ ${#failed_components[@]} -eq 0 ]; then
|
||
print_success "所有镜像构建成功"
|
||
return 0
|
||
else
|
||
print_error "以下镜像构建失败: ${failed_components[*]}"
|
||
return 1
|
||
fi
|
||
}
|
||
|
||
# ============================================
|
||
# 推送镜像
|
||
# ============================================
|
||
|
||
push_image() {
|
||
local component=$1
|
||
local version=${2:-${LJWX_VERSION}}
|
||
|
||
print_step "推送 ${component} 镜像"
|
||
|
||
local image_tag="${REGISTRY}/${component}:${version}"
|
||
local latest_tag="${REGISTRY}/${component}:latest"
|
||
|
||
# 推送版本标签
|
||
if docker push "$image_tag" 2>&1; then
|
||
print_success "${component}:${version} 推送成功"
|
||
else
|
||
print_error "${component}:${version} 推送失败"
|
||
return 1
|
||
fi
|
||
|
||
# 推送latest标签
|
||
if docker push "$latest_tag" 2>&1; then
|
||
print_success "${component}:latest 推送成功"
|
||
else
|
||
print_warning "${component}:latest 推送失败"
|
||
fi
|
||
|
||
return 0
|
||
}
|
||
|
||
push_all() {
|
||
print_header "推送所有镜像到 ${REGISTRY}"
|
||
|
||
check_registry || return 1
|
||
|
||
local components=(
|
||
"ljwx-mysql"
|
||
"ljwx-redis"
|
||
"ljwx-boot"
|
||
"ljwx-bigscreen"
|
||
"ljwx-admin"
|
||
)
|
||
|
||
local failed_components=()
|
||
|
||
for component in "${components[@]}"; do
|
||
local component_version_var="${component//-/_}_VERSION"
|
||
component_version_var="${component_version_var^^}"
|
||
local version="${!component_version_var:-${LJWX_VERSION}}"
|
||
|
||
if ! push_image "$component" "$version"; then
|
||
failed_components+=("$component")
|
||
fi
|
||
echo ""
|
||
done
|
||
|
||
if [ ${#failed_components[@]} -eq 0 ]; then
|
||
print_success "所有镜像推送成功"
|
||
return 0
|
||
else
|
||
print_error "以下镜像推送失败: ${failed_components[*]}"
|
||
return 1
|
||
fi
|
||
}
|
||
|
||
# ============================================
|
||
# 部署服务
|
||
# ============================================
|
||
|
||
deploy_services() {
|
||
print_header "部署服务 - 使用 ${COMPOSE_FILE}"
|
||
|
||
# 检查compose文件是否存在
|
||
if [ ! -f "$COMPOSE_FILE" ]; then
|
||
print_error "Compose文件不存在: $COMPOSE_FILE"
|
||
return 1
|
||
fi
|
||
|
||
# 检查Registry中是否有镜像
|
||
print_info "检查镜像可用性..."
|
||
|
||
local components=("ljwx-mysql" "ljwx-redis" "ljwx-boot" "ljwx-bigscreen" "ljwx-admin")
|
||
local missing_images=()
|
||
|
||
for component in "${components[@]}"; do
|
||
local component_version_var="${component//-/_}_VERSION"
|
||
component_version_var="${component_version_var^^}"
|
||
local version="${!component_version_var:-${LJWX_VERSION}}"
|
||
local image="${REGISTRY}/${component}:${version}"
|
||
|
||
# 尝试拉取镜像
|
||
if ! docker pull "$image" &>/dev/null; then
|
||
missing_images+=("$image")
|
||
fi
|
||
done
|
||
|
||
if [ ${#missing_images[@]} -gt 0 ]; then
|
||
print_warning "以下镜像不存在:"
|
||
for img in "${missing_images[@]}"; do
|
||
echo " - $img"
|
||
done
|
||
print_info "建议先执行: $0 -b -p"
|
||
read -p "是否继续部署? (yes/no): " confirm
|
||
if [ "$confirm" != "yes" ]; then
|
||
print_info "已取消部署"
|
||
return 1
|
||
fi
|
||
fi
|
||
|
||
# 停止旧服务
|
||
print_info "停止现有服务..."
|
||
docker-compose -f "$COMPOSE_FILE" down 2>&1 | tee "${DEPLOY_LOG_DIR}/down_$(date +%Y%m%d_%H%M%S).log"
|
||
|
||
# 启动新服务
|
||
print_info "启动服务..."
|
||
docker-compose -f "$COMPOSE_FILE" up -d 2>&1 | tee "${DEPLOY_LOG_DIR}/up_$(date +%Y%m%d_%H%M%S).log"
|
||
|
||
# 等待服务健康
|
||
print_info "等待服务就绪..."
|
||
sleep 10
|
||
|
||
# 检查服务状态
|
||
docker-compose -f "$COMPOSE_FILE" ps
|
||
|
||
# 应用数据库迁移
|
||
if [ "${DB_MIGRATION_AUTO_APPLY}" = "true" ] && [ -f ./db-migrate.sh ]; then
|
||
print_info "应用数据库迁移..."
|
||
sleep 20 # 等待MySQL完全启动
|
||
./db-migrate.sh apply-all || print_warning "数据库迁移失败"
|
||
fi
|
||
|
||
print_success "服务部署完成"
|
||
|
||
# 显示访问地址
|
||
print_info "服务访问地址:"
|
||
echo " - 后端API: http://localhost:${BOOT_PORT:-9998}"
|
||
echo " - 管理界面: http://localhost:${ADMIN_EXTERNAL_PORT:-8080}"
|
||
echo " - 大屏服务: http://localhost:${BIGSCREEN_EXTERNAL_PORT:-8000}"
|
||
echo " - MySQL: localhost:${MYSQL_PORT:-3307} (${MYSQL_USER}/${MYSQL_PASSWORD})"
|
||
echo " - Redis: localhost:${REDIS_PORT:-6379} (password: ${REDIS_PASSWORD})"
|
||
}
|
||
|
||
# ============================================
|
||
# 主程序
|
||
# ============================================
|
||
|
||
show_help() {
|
||
cat <<EOF
|
||
${BLUE}LJWX 统一构建、推送、部署脚本 v2.0${NC}
|
||
|
||
用法:
|
||
$0 [选项]
|
||
|
||
选项:
|
||
-b, --build 仅构建镜像
|
||
-p, --push 仅推送镜像到Registry
|
||
-d, --deploy 仅部署服务
|
||
-a, --all 执行所有操作 (build + push + deploy)
|
||
|
||
-c <组件>, --component <组件>
|
||
指定要操作的组件
|
||
可选: ljwx-mysql, ljwx-redis, ljwx-boot,
|
||
ljwx-bigscreen, ljwx-admin
|
||
|
||
-v <版本>, --version <版本>
|
||
指定版本号(默认从.env读取)
|
||
|
||
-e, --export-db 导出宿主数据库
|
||
-m, --migrate 仅执行数据库迁移
|
||
|
||
-h, --help 显示此帮助信息
|
||
|
||
示例:
|
||
|
||
1. 一键构建、推送、部署所有组件
|
||
$ $0 -a
|
||
|
||
2. 仅构建所有镜像
|
||
$ $0 -b
|
||
|
||
3. 仅推送所有镜像
|
||
$ $0 -p
|
||
|
||
4. 仅部署服务
|
||
$ $0 -d
|
||
|
||
5. 构建单个组件
|
||
$ $0 -b -c ljwx-boot
|
||
|
||
6. 构建并推送单个组件
|
||
$ $0 -b -p -c ljwx-mysql
|
||
|
||
7. 导出数据库基线
|
||
$ $0 -e
|
||
|
||
8. 分步操作(推荐用于生产)
|
||
$ $0 -b # 构建镜像
|
||
$ $0 -p # 推送到Registry
|
||
$ $0 -e # 导出数据库(如需要)
|
||
$ $0 -d # 部署服务
|
||
|
||
配置:
|
||
配置文件: .env (从.env.unified复制)
|
||
版本号: ${LJWX_VERSION}
|
||
镜像仓库: ${REGISTRY}
|
||
Compose文件: ${COMPOSE_FILE}
|
||
|
||
更多信息:
|
||
- 数据库迁移: ./db-migrate.sh help
|
||
- Docker Compose: docker-compose -f ${COMPOSE_FILE} --help
|
||
|
||
EOF
|
||
}
|
||
|
||
# 解析命令行参数
|
||
DO_BUILD=false
|
||
DO_PUSH=false
|
||
DO_DEPLOY=false
|
||
DO_EXPORT_DB=false
|
||
DO_MIGRATE=false
|
||
COMPONENT=""
|
||
VERSION="${LJWX_VERSION}"
|
||
|
||
if [ $# -eq 0 ]; then
|
||
show_help
|
||
exit 0
|
||
fi
|
||
|
||
while [[ $# -gt 0 ]]; do
|
||
case $1 in
|
||
-b|--build)
|
||
DO_BUILD=true
|
||
shift
|
||
;;
|
||
-p|--push)
|
||
DO_PUSH=true
|
||
shift
|
||
;;
|
||
-d|--deploy)
|
||
DO_DEPLOY=true
|
||
shift
|
||
;;
|
||
-a|--all)
|
||
DO_BUILD=true
|
||
DO_PUSH=true
|
||
DO_DEPLOY=true
|
||
shift
|
||
;;
|
||
-c|--component)
|
||
COMPONENT="$2"
|
||
shift 2
|
||
;;
|
||
-v|--version)
|
||
VERSION="$2"
|
||
shift 2
|
||
;;
|
||
-e|--export-db)
|
||
DO_EXPORT_DB=true
|
||
shift
|
||
;;
|
||
-m|--migrate)
|
||
DO_MIGRATE=true
|
||
shift
|
||
;;
|
||
-h|--help)
|
||
show_help
|
||
exit 0
|
||
;;
|
||
*)
|
||
print_error "未知选项: $1"
|
||
show_help
|
||
exit 1
|
||
;;
|
||
esac
|
||
done
|
||
|
||
# 执行前检查
|
||
check_requirements
|
||
check_docker
|
||
|
||
# 显示配置信息
|
||
print_header "LJWX 构建部署工具 v2.0"
|
||
echo "版本: ${VERSION}"
|
||
echo "Registry: ${REGISTRY}"
|
||
echo "平台: ${PLATFORMS}"
|
||
echo "Compose: ${COMPOSE_FILE}"
|
||
echo ""
|
||
|
||
# 执行导出数据库
|
||
if [ "$DO_EXPORT_DB" = true ]; then
|
||
export_database
|
||
exit $?
|
||
fi
|
||
|
||
# 执行数据库迁移
|
||
if [ "$DO_MIGRATE" = true ]; then
|
||
if [ -f ./db-migrate.sh ]; then
|
||
./db-migrate.sh apply-all
|
||
else
|
||
print_error "db-migrate.sh 不存在"
|
||
exit 1
|
||
fi
|
||
exit $?
|
||
fi
|
||
|
||
# 执行构建
|
||
if [ "$DO_BUILD" = true ]; then
|
||
if [ -n "$COMPONENT" ]; then
|
||
build_image "$COMPONENT" "$VERSION" || exit 1
|
||
else
|
||
build_all || exit 1
|
||
fi
|
||
fi
|
||
|
||
# 执行推送
|
||
if [ "$DO_PUSH" = true ]; then
|
||
if [ -n "$COMPONENT" ]; then
|
||
push_image "$COMPONENT" "$VERSION" || exit 1
|
||
else
|
||
push_all || exit 1
|
||
fi
|
||
fi
|
||
|
||
# 执行部署
|
||
if [ "$DO_DEPLOY" = true ]; then
|
||
deploy_services || exit 1
|
||
fi
|
||
|
||
print_success "所有操作完成!"
|