feat: 新增 Docker 部署支持、Swoole/Octane 集成及相关优化

- 添加 Dockerfile 与多套 docker-compose 配置(开发/生产环境)
- 集成 Laravel Octane (Swoole) 提升性能
- 新增健康检查、监控脚本及部署文档
- 新增 Docker 镜像离线导入包(MySQL/Redis/Meilisearch)
- 优化文档转换、预览服务及队列任务
- 添加 CreateAdminUser 命令与路由健康检查接口
- 新增 Swoole 队列兼容性测试套件

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-28 15:51:19 +08:00
parent acf549c43c
commit 3c206e9e06
90 changed files with 12731 additions and 1255 deletions

616
docker/deploy-to-openeuler.sh Executable file
View File

@@ -0,0 +1,616 @@
#!/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