Files
java-template/build-and-push.sh
2025-11-28 15:58:43 +08:00

611 lines
16 KiB
Bash
Executable File
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/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 "所有操作完成!"