build(docker): 增加多阶段 Dockerfile 与编排配置,实现生产级无状态部署

This commit is contained in:
kennethcheng 2026-05-03 04:35:36 +08:00
parent 3ea8d5c550
commit f55113069c
5 changed files with 72 additions and 1 deletions

9
.dockerignore Normal file
View File

@ -0,0 +1,9 @@
node_modules
.next
.git
.env
.env.*
Memory.md
Dockerfile
docker-compose.yml
README.md

37
Dockerfile Normal file
View File

@ -0,0 +1,37 @@
# 阶段 1安装依赖
FROM node:18-alpine AS deps
RUN apk add --no-cache libc6-compat
WORKDIR /app
COPY package.json package-lock.json* ./
RUN npm ci
# 阶段 2构建产物
FROM node:18-alpine AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
# 禁用 Next.js 遥测,并注入占位环境变量以通过编译
ENV NEXT_TELEMETRY_DISABLED=1
RUN npm run build
# 阶段 3生产运行环境
FROM node:18-alpine AS runner
WORKDIR /app
ENV NODE_ENV=production
ENV NEXT_TELEMETRY_DISABLED=1
# 安全降权运行
RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs
COPY --from=builder /app/public ./public
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
USER nextjs
EXPOSE 8080
# 确保 Next.js 监听所有网卡并在正确端口启动
ENV PORT=8080
ENV HOSTNAME="0.0.0.0"
CMD ["node", "server.js"]

View File

@ -305,6 +305,16 @@
- 在 `src/actions/snapshots.ts` 中引入 `desc``gte` 操作符,彻底替换原始 SQL 模板拼接(`sql`"${date}" DESC``),消除 `ReferenceError: date is not defined` 运行时错误。
- 使用 `desc(portfolioSnapshots.date)` 实现降序排列,使用 `gte(portfolioSnapshots.date, startDate)` 实现日期范围过滤,并添加 `.$dynamic()` 支持动态条件拼接。
## 执行 Task 90完成项目无状态 Docker 容器化改造。配置 standalone 模式、多阶段 Dockerfile 及 docker-compose 编排,实现外部 PgSQL 密钥的运行时动态注入隔离 (Task 90)
- **Next.js Standalone 模式**:在 `next.config.ts` 中增加 `output: 'standalone'` 属性,构建时自动生成 `/.next/standalone` 目录,仅包含运行所需的最小文件集,大幅缩减镜像体积。
- **.dockerignore 防腐层**:创建 `.dockerignore` 排除 `node_modules`、`.next`、`.git`、`.env` 等敏感和无用文件,防止污染镜像上下文。
- **三阶段多阶段构建 Dockerfile**
- **阶段 1 (deps)**:基于 `node:18-alpine` 安装依赖,使用 `npm ci` 实现锁死版本的确定性安装。
- **阶段 2 (builder)**:复用 deps 阶段的 `node_modules`,完整复制项目源码并执行 `npm run build`,禁用 Next.js 遥测 (`NEXT_TELEMETRY_DISABLED=1`)。
- **阶段 3 (runner)**:极简生产环境,仅复制 `.next/standalone`、`.next/static` 和 `public` 目录;创建非 root 用户 `nextjs` (uid: 1001) 实现安全降权;暴露 8080 端口并监听 `0.0.0.0`
- **Docker Compose 编排**`docker-compose.yml` 配置 `env_file: .env` 实现运行时环境变量动态注入(数据库 URL、CRON_SECRET 等敏感密钥不打包进镜像);配置 `healthcheck` 使用 `wget` 进行健康探测,每 30 秒检查一次。
- **架构红线**所有生产敏感配置数据库连接串、CRON_SECRET 等)必须通过 `.env` 文件在运行时注入,严禁硬编码或打包进 Docker 镜像层。
## 持倉引擎 Native 幣種算法重構 (Task 38)
- 重構底層盈虧引擎,全面轉向 Native 原生幣種計算,新增浮動/累計盈虧及百分比指標。
- 徹底分離 Native 與 CNY 計算:單隻股票的成本與盈虧全部改用 Native (原幣種) 進行計算。

15
docker-compose.yml Normal file
View File

@ -0,0 +1,15 @@
version: '3.8'
services:
web:
build: .
container_name: stock-portfolio-web
ports:
- "8080:8080"
env_file:
- .env
restart: always
healthcheck:
test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:8080/"]
interval: 30s
timeout: 5s
retries: 3

View File

@ -1,7 +1,7 @@
import type { NextConfig } from "next";
const nextConfig: NextConfig = {
/* config options here */
output: 'standalone',
allowedDevOrigins: [
'10.10.10.1', // 允许该IP访问
// 'your-custom-domain.dev', // 如果有自定义域名也可以加在这里