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:
284
deploy.sh
Executable file
284
deploy.sh
Executable 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 "$@"
|
||||
Reference in New Issue
Block a user