110 lines
4.7 KiB
Docker
110 lines
4.7 KiB
Docker
# =============================================================================
|
||
# 追光AI 项目 Dockerfile (多阶段构建)
|
||
# - stage 1 deps : 安装全部依赖 (含 dev)
|
||
# - stage 2 builder : 跑 next build + prisma generate
|
||
# - stage 3 runner : 仅拷贝运行所需,最小化镜像
|
||
# - 一个镜像两种身份:APP 启动 nextjs / CRON 启动 supercronic(docker-compose 决定)
|
||
# =============================================================================
|
||
|
||
# ---- 阶段 1: 安装依赖 ----
|
||
FROM node:20-alpine AS deps
|
||
WORKDIR /app
|
||
|
||
# 装 python + make + g++ 给 mysql2 / bcryptjs 等原生模块编译用
|
||
# 先切换到 aliyun apk 镜像(默认源在中国大陆极慢,1m+ vs 0.1s)
|
||
RUN sed -i 's|dl-cdn.alpinelinux.org|mirrors.aliyun.com|g' /etc/apk/repositories \
|
||
&& apk add --no-cache python3 make g++ openssl
|
||
|
||
# 先 copy lock file 缓存层
|
||
COPY package.json package-lock.json ./
|
||
COPY prisma ./prisma
|
||
|
||
# 用 ci 安装保证版本一致(同时切到 npmmirror 加速)
|
||
RUN npm config set registry https://registry.npmmirror.com \
|
||
&& npm ci --no-audit --no-fund --include=dev
|
||
|
||
# ---- 阶段 2: 构建 ----
|
||
FROM node:20-alpine AS builder
|
||
WORKDIR /app
|
||
|
||
RUN sed -i 's|dl-cdn.alpinelinux.org|mirrors.aliyun.com|g' /etc/apk/repositories \
|
||
&& apk add --no-cache python3 make g++ openssl
|
||
ENV NEXT_TELEMETRY_DISABLED=1
|
||
ENV NODE_ENV=production
|
||
ENV npm_config_registry=https://registry.npmmirror.com
|
||
|
||
COPY --from=deps /app/node_modules ./node_modules
|
||
COPY . .
|
||
|
||
# 跑 next build 生成 .next/
|
||
# DATABASE_URL 在 build 时需要(next 预渲染静态页面时连接数据库)
|
||
# 用 .env.example 当 placeholder 生成 prisma client
|
||
# 同时注入 OPENAI_API_KEY / DEEPSEEK_API_KEY 占位值,避免 openai SDK v6+ 构造时抛错
|
||
# (build 期不会真正调用 AI,仅为通过模块求值)
|
||
ARG DATABASE_URL
|
||
RUN if [ ! -f .env ]; then cp .env.example .env 2>/dev/null || echo "DATABASE_URL=placeholder" > .env; fi \
|
||
&& export DATABASE_URL="${DATABASE_URL:-placeholder}" \
|
||
&& export DEEPSEEK_API_KEY="${DEEPSEEK_API_KEY:-sk-build-placeholder}" \
|
||
&& export OPENAI_API_KEY="${OPENAI_API_KEY:-sk-build-placeholder}" \
|
||
&& npx prisma generate \
|
||
&& DATABASE_URL="$DATABASE_URL" DEEPSEEK_API_KEY="$DEEPSEEK_API_KEY" OPENAI_API_KEY="$OPENAI_API_KEY" npm run build
|
||
|
||
# ---- 阶段 3: 运行 ----
|
||
FROM node:20-alpine AS runner
|
||
WORKDIR /app
|
||
|
||
# runtime 基础包
|
||
RUN apk add --no-cache openssl tini curl bash
|
||
# supercronic 用于 cron 容器调度(架构:alpine musl 兼容)
|
||
# 直接 COPY 本地预下载的二进制(避免 Docker build 阶段从 GitHub 下载超时)
|
||
# 下载源: https://github.com/aptible/supercronic/releases/tag/v0.2.33
|
||
# SHA1: 71b0d58cc53f6bd72cf2f293e09e294b79c666d8
|
||
ARG SUPERCRONIC=supercronic-linux-amd64
|
||
COPY --chown=root:root scripts/bin/${SUPERCRONIC} /usr/local/bin/${SUPERCRONIC}
|
||
RUN chmod +x /usr/local/bin/${SUPERCRONIC}
|
||
|
||
ENV NODE_ENV=production
|
||
ENV NEXT_TELEMETRY_DISABLED=1
|
||
ENV PORT=8301
|
||
ENV HOSTNAME=0.0.0.0
|
||
|
||
# 非 root 运行
|
||
RUN addgroup -g 1001 -S nodejs && adduser -S nextjs -u 1001
|
||
|
||
# 拷贝必要文件
|
||
COPY --from=builder /app/public ./public
|
||
COPY --from=builder --chown=nextjs:nodejs /app/.next ./.next
|
||
COPY --from=builder /app/node_modules ./node_modules
|
||
# .prisma 目录交给 nextjs 用户(entrypoint 启动时会重新 prisma generate 同步 schema)
|
||
RUN chown -R nextjs:nodejs /app/node_modules/.prisma
|
||
COPY --from=builder /app/package.json ./package.json
|
||
COPY --from=builder /app/prisma ./prisma
|
||
COPY --from=builder /app/prisma.config.ts ./prisma.config.ts
|
||
COPY --from=builder /app/scripts ./scripts
|
||
# seed data files for volume auto-fill
|
||
COPY --from=builder --chown=nextjs:nodejs /app/data ./data-seed
|
||
# 配置/类型文件(必须存在;如果不存在 COPY 会失败但构建继续)
|
||
COPY --from=builder /app/next.config.mjs ./next.config.mjs
|
||
COPY --from=builder /app/postcss.config.mjs ./postcss.config.mjs
|
||
COPY --from=builder /app/tsconfig.json ./tsconfig.json
|
||
COPY --from=builder /app/components.json ./components.json
|
||
|
||
# entrypoint 脚本
|
||
COPY --chown=root:root scripts/entrypoint-app.sh /usr/local/bin/entrypoint-app.sh
|
||
COPY --chown=root:root scripts/entrypoint-cron.sh /usr/local/bin/entrypoint-cron.sh
|
||
RUN chmod +x /usr/local/bin/entrypoint-app.sh /usr/local/bin/entrypoint-cron.sh
|
||
|
||
# 数据 / 日志目录交给 nextjs
|
||
RUN mkdir -p /app/data /app/public/bot-avatars /app/logs && chown -R nextjs:nodejs /app/data /app/public /app/logs
|
||
|
||
# 健康检查 (用 curl 探活)
|
||
HEALTHCHECK --interval=30s --timeout=5s --start-period=30s --retries=3 \
|
||
CMD curl -fs http://localhost:8301/api/health || exit 1
|
||
|
||
# 默认是 app 身份,cron 容器在 compose 里 override command
|
||
USER nextjs
|
||
EXPOSE 8301
|
||
|
||
ENTRYPOINT ["/sbin/tini", "--"]
|
||
CMD ["/usr/local/bin/entrypoint-app.sh"]
|