CI/CD 自动化部署完全指南(二):快速开始与配置详解

DevOps Automator··17 min read·评论

CI/CD 自动化部署完全指南(二):快速开始与配置详解

10 分钟完成环境搭建,让部署自动化起来!

上一篇文章中,我们介绍了 CI/CD 自动化部署的方案概述和架构设计。本篇将手把手教你如何快速搭建这套系统。


快速开始

1. 前置准备

1.1 创建镜像仓库

首先需要一个镜像仓库来存储 Docker 镜像。国内推荐使用阿里云容器镜像服务 (ACR)腾讯云容器镜像服务 (TCR)

阿里云 ACR 配置步骤

  1. 登录 阿里云容器镜像服务控制台
  2. 创建命名空间(例如:your-company
  3. 创建镜像仓库(例如:your-app
  4. 获取登录凭证(用户名和密码)

腾讯云 TCR 配置步骤

  1. 登录 腾讯云容器镜像服务控制台
  2. 创建命名空间
  3. 创建镜像仓库
  4. 获取登录凭证

创建完成后,记录以下信息:

镜像仓库地址: registry.cn-hangzhou.aliyuncs.com  # 或 tcr.tencentcloudcr.com
命名空间: your-company
仓库名: your-app
用户名: your-username
密码: your-password

1.2 配置 GitHub Secrets

镜像仓库准备好了,现在需要把凭证配置到 GitHub 仓库中。

步骤

  1. 进入你的 GitHub 仓库
  2. 点击 Settings → Secrets and variables → Actions
  3. 点击 New repository secret
  4. 添加以下 Secrets:
# 镜像仓库凭证
DOCKER_REGISTRY=registry.cn-hangzhou.aliyuncs.com
DOCKER_USERNAME=your-username
DOCKER_PASSWORD=your-password
 
# 开发服务器 SSH 凭证
DEV_SERVER_HOST=dev.example.com
DEV_SERVER_USER=ubuntu
DEV_SERVER_SSH_KEY=-----BEGIN OPENSSH PRIVATE KEY-----
.......................................................
-----END OPENSSH PRIVATE KEY-----
 
# 生产服务器 SSH 凭证
PROD_SERVER_HOST=prod.example.com
PROD_SERVER_USER=ubuntu
PROD_SERVER_SSH_KEY=-----BEGIN OPENSSH PRIVATE KEY-----
.......................................................
-----END OPENSSH PRIVATE KEY-----
 
# 通知配置(可选)
SLACK_WEBHOOK=https://hooks.slack.com/services/xxx/xxx/xxx

如何生成 SSH 密钥对?

# 在本地机器生成密钥对
ssh-keygen -t ed25519 -C "deploy@your-company.com" -f ~/.ssh/deploy_key
 
# 会生成两个文件:
# ~/.ssh/deploy_key      (私钥,添加到 GitHub Secrets 的 DEV_SERVER_SSH_KEY)
# ~/.ssh/deploy_key.pub  (公钥,添加到服务器的 ~/.ssh/authorized_keys)

将公钥添加到服务器

# 假设你的服务器 IP 是 1.2.3.4
ssh-copy-id -i ~/.ssh/deploy_key.pub ubuntu@1.2.3.4
 
# 或者手动添加:
cat ~/.ssh/deploy_key.pub | ssh ubuntu@1.2.3.4 "mkdir -p ~/.ssh && cat >> ~/.ssh/authorized_keys"

1.3 配置环境保护(生产环境)

为了安全起见,生产环境部署应该需要手动审批

配置步骤

  1. 进入 GitHub 仓库 Settings → Environments
  2. 点击 New environment
  3. 环境名称填写:production
  4. 勾选 Required reviewers,添加审批人
  5. 点击 Save

这样,每次部署到生产环境时,都需要审批人手动批准后才会执行。


2. 项目配置

2.1 修改 .github/workflows/ci-cd.yml

打开 .github/workflows/ci-cd.yml,修改以下配置:

# 在文件开头找到 env 配置
env:
  DOCKER_REGISTRY: registry.cn-hangzhou.aliyuncs.com  # 修改为你的镜像仓库
  IMAGE_NAME: your-project-name  # 修改为你的项目名

示例

如果你的镜像仓库是 registry.cn-hangzhou.aliyuncs.com/your-company/your-app,那么:

env:
  DOCKER_REGISTRY: registry.cn-hangzhou.aliyuncs.com
  IMAGE_NAME: your-company/your-app

2.2 修改 docker-compose.yml

打开 docker-compose.yml,修改镜像名称:

services:
  app-blue:
    # 修改前
    # image: ${DOCKER_REGISTRY:-registry.cn-hangzhou.aliyuncs.com}/${IMAGE_NAME:-your-app}:${BLUE_TAG:-stable}
    
    # 修改后(可以直接写死,也可以通过环境变量)
    image: registry.cn-hangzhou.aliyuncs.com/your-company/your-app:${BLUE_TAG:-stable}
    
    # ... 其他配置保持不变

2.3 确保应用有健康检查端点

蓝绿部署依赖健康检查来确定新版本是否正常运行。你需要在应用中添加一个健康检查路由。

Express.js 示例

// routes/health.js
const express = require('express');
const router = express.Router();
 
router.get('/health', async (req, res) => {
  try {
    // 检查数据库连接
    await checkDatabaseConnection();
    
    // 检查 Redis 连接(如果使用)
    await checkRedisConnection();
    
    // 检查其他关键服务...
    
    // 所有检查都通过
    res.status(200).json({
      status: 'healthy',
      timestamp: new Date().toISOString(),
      version: process.env.npm_package_version || 'unknown'
    });
  } catch (error) {
    // 健康检查失败
    res.status(503).json({
      status: 'unhealthy',
      error: error.message,
      timestamp: new Date().toISOString()
    });
  }
});
 
module.exports = router;
 
// app.js
app.use('/health', require('./routes/health'));

NestJS 示例

// health/health.controller.ts
import { Controller, Get, Res } from '@nestjs/common';
import { Response } from 'express';
 
@Controller('health')
export class HealthController {
  @Get()
  async checkHealth(@Res() res: Response) {
    try {
      // 检查数据库连接
      // ...
      
      return res.status(200).json({
        status: 'healthy',
        timestamp: new Date().toISOString(),
      });
    } catch (error) {
      return res.status(503).json({
        status: 'unhealthy',
        error: error.message,
      });
    }
  }
}

3. 服务器初始化

现在需要在服务器上安装 Docker 和 Docker Compose。

3.1 安装 Docker 和 Docker Compose

在服务器上执行

# 1. 安装 Docker
curl -fsSL https://get.docker.com -o get-docker.sh
sudo sh get-docker.sh
 
# 2. 安装 Docker Compose
sudo curl -L "https://github.com/docker/compose/releases/download/v2.20.0/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose
 
# 3. 验证安装
docker --version
docker-compose --version
 
# 4. 启动 Docker 服务
sudo systemctl enable docker
sudo systemctl start docker

国内服务器加速

# 配置 Docker 镜像加速器(阿里云)
sudo mkdir -p /etc/docker
sudo tee /etc/docker/daemon.json <<-'EOF'
{
  "registry-mirrors": ["https://mirror.aliyuncs.com"]
}
EOF
 
sudo systemctl daemon-reload
sudo systemctl restart docker

3.2 配置服务器目录结构

# 创建项目目录
sudo mkdir -p /opt/your-app/{deploy,logs,data}
cd /opt/your-app
 
# 创建部署用户(推荐,提高安全性)
sudo useradd -m -s /bin/bash deployer
sudo usermod -aG docker deployer
 
# 设置目录权限
sudo chown -R deployer:deployer /opt/your-app
sudo chmod 755 /opt/your-app
 
# 切换到 deployer 用户
su - deployer

3.3 首次部署(手动)

# 登录服务器
ssh deployer@your-server.com
 
# 克隆代码
git clone https://github.com/learnCon/blog.git /opt/your-app
cd /opt/your-app
 
# 创建 .env 文件
cat > .env <<EOF
DOCKER_REGISTRY=registry.cn-hangzhou.aliyuncs.com
IMAGE_NAME=your-company/your-app
BLUE_TAG=stable
GREEN_TAG=latest
GRAFANA_PASSWORD=your-secure-password
EOF
 
# 登录镜像仓库
docker login registry.cn-hangzhou.aliyuncs.com \
  -u $DOCKER_USERNAME -p $DOCKER_PASSWORD
 
# 拉取并启动服务
docker-compose up -d
 
# 检查状态
docker-compose ps
 
# 测试健康检查
curl http://localhost/health

如果一切正常,你应该看到:

{
  "status": "healthy",
  "timestamp": "2026-06-20T10:30:00.000Z",
  "version": "1.0.0"
}

配置详解

GitHub Actions 流水线配置

让我们深入了解 .github/workflows/ci-cd.yml 的每个部分。

触发器配置

on:
  push:
    branches: [ main, develop ]  # 推送到这些分支时触发
  pull_request:
    branches: [ main ]  # PR 到 main 时触发

说明

  • 当推送到 maindevelop 分支时,触发流水线
  • 当创建指向 main 的 Pull Request 时,触发流水线(用于代码检查)

自定义

# 只在特定路径变更时触发
on:
  push:
    branches: [ main ]
    paths:
      - 'src/**'
      - 'package*.json'
      - 'Dockerfile'
      - 'docker-compose.yml'

环境变量

env:
  DOCKER_REGISTRY: registry.cn-hangzhou.aliyuncs.com
  IMAGE_NAME: your-project-name

可用的环境变量

变量名 说明 默认值
DOCKER_REGISTRY 镜像仓库地址 registry.cn-hangzhou.aliyuncs.com
IMAGE_NAME 镜像名称 your-project-name

Jobs 说明

Job 1: security-and-quality - 代码质量检查

security-and-quality:
  runs-on: ubuntu-latest
  steps:
    - name: 检出代码
      uses: actions/checkout@v3
 
    - name: 设置 Node.js
      uses: actions/setup-node@v3
      with:
        node-version: '18'
        cache: 'npm'
 
    - name: 安装依赖
      run: npm ci
 
    - name: 代码 Lint 检查
      run: npm run lint
 
    - name: 安全漏洞扫描
      run: |
        npm audit --audit-level=high
        # 可选:使用 Snyk 进行深度安全扫描
        # npx snyk test
 
    - name: 依赖许可检查
      run: |
        npx license-checker --onlyAllow "MIT;ISC;BSD-2-Clause;BSD-3-Clause;Apache-2.0"
      continue-on-error: true

作用

  • ✅ 确保代码风格一致(Lint)
  • ✅ 发现依赖中的安全漏洞
  • ✅ 确保依赖许可合规

Job 2: test - 自动化测试

test:
  needs: security-and-quality
  runs-on: ubuntu-latest
  steps:
    - name: 运行单元测试
      run: npm test
 
    - name: 上传测试覆盖率
      uses: actions/upload-artifact@v3
      if: always()
      with:
        name: coverage-report
        path: coverage/
        retention-days: 30

说明

  • needs: security-and-quality 表示依赖前一个 Job,只有代码检查通过才会运行测试
  • 测试覆盖率报告会保存 30 天,方便查看

Job 3: build - 构建镜像

build:
  needs: test
  runs-on: ubuntu-latest
  outputs:
    image-tag: ${{ steps.meta.outputs.tags }}
    image-digest: ${{ steps.build.outputs.digest }}
  steps:
    - name: 设置 Docker Buildx
      uses: docker/setup-buildx-action@v2
 
    - name: 登录镜像仓库
      uses: docker/login-action@v2
      with:
        registry: ${{ env.DOCKER_REGISTRY }}
        username: ${{ secrets.DOCKER_USERNAME }}
        password: ${{ secrets.DOCKER_PASSWORD }}
 
    - name: 提取元数据
      id: meta
      uses: docker/metadata-action@v4
      with:
        images: ${{ env.DOCKER_REGISTRY }}/${{ env.IMAGE_NAME }}
        tags: |
          type=ref,event=branch
          type=ref,event=pr
          type=semver,pattern={{version}}
          type=sha
 
    - name: 构建并推送镜像
      id: build
      uses: docker/build-push-action@v4
      with:
        context: .
        push: true
        tags: ${{ steps.meta.outputs.tags }}
        labels: ${{ steps.meta.outputs.labels }}
        cache-from: type=gha
        cache-to: type=gha,mode=max
 
    - name: 镜像安全扫描
      uses: aquasecurity/trivy-action@master
      with:
        image-ref: ${{ steps.meta.outputs.tags }}
        format: 'sarif'
        output: 'trivy-results.sarif'
        severity: 'CRITICAL,HIGH'
 
    - name: 上传扫描结果
      uses: github/codeql-action/upload-sarif@v2
      if: always()
      with:
        sarif_file: 'trivy-results.sarif'

亮点

  • ✅ 多阶段构建缓存,加速构建
  • ✅ 自动生成镜像标签(分支名、PR 号、commit SHA)
  • ✅ 镜像安全扫描(Trivy),发现基础镜像中的漏洞
  • ✅ 扫描结果上传到 GitHub Security 面板

Job 4: deploy-dev - 部署到开发环境

deploy-dev:
  runs-on: ubuntu-latest
  needs: build
  if: github.ref == 'refs/heads/develop'
  environment:
    name: development
    url: https://dev.your-domain.com
  steps:
    - name: 部署到开发环境
      uses: appleboy/ssh-action@v0.1.5
      with:
        host: ${{ secrets.DEV_SERVER_HOST }}
        username: ${{ secrets.DEV_SERVER_USER }}
        key: ${{ secrets.DEV_SERVER_SSH_KEY }}
        script: |
          cd /opt/your-app
          docker-compose pull
          docker-compose up -d
          docker-compose ps
          # 健康检查
          sleep 10
          curl -f http://localhost:3000/health || exit 1

说明

  • if: github.ref == 'refs/heads/develop' 表示只在 develop 分支触发
  • 自动部署到开发环境,无需人工干预

Job 5: deploy-prod - 部署到生产环境

deploy-prod:
  runs-on: ubuntu-latest
  needs: build
  if: github.ref == 'refs/heads/main'
  environment:
    name: production
    url: https://your-domain.com
  steps:
    # ... 蓝绿部署步骤(详见上一篇)
    
    - name: 失败时自动回滚
      if: failure()
      uses: appleboy/ssh-action@v0.1.5
      with:
        script: |
          echo "检测到部署失败,开始回滚..."
          sudo cp /etc/nginx/conf.d/blue.conf /etc/nginx/conf.d/active.conf
          sudo nginx -s reload
          docker stop your-app-green || true
          echo "回滚完成"

说明

  • 需要手动审批(因为配置了 environment: production
  • 蓝绿部署,零停机
  • 失败时自动回滚

Docker 配置详解

Dockerfile 多阶段构建

# 阶段1: 构建阶段
FROM node:18-alpine AS builder
 
# 设置工作目录
WORKDIR /app
 
# 复制依赖文件并安装(利用 Docker 缓存)
COPY package*.json ./
RUN npm ci --only=production && npm cache clean --force
 
# 复制源代码
COPY . .
 
# 运行构建(如果有构建步骤)
RUN npm run build --if-present
 
# 阶段2: 生产镜像
FROM node:18-alpine
 
# 添加非 root 用户(提高安全性)
RUN addgroup -g 1001 -S nodejs && \
    adduser -S nodejs -u 1001
 
# 设置工作目录
WORKDIR /app
 
# 从构建阶段复制产物
COPY --from=builder /app/package*.json ./
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/public ./public
 
# 切换到非 root 用户
USER nodejs
 
# 暴露端口
EXPOSE 3000
 
# 健康检查
HEALTHCHECK --interval=30s --timeout=10s --start-period=40s --retries=3 \
  CMD node -e "require('http').get('http://localhost:3000/health', (r) => {process.exit(r.statusCode === 200 ? 0 : 1)})" || exit 1
 
# 启动应用
CMD ["node", "dist/server.js"]

优化效果

对比项 单层构建 多阶段构建
镜像体积 1.2 GB 150 MB
构建时间(首次) 5 分钟 6 分钟
构建时间(增量) 5 分钟 2 分钟
安全性 低(root 用户) 高(非 root 用户)

docker-compose.yml 配置

version: '3.8'
 
services:
  # 蓝环境(当前生产版本)
  app-blue:
    image: ${DOCKER_REGISTRY:-registry.cn-hangzhou.aliyuncs.com}/${IMAGE_NAME:-your-app}:${BLUE_TAG:-stable}
    container_name: your-app-blue
    restart: unless-stopped
    ports:
      - "3000:3000"
    environment:
      - NODE_ENV=production
      - PORT=3000
    networks:
      - app-network
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 40s
    deploy:
      resources:
        limits:
          cpus: '1'
          memory: 512M
        reservations:
          cpus: '0.5'
          memory: 256M
 
  # 绿环境(新版本)
  app-green:
    image: ${DOCKER_REGISTRY:-registry.cn-hangzhou.aliyuncs.com}/${IMAGE_NAME:-your-app}:${GREEN_TAG:-latest}
    container_name: your-app-green
    restart: unless-stopped
    ports:
      - "3001:3000"
    environment:
      - NODE_ENV=production
      - PORT=3000
    networks:
      - app-network
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 40s
    deploy:
      resources:
        limits:
          cpus: '1'
          memory: 512M
        reservations:
          cpus: '0.5'
          memory: 256M
    profiles:
      - green  # 默认不启动,需要时才启动
 
  # Nginx 反向代理(蓝绿切换)
  nginx:
    image: nginx:alpine
    container_name: your-app-nginx
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./docker/nginx/nginx.conf:/etc/nginx/nginx.conf:ro
      - ./docker/nginx/conf.d:/etc/nginx/conf.d:ro
      - ./logs/nginx:/var/log/nginx
    depends_on:
      - app-blue
    networks:
      - app-network
    healthcheck:
      test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://localhost/health"]
      interval: 30s
      timeout: 10s
      retries: 3

关键点

  1. 资源限制:防止单个容器占用过多资源
  2. 健康检查:Docker 会自动重启不健康的容器
  3. profiles:绿环境默认不启动,部署时才启动
  4. 数据卷:Nginx 配置和日志持久化

日常开发流程

功能开发流程

# 1. 从 develop 分支创建功能分支
git checkout develop
git pull origin develop
git checkout -b feature/new-feature
 
# 2. 开发功能
# ... 编写代码 ...
npm run lint  # 本地先检查代码规范
npm test       # 本地先运行测试
 
# 3. 提交代码
git add .
git commit -m "feat: 添加新功能"
git push origin feature/new-feature
 
# 4. 创建 Pull Request
# 在 GitHub 上创建 PR:feature/new-feature → develop
# GitHub Actions 自动运行:代码检查 → 测试 → 构建镜像
 
# 5. Code Review
# 团队成员进行代码审查
 
# 6. 合并到 develop
# PR 通过后,合并到 develop 分支
# GitHub Actions 自动部署到开发环境
 
# 7. 验证开发环境
curl https://dev.your-domain.com/health

发布到生产环境

# 1. 确保 develop 分支稳定
git checkout develop
git pull origin develop
 
# 2. 合并到 main 分支
git checkout main
git pull origin main
git merge develop
git push origin main
 
# 3. GitHub Actions 自动运行
# - 代码检查 + 测试 + 构建镜像
# - 等待审批(如果配置了环境保护)
# - 审批通过后,自动部署到生产环境(蓝绿部署)
# - 健康检查
# - 切换流量
 
# 4. 验证生产环境
curl https://your-domain.com/health

紧急回滚

如果生产环境出现问题,需要立即回滚:

方法 1: 通过 GitHub Actions 回滚

# 1. 在 GitHub 仓库的 Actions 标签页
# 2. 找到最近的部署 workflow run
# 3. 点击 "Re-run all jobs" 或手动触发回滚

方法 2: 手动回滚

# 登录生产服务器
ssh deployer@prod-server.com
 
# 方法 A: 使用部署脚本
cd /opt/your-app
./deploy/deploy.sh rollback
 
# 方法 B: 手动操作
# 切换 Nginx 配置回蓝环境
sudo cp docker/nginx/conf.d/blue.conf docker/nginx/conf.d/active.conf
docker-compose exec nginx nginx -s reload
 
# 停止绿环境
docker-compose --profile green stop
 
# 验证
curl http://localhost/health

下一步

在系列的下一篇文章中,我们将详细介绍:

  • 📊 监控告警:Prometheus + Grafana 配置详解
  • 🔧 故障排查:常见问题及解决方案
  • 💡 最佳实践:生产环境经验总结

总结

本文详细介绍了:

  1. 快速开始 - 10 分钟完成环境搭建
  2. 配置详解 - GitHub Actions 和 Docker 配置说明
  3. 日常开发流程 - 从功能开发到生产发布的完整流程
  4. 紧急回滚 - 出现问题时的应对措施

通过本文的介绍,你应该已经能够:

✅ 配置镜像仓库和 GitHub Secrets
✅ 初始化服务器环境
✅ 理解 CI/CD 流水线的每个步骤
✅ 进行日常的开发和部署

下一篇预告:《CI/CD 自动化部署完全指南(三):监控告警与故障排查》


如果觉得这篇文章对你有帮助,欢迎点赞、收藏、转发! 🙏

有任何问题或建议,欢迎在评论区留言讨论!

评论

评论需要填写昵称和邮箱。评论内容将公开显示。

评论区加载中...