- 添加 Dockerfile 与多套 docker-compose 配置(开发/生产环境) - 集成 Laravel Octane (Swoole) 提升性能 - 新增健康检查、监控脚本及部署文档 - 新增 Docker 镜像离线导入包(MySQL/Redis/Meilisearch) - 优化文档转换、预览服务及队列任务 - 添加 CreateAdminUser 命令与路由健康检查接口 - 新增 Swoole 队列兼容性测试套件 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
616 lines
14 KiB
Bash
Executable File
616 lines
14 KiB
Bash
Executable File
#!/bin/bash
|
||
|
||
# OpenEuler服务器部署脚本
|
||
# 用于在OpenEuler服务器上部署Laravel知识库系统
|
||
|
||
set -e
|
||
|
||
# 脚本配置
|
||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||
PROJECT_NAME="knowledge-base"
|
||
DEPLOY_DIR="/opt/${PROJECT_NAME}"
|
||
BACKUP_DIR="/opt/${PROJECT_NAME}-backup"
|
||
LOG_FILE="/var/log/${PROJECT_NAME}-deploy.log"
|
||
|
||
# 颜色输出
|
||
RED='\033[0;31m'
|
||
GREEN='\033[0;32m'
|
||
YELLOW='\033[1;33m'
|
||
BLUE='\033[0;34m'
|
||
NC='\033[0m' # No Color
|
||
|
||
# 日志函数
|
||
log() {
|
||
echo -e "${BLUE}[$(date +'%Y-%m-%d %H:%M:%S')]${NC} $1" | tee -a "$LOG_FILE"
|
||
}
|
||
|
||
log_success() {
|
||
echo -e "${GREEN}[$(date +'%Y-%m-%d %H:%M:%S')] ✓${NC} $1" | tee -a "$LOG_FILE"
|
||
}
|
||
|
||
log_warning() {
|
||
echo -e "${YELLOW}[$(date +'%Y-%m-%d %H:%M:%S')] ⚠${NC} $1" | tee -a "$LOG_FILE"
|
||
}
|
||
|
||
log_error() {
|
||
echo -e "${RED}[$(date +'%Y-%m-%d %H:%M:%S')] ✗${NC} $1" | tee -a "$LOG_FILE"
|
||
}
|
||
|
||
# 显示帮助信息
|
||
show_help() {
|
||
cat << EOF
|
||
OpenEuler服务器部署脚本
|
||
|
||
用法: $0 [选项] [镜像目录]
|
||
|
||
选项:
|
||
-h, --help 显示此帮助信息
|
||
-d, --deploy-dir DIR 部署目录 (默认: /opt/knowledge-base)
|
||
-b, --backup 部署前备份现有安装
|
||
-u, --update 更新现有部署
|
||
-r, --rollback 回滚到上一个版本
|
||
--skip-docker-install 跳过Docker安装
|
||
--skip-images 跳过镜像导入
|
||
--skip-env-setup 跳过环境配置
|
||
--dry-run 仅显示将要执行的操作
|
||
|
||
参数:
|
||
镜像目录 包含Docker镜像文件的目录
|
||
|
||
示例:
|
||
$0 /path/to/docker-images # 全新部署
|
||
$0 -u /path/to/docker-images # 更新现有部署
|
||
$0 -b -d /custom/path # 备份并部署到自定义目录
|
||
$0 --rollback # 回滚到上一个版本
|
||
|
||
EOF
|
||
}
|
||
|
||
# 默认配置
|
||
BACKUP=false
|
||
UPDATE=false
|
||
ROLLBACK=false
|
||
SKIP_DOCKER_INSTALL=false
|
||
SKIP_IMAGES=false
|
||
SKIP_ENV_SETUP=false
|
||
DRY_RUN=false
|
||
IMAGES_DIR=""
|
||
|
||
# 解析命令行参数
|
||
while [[ $# -gt 0 ]]; do
|
||
case $1 in
|
||
-h|--help)
|
||
show_help
|
||
exit 0
|
||
;;
|
||
-d|--deploy-dir)
|
||
DEPLOY_DIR="$2"
|
||
BACKUP_DIR="${DEPLOY_DIR}-backup"
|
||
shift 2
|
||
;;
|
||
-b|--backup)
|
||
BACKUP=true
|
||
shift
|
||
;;
|
||
-u|--update)
|
||
UPDATE=true
|
||
shift
|
||
;;
|
||
-r|--rollback)
|
||
ROLLBACK=true
|
||
shift
|
||
;;
|
||
--skip-docker-install)
|
||
SKIP_DOCKER_INSTALL=true
|
||
shift
|
||
;;
|
||
--skip-images)
|
||
SKIP_IMAGES=true
|
||
shift
|
||
;;
|
||
--skip-env-setup)
|
||
SKIP_ENV_SETUP=true
|
||
shift
|
||
;;
|
||
--dry-run)
|
||
DRY_RUN=true
|
||
shift
|
||
;;
|
||
-*)
|
||
log_error "未知参数: $1"
|
||
show_help
|
||
exit 1
|
||
;;
|
||
*)
|
||
IMAGES_DIR="$1"
|
||
shift
|
||
;;
|
||
esac
|
||
done
|
||
|
||
# 检查是否以root权限运行
|
||
if [[ $EUID -ne 0 ]]; then
|
||
log_error "此脚本需要root权限运行"
|
||
exit 1
|
||
fi
|
||
|
||
# 创建日志目录
|
||
mkdir -p "$(dirname "$LOG_FILE")"
|
||
|
||
log "开始OpenEuler服务器部署..."
|
||
log "部署目录: $DEPLOY_DIR"
|
||
log "备份目录: $BACKUP_DIR"
|
||
log "镜像目录: $IMAGES_DIR"
|
||
|
||
# 检查系统信息
|
||
check_system() {
|
||
log "检查系统信息..."
|
||
|
||
# 检查操作系统
|
||
if [[ -f /etc/os-release ]]; then
|
||
source /etc/os-release
|
||
log "操作系统: $NAME $VERSION"
|
||
|
||
if [[ "$ID" != "openEuler" ]] && [[ "$ID_LIKE" != *"rhel"* ]]; then
|
||
log_warning "此脚本专为OpenEuler设计,当前系统: $NAME"
|
||
read -p "是否继续? (y/N): " -n 1 -r
|
||
echo
|
||
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
|
||
exit 1
|
||
fi
|
||
fi
|
||
else
|
||
log_warning "无法检测操作系统版本"
|
||
fi
|
||
|
||
# 检查架构
|
||
local arch=$(uname -m)
|
||
log "系统架构: $arch"
|
||
|
||
if [[ "$arch" != "x86_64" ]]; then
|
||
log_warning "推荐使用x86_64架构,当前: $arch"
|
||
fi
|
||
|
||
# 检查内存
|
||
local memory=$(free -h | awk '/^Mem:/ {print $2}')
|
||
log "系统内存: $memory"
|
||
|
||
# 检查磁盘空间
|
||
local disk_space=$(df -h / | awk 'NR==2 {print $4}')
|
||
log "可用磁盘空间: $disk_space"
|
||
}
|
||
|
||
# 安装Docker
|
||
install_docker() {
|
||
if [[ "$SKIP_DOCKER_INSTALL" == true ]]; then
|
||
log "跳过Docker安装"
|
||
return 0
|
||
fi
|
||
|
||
log "检查Docker安装状态..."
|
||
|
||
if command -v docker >/dev/null 2>&1; then
|
||
local docker_version=$(docker --version)
|
||
log "Docker已安装: $docker_version"
|
||
|
||
# 检查Docker是否运行
|
||
if systemctl is-active --quiet docker; then
|
||
log_success "Docker服务正在运行"
|
||
else
|
||
log "启动Docker服务..."
|
||
systemctl start docker
|
||
systemctl enable docker
|
||
log_success "Docker服务已启动"
|
||
fi
|
||
return 0
|
||
fi
|
||
|
||
log "安装Docker..."
|
||
|
||
if [[ "$DRY_RUN" == true ]]; then
|
||
log "[DRY RUN] 将安装Docker"
|
||
return 0
|
||
fi
|
||
|
||
# 更新系统包
|
||
dnf update -y
|
||
|
||
# 安装必要的包
|
||
dnf install -y dnf-plugins-core
|
||
|
||
# 添加Docker仓库
|
||
dnf config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
|
||
|
||
# 安装Docker
|
||
dnf install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
|
||
|
||
# 启动Docker服务
|
||
systemctl start docker
|
||
systemctl enable docker
|
||
|
||
# 验证安装
|
||
if docker --version >/dev/null 2>&1; then
|
||
log_success "Docker安装成功"
|
||
else
|
||
log_error "Docker安装失败"
|
||
exit 1
|
||
fi
|
||
|
||
# 添加当前用户到docker组(如果不是root)
|
||
if [[ -n "$SUDO_USER" ]]; then
|
||
usermod -aG docker "$SUDO_USER"
|
||
log "已将用户 $SUDO_USER 添加到docker组"
|
||
fi
|
||
}
|
||
|
||
# 安装Docker Compose
|
||
install_docker_compose() {
|
||
log "检查Docker Compose..."
|
||
|
||
if docker compose version >/dev/null 2>&1; then
|
||
local compose_version=$(docker compose version)
|
||
log_success "Docker Compose已安装: $compose_version"
|
||
return 0
|
||
fi
|
||
|
||
log "安装Docker Compose..."
|
||
|
||
if [[ "$DRY_RUN" == true ]]; then
|
||
log "[DRY RUN] 将安装Docker Compose"
|
||
return 0
|
||
fi
|
||
|
||
# Docker Compose通常随Docker一起安装
|
||
# 如果没有,可以手动安装
|
||
if ! docker compose version >/dev/null 2>&1; then
|
||
log_error "Docker Compose未找到,请手动安装"
|
||
exit 1
|
||
fi
|
||
}
|
||
|
||
# 备份现有部署
|
||
backup_existing() {
|
||
if [[ "$BACKUP" == false ]]; then
|
||
return 0
|
||
fi
|
||
|
||
if [[ ! -d "$DEPLOY_DIR" ]]; then
|
||
log "没有现有部署需要备份"
|
||
return 0
|
||
fi
|
||
|
||
log "备份现有部署..."
|
||
|
||
if [[ "$DRY_RUN" == true ]]; then
|
||
log "[DRY RUN] 将备份 $DEPLOY_DIR 到 $BACKUP_DIR"
|
||
return 0
|
||
fi
|
||
|
||
# 停止现有服务
|
||
if [[ -f "$DEPLOY_DIR/docker-compose.yml" ]]; then
|
||
log "停止现有服务..."
|
||
cd "$DEPLOY_DIR"
|
||
docker compose down || true
|
||
fi
|
||
|
||
# 创建备份
|
||
local backup_name="${BACKUP_DIR}-$(date +%Y%m%d-%H%M%S)"
|
||
|
||
if cp -r "$DEPLOY_DIR" "$backup_name"; then
|
||
log_success "备份创建成功: $backup_name"
|
||
|
||
# 创建符号链接到最新备份
|
||
rm -f "$BACKUP_DIR"
|
||
ln -s "$backup_name" "$BACKUP_DIR"
|
||
else
|
||
log_error "备份创建失败"
|
||
exit 1
|
||
fi
|
||
}
|
||
|
||
# 回滚部署
|
||
rollback_deployment() {
|
||
if [[ "$ROLLBACK" == false ]]; then
|
||
return 0
|
||
fi
|
||
|
||
log "回滚部署..."
|
||
|
||
if [[ ! -L "$BACKUP_DIR" ]] || [[ ! -d "$BACKUP_DIR" ]]; then
|
||
log_error "没有找到备份,无法回滚"
|
||
exit 1
|
||
fi
|
||
|
||
if [[ "$DRY_RUN" == true ]]; then
|
||
log "[DRY RUN] 将从 $BACKUP_DIR 回滚"
|
||
return 0
|
||
fi
|
||
|
||
# 停止当前服务
|
||
if [[ -f "$DEPLOY_DIR/docker-compose.yml" ]]; then
|
||
log "停止当前服务..."
|
||
cd "$DEPLOY_DIR"
|
||
docker compose down || true
|
||
fi
|
||
|
||
# 恢复备份
|
||
rm -rf "$DEPLOY_DIR"
|
||
cp -r "$BACKUP_DIR" "$DEPLOY_DIR"
|
||
|
||
# 启动服务
|
||
cd "$DEPLOY_DIR"
|
||
docker compose up -d
|
||
|
||
log_success "回滚完成"
|
||
exit 0
|
||
}
|
||
|
||
# 导入Docker镜像
|
||
import_images() {
|
||
if [[ "$SKIP_IMAGES" == true ]]; then
|
||
log "跳过镜像导入"
|
||
return 0
|
||
fi
|
||
|
||
if [[ -z "$IMAGES_DIR" ]] || [[ ! -d "$IMAGES_DIR" ]]; then
|
||
log_error "镜像目录不存在或未指定: $IMAGES_DIR"
|
||
exit 1
|
||
fi
|
||
|
||
log "导入Docker镜像..."
|
||
|
||
# 查找镜像文件
|
||
local image_files=($(find "$IMAGES_DIR" -name "*.tar*" -type f))
|
||
|
||
if [[ ${#image_files[@]} -eq 0 ]]; then
|
||
log_error "在 $IMAGES_DIR 中没有找到镜像文件"
|
||
exit 1
|
||
fi
|
||
|
||
log "找到 ${#image_files[@]} 个镜像文件"
|
||
|
||
if [[ "$DRY_RUN" == true ]]; then
|
||
for file in "${image_files[@]}"; do
|
||
log "[DRY RUN] 将导入: $(basename "$file")"
|
||
done
|
||
return 0
|
||
fi
|
||
|
||
# 导入镜像
|
||
for file in "${image_files[@]}"; do
|
||
local filename=$(basename "$file")
|
||
log "导入镜像: $filename"
|
||
|
||
if [[ "$file" == *.gz ]]; then
|
||
# 解压并导入
|
||
if gunzip -c "$file" | docker load; then
|
||
log_success "镜像导入成功: $filename"
|
||
else
|
||
log_error "镜像导入失败: $filename"
|
||
exit 1
|
||
fi
|
||
else
|
||
# 直接导入
|
||
if docker load -i "$file"; then
|
||
log_success "镜像导入成功: $filename"
|
||
else
|
||
log_error "镜像导入失败: $filename"
|
||
exit 1
|
||
fi
|
||
fi
|
||
done
|
||
|
||
# 显示导入的镜像
|
||
log "已导入的镜像:"
|
||
docker images --format "table {{.Repository}}\t{{.Tag}}\t{{.Size}}"
|
||
}
|
||
|
||
# 设置部署环境
|
||
setup_deployment() {
|
||
log "设置部署环境..."
|
||
|
||
if [[ "$DRY_RUN" == true ]]; then
|
||
log "[DRY RUN] 将创建部署目录: $DEPLOY_DIR"
|
||
return 0
|
||
fi
|
||
|
||
# 创建部署目录
|
||
mkdir -p "$DEPLOY_DIR"
|
||
cd "$DEPLOY_DIR"
|
||
|
||
# 复制配置文件(如果镜像目录包含)
|
||
if [[ -n "$IMAGES_DIR" ]]; then
|
||
# 查找配置文件
|
||
local config_files=(
|
||
"docker-compose.yml"
|
||
".env.production"
|
||
"docker"
|
||
)
|
||
|
||
for config in "${config_files[@]}"; do
|
||
if [[ -e "$IMAGES_DIR/../$config" ]]; then
|
||
log "复制配置: $config"
|
||
cp -r "$IMAGES_DIR/../$config" .
|
||
fi
|
||
done
|
||
fi
|
||
|
||
# 创建必要的目录
|
||
mkdir -p storage/{mysql,redis,meilisearch,app,logs}
|
||
mkdir -p storage/logs/{app,queue}
|
||
|
||
# 设置权限
|
||
chown -R 1000:1000 storage/
|
||
chmod -R 755 storage/
|
||
|
||
log_success "部署环境设置完成"
|
||
}
|
||
|
||
# 配置环境变量
|
||
setup_environment() {
|
||
if [[ "$SKIP_ENV_SETUP" == true ]]; then
|
||
log "跳过环境配置"
|
||
return 0
|
||
fi
|
||
|
||
log "配置环境变量..."
|
||
|
||
local env_file="$DEPLOY_DIR/.env"
|
||
|
||
if [[ "$DRY_RUN" == true ]]; then
|
||
log "[DRY RUN] 将创建环境配置文件: $env_file"
|
||
return 0
|
||
fi
|
||
|
||
# 生成随机密钥
|
||
local app_key="base64:$(openssl rand -base64 32)"
|
||
local db_password=$(openssl rand -base64 16)
|
||
local meilisearch_key=$(openssl rand -base64 32)
|
||
|
||
# 创建环境配置文件
|
||
cat > "$env_file" << EOF
|
||
# Laravel知识库系统 - 生产环境配置
|
||
# 生成时间: $(date)
|
||
|
||
# 应用配置
|
||
APP_NAME="知识库系统"
|
||
APP_ENV=production
|
||
APP_KEY=$app_key
|
||
APP_DEBUG=false
|
||
APP_URL=http://localhost
|
||
|
||
# 数据库配置
|
||
DB_CONNECTION=mysql
|
||
DB_HOST=mysql
|
||
DB_PORT=3306
|
||
DB_DATABASE=knowledge_base
|
||
DB_USERNAME=knowledge_user
|
||
DB_PASSWORD=$db_password
|
||
|
||
# Redis配置
|
||
REDIS_HOST=redis
|
||
REDIS_PORT=6379
|
||
REDIS_PASSWORD=
|
||
|
||
# 缓存配置
|
||
CACHE_STORE=redis
|
||
SESSION_DRIVER=redis
|
||
QUEUE_CONNECTION=redis
|
||
|
||
# 搜索配置
|
||
SCOUT_DRIVER=meilisearch
|
||
MEILISEARCH_HOST=http://meilisearch:7700
|
||
MEILISEARCH_KEY=$meilisearch_key
|
||
|
||
# 日志配置
|
||
LOG_CHANNEL=stack
|
||
LOG_DEPRECATIONS_CHANNEL=null
|
||
LOG_LEVEL=error
|
||
|
||
# 文件系统
|
||
FILESYSTEM_DISK=local
|
||
|
||
# 邮件配置
|
||
MAIL_MAILER=log
|
||
MAIL_HOST=localhost
|
||
MAIL_PORT=2525
|
||
MAIL_USERNAME=null
|
||
MAIL_PASSWORD=null
|
||
MAIL_ENCRYPTION=null
|
||
MAIL_FROM_ADDRESS="hello@example.com"
|
||
MAIL_FROM_NAME="\${APP_NAME}"
|
||
EOF
|
||
|
||
log_success "环境配置文件创建成功"
|
||
log_warning "请根据实际情况修改 $env_file 中的配置"
|
||
}
|
||
|
||
# 启动服务
|
||
start_services() {
|
||
log "启动服务..."
|
||
|
||
if [[ "$DRY_RUN" == true ]]; then
|
||
log "[DRY RUN] 将启动Docker Compose服务"
|
||
return 0
|
||
fi
|
||
|
||
cd "$DEPLOY_DIR"
|
||
|
||
# 检查配置文件
|
||
if [[ ! -f "docker-compose.yml" ]]; then
|
||
log_error "docker-compose.yml 文件不存在"
|
||
exit 1
|
||
fi
|
||
|
||
if [[ ! -f ".env" ]]; then
|
||
log_error ".env 文件不存在"
|
||
exit 1
|
||
fi
|
||
|
||
# 启动服务
|
||
if docker compose up -d; then
|
||
log_success "服务启动成功"
|
||
else
|
||
log_error "服务启动失败"
|
||
exit 1
|
||
fi
|
||
|
||
# 等待服务就绪
|
||
log "等待服务就绪..."
|
||
sleep 30
|
||
|
||
# 检查服务状态
|
||
docker compose ps
|
||
|
||
# 运行Laravel初始化命令
|
||
log "运行Laravel初始化..."
|
||
docker compose exec -T app php artisan migrate --force || log_warning "数据库迁移失败"
|
||
docker compose exec -T app php artisan storage:link || log_warning "存储链接创建失败"
|
||
|
||
log_success "部署完成!"
|
||
}
|
||
|
||
# 显示部署信息
|
||
show_deployment_info() {
|
||
log "部署信息:"
|
||
log "部署目录: $DEPLOY_DIR"
|
||
log "访问地址: http://$(hostname -I | awk '{print $1}')"
|
||
log "管理命令:"
|
||
log " 查看日志: cd $DEPLOY_DIR && docker compose logs -f"
|
||
log " 重启服务: cd $DEPLOY_DIR && docker compose restart"
|
||
log " 停止服务: cd $DEPLOY_DIR && docker compose down"
|
||
log " 更新应用: cd $DEPLOY_DIR && docker compose pull && docker compose up -d"
|
||
}
|
||
|
||
# 主执行流程
|
||
main() {
|
||
check_system
|
||
|
||
# 处理回滚
|
||
rollback_deployment
|
||
|
||
# 备份现有部署
|
||
backup_existing
|
||
|
||
# 安装Docker
|
||
install_docker
|
||
install_docker_compose
|
||
|
||
# 导入镜像
|
||
import_images
|
||
|
||
# 设置部署环境
|
||
setup_deployment
|
||
|
||
# 配置环境
|
||
setup_environment
|
||
|
||
# 启动服务
|
||
start_services
|
||
|
||
# 显示部署信息
|
||
show_deployment_info
|
||
}
|
||
|
||
# 执行主流程
|
||
main |