ci: 添加 Docker + Jenkins 自动化部署配置

- Dockerfile 多阶段构建,standalone 模式输出
- Jenkinsfile 定义 GitLab 触发的 CI/CD 流水线
- docker-compose.yml 简化部署
- next.config.ts 开启 standalone 输出
This commit is contained in:
2026-02-25 12:10:32 +08:00
parent cc7f6d55a7
commit 76bd7b4756
5 changed files with 135 additions and 0 deletions
+8
View File
@@ -0,0 +1,8 @@
node_modules
.next
.git
.gitignore
*.md
.env*.local
prisma/dev.db
prisma/dev.db-journal
+38
View File
@@ -0,0 +1,38 @@
FROM node:20-alpine AS base
# --- Dependencies ---
FROM base AS deps
RUN apk add --no-cache libc6-compat
WORKDIR /app
COPY package.json package-lock.json* ./
RUN npm ci
# --- Build ---
FROM base AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
RUN npx prisma generate
RUN npm run build
# --- Production ---
FROM base AS runner
WORKDIR /app
ENV NODE_ENV=production
RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs
COPY --from=builder /app/public ./public
COPY --from=builder /app/prisma ./prisma
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
RUN mkdir -p /app/data && chown nextjs:nodejs /app/data
USER nextjs
EXPOSE 3000
ENV PORT=3000
ENV HOSTNAME="0.0.0.0"
CMD ["sh", "-c", "cp -n prisma/schema.prisma /app/data/ 2>/dev/null; npx prisma migrate deploy 2>/dev/null || npx prisma db push --skip-generate; node server.js"]
Vendored
+73
View File
@@ -0,0 +1,73 @@
pipeline {
agent any
environment {
APP_NAME = 'no-whatever'
DEPLOY_HOST = credentials('deploy-server-host') // 在 Jenkins 凭据中配置
DEPLOY_USER = credentials('deploy-server-user')
}
triggers {
gitlab(triggerOnPush: true, branchFilterType: 'All')
}
stages {
stage('Checkout') {
steps {
checkout scm
}
}
stage('Build Docker Image') {
steps {
script {
docker.build("${APP_NAME}:${BUILD_NUMBER}")
docker.build("${APP_NAME}:latest")
}
}
}
stage('Deploy') {
steps {
script {
// 方案 A: Jenkins 和部署机是同一台服务器
sh """
docker stop ${APP_NAME} || true
docker rm ${APP_NAME} || true
docker run -d \
--name ${APP_NAME} \
-p 3000:3000 \
-v ${APP_NAME}-data:/app/data \
--restart unless-stopped \
${APP_NAME}:latest
"""
// 方案 B: 部署到远程服务器 (取消注释并注释掉方案 A)
// sh """
// docker save ${APP_NAME}:latest | \
// ssh ${DEPLOY_USER}@${DEPLOY_HOST} 'docker load'
// ssh ${DEPLOY_USER}@${DEPLOY_HOST} << 'EOF'
// docker stop ${APP_NAME} || true
// docker rm ${APP_NAME} || true
// docker run -d \
// --name ${APP_NAME} \
// -p 3000:3000 \
// -v ${APP_NAME}-data:/app/data \
// --restart unless-stopped \
// ${APP_NAME}:latest
// EOF
// """
}
}
}
}
post {
success {
echo "Deployed ${APP_NAME} build #${BUILD_NUMBER} successfully"
}
failure {
echo "Build #${BUILD_NUMBER} failed"
}
}
}
+15
View File
@@ -0,0 +1,15 @@
services:
app:
build: .
image: no-whatever:latest
container_name: no-whatever
ports:
- "3000:3000"
volumes:
- app-data:/app/data
environment:
- DATABASE_URL=file:/app/data/dev.db
restart: unless-stopped
volumes:
app-data:
+1
View File
@@ -1,6 +1,7 @@
import type { NextConfig } from "next"; import type { NextConfig } from "next";
const nextConfig: NextConfig = { const nextConfig: NextConfig = {
output: "standalone",
images: { images: {
remotePatterns: [ remotePatterns: [
{ {