# PandaWiki Ubuntu 22.04 内网部署指南 本文档涵盖从源码传输、构建 Docker 镜像到部署运行的完整流程。 --- ## 一、整体部署架构 ``` ┌──────────────────────────────────────────────────┐ │ Ubuntu 22.04 Server │ │ │ │ ┌─────────────┐ ┌─────────────┐ │ │ │ Nginx/Caddy │ │ Admin 前端 │ │ │ │ (反向代理) │ │ (Nginx) │ │ │ │ 端口: 80/443│ │ 端口: 5000 │ │ │ └──────┬──────┘ └─────────────┘ │ │ │ │ │ ▼ │ │ ┌──────────────────────┐ │ │ │ API 服务 (Go) │ 端口: 8000 │ │ │ Consumer 服务 (Go) │ 后台异步任务 │ │ └──────────┬───────────┘ │ │ │ │ │ │ │ │ ▼ ▼ ▼ ▼ │ │ ┌──────┐ ┌──────┐ ┌──────┐ ┌──────┐ │ │ │ PG │ │Redis │ │ NATS │ │MinIO │ │ │ │ :5432│ │:6379 │ │:4222 │ │:9000 │ │ │ └──────┘ └──────┘ └──────┘ └──────┘ │ │ │ │ ┌──────────────────────┐ │ │ │ RAG 向量检索服务 │ 端口: 5050 │ │ └──────────────────────┘ │ │ │ │ ┌──────────────────────┐ │ │ │ AI 大模型 API │ 外部配置 │ │ └──────────────────────┘ │ └──────────────────────────────────────────────────┘ ``` --- ## 二、服务器环境准备 ### 2.1 系统要求 ```bash # 确认系统版本 lsb_release -a # Ubuntu 22.04.x LTS # 建议配置 # CPU: 4 核以上 # 内存: 8 GB 以上 (AI 相关功能需要更多) # 磁盘: 50 GB 以上 ``` ### 2.2 安装 Docker ```bash # 卸载旧版本 (如果有) sudo apt-get remove docker docker-engine docker.io containerd runc # 安装依赖 sudo apt-get update sudo apt-get install -y \ ca-certificates \ curl \ gnupg \ lsb-release # 添加 Docker 官方 GPG 密钥 sudo mkdir -p /etc/apt/keyrings curl -fsSL https://download.docker.com/linux/ubuntu/gpg | \ sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg # 添加 Docker 仓库 echo \ "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] \ https://download.docker.com/linux/ubuntu \ $(lsb_release -cs) stable" | \ sudo tee /etc/apt/sources.list.d/docker.list > /dev/null # 安装 Docker Engine sudo apt-get update sudo apt-get install -y docker-ce docker-ce-cli containerd.io \ docker-buildx-plugin docker-compose-plugin # 验证安装 sudo docker --version sudo docker compose version # 将当前用户加入 docker 组 (免 sudo) sudo usermod -aG docker $USER newgrp docker # 设置 Docker 开机自启 sudo systemctl enable docker ``` ### 2.3 安装 Docker Compose (独立版,可选) Docker 新版已内置 `docker compose` 插件。如需独立版: ```bash sudo curl -L "https://github.com/docker/compose/releases/latest/download/docker-compose-$(uname -s)-$(uname -m)" \ -o /usr/local/bin/docker-compose sudo chmod +x /usr/local/bin/docker-compose ``` ### 2.4 安装 Git (用于获取源码) ```bash sudo apt-get install -y git ``` --- ## 三、传输源码到服务器 ### 方式 A:Git 仓库 (服务器有网络) ```bash # 在服务器上 git clone https://github.com/chaitin/PandaWiki.git cd PandaWiki # 然后将你 Windows 上修改过的文件传输到服务器覆盖对应文件 # 需要覆盖的文件: # backend/domain/license.go # backend/usecase/stat.go # backend/repo/pg/auth.go # web/admin/src/constant/version.ts ``` ### 方式 B:直接 SCP 传输整个项目 (推荐) 在 Windows 上使用 PowerShell 或 WSL: ```powershell # 在 Windows 上执行,将整个项目传到服务器 scp -r "E:\code project\PandaWiki-3.85.0" user@your-server-ip:/home/user/ # 或者用 rsync (WSL/Linux) rsync -avz --progress \ "/mnt/e/code project/PandaWiki-3.85.0/" \ user@your-server-ip:/home/user/PandaWiki/ ``` ### 方式 C:打包传输 ```bash # 在 Windows 上打包 (Git Bash / WSL) cd "E:\code project\PandaWiki-3.85.0" tar --exclude='.git' -czf panda-wiki.tar.gz . # 传输到服务器 scp panda-wiki.tar.gz user@your-server-ip:/home/user/ # 在服务器上解压 mkdir -p ~/PandaWiki && cd ~/PandaWiki tar -xzf ~/panda-wiki.tar.gz ``` --- ## 四、创建 Docker Compose 配置 在项目根目录创建 `docker-compose.yml`: ```bash cd ~/PandaWiki ``` ```yaml # docker-compose.yml version: '3.8' services: # ========== 基础设施 ========== postgres: image: postgres:16-alpine container_name: panda-wiki-postgres restart: unless-stopped environment: POSTGRES_USER: panda-wiki POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-ChangeMe123!} POSTGRES_DB: panda-wiki volumes: - pg_data:/var/lib/postgresql/data ports: - "127.0.0.1:5432:5432" healthcheck: test: ["CMD-SHELL", "pg_isready -U panda-wiki"] interval: 5s timeout: 5s retries: 5 networks: - panda-wiki redis: image: redis:7-alpine container_name: panda-wiki-redis restart: unless-stopped command: redis-server --appendonly yes --requirepass ${REDIS_PASSWORD:-ChangeMe123!} volumes: - redis_data:/data ports: - "127.0.0.1:6379:6379" healthcheck: test: ["CMD", "redis-cli", "--raw", "incr", "ping"] interval: 5s timeout: 5s retries: 5 networks: - panda-wiki nats: image: nats:2-alpine container_name: panda-wiki-nats restart: unless-stopped command: > -js -m 8222 --user ${NATS_USER:-panda-wiki} --pass ${NATS_PASSWORD:-ChangeMe123!} ports: - "127.0.0.1:4222:4222" - "127.0.0.1:8222:8222" networks: - panda-wiki minio: image: minio/minio:latest container_name: panda-wiki-minio restart: unless-stopped command: server /data --console-address ":9001" environment: MINIO_ROOT_USER: ${S3_ACCESS_KEY:-s3panda-wiki} MINIO_ROOT_PASSWORD: ${S3_SECRET_KEY:-ChangeMe123!} volumes: - minio_data:/data ports: - "127.0.0.1:9000:9000" - "127.0.0.1:9001:9001" healthcheck: test: ["CMD", "curl", "-f", "http://localhost:9000/minio/health/live"] interval: 10s timeout: 5s retries: 5 networks: - panda-wiki # ========== 业务服务 ========== api: build: context: ./backend dockerfile: Dockerfile.api image: panda-wiki-api:latest container_name: panda-wiki-api restart: unless-stopped environment: POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-ChangeMe123!} NATS_PASSWORD: ${NATS_PASSWORD:-ChangeMe123!} REDIS_PASSWORD: ${REDIS_PASSWORD:-ChangeMe123!} JWT_SECRET: ${JWT_SECRET:-your-jwt-secret-change-me} S3_SECRET_KEY: ${S3_SECRET_KEY:-ChangeMe123!} ADMIN_PASSWORD: ${ADMIN_PASSWORD:-admin123} LOG_LEVEL: ${LOG_LEVEL:-0} ENV: ${ENV:-production} SENTRY_ENABLED: "false" PG_DSN: "host=panda-wiki-postgres user=panda-wiki password=${POSTGRES_PASSWORD:-ChangeMe123!} dbname=panda-wiki port=5432 sslmode=disable TimeZone=Asia/Shanghai" MQ_NATS_SERVER: "nats://panda-wiki-nats:4222" REDIS_ADDR: "panda-wiki-redis:6379" S3_ENDPOINT: "panda-wiki-minio:9000" RAG_CT_RAG_BASE_URL: ${RAG_BASE_URL:-http://host.docker.internal:5050} ports: - "127.0.0.1:8000:8000" depends_on: postgres: condition: service_healthy redis: condition: service_healthy nats: condition: service_started minio: condition: service_healthy networks: - panda-wiki consumer: build: context: ./backend dockerfile: Dockerfile.consumer image: panda-wiki-consumer:latest container_name: panda-wiki-consumer restart: unless-stopped environment: POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-ChangeMe123!} NATS_PASSWORD: ${NATS_PASSWORD:-ChangeMe123!} REDIS_PASSWORD: ${REDIS_PASSWORD:-ChangeMe123!} JWT_SECRET: ${JWT_SECRET:-your-jwt-secret-change-me} S3_SECRET_KEY: ${S3_SECRET_KEY:-ChangeMe123!} LOG_LEVEL: ${LOG_LEVEL:-0} ENV: ${ENV:-production} SENTRY_ENABLED: "false" PG_DSN: "host=panda-wiki-postgres user=panda-wiki password=${POSTGRES_PASSWORD:-ChangeMe123!} dbname=panda-wiki port=5432 sslmode=disable TimeZone=Asia/Shanghai" MQ_NATS_SERVER: "nats://panda-wiki-nats:4222" REDIS_ADDR: "panda-wiki-redis:6379" S3_ENDPOINT: "panda-wiki-minio:9000" RAG_CT_RAG_BASE_URL: ${RAG_BASE_URL:-http://host.docker.internal:5050} depends_on: postgres: condition: service_healthy redis: condition: service_healthy nats: condition: service_started minio: condition: service_healthy networks: - panda-wiki # Nginx 反向代理 (可选,用于统一入口) nginx: image: nginx:alpine container_name: panda-wiki-nginx restart: unless-stopped volumes: - ./nginx.conf:/etc/nginx/conf.d/default.conf:ro ports: - "80:80" depends_on: - api networks: - panda-wiki volumes: pg_data: redis_data: minio_data: networks: panda-wiki: driver: bridge ``` ### 创建 Nginx 反向代理配置 ```bash cat > nginx.conf << 'EOF' server { listen 80; server_name _; client_max_body_size 100M; # API 服务 location /api/ { proxy_pass http://panda-wiki-api:8000; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } location /share/ { proxy_pass http://panda-wiki-api:8000; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } # 健康检查 location /health { proxy_pass http://panda-wiki-api:8000; } } EOF ``` ### 创建环境变量文件 ```bash cat > .env << 'EOF' # 数据库密码 POSTGRES_PASSWORD=ChangeMe123! # Redis 密码 REDIS_PASSWORD=ChangeMe123! # NATS 密码 NATS_USER=panda-wiki NATS_PASSWORD=ChangeMe123! # MinIO/S3 S3_ACCESS_KEY=s3panda-wiki S3_SECRET_KEY=ChangeMe123! # JWT 密钥 (务必修改为随机字符串) JWT_SECRET=your-jwt-secret-change-me-to-random-string # 管理员初始密码 (首次登录后修改) ADMIN_PASSWORD=admin123 # 日志级别: -4=debug, 0=info, 4=warn, 8=error LOG_LEVEL=0 # RAG 向量检索服务地址 RAG_BASE_URL=http://host.docker.internal:5050 EOF ``` --- ## 五、构建并启动 ### 5.1 构建后端 Docker 镜像 ```bash cd ~/PandaWiki # 构建 API 镜像 (包含数据库迁移) docker build -f backend/Dockerfile.api -t panda-wiki-api:latest ./backend # 构建 Consumer 镜像 docker build -f backend/Dockerfile.consumer -t panda-wiki-consumer:latest ./backend ``` ### 5.2 构建前端 (可选) 如果需要在服务器上运行前端: ```bash # 安装 Node.js 22 (管理员前端构建) curl -fsSL https://deb.nodesource.com/setup_22.x | sudo -E bash - sudo apt-get install -y nodejs # 安装 pnpm sudo npm install -g pnpm # 构建管理员前端 cd ~/PandaWiki/web pnpm install pnpm --filter panda-wiki-admin build # 构建用户端前端 pnpm --filter panda-wiki-app build # 管理员前端会输出到 web/admin/dist/ # 用户端前端会输出到 web/app/dist/ # 可以用 Nginx 托管这些静态文件 ``` ### 5.3 启动全部服务 ```bash cd ~/PandaWiki # 先启动基础设施 docker compose up -d postgres redis nats minio # 等待基础设施就绪 sleep 15 # 启动业务服务 docker compose up -d api consumer nginx # 查看所有容器状态 docker compose ps # 查看日志 docker compose logs -f api ``` ### 5.4 验证部署 ```bash # 检查 API 是否正常 curl http://localhost:8000/api/v1/health # 查看容器日志 docker compose logs api | tail -50 docker compose logs consumer | tail -50 # 确认数据库迁移成功 docker compose logs api | grep -i migrate ``` --- ## 六、AI 模型配置 首次登录管理后台后,需要配置 AI 大模型才能使用 AI 创作、AI 问答等功能。 ### 支持的模型接入方式 | 方式 | 说明 | |------|------| | OpenAI 兼容 API | 任何兼容 OpenAI 接口的模型服务 | | 本地 Ollama | 在内网服务器上自建 Ollama 服务 | | DeepSeek API | DeepSeek 官方 API | | Gemini API | Google Gemini | ### 内网环境推荐:自建 Ollama ```bash # 在服务器上安装 Ollama curl -fsSL https://ollama.com/install.sh | sh # 拉取模型 (根据服务器配置选择) ollama pull qwen2.5:7b # 7B 参数,需要约 8GB 显存/内存 ollama pull qwen2.5:14b # 14B 参数 ollama pull llama3.1:8b # 8B 参数 # Ollama API 默认监听 11434 端口 # 在 PandaWiki 管理后台配置模型时,填写: # API 地址: http://host.docker.internal:11434/v1 # 模型名称: qwen2.5:7b ``` --- ## 七、Nginx 前端托管 (可选) 将构建好的前端静态文件用 Nginx 托管: ```bash # 创建前端部署目录 sudo mkdir -p /var/www/panda-wiki/{admin,app} # 复制构建产物 sudo cp -r ~/PandaWiki/web/admin/dist/* /var/www/panda-wiki/admin/ sudo cp -r ~/PandaWiki/web/app/dist/* /var/www/panda-wiki/app/ # Nginx 配置 sudo tee /etc/nginx/sites-available/panda-wiki << 'EOF' server { listen 80; server_name your-domain.com; client_max_body_size 100M; # 管理后台 location /admin { alias /var/www/panda-wiki/admin; index index.html; try_files $uri $uri/ /admin/index.html; } # Wiki 用户端 location / { root /var/www/panda-wiki/app; index index.html; try_files $uri $uri/ /index.html; } # API 代理 location /api/ { proxy_pass http://127.0.0.1:8000; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } location /share/ { proxy_pass http://127.0.0.1:8000; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } } EOF sudo ln -s /etc/nginx/sites-available/panda-wiki /etc/nginx/sites-enabled/ sudo nginx -t sudo systemctl reload nginx ``` --- ## 八、内网完全离线部署 如果服务器**完全无法访问外网**,需要在有网络的机器上预先准备所有材料。 ### 8.1 准备阶段 (在有网络的机器上) ```bash # 1. 拉取所有基础 Docker 镜像 docker pull postgres:16-alpine docker pull redis:7-alpine docker pull nats:2-alpine docker pull minio/minio:latest docker pull nginx:alpine docker pull golang:1.24.3-alpine docker pull alpine:3.21 # 2. 导出镜像为 tar 文件 docker save -o postgres-16.tar postgres:16-alpine docker save -o redis-7.tar redis:7-alpine docker save -o nats-2.tar nats:2-alpine docker save -o minio-latest.tar minio/minio:latest docker save -o nginx-alpine.tar nginx:alpine docker save -o golang-1.24.tar golang:1.24.3-alpine docker save -o alpine-3.21.tar alpine:3.21 # 3. 下载 Go 依赖 (可选,Docker 构建时会自动处理) # 如果有网络,直接 Docker build 即可 # 如果完全离线,需要预先 go mod download 并复制缓存 # 4. 打包项目源码 cd ~/PandaWiki tar czf panda-wiki-source.tar.gz \ --exclude='.git' \ --exclude='node_modules' \ --exclude='web/.pnpm-store' \ . # 5. 将镜像 tar 和源码 tar 传输到内网服务器 scp *.tar user@internal-server:/home/user/ ``` ### 8.2 部署阶段 (在内网服务器上) ```bash # 1. 导入 Docker 镜像 cd /home/user for f in *.tar; do docker load -i "$f" done # 2. 解压项目源码 mkdir -p ~/PandaWiki && cd ~/PandaWiki tar xzf ~/panda-wiki-source.tar.gz # 3. 修改 docker-compose.yml 中的 build 部分 # 将 BUILDKIT 缓存挂载移除 (离线环境不支持) # 将 Dockerfile 中的 --mount=type=cache 替换为普通 COPY # 4. 创建离线版 Dockerfile cat > backend/Dockerfile.api.offline << 'DOCKERFILE' FROM golang:1.24.3-alpine AS builder WORKDIR /src ENV CGO_ENABLED=0 COPY backend/go.mod backend/go.sum ./ RUN go mod download COPY backend/ . ARG TARGETOS TARGETARCH VERSION RUN GOOS=$TARGETOS GOARCH=$TARGETARCH go build \ -ldflags "-s -w -extldflags '-static'" \ -o /build/panda-wiki-api \ cmd/api/main.go cmd/api/wire_gen.go \ && GOOS=$TARGETOS GOARCH=$TARGETARCH go build \ -ldflags "-s -w -extldflags '-static'" \ -o /build/panda-wiki-migrate \ cmd/migrate/main.go cmd/migrate/wire_gen.go FROM alpine:3.21 AS api RUN apk update && apk upgrade && apk add --no-cache ca-certificates tzdata \ && update-ca-certificates 2>/dev/null || true && rm -rf /var/cache/apk/* WORKDIR /app COPY --from=builder /build/panda-wiki-api /app/panda-wiki-api COPY --from=builder /build/panda-wiki-migrate /app/panda-wiki-migrate COPY --from=builder /src/store/pg/migration /app/migration CMD ["sh", "-c", "/app/panda-wiki-migrate && /app/panda-wiki-api"] DOCKERFILE # 5. 使用离线 Dockerfile 构建 docker build -f backend/Dockerfile.api.offline -t panda-wiki-api:latest . # 6. 启动服务 docker compose up -d ``` --- ## 九、常用运维命令 ```bash # 查看所有服务状态 docker compose ps # 查看某个服务的实时日志 docker compose logs -f api docker compose logs -f consumer # 重启某个服务 docker compose restart api # 进入容器调试 docker exec -it panda-wiki-api sh # 数据库备份 docker exec panda-wiki-postgres pg_dump -U panda-wiki panda-wiki > backup.sql # 数据库恢复 docker exec -i panda-wiki-postgres psql -U panda-wiki panda-wiki < backup.sql # 更新代码后重新构建 docker compose build api consumer docker compose up -d api consumer # 查看资源占用 docker stats # 完全停止 docker compose down # 停止并删除数据卷 (危险!会丢失所有数据) docker compose down -v ``` --- ## 十、访问地址 部署完成后: | 地址 | 说明 | |------|------| | `http://服务器IP:8000` | 后端 API (通过 Nginx 代理则为 80 端口) | | `http://服务器IP:2443` | 管理后台 (如使用官方 Caddy 配置) | | `http://服务器IP:9001` | MinIO 管理控制台 | 首次登录使用 `.env` 中配置的 `ADMIN_PASSWORD` 密码,用户名为 `admin`。 --- ## 十一、故障排查 ### 数据库连接失败 ```bash # 检查 postgres 是否就绪 docker compose logs postgres # 手动连接测试 docker exec -it panda-wiki-postgres psql -U panda-wiki -d panda-wiki ``` ### API 启动失败 ```bash # 查看详细日志 docker compose logs api # 常见原因: # 1. 数据库迁移失败 - 检查 PG_DSN 配置 # 2. NATS 连接失败 - 检查 MQ_NATS_SERVER 配置 # 3. Redis 连接失败 - 检查 REDIS_ADDR 配置 ``` ### 前端页面空白 ```bash # 检查 API 是否可达 curl http://localhost:8000/api/v1/health # 检查浏览器控制台是否有 CORS 或 API 404 错误 # 确保前端配置的 API 地址正确 ``` ### 容器之间网络不通 ```bash # 检查网络 docker network inspect panda-wiki_panda-wiki # 容器间通信测试 docker exec panda-wiki-api ping panda-wiki-postgres ```