mcpField
D
deployment

deployment

github.com/mgd34msu云与 DevOps653 次安装10 周前更新
代码仓库

关于 deployment

deployment 是指导应用上线的技能,面向部署 Next.js、全栈、容器化和无服务器应用的开发者。内容涵盖平台选择、CI/CD、健康检查、监控与回滚,并附有部署检查脚本。

主要功能

  1. 01选择部署平台并配置环境
  2. 02设置 GitHub Actions CI/CD
  3. 03构建生产 Docker 镜像
  4. 04配置健康检查与监控
  5. 05规划预览部署和回滚
  6. 06运行部署前检查脚本

适用场景

  1. 01将 Next.js 应用部署到生产环境
  2. 02为容器服务设置上线流程
  3. 03为拉取请求配置预览部署
  4. 04检查上线配置并准备回滚

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/mgd34msu/goodvibes-plugin --skill deployment

安装后在 Claude Code 中输入/deployment确认已加载。

附带文件references/deployment-platforms.mdscripts/validate-deployment.sh

详细介绍

资源

scripts/
  validate-deployment.sh
references/
  deployment-platforms.md

部署

本技能将指导你使用现代平台和工具将应用部署到生产环境。在部署 Next.js、全栈应用、容器化服务或无服务器函数时,请使用此工作流。

何时使用此技能

  • 将应用部署到 Vercel、Railway、Fly.io 或 AWS
  • 使用 GitHub Actions 设置 CI/CD 流水线
  • 配置用于生产环境的 Docker 容器
  • 实施运行状况检查和监控
  • 为拉取请求创建预览部署
  • 设置回滚和金丝雀部署策略

平台选择

根据应用需求选择合适的平台:

Vercel(最适合 Next.js)

适用场景:

  • Next.js 应用(App Router 或 Pages Router)
  • 带边缘函数的静态站点
  • 为每个 PR 自动创建预览部署
  • 零配置部署

设置:

# Install Vercel CLI
npm install -g vercel

# Deploy
vercel --prod

环境变量:

# Set production secrets
vercel env add DATABASE_URL production
vercel env add NEXTAUTH_SECRET production
Railway(最适合全栈应用)

适用场景:

  • 带数据库的全栈应用
  • 单体仓库部署
  • 托管 PostgreSQL、Redis、MongoDB
  • 支持 WebSocket

设置:

# Install Railway CLI
npm install -g @railway/cli

# Login and deploy
railway login
railway up

railway.json:

{
  "$schema": "https://railway.app/railway.schema.json",
  "build": {
    "builder": "NIXPACKS",
    "buildCommand": "npm run build"
  },
  "deploy": {
    "startCommand": "npm start",
    "healthcheckPath": "/api/health",
    "healthcheckTimeout": 100,
    "restartPolicyType": "ON_FAILURE",
    "restartPolicyMaxRetries": 3
  }
}
Fly.io(最适合容器)

适用场景:

  • 自定义容器需求
  • 全球边缘部署
  • 长时间运行的进程
  • 精细控制扩缩容

设置:

# Install Fly CLI
curl -L https://fly.io/install.sh | sh

# Initialize and deploy
fly launch
fly deploy

fly.toml:

app = "my-app"
primary_region = "sjc"

[build]
  dockerfile = "Dockerfile"

[env]
  PORT = "8080"

[http_service]
  internal_port = 8080
  force_https = true
  auto_stop_machines = true
  auto_start_machines = true
  min_machines_running = 0

[[http_service.checks]]
  grace_period = "10s"
  interval = "30s"
  method = "GET"
  timeout = "5s"
  path = "/api/health"

[[vm]]
  cpu_kind = "shared"
  cpus = 1
  memory_mb = 256
Docker(最适合自托管)

适用场景:

  • 自托管基础设施
  • VPS 部署(DigitalOcean、Linode)
  • 与本地开发环境保持一致
  • 多服务编排

Dockerfile 示例请参见 references/deployment-platforms.md。

AWS(最适合企业)

适用场景:

  • 企业需求
  • 合规/监管要求
  • 复杂基础设施
  • 多区域部署

服务:

  • ECS Fargate:无服务器容器
  • Lambda:无服务器函数
  • Amplify:全栈部署
  • Elastic Beanstalk:托管平台

环境配置

.env 管理

切勿将密钥提交到 git。始终使用 .env.example 编写文档:

.env.example:

# Database
DATABASE_URL="postgresql://user:password@localhost:5432/mydb"

# Authentication
NEXTAUTH_URL="http://localhost:3000"
NEXTAUTH_SECRET="generate-with-openssl-rand-base64-32"

# External APIs
STRIPE_SECRET_KEY="sk_test_..."
STRIPE_WEBHOOK_SECRET="whsec_..."

# Feature Flags
NEXT_PUBLIC_ENABLE_ANALYTICS="false"
环境变量验证

在构建时验证环境变量,以便尽早失败:

src/env.mjs:

import { z } from 'zod';

const server = z.object({
  DATABASE_URL: z.string().url(),
  NEXTAUTH_SECRET: z.string().min(32),
  STRIPE_SECRET_KEY: z.string().startsWith('sk_'),
});

const client = z.object({
  NEXT_PUBLIC_APP_URL: z.string().url(),
});

const processEnv = {
  DATABASE_URL: process.env.DATABASE_URL,
  NEXTAUTH_SECRET: process.env.NEXTAUTH_SECRET,
  STRIPE_SECRET_KEY: process.env.STRIPE_SECRET_KEY,
  NEXT_PUBLIC_APP_URL: process.env.NEXT_PUBLIC_APP_URL,
};

const merged = server.merge(client);
const parsed = merged.safeParse(processEnv);

if (!parsed.success) {
  console.error('[FAIL] Invalid environment variables:', parsed.error.flatten().fieldErrors);
  throw new Error('Invalid environment variables');
}

export const env = parsed.data;

在应用顶部导入,以便在启动时进行验证:

import { env } from './env.mjs';

// Use typed, validated env
const db = new PrismaClient({
  datasources: { db: { url: env.DATABASE_URL } },
});

CI/CD 流水线

GitHub Actions 工作流

创建 .github/workflows/deploy.yml:

name: Deploy

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

env:
  NODE_VERSION: '20'

jobs:
  lint:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: ${{ env.NODE_VERSION }}
          cache: 'npm'
      
      - name: Install dependencies
        run: npm ci
      
      - name: Lint
        run: npm run lint

  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: ${{ env.NODE_VERSION }}
          cache: 'npm'
      
      - name: Install dependencies
        run: npm ci
      
      - name: Run tests
        run: npm test

  build:
    runs-on: ubuntu-latest
    needs: [lint, test]
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: ${{ env.NODE_VERSION }}
          cache: 'npm'
      
      - name: Install dependencies
        run: npm ci
      
      - name: Build
        run: npm run build
        env:
          SKIP_ENV_VALIDATION: true

  deploy:
    runs-on: ubuntu-latest
    needs: [build]
    if: github.ref == 'refs/heads/main'
    steps:
      - uses: actions/checkout@v4
      
      - name: Deploy to Vercel
        uses: amondnet/vercel-action@v25
        with:
          vercel-token: ${{ secrets.VERCEL_TOKEN }}
          vercel-org-id: ${{ secrets.VERCEL_ORG_ID }}
          vercel-project-id: ${{ secrets.VERCEL_PROJECT_ID }}
          vercel-args: '--prod'
缓存策略

使用适当的缓存加快 CI/CD:

- name: Cache dependencies
  uses: actions/cache@v4
  with:
    path: |
      ~/.npm
      .next/cache
    key: ${{ runner.os }}-nextjs-${{ hashFiles('**/package-lock.json') }}-${{ hashFiles('**/*.js', '**/*.jsx', '**/*.ts', '**/*.tsx') }}
    restore-keys: |
      ${{ runner.os }}-nextjs-${{ hashFiles('**/package-lock.json') }}-
      ${{ runner.os }}-nextjs-

Docker 生产环境设置

多阶段 Dockerfile

创建经过优化的生产环境 Dockerfile:

Dockerfile(Next.js):

FROM node:20-alpine AS base

# Install dependencies only when needed
FROM base AS deps
RUN apk add --no-cache libc6-compat
WORKDIR /app

COPY package.json package-lock.json ./
RUN npm ci

# Rebuild the source code only when needed
FROM base AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .

ENV NEXT_TELEMETRY_DISABLED=1

RUN npm run build

# Production image, copy all the files and run next
FROM base 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

# Set the correct permission for prerender cache
RUN mkdir .next
RUN chown nextjs:nodejs .next

# Automatically leverage output traces to reduce image size
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static

USER nextjs

EXPOSE 3000

ENV PORT=3000
ENV HOSTNAME="0.0.0.0"

CMD ["node", "server.js"]

next.config.js:

module.exports = {
  output: 'standalone', // Required for Docker
};
.dockerignore

从 Docker 构建中排除不必要的文件:

Dockerfile
.dockerignore
node_modules
npm-debug.log
README.md
.next
.git
.gitignore
.env*.local
.vscode
.idea
dist
build
coverage
*.md
!README.md
构建和运行
# Build the image
docker build -t my-app .

# Run with environment variables
docker run -p 3000:3000 \
  -e DATABASE_URL="postgresql://..." \
  -e NEXTAUTH_SECRET="..." \
  my-app

运行状况检查

运行状况检查端点

创建运行状况检查端点以进行监控:

app/api/health/route.ts:

import { NextResponse } from 'next/server';
import { prisma } from '@/lib/prisma';

export async function GET() {
  try {
    // Check database connection
    await prisma.$queryRaw`SELECT 1`;
    
    return NextResponse.json({
      status: 'healthy',
      timestamp: new Date().toISOString(),
      uptime: process.uptime(),
      database: 'connected',
    });
  } catch (error) {
    return NextResponse.json(
      {
        status: 'unhealthy',
        timestamp: new Date().toISOString(),
        database: 'disconnected',
        error: error instanceof Error ? error.message : 'Unknown error',
      },
      { status: 503 }
    );
  }
}
Docker 运行状况检查

在 Dockerfile 中添加运行状况检查:

HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
  CMD node -e "require('http').get('http://localhost:3000/api/health', (r) => process.exit(r.statusCode === 200 ? 0 : 1))"
Kubernetes 存活/就绪探针
livenessProbe:
  httpGet:
    path: /api/health
    port: 3000
  initialDelaySeconds: 15
  periodSeconds: 20

readinessProbe:
  httpGet:
    path: /api/health
    port: 3000
  initialDelaySeconds: 5
  periodSeconds: 10

预览部署

自动 PR 预览

Vercel 和 Railway 会自动为拉取请求创建预览部署。

用于 Railway 的 GitHub Actions:

on:
  pull_request:
    branches: [main]

jobs:
  preview:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      
      - name: Deploy to Railway (PR)
        uses: bervProject/railway-deploy@main
        with:
          railway_token: ${{ secrets.RAILWAY_TOKEN }}
          service: ${{ secrets.RAILWAY_SERVICE }}
在 PR 评论中添加预览 URL
- name: Comment PR
  uses: actions/github-script@v7
  with:
    script: |
      github.rest.issues.createComment({
        issue_number: context.issue.number,
        owner: context.repo.owner,
        repo: context.repo.repo,
        body: '[DEPLOY] Preview deployed to: https://pr-${{ github.event.number }}.myapp.com'
      })

回滚策略

即时回滚(Vercel)
# List deployments
vercel ls

# Promote a previous deployment to production
vercel promote <deployment-url>
蓝绿部署

部署新版本,同时保留旧版本,然后切换流量:

# Deploy new version (green)
fly deploy --strategy bluegreen

# Traffic switches automatically after health checks pass
# Rollback if needed:
fly releases rollback
金丝雀部署

逐步将流量切换到新版本:

Fly.io 金丝雀部署:

# Deploy canary (10% traffic)
fly deploy --strategy canary

# Promote to 100% if successful
fly releases promote

监控和错误跟踪

Sentry 集成
npm install @sentry/nextjs

sentry.client.config.ts:

import * as Sentry from '@sentry/nextjs';

Sentry.init({
  dsn: process.env.NEXT_PUBLIC_SENTRY_DSN,
  tracesSampleRate: 1.0,
  environment: process.env.NODE_ENV,
  enabled: process.env.NODE_ENV === 'production',
});

sentry.server.config.ts:

import * as Sentry from '@sentry/nextjs';

Sentry.init({
  dsn: process.env.SENTRY_DSN,
  tracesSampleRate: 1.0,
  environment: process.env.NODE_ENV,
});
正常运行时间监控

使用外部服务监控可用性:

每隔 1-5 分钟监控一次 /api/health 端点。

日志聚合

Vercel:

  • 内置日志流式传输
  • 与 Datadog、LogDNA、Axiom 集成

Railway:

  • 仪表板内置日志
  • 导出到外部服务

自托管:

# Use Docker logging driver
docker run --log-driver=json-file \
  --log-opt max-size=10m \
  --log-opt max-file=3 \
  my-app

CI/CD 中的数据库迁移

Prisma 迁移

在部署前运行迁移:

GitHub Actions:

- name: Run migrations
  run: npx prisma migrate deploy
  env:
    DATABASE_URL: ${{ secrets.DATABASE_URL }}

Railway: 添加到 railway.json:

{
  "deploy": {
    "startCommand": "npx prisma migrate deploy && npm start"
  }
}
迁移安全性

切勿自动运行有破坏性的迁移:

  1. 优先执行向后兼容的迁移

    • 将新列添加为可空
    • 部署兼容新旧架构的代码
    • 运行迁移
    • 部署依赖新架构的代码
    • 在未来的迁移中删除旧列
  2. 生产环境需要手动审批

    - name: Run migrations
      if: github.event_name == 'workflow_dispatch'
      run: npx prisma migrate deploy
    

Precision 工具集成

使用 precision_exec 验证部署

使用 precision_exec 运行部署命令并验证预期结果:

precision_exec:
  commands:
    - cmd: "npm run build"
      expect:
        exit_code: 0
    - cmd: "docker build -t my-app ."
      expect:
        exit_code: 0
    - cmd: "npm run typecheck"
      expect:
        exit_code: 0
  verbosity: minimal
使用 precision_fetch 进行健康检查

验证部署健康状况:

precision_fetch:
  requests:
    - url: "https://my-app.com/api/health"
      method: GET
      expect:
        status: 200
        body_contains: '"status":"healthy"'
发现部署缺口

部署前,检查是否缺少配置:

discover:
  queries:
    - id: env_example
      type: glob
      patterns: [".env.example"]
    - id: dockerfile
      type: glob
      patterns: ["Dockerfile", "docker-compose.yml"]
    - id: ci_config
      type: glob
      patterns: [".github/workflows/*.yml"]
    - id: health_check
      type: grep
      pattern: '/api/health|/health'
      glob: "**/*.{ts,tsx,js,jsx}"
  output_mode: count_only

部署前检查清单

运行验证脚本:

./plugins/goodvibes/skills/outcome/deployment/scripts/validate-deployment.sh /path/to/project

脚本会检查:

  1. Dockerfile 是否存在
  2. .env.example 是否存在且记录了必需变量
  3. CI/CD 配置是否存在
  4. 是否实现了健康检查端点
  5. .dockerignore 是否存在
  6. 代码中是否没有硬编码的密钥
  7. 构建命令是否成功
  8. 数据库迁移配置是否存在

常见陷阱

1. 缺少环境变量

问题: 未设置环境变量导致部署失败。

解决方案: 在 .env.example 中记录所有变量,并在构建时使用 zod 进行验证。

2. 数据库连接池

问题: 无服务器函数耗尽数据库连接。

解决方案: 使用连接池(PgBouncer、Prisma Accelerate、Supabase pooler)。

// Use connection pooler in serverless
const prisma = new PrismaClient({
  datasources: {
    db: {
      url: process.env.DATABASE_URL, // Use pooled connection string
    },
  },
});
3. 构建输出未优化

问题: Docker 镜像过大,冷启动缓慢。

解决方案: 使用多阶段构建、Next.js 的 standalone 输出以及适当的 .dockerignore。

4. 每次部署都会运行迁移

问题: Prisma 迁移在每次容器启动时都会运行。

解决方案: 在 CI/CD 中将迁移步骤与应用启动分开。

5. 没有回滚计划

问题: 有问题的部署会破坏生产环境,且没有简单的修复方法。

解决方案: 使用支持即时回滚的平台(Vercel、Railway、Fly.io),或保留之前的 Docker 镜像。

总结

核心原则:

  1. 在构建时验证环境变量 - 尽早失败,而不是在生产环境中失败
  2. 自动化所有流程 - CI/CD 应负责 lint、测试、构建和部署
  3. 健康检查是必需的 - 每项服务都需要健康检查端点
  4. 为每个 PR 创建预览部署 - 在合并前发现问题
  5. 始终制定回滚计划 - 即时回滚优于向前修复
  6. 从第一天起就开始监控 - 错误跟踪和运行时间监控不可或缺
  7. 迁移存在风险 - 谨慎执行,并确保向后兼容

后续步骤:

  1. 在项目上运行 validate-deployment.sh
  2. 使用 GitHub Actions 设置 CI/CD 流水线
  3. 在你的平台中配置环境变量
  4. 添加健康检查端点
  5. 在预发布环境中测试部署
  6. 部署到生产环境
  7. 设置监控和告警

有关详细的平台配置和模板,请参阅 references/deployment-platforms.md。

评论

支持 Markdown