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

284
deploy.sh Executable file
View File

@@ -0,0 +1,284 @@
#!/bin/bash
# 知识库系统部署脚本
# 使用Laravel Octane + Swoole
set -e # 遇到错误立即退出
# 配置变量
SERVER_HOST="192.168.1.33"
SERVER_USER="root"
SERVER_PASSWORD="Sipai@123"
SERVER_PATH="/opt/KnowledgeBase"
IMAGE_NAME="knowledge-base-app"
IMAGE_TAG="latest"
COMPOSE_VERSION="1.25.5"
# 颜色输出
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
log_info() {
echo -e "${GREEN}[INFO]${NC} $1"
}
log_warn() {
echo -e "${YELLOW}[WARN]${NC} $1"
}
log_error() {
echo -e "${RED}[ERROR]${NC} $1"
}
# 检查必要工具
check_requirements() {
log_info "检查部署环境..."
if ! command -v docker &> /dev/null; then
log_error "Docker 未安装"
exit 1
fi
if ! command -v sshpass &> /dev/null; then
log_error "sshpass 未安装,请先安装: brew install sshpass"
exit 1
fi
log_info "环境检查完成"
}
# 设置网络代理(如果需要)
setup_proxy() {
if [ "$USE_PROXY" = "true" ]; then
log_info "设置网络代理..."
export https_proxy=http://127.0.0.1:7890
export http_proxy=http://127.0.0.1:7890
export all_proxy=socks5://127.0.0.1:7890
log_info "代理设置完成"
fi
}
# 清理本地构建文件
clean_local() {
log_info "清理本地构建文件..."
# 清理不需要的文件
rm -rf node_modules/.cache
rm -rf storage/logs/*.log
rm -rf storage/framework/cache/data/*
rm -rf storage/framework/sessions/*
rm -rf storage/framework/views/*
log_info "本地清理完成"
}
# 构建Docker镜像
build_image() {
log_info "开始构建Docker镜像..."
# 构建镜像
docker build --platform linux/amd64 -t ${IMAGE_NAME}:${IMAGE_TAG} .
if [ $? -eq 0 ]; then
log_info "Docker镜像构建成功"
else
log_error "Docker镜像构建失败"
exit 1
fi
}
# 导出镜像为tar包
export_image() {
log_info "导出Docker镜像..."
docker save ${IMAGE_NAME}:${IMAGE_TAG} | gzip > ${IMAGE_NAME}-${IMAGE_TAG}.tar.gz
if [ $? -eq 0 ]; then
log_info "镜像导出成功: ${IMAGE_NAME}-${IMAGE_TAG}.tar.gz"
else
log_error "镜像导出失败"
exit 1
fi
}
# 同步代码到服务器
sync_code() {
log_info "同步代码到服务器..."
# 创建临时目录用于同步
TEMP_DIR=$(mktemp -d)
# 复制需要的文件
cp -r . "$TEMP_DIR/"
# 删除不需要的文件
cd "$TEMP_DIR"
rm -rf node_modules
# 保留vendor目录因为服务器上缺少Octane包
rm -rf storage/logs/*.log
rm -rf storage/framework/cache/data/*
rm -rf storage/framework/sessions/*
rm -rf storage/framework/views/*
rm -rf .git
rm -rf tests
rm -rf docs
rm -rf .DS_Store
rm -rf *.tar.gz
# 同步到服务器
sshpass -p "${SERVER_PASSWORD}" rsync -avz --delete \
--exclude='storage/mysql/' \
--exclude='storage/redis/' \
--exclude='storage/meilisearch/' \
--exclude='storage/app/documents/' \
--exclude='storage/app/markdown/' \
"$TEMP_DIR/" "${SERVER_USER}@${SERVER_HOST}:${SERVER_PATH}/"
# 清理临时目录
rm -rf "$TEMP_DIR"
log_info "代码同步完成"
}
# 上传Docker镜像
upload_image() {
log_info "上传Docker镜像到服务器..."
sshpass -p "${SERVER_PASSWORD}" scp ${IMAGE_NAME}-${IMAGE_TAG}.tar.gz \
${SERVER_USER}@${SERVER_HOST}:${SERVER_PATH}/
log_info "镜像上传完成"
}
# 在服务器上部署
deploy_on_server() {
log_info "在服务器上执行部署..."
sshpass -p "${SERVER_PASSWORD}" ssh ${SERVER_USER}@${SERVER_HOST} << EOF
cd ${SERVER_PATH}
# 停止现有服务
echo "停止现有服务..."
docker-compose -f docker-compose-simple.yml down || true
# 删除旧镜像
echo "清理旧镜像..."
docker rmi ${IMAGE_NAME}:${IMAGE_TAG} || true
docker system prune -f
# 加载新镜像
echo "加载新镜像..."
docker load < ${IMAGE_NAME}-${IMAGE_TAG}.tar.gz
# 删除镜像文件
rm -f ${IMAGE_NAME}-${IMAGE_TAG}.tar.gz
# 复制生产环境配置
echo "设置生产环境配置..."
cp .env.production .env
# 生成新的APP_KEY如果需要
if grep -q "your-app-key-here" .env; then
echo "生成新的APP_KEY..."
APP_KEY=\$(openssl rand -base64 32)
sed -i "s|your-app-key-here-change-this-in-production|base64:\$APP_KEY|g" .env
fi
# 生成新的MEILISEARCH_KEY如果需要
if grep -q "your-master-key-change-this-in-production" .env; then
echo "生成新的MEILISEARCH_KEY..."
MEILI_KEY=\$(openssl rand -hex 32)
sed -i "s|your-master-key-change-this-in-production|\$MEILI_KEY|g" .env
fi
# 启动服务
echo "启动服务..."
docker-compose -f docker-compose-simple.yml up -d
# 等待服务启动
echo "等待服务启动..."
sleep 30
# 运行数据库迁移
echo "运行数据库迁移..."
docker-compose -f docker-compose-simple.yml exec -T app php artisan migrate --force
# 清理缓存
echo "清理应用缓存..."
docker-compose -f docker-compose-simple.yml exec -T app php artisan config:clear
docker-compose -f docker-compose-simple.yml exec -T app php artisan cache:clear
docker-compose -f docker-compose-simple.yml exec -T app php artisan route:clear
docker-compose -f docker-compose-simple.yml exec -T app php artisan view:clear
# 重新生成缓存
echo "重新生成缓存..."
docker-compose -f docker-compose-simple.yml exec -T app php artisan config:cache
docker-compose -f docker-compose-simple.yml exec -T app php artisan route:cache
docker-compose -f docker-compose-simple.yml exec -T app php artisan view:cache
# 创建搜索索引
echo "创建搜索索引..."
docker-compose -f docker-compose-simple.yml exec -T app php artisan scout:import || true
echo "部署完成!"
EOF
log_info "服务器部署完成"
}
# 验证部署
verify_deployment() {
log_info "验证部署状态..."
sshpass -p "${SERVER_PASSWORD}" ssh ${SERVER_USER}@${SERVER_HOST} << EOF
cd ${SERVER_PATH}
echo "=== 服务状态 ==="
docker-compose -f docker-compose-simple.yml ps
echo "=== 应用健康检查 ==="
curl -f http://localhost:8000/health || echo "健康检查失败"
echo "=== 队列状态 ==="
docker-compose -f docker-compose-simple.yml exec -T app php artisan queue:work --once --timeout=10 || echo "队列测试失败"
echo "=== Meilisearch状态 ==="
curl -f http://localhost:7700/health || echo "Meilisearch连接失败"
echo "=== 数据库连接 ==="
docker-compose -f docker-compose-simple.yml exec -T app php artisan tinker --execute="DB::connection()->getPdo(); echo 'Database connected successfully';" || echo "数据库连接失败"
EOF
log_info "部署验证完成"
}
# 清理本地文件
cleanup() {
log_info "清理本地文件..."
rm -f ${IMAGE_NAME}-${IMAGE_TAG}.tar.gz
log_info "清理完成"
}
# 主函数
main() {
log_info "开始部署知识库系统..."
check_requirements
setup_proxy
clean_local
build_image
export_image
sync_code
upload_image
deploy_on_server
verify_deployment
cleanup
log_info "部署流程全部完成!"
log_info "访问地址: http://${SERVER_HOST}:8000"
}
# 执行主函数
main "$@"