部署与CI/CD技能 deployment-cicd

此技能专注于自动化应用程序部署,使用持续集成/持续部署(CI/CD)管道、容器化(如Docker)和基础设施即代码(IaC)技术。涵盖GitHub Actions工作流、云平台部署(如Vercel、Railway、Fly.io)、环境管理、数据库迁移、回滚策略和健康检查,旨在提高代码交付的可靠性、效率和可观察性,适用于DevOps实践和软件开发流程优化。关键词:CI/CD,自动化部署,Docker,GitHub Actions,云部署,DevOps,基础设施即代码,回滚策略,健康检查。

DevOps 0 次安装 0 次浏览 更新于 3/7/2026

名称: 部署-cicd 描述: 使用CI/CD管道、容器化和基础设施即代码,自信地部署应用程序。涵盖GitHub Actions、Docker、Vercel和云部署模式。触发于部署、CI/CD、Docker、GitHub Actions或DevOps请求。 许可证: MIT

部署与CI/CD

可靠且自动地交付代码。

部署哲学

部署管道

代码 → 构建 → 测试 → 阶段 → 生产

 │       │       │       │        │
 └───────┴───────┴───────┴────────┘
         自动化、可重复

原则

  1. 自动化一切 - 无手动步骤
  2. 快速失败 - 及早发现问题
  3. 准备回滚 - 总有逃生路径
  4. 环境一致性 - 开发 ≈ 阶段 ≈ 生产
  5. 可观察性 - 了解正在发生什么

GitHub Actions

基本工作流

# .github/workflows/ci.yml
名称: CI

触发于:
  推送:
    分支: [主]
  拉取请求:
    分支: [主]

任务:
  构建和测试:
    运行在: ubuntu最新版
    
    步骤:
      - 使用: actions/checkout@v4
      
      - 名称: 设置Node.js
        使用: actions/setup-node@v4
        带:
          节点版本: '20'
          缓存: 'npm'
      
      - 名称: 安装依赖
        运行: npm ci
      
      - 名称: 运行检查器
        运行: npm run lint
      
      - 名称: 运行测试
        运行: npm test
      
      - 名称: 构建
        运行: npm run build

矩阵构建

任务:
  测试:
    运行在: ubuntu最新版
    策略:
      矩阵:
        节点版本: [18, 20, 22]
        操作系统: [ubuntu最新版, macos最新版]
    
    步骤:
      - 使用: actions/checkout@v4
      - 使用: actions/setup-node@v4
        带:
          节点版本: ${{ matrix.node-version }}
      - 运行: npm ci
      - 运行: npm test

推送到主分支时部署

名称: 部署

触发于:
  推送:
    分支: [主]

任务:
  部署:
    运行在: ubuntu最新版
    
    步骤:
      - 使用: actions/checkout@v4
      
      - 名称: 设置Node.js
        使用: actions/setup-node@v4
        带:
          节点版本: '20'
          缓存: 'npm'
      
      - 运行: npm ci
      - 运行: npm run build
      
      - 名称: 部署到Vercel
        使用: amondnet/vercel-action@v25
        带:
          vercel令牌: ${{ secrets.VERCEL_TOKEN }}
          vercel组织ID: ${{ secrets.VERCEL_ORG_ID }}
          vercel项目ID: ${{ secrets.VERCEL_PROJECT_ID }}
          vercel参数: '--prod'

秘密管理

环境:
  数据库URL: ${{ secrets.DATABASE_URL }}
  API密钥: ${{ secrets.API_KEY }}

# 在GitHub中: 设置 → 秘密和变量 → Actions

缓存

- 名称: 缓存节点模块
  使用: actions/cache@v3
  带:
    路径: ~/.npm
    密钥: ${{ runner.os }}-节点-${{ hashFiles('**/package-lock.json') }}
    恢复密钥: |
      ${{ runner.os }}-节点-

Docker

Node.js Dockerfile

# 构建阶段
FROM node:20-alpine AS 构建器
工作目录 /app
复制 package*.json ./
运行 npm ci
复制 . .
运行 npm run build

# 生产阶段
FROM node:20-alpine AS 运行器
工作目录 /app
环境变量 NODE_ENV=production

复制 --from=构建器 /app/package*.json ./
复制 --from=构建器 /app/node_modules ./node_modules
复制 --from=构建器 /app/.next ./.next
复制 --from=构建器 /app/public ./public

暴露 3000
命令 ["npm", "start"]

Docker Compose

# docker-compose.yml
版本: '3.8'

服务:
  应用:
    构建: .
    端口:
      - "3000:3000"
    环境:
      - 数据库URL=postgresql://postgres:password@db:5432/mydb
    依赖于:
      - 数据库
  
  数据库:
    镜像: postgres:15-alpine
    环境:
      后置用户: postgres
      后置密码: password
      后置数据库: mydb
    卷:
      - 后置数据:/var/lib/postgresql/data
    端口:
      - "5432:5432"

卷:
  后置数据:

.dockerignore

node_modules
.git
.gitignore
README.md
.env
.env.*
Dockerfile
docker-compose.yml
.next
coverage

平台部署

Vercel (Next.js)

// vercel.json
{
  "框架": "nextjs",
  "构建命令": "npm run build",
  "输出目录": ".next",
  "环境": {
    "数据库URL": "@database-url"
  },
  "头信息": [
    {
      "源": "/api/(.*)",
      "头信息": [
        { "键": "缓存控制", "值": "不存储" }
      ]
    }
  ]
}

Railway

# railway.toml
[构建]
构建器 = "nixpacks"
构建命令 = "npm run build"

[部署]
启动命令 = "npm start"
健康检查路径 = "/api/health"
健康检查超时 = 100
重启策略类型 = "失败时"
重启策略最大重试 = 3

Fly.io

# fly.toml
应用 = "我的应用"
主要区域 = "ord"

[构建]
  dockerfile = "Dockerfile"

[http服务]
  内部端口 = 3000
  强制HTTPS = true
  自动停止机器 = true
  自动启动机器 = true
  最小运行机器 = 0

[[服务]]
  内部端口 = 3000
  协议 = "tcp"

  [[服务.端口]]
    端口 = 80
    处理器 = ["http"]

  [[服务.端口]]
    端口 = 443
    处理器 = ["tls", "http"]

环境管理

环境文件

.env                # 共享默认值(提交)
.env.local          # 本地覆盖(git忽略)
.env.development    # 开发特定
.env.production     # 生产特定
.env.test           # 测试特定

环境变量模式

// lib/env.ts
导入 { z } 从 'zod';

常量 环境模式 = z.对象({
  数据库URL: z.字符串().url(),
  API密钥: z.字符串().最小长度(1), <!-- allow-secret -->
  节点环境: z.枚举(['development', 'production', 'test']),
});

导出 常量 环境 = 环境模式.解析(process.env);

数据库迁移

Prisma迁移

# 创建迁移
npx prisma migrate dev --name init

# 应用迁移(CI/CD)
npx prisma migrate deploy

# 生成客户端
npx prisma generate

CI/CD中的迁移

- 名称: 运行数据库迁移
  运行: npx prisma migrate deploy
  环境:
    数据库URL: ${{ secrets.DATABASE_URL }}

回滚策略

蓝绿部署

          流量 → │  蓝   │ ← 当前
          └─────────┘
          
          ┌─────────┐
          │  绿   │ ← 新版本(测试)
          └─────────┘

验证后: 切换流量到绿

金丝雀部署

          90% ────→ │ 当前 │
          └─────────┘
          
          ┌─────────┐
10% ────→ │   新   │ ← 监控问题
          └─────────┘

逐步增加新版本流量

即时回滚(Vercel)

# 回滚到之前部署
vercel rollback

# 或通过面板: 部署 → ... → 提升到生产

健康检查

健康端点

// app/api/health/route.ts
导入 { 数据库 } 从 '@/lib/db';

导出 异步 函数 GET() {
  尝试 {
    // 检查数据库
    等待 数据库.$queryRaw`SELECT 1`;
    
    返回 Response.json({
      状态: '健康',
      时间戳: 新 Date().toISOString(),
      版本: process.env.应用版本 || '未知',
    });
  } 捕获 (错误) {
    返回 Response.json(
      { 状态: '不健康', 错误: '数据库连接失败' },
      { 状态: 503 }
    );
  }
}

完整CI/CD管道

名称: CI/CD

触发于:
  推送:
    分支: [主, 开发]
  拉取请求:
    分支: [主]

环境:
  节点版本: '20'

任务:
  检查:
    运行在: ubuntu最新版
    步骤:
      - 使用: actions/checkout@v4
      - 使用: actions/setup-node@v4
        带:
          节点版本: ${{ env.NODE_VERSION }}
          缓存: 'npm'
      - 运行: npm ci
      - 运行: npm run lint

  测试:
    运行在: ubuntu最新版
    步骤:
      - 使用: actions/checkout@v4
      - 使用: actions/setup-node@v4
        带:
          节点版本: ${{ env.NODE_VERSION }}
          缓存: 'npm'
      - 运行: npm ci
      - 运行: npm test -- --coverage
      - 使用: codecov/codecov-action@v3

  构建:
    需要: [检查, 测试]
    运行在: ubuntu最新版
    步骤:
      - 使用: actions/checkout@v4
      - 使用: actions/setup-node@v4
        带:
          节点版本: ${{ env.NODE_VERSION }}
          缓存: 'npm'
      - 运行: npm ci
      - 运行: npm run build
      - 使用: actions/upload-artifact@v3
        带:
          名称: 构建
          路径: .next

  部署预览:
    如果: github.event_name == 'pull_request'
    需要: 构建
    运行在: ubuntu最新版
    步骤:
      - 使用: actions/checkout@v4
      - 使用: amondnet/vercel-action@v25
        带:
          vercel令牌: ${{ secrets.VERCEL_TOKEN }}
          vercel组织ID: ${{ secrets.VERCEL_ORG_ID }}
          vercel项目ID: ${{ secrets.VERCEL_PROJECT_ID }}

  部署生产:
    如果: github.ref == 'refs/heads/main'
    需要: 构建
    运行在: ubuntu最新版
    环境: 生产
    步骤:
      - 使用: actions/checkout@v4
      - 使用: amondnet/vercel-action@v25
        带:
          vercel令牌: ${{ secrets.VERCEL_TOKEN }}
          vercel组织ID: ${{ secrets.VERCEL_ORG_ID }}
          vercel项目ID: ${{ secrets.VERCEL_PROJECT_ID }}
          vercel参数: '--prod'

参考

  • references/github-actions-recipes.md - 常见工作流模式
  • references/docker-patterns.md - Docker最佳实践
  • references/monitoring-setup.md - 可观察性配置