From a1667eac513746c0f80a9188727d7590c3b4d097 Mon Sep 17 00:00:00 2001 From: dekun Date: Wed, 10 Jun 2026 21:13:12 +0800 Subject: [PATCH] Add Docker deployment as the recommended production setup. Include Dockerfile, docker-compose on port 3130, npm mirror config, deploy scripts, and DOCKER.md; keep PM2 docs as fallback. Co-authored-by: Cursor --- .dockerignore | 18 ++++++++ .env.example | 2 +- .npmrc | 5 +++ Dockerfile | 38 ++++++++++++++++ README.md | 28 +++++++----- docker-compose.yml | 16 +++++++ docs/DEPLOY.md | 35 ++++++++++++++- docs/DOCKER.md | 96 ++++++++++++++++++++++++++++++++++++++++ scripts/deploy.sh | 10 ++++- scripts/docker-deploy.sh | 35 +++++++++++++++ 10 files changed, 268 insertions(+), 15 deletions(-) create mode 100644 .dockerignore create mode 100644 .npmrc create mode 100644 Dockerfile create mode 100644 docker-compose.yml create mode 100644 docs/DOCKER.md create mode 100644 scripts/docker-deploy.sh diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..1f48817 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,18 @@ +node_modules +.next +.git +.gitignore +.env +.env.local +.env*.local +logs +*.log +npm-debug.log* +.DS_Store +coverage +.cursor +README.md +docs +content/zhouyi/scripts +ecosystem.config.cjs +scripts/deploy.sh diff --git a/.env.example b/.env.example index 133bbcf..574cc79 100644 --- a/.env.example +++ b/.env.example @@ -13,7 +13,7 @@ OPENAI_BASE_URL=https://op.bz121.com/v1 # AI 模型 OPENAI_MODEL=huihui_ai/gemma-4-abliterated:e4b -# 服务端口(生产 PM2 默认 3130;本地 dev 仍用 3000) +# 服务端口(Docker / PM2 默认 3130;本地 dev 仍用 3000) PORT=3130 # 运行环境 diff --git a/.npmrc b/.npmrc new file mode 100644 index 0000000..b0dca04 --- /dev/null +++ b/.npmrc @@ -0,0 +1,5 @@ +registry=https://registry.npmmirror.com +fetch-timeout=600000 +fetch-retries=5 +audit=false +fund=false diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..7356ee4 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,38 @@ +# 知命阁(zhimingge)— Next.js standalone 生产镜像 +FROM node:20-alpine AS base +RUN apk add --no-cache libc6-compat + +FROM base AS deps +WORKDIR /app +COPY package.json package-lock.json .npmrc ./ +RUN npm ci + +FROM base AS builder +WORKDIR /app +COPY --from=deps /app/node_modules ./node_modules +COPY . . +ENV NEXT_TELEMETRY_DISABLED=1 +RUN npm run build + +FROM base AS runner +WORKDIR /app +ENV NODE_ENV=production +ENV NEXT_TELEMETRY_DISABLED=1 +ENV PORT=3130 +ENV HOSTNAME=0.0.0.0 + +RUN addgroup --system --gid 1001 nodejs \ + && adduser --system --uid 1001 nextjs + +# standalone 产物 +COPY --from=builder /app/.next/standalone ./ +COPY --from=builder /app/.next/static ./.next/static +# 卦辞 Markdown(运行时读取) +COPY --from=builder /app/content ./content + +RUN mkdir -p public && chown -R nextjs:nodejs /app + +USER nextjs +EXPOSE 3130 + +CMD ["node", "server.js"] diff --git a/README.md b/README.md index 0b4d3db..d0f2357 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,8 @@ ## 文档 - [产品说明文档(SPEC)](./docs/SPEC.md) — 功能规格、架构、实施分期 -- [Ubuntu 部署文档(DEPLOY)](./docs/DEPLOY.md) — PM2 部署至 `/opt/zhimingge` +- [Docker 部署文档(DOCKER)](./docs/DOCKER.md) — **推荐** 生产部署 +- [Ubuntu PM2 部署文档(DEPLOY)](./docs/DEPLOY.md) — 备选方案 ## 环境变量 @@ -40,22 +41,22 @@ pnpm run dev 访问 http://localhost:3000(`pnpm run dev` 默认端口;生产 PM2 为 **3130**) -## 生产部署(摘要) +## 生产部署(Docker,推荐) ```bash cd /opt/zhimingge -cp .env.example .env.local && nano .env.local # 填写 OPENAI_API_KEY,PORT=3130 -npm install --frozen-lockfile -npm run build -pm2 start ecosystem.config.cjs -pm2 save && pm2 startup +cp .env.example .env.local && nano .env.local # 填写 OPENAI_API_KEY +docker compose build +docker compose up -d ``` -服务监听 **3130** 端口:`http://服务器IP:3130` +服务监听 **3130** 端口。完整说明见 [docs/DOCKER.md](./docs/DOCKER.md)。 -日常更新:`bash scripts/deploy.sh` +日常更新:`bash scripts/docker-deploy.sh` -完整步骤见 [docs/DEPLOY.md](./docs/DEPLOY.md)。 +### PM2 部署(备选) + +见 [docs/DEPLOY.md](./docs/DEPLOY.md)。 ## 目录说明 @@ -66,8 +67,11 @@ zhimingge/ ├── content/zhouyi/ # 64 卦 Markdown 原文 ├── docs/ # 项目文档 ├── lib/ # 工具与数据 -├── ecosystem.config.cjs # PM2 配置 -└── scripts/deploy.sh # 服务器更新脚本 +├── Dockerfile # Docker 生产镜像 +├── docker-compose.yml # Docker Compose 配置 +├── ecosystem.config.cjs # PM2 配置(备选) +├── scripts/docker-deploy.sh # Docker 更新脚本 +└── scripts/deploy.sh # PM2 更新脚本(备选) ``` ## 技术栈 diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..f73dd5c --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,16 @@ +services: + zhimingge: + build: + context: . + dockerfile: Dockerfile + image: zhimingge:latest + container_name: zhimingge + restart: unless-stopped + ports: + - "3130:3130" + env_file: + - .env.local + environment: + NODE_ENV: production + PORT: "3130" + HOSTNAME: "0.0.0.0" diff --git a/docs/DEPLOY.md b/docs/DEPLOY.md index b44565b..67e4dbc 100644 --- a/docs/DEPLOY.md +++ b/docs/DEPLOY.md @@ -1,7 +1,9 @@ -# 知命阁(zhimingge)Ubuntu 部署文档 +# 知命阁(zhimingge)Ubuntu 部署文档(PM2 备选) > 仓库:[https://git.bz121.com/dekun/zhimingge.git](https://git.bz121.com/dekun/zhimingge.git) +> **推荐改用 Docker 部署**,见 [DOCKER.md](./DOCKER.md)。本文档保留 PM2 手动部署说明。 + 本文档说明在 **Ubuntu 服务器**上使用 **root 用户**、**PM2** 将知命阁部署到 **`/opt/zhimingge`** 的完整流程。 --- @@ -391,6 +393,37 @@ pm2 logs zhimingge --lines 100 | 构建 OOM | 内存不足 | 增加 swap 或升级内存 | | 流式输出中断 | Nginx 缓冲 | 设置 `proxy_buffering off` | | Git pull 失败 | 凭据过期 | 重新配置 Token 或 SSH | +| `npm install` 长时间无输出 | 官方 npm 源慢 / 下载 `@next/swc` 等大包 | 见下方「npm 安装卡住」 | + +### npm 安装卡住 + +国内服务器访问 `registry.npmjs.org` 常极慢,表现为 `npm install` 只有转圈、无进度。 + +**1. 先 Ctrl+C 中断,改用国内镜像:** + +```bash +cd /opt/zhimingge +npm config set registry https://registry.npmmirror.com +npm config set fetch-timeout 600000 +npm install --no-audit --no-fund --loglevel=info +``` + +项目根目录已包含 `.npmrc`(`git pull` 后自动生效),也可直接: + +```bash +git pull origin main +npm install --loglevel=info +``` + +**2. 看卡在哪一步**(可选): + +```bash +npm install --loglevel=verbose 2>&1 | tee /tmp/npm-install.log +``` + +若长时间停在 `@next/swc-*` 或 `sharp`,属正常现象,多等几分钟;超 10 分钟仍无进展再换镜像重试。 + +**3. 内存不足时**(构建阶段 OOM)见下方 Swap 配置。 ### 构建内存不足时增加 Swap diff --git a/docs/DOCKER.md b/docs/DOCKER.md new file mode 100644 index 0000000..87921da --- /dev/null +++ b/docs/DOCKER.md @@ -0,0 +1,96 @@ +# 知命阁 Docker 部署 + +> 推荐生产部署方式,端口 **3130** + +## 前置条件 + +```bash +# Ubuntu 安装 Docker +apt update +apt install -y docker.io docker-compose-v2 +systemctl enable docker --now +docker --version +docker compose version +``` + +## 首次部署 + +```bash +cd /opt +git clone https://git.bz121.com/dekun/zhimingge.git zhimingge +cd /opt/zhimingge + +# 环境变量(必填 OPENAI_API_KEY) +cp .env.example .env.local +nano .env.local +chmod 600 .env.local + +# 构建并启动 +docker compose build +docker compose up -d +``` + +访问:`http://服务器IP:3130` + +## 日常更新 + +```bash +cd /opt/zhimingge +bash scripts/docker-deploy.sh +``` + +或手动: + +```bash +git pull origin main +docker compose build +docker compose up -d +``` + +## 常用命令 + +```bash +docker compose ps # 状态 +docker compose logs -f zhimingge # 日志 +docker compose restart zhimingge # 重启 +docker compose down # 停止并删除容器 +docker compose up -d --build # 重建并启动 +``` + +## 环境变量 + +通过 `.env.local` 注入容器(见 `docker-compose.yml` 的 `env_file`): + +| 变量 | 必填 | 说明 | +|------|------|------| +| `OPENAI_API_KEY` | 是 | AI 接口密钥 | +| `OPENAI_BASE_URL` | 否 | 默认 `https://op.bz121.com/v1` | +| `OPENAI_MODEL` | 否 | 默认 `huihui_ai/gemma-4-abliterated:e4b` | +| `PORT` | 否 | 容器内 3130 | + +## 防火墙 + +```bash +ufw allow 3130 +# 或使用 Nginx 反代 80/443 → 3130 +``` + +## 从 PM2 迁移 + +```bash +pm2 stop zhimingge +pm2 delete zhimingge +cd /opt/zhimingge +docker compose up -d --build +``` + +## 排错 + +| 现象 | 处理 | +|------|------| +| 构建慢 / 超时 | Dockerfile 使用 `.npmrc` 国内镜像;重试 `docker compose build` | +| 容器反复重启 | `docker compose logs zhimingge` 查看报错 | +| AI 失败 | 检查 `.env.local` 中 `OPENAI_API_KEY` | +| 卦辞 404 | 确认镜像内 `/app/content/zhouyi/docs` 存在 | + +构建在镜像内完成,**无需**在宿主机单独 `npm install` / `npm run build`。 diff --git a/scripts/deploy.sh b/scripts/deploy.sh index 21f0f72..a3398b9 100644 --- a/scripts/deploy.sh +++ b/scripts/deploy.sh @@ -16,13 +16,21 @@ git pull origin main echo "==> 安装依赖..." npm ci || npm install +echo "==> 停止 PM2(避免无构建产物时反复重启)..." +pm2 stop "$APP_NAME" 2>/dev/null || true + echo "==> 生产构建..." npm run build +if [[ ! -f .next/BUILD_ID ]]; then + echo "ERROR: 构建失败,未找到 .next/BUILD_ID,请先解决 npm run build 报错后再启动。" + exit 1 +fi + echo "==> 确保日志目录存在..." mkdir -p logs -echo "==> 重启 PM2..." +echo "==> 启动 PM2..." if pm2 describe "$APP_NAME" > /dev/null 2>&1; then pm2 restart "$APP_NAME" else diff --git a/scripts/docker-deploy.sh b/scripts/docker-deploy.sh new file mode 100644 index 0000000..3d3eee8 --- /dev/null +++ b/scripts/docker-deploy.sh @@ -0,0 +1,35 @@ +#!/usr/bin/env bash +# 知命阁 Docker 部署脚本 +# 用法:cd /opt/zhimingge && bash scripts/docker-deploy.sh + +set -euo pipefail + +APP_DIR="/opt/zhimingge" +APP_PORT="${PORT:-3130}" + +cd "$APP_DIR" + +if [[ ! -f .env.local ]]; then + echo "ERROR: 缺少 .env.local,请先:cp .env.example .env.local && nano .env.local" + exit 1 +fi + +echo "==> 拉取最新代码..." +git pull origin main + +echo "==> 停止旧 PM2 进程(若存在)..." +pm2 stop zhimingge 2>/dev/null || true +pm2 delete zhimingge 2>/dev/null || true + +echo "==> 构建 Docker 镜像..." +docker compose build + +echo "==> 启动容器..." +docker compose up -d + +echo "==> 状态" +docker compose ps +sleep 2 +curl -s -o /dev/null -w "HTTP %{http_code}\n" "http://127.0.0.1:${APP_PORT}" || true + +echo "==> 部署完成。日志:docker compose logs -f zhimingge"