新增自动化部署

This commit is contained in:
2026-05-21 20:34:55 +08:00
parent f78d82040b
commit c59eb09379

518
deploy.sh Normal file
View File

@@ -0,0 +1,518 @@
#!/usr/bin/env bash
# ============================================
# PandaWiki 一键部署脚本
# 用法: ./deploy.sh [选项]
#
# 选项:
# --build 强制重新构建所有镜像
# --skip-build 跳过镜像构建 (使用已有镜像)
# --pull 拉取最新的基础镜像
# --clean 停止并清理所有容器和数据 (危险!)
# --stop 停止所有服务
# --restart 重启所有服务
# --logs 查看所有服务日志
# --status 查看服务运行状态
# --help 显示帮助信息
# ============================================
set -e
# ============================================
# 配置
# ============================================
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
cd "$SCRIPT_DIR"
ENV_FILE=".env"
COMPOSE_FILE="docker-compose.yml"
# 默认端口
ADMIN_PORT="${ADMIN_PORT:-2443}"
APP_PORT="${APP_PORT:-3010}"
API_PORT="${API_PORT:-8000}"
# 颜色输出
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
CYAN='\033[0;36m'
BOLD='\033[1m'
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"; }
log_step() { echo -e "\n${CYAN}${BOLD}==>${NC} ${CYAN}$1${NC}"; }
log_title() { echo -e "\n${BLUE}${BOLD}━━━ $1 ━━━${NC}"; }
banner() {
echo -e "${BLUE}"
echo " ╔══════════════════════════════════════╗"
echo " ║ PandaWiki 部署工具 ║"
echo " ║ v3.85.0 (功能全解锁版) ║"
echo " ╚══════════════════════════════════════╝"
echo -e "${NC}"
}
# ============================================
# 前置检查
# ============================================
check_prerequisites() {
log_step "环境检查"
# 检查 Docker
if ! command -v docker &> /dev/null; then
log_error "未找到 Docker请先安装 Docker Engine 20.x+"
echo " Ubuntu: sudo apt-get install docker-ce"
echo " 官方文档: https://docs.docker.com/engine/install/"
exit 1
fi
log_info "Docker: $(docker --version)"
# 检查 Docker Compose
if docker compose version &> /dev/null; then
log_info "Docker Compose: $(docker compose version --short)"
elif command -v docker-compose &> /dev/null; then
log_info "Docker Compose (独立版): $(docker-compose --version)"
else
log_error "未找到 Docker Compose"
echo " 安装: sudo apt-get install docker-compose-plugin"
exit 1
fi
# 检查 docker-compose.yml
if [ ! -f "$COMPOSE_FILE" ]; then
log_error "未找到 $COMPOSE_FILE,请确保在项目根目录运行此脚本"
exit 1
fi
# 检查磁盘空间 (至少 10GB)
local available_gb
available_gb=$(df -BG . | awk 'NR==2 {print $4}' | sed 's/G//')
if [ "${available_gb:-0}" -lt 10 ]; then
log_warn "磁盘可用空间不足 10GB (当前: ${available_gb}GB),建议预留足够空间"
fi
log_info "环境检查通过"
}
# ============================================
# 环境变量配置
# ============================================
setup_env() {
log_step "配置环境变量"
if [ -f "$ENV_FILE" ]; then
log_info "已找到 ${ENV_FILE},跳过创建"
# 加载环境变量
set -a
source "$ENV_FILE"
set +a
return
fi
log_warn "未找到 ${ENV_FILE},创建默认配置..."
# 生成随机密码
local pg_pass=$(openssl rand -base64 16 2>/dev/null || head -c 16 /dev/urandom | base64)
local redis_pass=$(openssl rand -base64 16 2>/dev/null || head -c 16 /dev/urandom | base64)
local nats_pass=$(openssl rand -base64 16 2>/dev/null || head -c 16 /dev/urandom | base64)
local s3_pass=$(openssl rand -base64 16 2>/dev/null || head -c 16 /dev/urandom | base64)
local jwt_secret=$(openssl rand -base64 32 2>/dev/null || head -c 32 /dev/urandom | base64)
cat > "$ENV_FILE" << EOF
# ============================================
# PandaWiki 环境变量配置
# 生成时间: $(date '+%Y-%m-%d %H:%M:%S')
# ============================================
# 数据库 (PostgreSQL)
POSTGRES_PASSWORD=${pg_pass}
# 缓存 (Redis)
REDIS_PASSWORD=${redis_pass}
# 消息队列 (NATS)
NATS_USER=panda-wiki
NATS_PASSWORD=${nats_pass}
# 对象存储 (MinIO / S3)
S3_ACCESS_KEY=s3panda-wiki
S3_SECRET_KEY=${s3_pass}
# JWT 签名密钥
JWT_SECRET=${jwt_secret}
# 管理员初始密码 (首次登录后请修改)
ADMIN_PASSWORD=admin123
# RAG 向量检索服务地址 (留空使用默认)
# RAG_BASE_URL=http://your-rag-server:5050
# 日志级别: -4=debug, 0=info, 4=warn, 8=error
LOG_LEVEL=0
# 端口配置 (如需修改,请确保与防火墙规则一致)
ADMIN_PORT=2443
APP_PORT=3010
API_PORT=8000
EOF
chmod 600 "$ENV_FILE"
log_info "已创建 ${ENV_FILE},密码已随机生成并保存"
set -a
source "$ENV_FILE"
set +a
}
# ============================================
# 构建镜像
# ============================================
build_images() {
log_step "构建 Docker 镜像"
local build_start
build_start=$(date +%s)
# 构建后端
log_info "构建 panda-wiki-api ..."
docker compose -f "$COMPOSE_FILE" build api
log_info "构建 panda-wiki-consumer ..."
docker compose -f "$COMPOSE_FILE" build consumer
# 构建前端 (多阶段构建,耗时较长)
log_info "构建 panda-wiki-admin (管理后台) ..."
docker compose -f "$COMPOSE_FILE" build admin
log_info "构建 panda-wiki-app (Wiki 用户端) ..."
docker compose -f "$COMPOSE_FILE" build app
local build_end
build_end=$(date +%s)
local build_duration=$((build_end - build_start))
log_info "全部镜像构建完成 (耗时 ${build_duration} 秒)"
# 显示构建的镜像
echo ""
docker images --format "table {{.Repository}}\t{{.Tag}}\t{{.Size}}" \
| grep -E "panda-wiki|REPOSITORY"
}
# ============================================
# 拉取基础镜像
# ============================================
pull_base_images() {
log_step "拉取基础镜像"
docker pull postgres:16-alpine &
docker pull redis:7-alpine &
docker pull nats:2-alpine &
docker pull minio/minio:latest &
wait
log_info "基础镜像拉取完成"
}
# ============================================
# 启动服务
# ============================================
start_services() {
log_step "启动服务"
# 先启动基础设施
log_info "启动基础设施 (PostgreSQL, Redis, NATS, MinIO) ..."
docker compose -f "$COMPOSE_FILE" up -d postgres redis nats minio
# 等待基础设施就绪
log_info "等待基础设施就绪 ..."
local max_wait=60
local waited=0
while [ $waited -lt $max_wait ]; do
if docker compose -f "$COMPOSE_FILE" ps postgres 2>/dev/null | grep -q "healthy"; then
break
fi
sleep 2
waited=$((waited + 2))
echo -n "."
done
echo ""
if [ $waited -ge $max_wait ]; then
log_error "基础设施启动超时,请检查: docker compose logs postgres"
exit 1
fi
log_info "基础设施就绪"
# 启动业务服务
log_info "启动业务服务 (API, Consumer, Admin, App) ..."
docker compose -f "$COMPOSE_FILE" up -d api consumer admin app
# 等待 API 就绪
log_info "等待 API 服务就绪 ..."
waited=0
while [ $waited -lt 30 ]; do
if curl -sf http://localhost:${API_PORT}/api/v1/health > /dev/null 2>&1; then
break
fi
sleep 2
waited=$((waited + 2))
echo -n "."
done
echo ""
log_info "全部服务启动完成"
}
# ============================================
# 显示部署信息
# ============================================
show_info() {
log_title "部署完成"
# 尝试获取服务器 IP
local server_ip
if command -v hostname &> /dev/null; then
server_ip=$(hostname -I 2>/dev/null | awk '{print $1}')
fi
if [ -z "$server_ip" ]; then
server_ip="服务器IP"
fi
echo ""
echo -e " ${BOLD}PandaWiki 已成功部署${NC}"
echo ""
echo -e " ${CYAN}访问地址:${NC}"
echo -e " ┌─────────────────────────────────────────────┐"
echo -e " │ 管理后台: ${GREEN}http://${server_ip}:${ADMIN_PORT}${NC}"
echo -e " │ Wiki 前端: ${GREEN}http://${server_ip}:${APP_PORT}${NC}"
echo -e " │ API 服务: ${GREEN}http://${server_ip}:${API_PORT}${NC}"
echo -e " │ MinIO 控制台: ${GREEN}http://${server_ip}:9001${NC}"
echo -e " └─────────────────────────────────────────────┘"
echo ""
echo -e " ${CYAN}登录信息:${NC}"
echo -e " ┌─────────────────────────────────────────────┐"
echo -e " │ 用户名: ${BOLD}admin${NC}"
echo -e " │ 密码: ${BOLD}${ADMIN_PASSWORD:-admin123}${NC} (首次登录后请修改)"
echo -e " └─────────────────────────────────────────────┘"
echo ""
echo -e " ${CYAN}常用命令:${NC}"
echo -e " ┌─────────────────────────────────────────────┐"
echo -e " │ 查看日志: ${BOLD}docker compose logs -f${NC}"
echo -e " │ 查看状态: ${BOLD}./deploy.sh --status${NC}"
echo -e " │ 重启服务: ${BOLD}./deploy.sh --restart${NC}"
echo -e " │ 停止服务: ${BOLD}./deploy.sh --stop${NC}"
echo -e " └─────────────────────────────────────────────┘"
echo ""
echo -e " ${YELLOW}下一步: 登录管理后台 → 配置 AI 模型 → 创建知识库${NC}"
echo ""
}
# ============================================
# 辅助命令
# ============================================
stop_services() {
log_step "停止所有服务"
docker compose -f "$COMPOSE_FILE" down
log_info "所有服务已停止"
}
restart_services() {
log_step "重启所有服务"
docker compose -f "$COMPOSE_FILE" restart
log_info "所有服务已重启"
}
show_logs() {
log_step "查看服务日志 (按 Ctrl+C 退出)"
docker compose -f "$COMPOSE_FILE" logs -f --tail=50
}
show_status() {
log_step "服务运行状态"
echo ""
docker compose -f "$COMPOSE_FILE" ps
echo ""
# 检查 API 健康状态
if curl -sf http://localhost:${API_PORT}/api/v1/health > /dev/null 2>&1; then
echo -e " API 健康检查: ${GREEN}正常${NC}"
else
echo -e " API 健康检查: ${RED}异常${NC}"
fi
# 显示资源占用
echo ""
echo " 容器资源占用:"
docker stats --no-stream --format "table {{.Name}}\t{{.CPUPerc}}\t{{.MemUsage}}\t{{.NetIO}}" \
$(docker compose -f "$COMPOSE_FILE" ps -q) 2>/dev/null || true
}
clean_all() {
log_step "清理所有容器和数据"
echo -e "${RED}${BOLD}警告: 此操作将删除所有容器、数据卷和镜像!${NC}"
echo -e "${RED}数据库中的所有数据将永久丢失!${NC}"
echo ""
read -p "确认清理? 输入 DELETE 继续: " confirm
if [ "$confirm" != "DELETE" ]; then
log_info "已取消清理操作"
exit 0
fi
log_info "停止并删除容器 ..."
docker compose -f "$COMPOSE_FILE" down -v --remove-orphans
log_info "删除构建的镜像 ..."
docker images --format "{{.Repository}}:{{.Tag}}" \
| grep "panda-wiki" \
| xargs -r docker rmi 2>/dev/null || true
log_info "清理完成"
}
# ============================================
# 主流程
# ============================================
main() {
local DO_BUILD=true
local DO_PULL=false
# 解析命令行参数
while [[ $# -gt 0 ]]; do
case "$1" in
--build)
DO_BUILD=true
shift
;;
--skip-build)
DO_BUILD=false
shift
;;
--pull)
DO_PULL=true
shift
;;
--clean)
banner
clean_all
exit 0
;;
--stop)
banner
stop_services
exit 0
;;
--restart)
banner
restart_services
show_info
exit 0
;;
--logs)
show_logs
exit 0
;;
--status)
banner
show_status
exit 0
;;
--help|-h)
banner
echo "用法: $0 [选项]"
echo ""
echo "选项:"
echo " --build 强制重新构建所有镜像 (默认)"
echo " --skip-build 跳过镜像构建步骤"
echo " --pull 拉取最新的基础镜像"
echo " --clean 停止并清理所有容器和数据 (危险!)"
echo " --stop 停止所有服务"
echo " --restart 重启所有服务"
echo " --logs 查看所有服务日志"
echo " --status 查看服务运行状态"
echo " --help 显示此帮助信息"
echo ""
echo "示例:"
echo " $0 # 完整部署 (构建 + 启动)"
echo " $0 --skip-build # 跳过构建,直接启动"
echo " $0 --restart # 重启已部署的服务"
echo " $0 --status # 查看运行状态"
exit 0
;;
*)
log_error "未知选项: $1"
echo "使用 --help 查看帮助"
exit 1
;;
esac
done
banner
# 检查是否已有正在运行的服务
if docker compose -f "$COMPOSE_FILE" ps --services --filter "status=running" 2>/dev/null | grep -q .; then
log_warn "检测到已有服务在运行"
read -p "是否重新部署? [y/N]: " redeploy
if [ "$redeploy" != "y" ] && [ "$redeploy" != "Y" ]; then
log_info "已取消。使用 --restart 重启,或 --stop 停止"
exit 0
fi
docker compose -f "$COMPOSE_FILE" down
fi
# 部署流程
check_prerequisites
setup_env
if [ "$DO_PULL" = true ]; then
pull_base_images
fi
if [ "$DO_BUILD" = true ]; then
build_images
else
log_step "跳过镜像构建"
# 检查镜像是否存在
local missing_images=()
for img in panda-wiki-api:latest panda-wiki-consumer:latest panda-wiki-admin:latest panda-wiki-app:latest; do
if ! docker image inspect "$img" &> /dev/null; then
missing_images+=("$img")
fi
done
if [ ${#missing_images[@]} -gt 0 ]; then
log_warn "以下镜像不存在: ${missing_images[*]}"
read -p "是否现在构建? [Y/n]: " do_build_now
if [ "$do_build_now" != "n" ] && [ "$do_build_now" != "N" ]; then
build_images
else
log_error "缺少必需镜像,无法继续"
exit 1
fi
fi
fi
start_services
show_info
}
main "$@"