GitHub Actions CI/CD 自动化部署完全指南
文章目录
- [GitHub Actions CI/CD 自动化部署完全指南](#GitHub Actions CI/CD 自动化部署完全指南)
-
- [什么是 CI/CD 和 GitHub Actions](#什么是 CI/CD 和 GitHub Actions)
-
- [CI/CD 概念](#CI/CD 概念)
- [GitHub Actions 简介](#GitHub Actions 简介)
-
- [🚀 主要优势](#🚀 主要优势)
- [📊 使用统计](#📊 使用统计)
- [GitHub Actions 核心概念](#GitHub Actions 核心概念)
- 基础工作流配置
-
- 创建第一个工作流
-
- [1. 创建工作流文件](#1. 创建工作流文件)
- [2. 常用触发事件](#2. 常用触发事件)
- [3. 环境变量配置](#3. 环境变量配置)
- [前端项目 CI/CD 实践](#前端项目 CI/CD 实践)
-
- [React 项目完整工作流](#React 项目完整工作流)
- [Vue.js 项目工作流](#Vue.js 项目工作流)
- [后端项目 CI/CD 实践](#后端项目 CI/CD 实践)
-
- [Node.js Express 应用](#Node.js Express 应用)
- [Python Django 应用](#Python Django 应用)
- [全栈项目 CI/CD 实践](#全栈项目 CI/CD 实践)
-
- [Monorepo 项目结构](#Monorepo 项目结构)
- 安全性和性能优化
- 常见问题和解决方案
- 总结
- 部署
-
- 总结
-
- [🎯 核心收获](#🎯 核心收获)
- [📈 进阶建议](#📈 进阶建议)
- [🔗 有用资源](#🔗 有用资源)
- [🚀 下一步行动](#🚀 下一步行动)
- 部署
-
- 安全性和性能优化
- 常见问题和解决方案
-
- [问题 1:工作流执行时间过长](#问题 1:工作流执行时间过长)
- [问题 2:Secrets 管理混乱](#问题 2:Secrets 管理混乱)
- [问题 3:依赖冲突和版本问题](#问题 3:依赖冲突和版本问题)
- [问题 4:环境变量和配置管理](#问题 4:环境变量和配置管理)
- [问题 5:Docker 构建优化](#问题 5:Docker 构建优化)
- 实用配置模板
-
- [模板 1:React + TypeScript 项目](#模板 1:React + TypeScript 项目)
- [模板 2:Node.js API 项目](#模板 2:Node.js API 项目)
什么是 CI/CD 和 GitHub Actions
CI/CD 概念
持续集成(Continuous Integration, CI) 是一种开发实践,开发者频繁地将代码集成到主分支中,每次集成都通过自动化构建来验证,从而尽早发现集成错误。
持续部署(Continuous Deployment, CD) 是在持续集成的基础上,将通过测试的代码自动部署到生产环境的实践。
GitHub Actions 简介
GitHub Actions 是 GitHub 提供的 CI/CD 平台,允许您直接在 GitHub 仓库中自动化软件开发工作流程。
🚀 主要优势
- 原生集成:与 GitHub 仓库无缝集成
- 免费额度:公共仓库免费,私有仓库每月 2000 分钟免费
- 丰富的 Actions 市场:数千个预构建的 Actions 可供使用
- 多平台支持:支持 Linux、Windows、macOS
- 灵活配置:支持复杂的工作流程和条件执行
📊 使用统计
GitHub Actions 使用数据:
- 超过 300 万个组织在使用
- 每月执行超过 10 亿次工作流
- 市场中有超过 20,000 个 Actions
GitHub Actions 核心概念
基本组件
1. Workflow(工作流)
- 定义在
.github/workflows/目录下的 YAML 文件 - 包含一个或多个 Jobs
- 由特定事件触发
2. Event(事件)
- 触发工作流的特定活动
- 常见事件:
push、pull_request、schedule、workflow_dispatch
3. Job(作业)
- 工作流中的一组步骤
- 在同一个运行器上执行
- 可以并行或串行执行
4. Step(步骤)
- Job 中的单个任务
- 可以运行命令或使用 Action
5. Action(动作)
- 可重用的代码单元
- 可以是自定义的或来自市场
6. Runner(运行器)
- 执行工作流的服务器
- GitHub 提供托管运行器,也可以自托管
工作流语法结构
yaml
name: 工作流名称
# 触发条件
on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main ]
# 环境变量
env:
NODE_VERSION: '18'
# 作业定义
jobs:
job-name:
runs-on: ubuntu-latest
steps:
- name: 步骤名称
uses: actions/checkout@v4
- name: 另一个步骤
run: echo "Hello World"
基础工作流配置
创建第一个工作流
1. 创建工作流文件
在项目根目录创建 .github/workflows/ci.yml:
yaml
name: Basic CI
# 触发条件:推送到 main 分支或创建 PR
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
jobs:
test:
name: 运行测试
runs-on: ubuntu-latest
steps:
# 检出代码
- name: Checkout code
uses: actions/checkout@v4
# 设置 Node.js 环境
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '18'
cache: 'npm'
# 安装依赖
- name: Install dependencies
run: npm ci
# 运行测试
- name: Run tests
run: npm test
# 运行代码检查
- name: Run linting
run: npm run lint
2. 常用触发事件
yaml
on:
# 推送到指定分支
push:
branches: [ main, develop ]
paths:
- 'src/**'
- 'package.json'
# Pull Request
pull_request:
branches: [ main ]
types: [opened, synchronize, reopened]
# 定时执行
schedule:
- cron: '0 2 * * *' # 每天凌晨2点
# 手动触发
workflow_dispatch:
inputs:
environment:
description: '部署环境'
required: true
default: 'staging'
type: choice
options:
- staging
- production
# 发布 Release
release:
types: [published]
3. 环境变量配置
yaml
env:
# 全局环境变量
NODE_VERSION: '18'
REGISTRY_URL: 'https://registry.npmjs.org'
jobs:
build:
runs-on: ubuntu-latest
env:
# Job 级别环境变量
BUILD_ENV: production
steps:
- name: Build application
env:
# Step 级别环境变量
API_URL: ${{ secrets.API_URL }}
run: |
echo "Node version: $NODE_VERSION"
echo "Build environment: $BUILD_ENV"
echo "API URL: $API_URL"
前端项目 CI/CD 实践
React 项目完整工作流
yaml
name: React App CI/CD
on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main ]
env:
NODE_VERSION: '18'
REGISTRY_URL: 'https://registry.npmjs.org'
jobs:
# 代码质量检查
quality-check:
name: 代码质量检查
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: 'npm'
registry-url: ${{ env.REGISTRY_URL }}
- name: Install dependencies
run: npm ci
- name: Run ESLint
run: npm run lint
- name: Run Prettier check
run: npm run format:check
- name: Run type check
run: npm run type-check
- name: Run tests with coverage
run: npm run test:coverage
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v3
with:
file: ./coverage/lcov.info
flags: unittests
name: codecov-umbrella
# 构建应用
build:
name: 构建应用
runs-on: ubuntu-latest
needs: quality-check
strategy:
matrix:
environment: [staging, production]
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Build for ${{ matrix.environment }}
env:
REACT_APP_ENV: ${{ matrix.environment }}
REACT_APP_API_URL: ${{ secrets[format('API_URL_{0}', matrix.environment)] }}
run: npm run build
- name: Upload build artifacts
uses: actions/upload-artifact@v3
with:
name: build-${{ matrix.environment }}
path: build/
retention-days: 7
# 部署到 Vercel
deploy-vercel:
name: 部署到 Vercel
runs-on: ubuntu-latest
needs: build
if: github.ref == 'refs/heads/main'
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Download build artifacts
uses: actions/download-artifact@v3
with:
name: build-production
path: build/
- 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 }}
working-directory: ./
vercel-args: '--prod'
# 部署到 Netlify
deploy-netlify:
name: 部署到 Netlify
runs-on: ubuntu-latest
needs: build
if: github.ref == 'refs/heads/develop'
steps:
- name: Download build artifacts
uses: actions/download-artifact@v3
with:
name: build-staging
path: build/
- name: Deploy to Netlify
uses: nwtgck/actions-netlify@v2.0
with:
publish-dir: './build'
production-branch: main
github-token: ${{ secrets.GITHUB_TOKEN }}
deploy-message: "Deploy from GitHub Actions"
env:
NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }}
NETLIFY_SITE_ID: ${{ secrets.NETLIFY_SITE_ID }}
Vue.js 项目工作流
yaml
name: Vue.js App CI/CD
on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main ]
jobs:
test-and-build:
name: 测试和构建
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [16, 18, 20]
steps:
- uses: actions/checkout@v4
- name: Setup Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Run unit tests
run: npm run test:unit
- name: Run e2e tests
run: npm run test:e2e:headless
- name: Build application
run: npm run build
- name: Upload build artifacts
if: matrix.node-version == 18
uses: actions/upload-artifact@v3
with:
name: vue-build
path: dist/
deploy:
name: 部署应用
runs-on: ubuntu-latest
needs: test-and-build
if: github.ref == 'refs/heads/main'
steps:
- name: Download build artifacts
uses: actions/download-artifact@v3
with:
name: vue-build
path: dist/
- name: Deploy to GitHub Pages
uses: peaceiris/actions-gh-pages@v3
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: ./dist
后端项目 CI/CD 实践
Node.js Express 应用
yaml
name: Node.js Express API CI/CD
on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main ]
env:
NODE_VERSION: '18'
DOCKER_REGISTRY: 'ghcr.io'
jobs:
# 测试阶段
test:
name: 运行测试
runs-on: ubuntu-latest
services:
# PostgreSQL 服务
postgres:
image: postgres:15
env:
POSTGRES_PASSWORD: postgres
POSTGRES_DB: test_db
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
ports:
- 5432:5432
# Redis 服务
redis:
image: redis:7
options: >-
--health-cmd "redis-cli ping"
--health-interval 10s
--health-timeout 5s
--health-retries 5
ports:
- 6379:6379
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Run database migrations
env:
DATABASE_URL: postgresql://postgres:postgres@localhost:5432/test_db
run: npm run db:migrate
- name: Run unit tests
env:
DATABASE_URL: postgresql://postgres:postgres@localhost:5432/test_db
REDIS_URL: redis://localhost:6379
run: npm run test
- name: Run integration tests
env:
DATABASE_URL: postgresql://postgres:postgres@localhost:5432/test_db
REDIS_URL: redis://localhost:6379
run: npm run test:integration
- name: Generate test coverage
run: npm run test:coverage
- name: Upload coverage reports
uses: codecov/codecov-action@v3
with:
file: ./coverage/lcov.info
# 安全扫描
security-scan:
name: 安全扫描
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Run npm audit
run: npm audit --audit-level moderate
- name: Run Snyk security scan
uses: snyk/actions/node@master
env:
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
with:
args: --severity-threshold=high
# 构建 Docker 镜像
build-docker:
name: 构建 Docker 镜像
runs-on: ubuntu-latest
needs: [test, security-scan]
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to Container Registry
uses: docker/login-action@v3
with:
registry: ${{ env.DOCKER_REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.DOCKER_REGISTRY }}/${{ github.repository }}
tags: |
type=ref,event=branch
type=ref,event=pr
type=sha,prefix={{branch}}-
type=raw,value=latest,enable={{is_default_branch}}
- name: Build and push Docker image
uses: docker/build-push-action@v5
with:
context: .
platforms: linux/amd64,linux/arm64
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
# 部署到 staging
deploy-staging:
name: 部署到 Staging
runs-on: ubuntu-latest
needs: build-docker
if: github.ref == 'refs/heads/develop'
environment: staging
steps:
- name: Deploy to staging server
uses: appleboy/ssh-action@v1.0.0
with:
host: ${{ secrets.STAGING_HOST }}
username: ${{ secrets.STAGING_USER }}
key: ${{ secrets.STAGING_SSH_KEY }}
script: |
docker pull ${{ env.DOCKER_REGISTRY }}/${{ github.repository }}:develop
docker stop api-staging || true
docker rm api-staging || true
docker run -d \
--name api-staging \
--restart unless-stopped \
-p 3000:3000 \
-e NODE_ENV=staging \
-e DATABASE_URL=${{ secrets.STAGING_DATABASE_URL }} \
${{ env.DOCKER_REGISTRY }}/${{ github.repository }}:develop
# 部署到 production
deploy-production:
name: 部署到 Production
runs-on: ubuntu-latest
needs: build-docker
if: github.ref == 'refs/heads/main'
environment: production
steps:
- name: Deploy to Kubernetes
uses: azure/k8s-deploy@v1
with:
manifests: |
k8s/deployment.yaml
k8s/service.yaml
images: |
${{ env.DOCKER_REGISTRY }}/${{ github.repository }}:latest
kubeconfig: ${{ secrets.KUBE_CONFIG }}
Python Django 应用
yaml
name: Django App CI/CD
on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main ]
jobs:
test:
name: 测试 Django 应用
runs-on: ubuntu-latest
strategy:
matrix:
python-version: [3.9, 3.10, 3.11]
services:
postgres:
image: postgres:15
env:
POSTGRES_PASSWORD: postgres
POSTGRES_DB: django_test
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
ports:
- 5432:5432
steps:
- uses: actions/checkout@v4
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
pip install -r requirements-dev.txt
- name: Run migrations
env:
DATABASE_URL: postgresql://postgres:postgres@localhost:5432/django_test
run: python manage.py migrate
- name: Run tests
env:
DATABASE_URL: postgresql://postgres:postgres@localhost:5432/django_test
run: |
python manage.py test
coverage run --source='.' manage.py test
coverage xml
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v3
with:
file: ./coverage.xml
deploy:
name: 部署到 Heroku
runs-on: ubuntu-latest
needs: test
if: github.ref == 'refs/heads/main'
steps:
- uses: actions/checkout@v4
- name: Deploy to Heroku
uses: akhileshns/heroku-deploy@v3.12.14
with:
heroku_api_key: ${{ secrets.HEROKU_API_KEY }}
heroku_app_name: "your-django-app"
heroku_email: "your-email@example.com"
全栈项目 CI/CD 实践
Monorepo 项目结构
yaml
name: Full Stack Monorepo CI/CD
on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main ]
env:
NODE_VERSION: '18'
PYTHON_VERSION: '3.11'
jobs:
# 检测变更
detect-changes:
name: 检测文件变更
runs-on: ubuntu-latest
outputs:
frontend: ${{ steps.changes.outputs.frontend }}
backend: ${{ steps.changes.outputs.backend }}
shared: ${{ steps.changes.outputs.shared }}
steps:
- uses: actions/checkout@v4
- uses: dorny/paths-filter@v2
id: changes
with:
filters: |
frontend:
- 'frontend/**'
- 'shared/**'
backend:
- 'backend/**'
- 'shared/**'
shared:
- 'shared/**'
# 前端测试和构建
frontend-ci:
name: 前端 CI
runs-on: ubuntu-latest
needs: detect-changes
if: needs.detect-changes.outputs.frontend == 'true'
defaults:
run:
working-directory: ./frontend
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: 'npm'
cache-dependency-path: frontend/package-lock.json
- name: Install dependencies
run: npm ci
- name: Run tests
run: npm test
- name: Build application
run: npm run build
- name: Upload build artifacts
uses: actions/upload-artifact@v3
with:
name: frontend-build
path: frontend/dist/
# 后端测试
backend-ci:
name: 后端 CI
runs-on: ubuntu-latest
needs: detect-changes
if: needs.detect-changes.outputs.backend == 'true'
defaults:
run:
working-directory: ./backend
services:
postgres:
image: postgres:15
env:
POSTGRES_PASSWORD: postgres
POSTGRES_DB: test_db
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
ports:
- 5432:5432
steps:
- uses: actions/checkout@v4
- name: Setup Python
uses: actions/setup-python@v4
with:
python-version: ${{ env.PYTHON_VERSION }}
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
- name: Run tests
env:
DATABASE_URL: postgresql://postgres:postgres@localhost:5432/test_db
run: pytest
# 集成测试
integration-test:
name: 集成测试
runs-on: ubuntu-latest
needs: [frontend-ci, backend-ci]
if: always() && (needs.frontend-ci.result == 'success' || needs.frontend-ci.result == 'skipped') && (needs.backend-ci.result == 'success' || needs.backend-ci.result == 'skipped')
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
- name: Install Playwright
run: |
npm install -g @playwright/test
npx playwright install
- name: Start services with Docker Compose
run: docker-compose -f docker-compose.test.yml up -d
- name: Wait for services
run: |
timeout 60 bash -c 'until curl -f http://localhost:3000/health; do sleep 2; done'
timeout 60 bash -c 'until curl -f http://localhost:8000/health; do sleep 2; done'
- name: Run E2E tests
run: npx playwright test
- name: Upload test results
uses: actions/upload-artifact@v3
if: always()
with:
name: playwright-report
path: playwright-report/
# 部署
deploy:
name: 部署
runs-on: ubuntu-latest
needs: [frontend, backend, e2e]
if: github.ref == 'refs/heads/main' && success()
steps:
- name: Download frontend build
uses: actions/download-artifact@v3
with:
name: frontend-build
path: frontend-dist/
- name: Deploy frontend to CDN
run: |
# 部署前端到 CDN
echo "Deploying frontend to CDN..."
- name: Deploy backend to Kubernetes
run: |
# 部署后端到 K8s
echo "Deploying backend to Kubernetes..."
安全性和性能优化
安全最佳实践
1. Secrets 管理
yaml
name: Secure CI/CD
on:
push:
branches: [ main ]
jobs:
secure-build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
# ✅ 正确的 secrets 使用
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: us-east-1
# ❌ 错误:不要在日志中暴露敏感信息
# - name: Debug secrets
# run: echo "API Key: ${{ secrets.API_KEY }}"
# ✅ 正确:使用环境变量
- name: Deploy with secrets
env:
API_KEY: ${{ secrets.API_KEY }}
DATABASE_URL: ${{ secrets.DATABASE_URL }}
run: |
# 使用环境变量而不是直接引用 secrets
./deploy.sh
2. 权限最小化
yaml
name: Minimal Permissions
on:
push:
branches: [ main ]
# 设置最小权限
permissions:
contents: read
packages: write
security-events: write
jobs:
build:
runs-on: ubuntu-latest
# Job 级别权限覆盖
permissions:
contents: read
packages: write
steps:
- uses: actions/checkout@v4
- name: Build and push Docker image
uses: docker/build-push-action@v5
with:
context: .
push: true
tags: ghcr.io/${{ github.repository }}:latest
3. 依赖安全扫描
yaml
name: Security Scanning
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
schedule:
- cron: '0 6 * * 1' # 每周一早上6点
jobs:
security-scan:
name: 安全扫描
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
# npm 审计
- name: Run npm audit
run: |
npm audit --audit-level moderate
npm audit fix --dry-run
# Snyk 安全扫描
- name: Run Snyk to check for vulnerabilities
uses: snyk/actions/node@master
env:
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
with:
args: --severity-threshold=high --fail-on=upgradable
# CodeQL 代码分析
- name: Initialize CodeQL
uses: github/codeql-action/init@v2
with:
languages: javascript, typescript
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v2
# 容器镜像扫描
- name: Run Trivy vulnerability scanner
uses: aquasecurity/trivy-action@master
with:
image-ref: 'myapp:latest'
format: 'sarif'
output: 'trivy-results.sarif'
- name: Upload Trivy scan results
uses: github/codeql-action/upload-sarif@v2
with:
sarif_file: 'trivy-results.sarif'
4. 环境隔离和保护
yaml
name: Environment Protection
on:
push:
branches: [ main, develop ]
jobs:
deploy-staging:
name: 部署到 Staging
runs-on: ubuntu-latest
environment: staging
if: github.ref == 'refs/heads/develop'
steps:
- name: Deploy to staging
run: echo "Deploying to staging environment"
deploy-production:
name: 部署到 Production
runs-on: ubuntu-latest
environment:
name: production
url: https://myapp.com
if: github.ref == 'refs/heads/main'
steps:
- name: Deploy to production
run: echo "Deploying to production environment"
性能优化策略
1. 缓存优化
yaml
name: Optimized Caching
on:
push:
branches: [ main ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
# Node.js 依赖缓存
- name: Setup Node.js with cache
uses: actions/setup-node@v4
with:
node-version: '18'
cache: 'npm'
cache-dependency-path: |
package-lock.json
frontend/package-lock.json
backend/package-lock.json
# 自定义缓存
- name: Cache node modules
uses: actions/cache@v3
with:
path: |
~/.npm
node_modules
*/node_modules
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-node-
# Docker 层缓存
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Build with cache
uses: docker/build-push-action@v5
with:
context: .
cache-from: type=gha
cache-to: type=gha,mode=max
push: true
tags: myapp:latest
2. 并行执行优化
yaml
name: Parallel Execution
on:
push:
branches: [ main ]
jobs:
# 并行测试矩阵
test:
name: 测试
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [16, 18, 20]
test-suite: [unit, integration, e2e]
fail-fast: false # 不因单个失败而停止所有任务
max-parallel: 6 # 最大并行数
steps:
- uses: actions/checkout@v4
- name: Setup Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Run ${{ matrix.test-suite }} tests
run: npm run test:${{ matrix.test-suite }}
# 并行构建不同平台
build:
name: 构建
runs-on: ubuntu-latest
needs: test
strategy:
matrix:
platform: [linux/amd64, linux/arm64]
steps:
- uses: actions/checkout@v4
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Build for ${{ matrix.platform }}
uses: docker/build-push-action@v5
with:
context: .
platforms: ${{ matrix.platform }}
push: true
tags: myapp:${{ matrix.platform }}
3. 资源优化
yaml
name: Resource Optimization
on:
push:
branches: [ main ]
jobs:
optimized-build:
name: 优化构建
runs-on: ubuntu-latest
# 设置超时时间
timeout-minutes: 30
steps:
- uses: actions/checkout@v4
with:
# 浅克隆以减少下载时间
fetch-depth: 1
# 只在需要时安装依赖
- name: Check if dependencies changed
uses: dorny/paths-filter@v2
id: changes
with:
filters: |
deps:
- 'package*.json'
- 'yarn.lock'
- name: Setup Node.js
if: steps.changes.outputs.deps == 'true'
uses: actions/setup-node@v4
with:
node-version: '18'
cache: 'npm'
- name: Install dependencies
if: steps.changes.outputs.deps == 'true'
run: npm ci --prefer-offline --no-audit
# 条件执行构建
- name: Build application
if: steps.changes.outputs.deps == 'true' || github.event_name == 'push'
run: npm run build
常见问题和解决方案
构建和部署问题
1. 依赖安装失败
问题描述 :npm ci 或 npm install 失败
常见原因和解决方案:
yaml
# 解决方案 1:清理缓存
- name: Clear npm cache
run: npm cache clean --force
- name: Install dependencies
run: npm ci
# 解决方案 2:使用不同的 registry
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '18'
registry-url: 'https://registry.npmmirror.com'
# 解决方案 3:忽略可选依赖
- name: Install dependencies
run: npm ci --no-optional
# 解决方案 4:增加超时时间
- name: Install dependencies
run: npm ci --timeout=300000
2. Docker 构建失败
问题描述:Docker 镜像构建过程中出错
yaml
# 解决方案 1:多阶段构建优化
# Dockerfile
FROM node:18-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production && npm cache clean --force
FROM node:18-alpine AS runtime
WORKDIR /app
COPY --from=builder /app/node_modules ./node_modules
COPY . .
EXPOSE 3000
CMD ["npm", "start"]
# 解决方案 2:增加构建资源
- name: Build Docker image
uses: docker/build-push-action@v5
with:
context: .
push: true
tags: myapp:latest
build-args: |
BUILDKIT_INLINE_CACHE=1
cache-from: type=gha
cache-to: type=gha,mode=max
3. 测试超时或失败
yaml
# 解决方案 1:增加测试超时时间
- name: Run tests
run: npm test
timeout-minutes: 10
# 解决方案 2:并行测试
- name: Run tests in parallel
run: npm test -- --maxWorkers=2
# 解决方案 3:重试机制
- name: Run tests with retry
uses: nick-invision/retry@v2
with:
timeout_minutes: 5
max_attempts: 3
command: npm test
权限和安全问题
1. 权限被拒绝错误
yaml
# 问题:Permission denied
# 解决方案:设置正确的权限
permissions:
contents: read
packages: write
deployments: write
# 或者在 job 级别设置
jobs:
deploy:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
# ...
2. Secrets 访问问题
yaml
# 问题:无法访问 secrets
# 解决方案 1:检查 secrets 名称
- name: Use secret
env:
API_KEY: ${{ secrets.API_KEY }} # 确保名称正确
run: echo "Using API key"
# 解决方案 2:环境级别的 secrets
jobs:
deploy:
environment: production # 使用环境级别的 secrets
steps:
- name: Deploy
env:
DEPLOY_KEY: ${{ secrets.DEPLOY_KEY }}
run: ./deploy.sh
性能问题
1. 构建时间过长
yaml
# 解决方案 1:使用缓存
- name: Cache dependencies
uses: actions/cache@v3
with:
path: ~/.npm
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
# 解决方案 2:并行构建
strategy:
matrix:
component: [frontend, backend, api]
max-parallel: 3
# 解决方案 3:条件执行
- name: Build only if changed
if: steps.changes.outputs.src == 'true'
run: npm run build
2. 资源限制问题
yaml
# 解决方案 1:使用更大的运行器
jobs:
build:
runs-on: ubuntu-latest-4-cores # 使用更多 CPU 核心
# 解决方案 2:优化内存使用
- name: Build with memory optimization
run: |
export NODE_OPTIONS="--max-old-space-size=4096"
npm run build
调试技巧
1. 启用调试日志
yaml
# 启用 Actions 调试
- name: Debug information
run: |
echo "Runner OS: ${{ runner.os }}"
echo "GitHub ref: ${{ github.ref }}"
echo "Event name: ${{ github.event_name }}"
env
# 启用 npm 调试
- name: Install with debug
run: npm ci --loglevel verbose
# 启用 Docker 调试
- name: Build Docker with debug
run: |
export DOCKER_BUILDKIT=1
export BUILDKIT_PROGRESS=plain
docker build --progress=plain .
2. 条件调试
yaml
- name: Debug on failure
if: failure()
run: |
echo "Build failed, collecting debug info..."
ls -la
cat package.json
npm ls --depth=0
总结
关键要点
- 渐进式采用:从简单的 CI 开始,逐步添加 CD 功能
- 安全第一:始终遵循安全最佳实践,保护敏感信息
- 性能优化:合理使用缓存和并行执行提高效率
- 监控和反馈:建立完善的监控和通知机制
- 文档化:维护清晰的工作流文档和最佳实践
最佳实践总结
✅ 推荐做法
- 使用语义化的工作流和作业名称
- 合理设置触发条件,避免不必要的执行
- 充分利用缓存机制提高构建速度
- 实施全面的测试策略(单元测试、集成测试、E2E 测试)
- 使用环境保护和审批流程
- 定期更新 Actions 版本
- 监控工作流执行时间和成本
❌ 避免的做法
- 在日志中暴露敏感信息
- 使用过于宽泛的权限
- 忽略安全扫描和依赖更新
- 创建过于复杂的单一工作流
- 缺乏错误处理和重试机制
- 不使用环境变量管理配置
进阶学习路径
-
基础阶段
- 掌握 YAML 语法和 GitHub Actions 基本概念
- 创建简单的 CI 工作流
- 学习常用的官方 Actions
-
中级阶段
- 实现完整的 CI/CD 流水线
- 掌握矩阵构建和并行执行
- 学习 Docker 集成和容器化部署
-
高级阶段
- 创建自定义 Actions
- 实现复杂的部署策略(蓝绿部署、金丝雀发布)
- 集成监控和可观测性工具
参考资料
官方文档
社区资源
工具和服务
博客和教程
结语
GitHub Actions 为现代软件开发提供了强大而灵活的 CI/CD 解决方案。通过本指南的学习和实践,您应该能够:
- 理解 CI/CD 的核心概念和价值
- 掌握 GitHub Actions 的基本使用方法
- 为不同类型的项目设计合适的工作流
- 实施安全和性能最佳实践
- 解决常见的构建和部署问题
记住,CI/CD 是一个持续改进的过程。随着项目的发展和团队的成长,您的工作流也应该不断优化和完善。保持学习新的工具和实践,关注社区的最新发展,这将帮助您构建更加高效和可靠的软件交付流水线。
Happy Coding! 🚀
name: playwright-report
path: playwright-report/
部署
deploy:
name: 部署
runs-on: ubuntu-latest
needs: [frontend, backend, e2e]
if: github.ref == 'refs/heads/main' && success()
steps:
- name: Download frontend build
uses: actions/download-artifact@v3
with:
name: frontend-build
path: frontend-dist/
- name: Deploy frontend to CDN
run: |
# 部署前端到 CDN
echo "Deploying frontend to CDN..."
- name: Deploy backend to Kubernetes
run: |
# 部署后端到 K8s
echo "Deploying backend to Kubernetes..."
### 模板 4:Python Django 项目
```yaml
name: Django CI/CD
on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main ]
env:
PYTHON_VERSION: '3.11'
jobs:
test:
name: 测试
runs-on: ubuntu-latest
services:
postgres:
image: postgres:15
env:
POSTGRES_PASSWORD: postgres
POSTGRES_DB: django_test
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
ports:
- 5432:5432
steps:
- uses: actions/checkout@v4
- name: Setup Python
uses: actions/setup-python@v4
with:
python-version: ${{ env.PYTHON_VERSION }}
cache: 'pip'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
pip install -r requirements-dev.txt
- name: Run migrations
env:
DATABASE_URL: postgresql://postgres:postgres@localhost:5432/django_test
run: python manage.py migrate
- name: Run tests
env:
DATABASE_URL: postgresql://postgres:postgres@localhost:5432/django_test
run: |
coverage run --source='.' manage.py test
coverage xml
- name: Upload coverage
uses: codecov/codecov-action@v3
with:
file: ./coverage.xml
deploy:
name: 部署到 Heroku
runs-on: ubuntu-latest
needs: test
if: github.ref == 'refs/heads/main'
steps:
- uses: actions/checkout@v4
- name: Deploy to Heroku
uses: akhileshns/heroku-deploy@v3.12.14
with:
heroku_api_key: ${{ secrets.HEROKU_API_KEY }}
heroku_app_name: "your-django-app"
heroku_email: "your-email@example.com"
总结
GitHub Actions 作为现代 CI/CD 的重要工具,为开发团队提供了强大而灵活的自动化能力。通过本指南,您应该能够:
🎯 核心收获
- 理解 CI/CD 概念:掌握持续集成和持续部署的基本原理
- 熟练使用 GitHub Actions:了解工作流、作业、步骤等核心概念
- 实践最佳实践:应用安全性、性能优化和错误处理策略
- 解决常见问题:快速诊断和解决工作流中的典型问题
📈 进阶建议
- 持续学习:关注 GitHub Actions 的新功能和社区最佳实践
- 监控优化:定期审查工作流性能,优化执行时间和资源使用
- 安全第一:始终遵循安全最佳实践,保护敏感信息
- 团队协作:建立团队 CI/CD 规范,确保一致性
🔗 有用资源
🚀 下一步行动
- 选择适合您项目的模板开始实践
- 根据项目需求定制工作流
- 逐步添加高级功能和优化
- 与团队分享经验和最佳实践
记住,优秀的 CI/CD 流程不是一蹴而就的,需要持续改进和优化。从简单开始,逐步完善,最终构建出适合您团队的完美自动化流程。
本指南将持续更新,以反映 GitHub Actions 的最新功能和社区最佳实践。如有问题或建议,欢迎提出反馈。
name: playwright-report
path: playwright-report/
部署
deploy:
name: 部署应用
runs-on: ubuntu-latest
needs: [frontend-ci, backend-ci, integration-test]
if: github.ref == 'refs/heads/main' && success()
steps:
- uses: actions/checkout@v4
- name: Download frontend build
uses: actions/download-artifact@v3
with:
name: frontend-build
path: frontend/dist/
- name: Deploy to AWS
env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
AWS_REGION: us-east-1
run: |
# 部署前端到 S3
aws s3 sync frontend/dist/ s3://your-frontend-bucket --delete
# 部署后端到 ECS
aws ecs update-service --cluster your-cluster --service your-service --force-new-deployment
---
## 高级功能和最佳实践
### 矩阵构建策略
```yaml
name: Matrix Build Strategy
on: [push, pull_request]
jobs:
test:
name: Test on ${{ matrix.os }} with Node ${{ matrix.node-version }}
runs-on: ${{ matrix.os }}
strategy:
# 失败时不停止其他任务
fail-fast: false
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
node-version: [16, 18, 20]
# 排除特定组合
exclude:
- os: windows-latest
node-version: 16
# 包含特定组合
include:
- os: ubuntu-latest
node-version: 21
experimental: true
# 允许实验性构建失败
continue-on-error: ${{ matrix.experimental || false }}
steps:
- uses: actions/checkout@v4
- name: Setup Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
- name: Install dependencies
run: npm ci
- name: Run tests
run: npm test
条件执行和依赖管理
yaml
name: Conditional Execution
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
jobs:
# 基础检查
lint:
name: 代码检查
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run linting
run: npm run lint
# 单元测试
unit-test:
name: 单元测试
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run unit tests
run: npm run test:unit
# 集成测试(依赖于前面的任务)
integration-test:
name: 集成测试
runs-on: ubuntu-latest
needs: [lint, unit-test]
steps:
- uses: actions/checkout@v4
- name: Run integration tests
run: npm run test:integration
# 构建(只在主分支)
build:
name: 构建应用
runs-on: ubuntu-latest
needs: [lint, unit-test]
if: github.ref == 'refs/heads/main'
steps:
- uses: actions/checkout@v4
- name: Build application
run: npm run build
# 部署(只在构建成功后)
deploy:
name: 部署应用
runs-on: ubuntu-latest
needs: [build, integration-test]
if: success() && github.ref == 'refs/heads/main'
environment: production
steps:
- name: Deploy to production
run: echo "Deploying to production..."
# 通知(总是执行)
notify:
name: 发送通知
runs-on: ubuntu-latest
needs: [deploy]
if: always()
steps:
- name: Send notification
env:
STATUS: ${{ needs.deploy.result }}
run: |
if [ "$STATUS" == "success" ]; then
echo "部署成功!"
else
echo "部署失败!"
fi
缓存策略优化
yaml
name: Advanced Caching
on: [push, pull_request]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
# Node.js 依赖缓存
- name: Setup Node.js with cache
uses: actions/setup-node@v4
with:
node-version: '18'
cache: 'npm'
# 自定义缓存
- name: Cache node modules
uses: actions/cache@v3
with:
path: |
~/.npm
node_modules
*/*/node_modules
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-node-
# 构建缓存
- name: Cache build output
uses: actions/cache@v3
with:
path: |
.next/cache
dist
build
key: ${{ runner.os }}-build-${{ github.sha }}
restore-keys: |
${{ runner.os }}-build-
# Docker 层缓存
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Build Docker image with cache
uses: docker/build-push-action@v5
with:
context: .
cache-from: type=gha
cache-to: type=gha,mode=max
tags: myapp:latest
并行任务和工作流复用
yaml
# .github/workflows/reusable-test.yml
name: Reusable Test Workflow
on:
workflow_call:
inputs:
node-version:
required: true
type: string
working-directory:
required: false
type: string
default: '.'
secrets:
NPM_TOKEN:
required: false
jobs:
test:
runs-on: ubuntu-latest
defaults:
run:
working-directory: ${{ inputs.working-directory }}
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ inputs.node-version }}
registry-url: 'https://registry.npmjs.org'
- name: Install dependencies
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
run: npm ci
- name: Run tests
run: npm test
yaml
# .github/workflows/main.yml
name: Main Workflow
on: [push, pull_request]
jobs:
# 使用可复用工作流
test-frontend:
uses: ./.github/workflows/reusable-test.yml
with:
node-version: '18'
working-directory: './frontend'
secrets:
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
test-backend:
uses: ./.github/workflows/reusable-test.yml
with:
node-version: '18'
working-directory: './backend'
# 并行执行多个任务
parallel-tasks:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run parallel commands
run: |
# 后台运行多个命令
npm run lint &
npm run type-check &
npm run test:unit &
# 等待所有后台任务完成
wait
安全性和性能优化
Secrets 管理最佳实践
1. 环境级别的 Secrets
yaml
name: Secure Deployment
on:
push:
branches: [ main ]
jobs:
deploy-staging:
runs-on: ubuntu-latest
environment: staging
steps:
- name: Deploy to staging
env:
# 环境特定的 secrets
API_KEY: ${{ secrets.STAGING_API_KEY }}
DATABASE_URL: ${{ secrets.STAGING_DATABASE_URL }}
run: |
echo "Deploying to staging with secure credentials"
deploy-production:
runs-on: ubuntu-latest
environment: production
# 需要手动批准
steps:
- name: Deploy to production
env:
API_KEY: ${{ secrets.PRODUCTION_API_KEY }}
DATABASE_URL: ${{ secrets.PRODUCTION_DATABASE_URL }}
run: |
echo "Deploying to production with secure credentials"
2. 动态 Secrets 引用
yaml
jobs:
deploy:
runs-on: ubuntu-latest
strategy:
matrix:
environment: [staging, production]
steps:
- name: Deploy to ${{ matrix.environment }}
env:
# 动态引用不同环境的 secrets
API_KEY: ${{ secrets[format('{0}_API_KEY', matrix.environment)] }}
DB_URL: ${{ secrets[format('{0}_DATABASE_URL', matrix.environment)] }}
run: |
echo "Deploying to ${{ matrix.environment }}"
权限最小化原则
yaml
name: Minimal Permissions
on: [push, pull_request]
# 全局权限设置
permissions:
contents: read
jobs:
test:
runs-on: ubuntu-latest
# 作业级别权限
permissions:
contents: read
checks: write
steps:
- uses: actions/checkout@v4
- name: Run tests
run: npm test
publish:
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main'
permissions:
contents: read
packages: write
steps:
- uses: actions/checkout@v4
- name: Publish package
run: npm publish
安全扫描集成
yaml
name: Security Scanning
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
schedule:
- cron: '0 2 * * 1' # 每周一凌晨2点
jobs:
# 依赖漏洞扫描
dependency-scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run npm audit
run: npm audit --audit-level moderate
- name: Snyk security scan
uses: snyk/actions/node@master
env:
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
with:
args: --severity-threshold=high
command: test
# 代码安全扫描
code-scan:
runs-on: ubuntu-latest
permissions:
security-events: write
steps:
- uses: actions/checkout@v4
- name: Initialize CodeQL
uses: github/codeql-action/init@v2
with:
languages: javascript, typescript
- name: Autobuild
uses: github/codeql-action/autobuild@v2
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v2
# Docker 镜像安全扫描
docker-scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Build Docker image
run: docker build -t myapp:latest .
- name: Scan Docker image
uses: aquasecurity/trivy-action@master
with:
image-ref: 'myapp:latest'
format: 'sarif'
output: 'trivy-results.sarif'
- name: Upload Trivy scan results
uses: github/codeql-action/upload-sarif@v2
with:
sarif_file: 'trivy-results.sarif'
性能优化策略
1. 并行化优化
yaml
name: Performance Optimized
on: [push, pull_request]
jobs:
# 快速反馈任务
quick-checks:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Quick lint check
run: npm run lint:fast
- name: Type check
run: npm run type-check
# 并行测试
test:
runs-on: ubuntu-latest
strategy:
matrix:
shard: [1, 2, 3, 4]
steps:
- uses: actions/checkout@v4
- name: Run test shard ${{ matrix.shard }}
run: npm run test -- --shard=${{ matrix.shard }}/4
# 并行构建
build:
runs-on: ubuntu-latest
strategy:
matrix:
target: [web, mobile, desktop]
steps:
- uses: actions/checkout@v4
- name: Build ${{ matrix.target }}
run: npm run build:${{ matrix.target }}
2. 智能缓存策略
yaml
name: Smart Caching
on: [push, pull_request]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
# 分层缓存策略
- name: Cache dependencies
uses: actions/cache@v3
with:
path: node_modules
key: deps-${{ runner.os }}-${{ hashFiles('package-lock.json') }}
restore-keys: deps-${{ runner.os }}-
- name: Cache build artifacts
uses: actions/cache@v3
with:
path: |
.next/cache
dist
key: build-${{ runner.os }}-${{ github.sha }}
restore-keys: |
build-${{ runner.os }}-${{ github.ref_name }}-
build-${{ runner.os }}-
# 条件性安装
- name: Install dependencies
if: steps.cache-deps.outputs.cache-hit != 'true'
run: npm ci
# 增量构建
- name: Build application
run: |
if [ -d "dist" ]; then
echo "Using incremental build"
npm run build:incremental
else
echo "Full build required"
npm run build
fi
常见问题和解决方案
问题 1:工作流执行时间过长
症状:
- 工作流执行超过 30 分钟
- 频繁超时失败
- 资源使用效率低
解决方案:
yaml
name: Optimized Workflow
on: [push, pull_request]
jobs:
# 1. 使用并行执行
test:
runs-on: ubuntu-latest
strategy:
matrix:
shard: [1, 2, 3, 4]
steps:
- uses: actions/checkout@v4
- name: Run tests in parallel
run: npm run test -- --shard=${{ matrix.shard }}/4
# 2. 优化缓存策略
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Restore cache
uses: actions/cache@v3
with:
path: |
node_modules
~/.npm
key: ${{ runner.os }}-${{ hashFiles('package-lock.json') }}
# 3. 使用更快的包管理器
- name: Setup pnpm
uses: pnpm/action-setup@v2
with:
version: 8
- name: Install dependencies
run: pnpm install --frozen-lockfile
# 4. 条件执行
deploy:
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
steps:
- name: Deploy only when necessary
run: echo "Deploying..."
问题 2:Secrets 管理混乱
症状:
- Secrets 命名不规范
- 环境间 Secrets 混用
- 安全性问题
解决方案:
yaml
name: Proper Secrets Management
on: [push]
jobs:
deploy:
runs-on: ubuntu-latest
strategy:
matrix:
environment: [staging, production]
environment: ${{ matrix.environment }}
steps:
- name: Deploy with proper secrets
env:
# 使用环境特定的 secrets
API_KEY: ${{ secrets.API_KEY }}
DATABASE_URL: ${{ secrets.DATABASE_URL }}
# 或者使用动态引用
DYNAMIC_SECRET: ${{ secrets[format('{0}_SECRET', matrix.environment)] }}
run: |
echo "Deploying to ${{ matrix.environment }}"
echo "API Key length: ${#API_KEY}"
Secrets 命名规范:
# 环境特定
STAGING_API_KEY
PRODUCTION_API_KEY
# 服务特定
GITHUB_TOKEN
DOCKER_REGISTRY_TOKEN
AWS_ACCESS_KEY_ID
# 功能特定
CODECOV_TOKEN
SNYK_TOKEN
问题 3:依赖冲突和版本问题
症状:
- 不同环境构建结果不一致
- 依赖版本冲突
- 构建失败
解决方案:
yaml
name: Dependency Management
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
# 1. 锁定 Node.js 版本
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version-file: '.nvmrc' # 使用项目指定版本
cache: 'npm'
# 2. 使用精确的依赖安装
- name: Install dependencies
run: |
# 使用 ci 而不是 install
npm ci
# 验证依赖完整性
npm audit --audit-level moderate
# 3. 版本一致性检查
- name: Check dependency versions
run: |
npm ls --depth=0
npm outdated || true
# 4. 多版本测试
multi-version-test:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [16, 18, 20]
steps:
- uses: actions/checkout@v4
- name: Setup Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
- name: Test compatibility
run: |
npm ci
npm test
问题 4:环境变量和配置管理
症状:
- 环境变量混乱
- 配置在不同环境不一致
- 敏感信息泄露
解决方案:
yaml
name: Environment Management
on: [push]
env:
# 全局环境变量
NODE_ENV: production
LOG_LEVEL: info
jobs:
deploy:
runs-on: ubuntu-latest
strategy:
matrix:
environment: [staging, production]
environment: ${{ matrix.environment }}
steps:
- uses: actions/checkout@v4
# 1. 环境特定配置
- name: Setup environment config
run: |
case "${{ matrix.environment }}" in
staging)
echo "API_URL=https://api-staging.example.com" >> $GITHUB_ENV
echo "DEBUG=true" >> $GITHUB_ENV
;;
production)
echo "API_URL=https://api.example.com" >> $GITHUB_ENV
echo "DEBUG=false" >> $GITHUB_ENV
;;
esac
# 2. 配置验证
- name: Validate configuration
env:
DATABASE_URL: ${{ secrets.DATABASE_URL }}
run: |
# 检查必需的环境变量
required_vars=("API_URL" "DATABASE_URL" "NODE_ENV")
for var in "${required_vars[@]}"; do
if [ -z "${!var}" ]; then
echo "Error: $var is not set"
exit 1
fi
done
# 3. 安全的配置文件生成
- name: Generate config file
env:
API_KEY: ${{ secrets.API_KEY }}
run: |
cat > config.json << EOF
{
"environment": "${{ matrix.environment }}",
"apiUrl": "$API_URL",
"debug": $DEBUG,
"version": "${{ github.sha }}"
}
EOF
# 不在日志中显示敏感信息
echo "Config file generated successfully"
问题 5:Docker 构建优化
症状:
- Docker 构建时间过长
- 镜像体积过大
- 缓存效率低
解决方案:
yaml
name: Optimized Docker Build
on: [push]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
# 1. 设置 Docker Buildx
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
# 2. 登录到镜像仓库
- name: Login to registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
# 3. 优化的多阶段构建
- name: Build and push
uses: docker/build-push-action@v5
with:
context: .
file: ./Dockerfile.optimized
platforms: linux/amd64,linux/arm64
push: true
tags: |
ghcr.io/${{ github.repository }}:latest
ghcr.io/${{ github.repository }}:${{ github.sha }}
# 使用 GitHub Actions 缓存
cache-from: type=gha
cache-to: type=gha,mode=max
# 构建参数
build-args: |
NODE_VERSION=18
BUILD_DATE=${{ github.event.head_commit.timestamp }}
VCS_REF=${{ github.sha }}
优化的 Dockerfile:
dockerfile
# Dockerfile.optimized
FROM node:18-alpine AS base
WORKDIR /app
COPY package*.json ./
FROM base AS deps
RUN npm ci --only=production && npm cache clean --force
FROM base AS build
RUN npm ci
COPY . .
RUN npm run build
FROM node:18-alpine AS runtime
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY --from=build /app/dist ./dist
COPY package*.json ./
EXPOSE 3000
CMD ["npm", "start"]
实用配置模板
模板 1:React + TypeScript 项目
yaml
name: React TypeScript CI/CD
on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main ]
env:
NODE_VERSION: '18'
CACHE_KEY_PREFIX: 'react-app'
jobs:
# 代码质量检查
quality:
name: 代码质量检查
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Type check
run: npm run type-check
- name: Lint
run: npm run lint
- name: Format check
run: npm run format:check
- name: Unit tests
run: npm run test:coverage
- name: Upload coverage
uses: codecov/codecov-action@v3
with:
file: ./coverage/lcov.info
# 构建
build:
name: 构建应用
runs-on: ubuntu-latest
needs: quality
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Node.js
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
- name: Upload build artifacts
uses: actions/upload-artifact@v3
with:
name: build-files
path: build/
retention-days: 7
# 部署
deploy:
name: 部署到 Vercel
runs-on: ubuntu-latest
needs: build
if: github.ref == 'refs/heads/main'
steps:
- name: Checkout
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'
模板 2:Node.js API 项目
yaml
name: Node.js API CI/CD
on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main ]
env:
NODE_VERSION: '18'
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
jobs:
# 测试
test:
name: 测试
runs-on: ubuntu-latest
services:
postgres:
image: postgres:15
env:
POSTGRES_PASSWORD: postgres
POSTGRES_DB: test_db
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
ports:
- 5432:5432
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Run migrations
env:
DATABASE_URL: postgresql://postgres:postgres@localhost:5432/test_db
run: npm run db:migrate
- name: Run tests
env:
DATABASE_URL: postgresql://postgres:postgres@localhost:5432/test_db
run: npm run test:coverage
- name: Upload coverage
uses: codecov/codecov-action@v3
# 构建 Docker 镜像
build:
name: 构建镜像
runs-on: ubuntu-latest
needs: test
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: |
type=ref,event=branch
type=ref,event=pr
type=sha
- name: Build and push
uses: docker/build-push-action@v5
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
# 部署
deploy:
name: 部署
runs-on: ubuntu-latest
needs: build
if: github.ref == 'refs/heads/main'
environment: production
steps:
- name: Deploy to server
uses: appleboy/ssh-action@v1.0.0
with:
host: ${{ secrets.HOST }}
username: ${{ secrets.USERNAME }}
key: ${{ secrets.SSH_KEY }}
script: |
docker pull ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:main
docker stop api || true
docker rm api || true
docker run -d \
--name api \
--restart unless-stopped \
-p 3000:3000 \
-e DATABASE_URL=${{ secrets.DATABASE_URL }} \
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:main
本指南涵盖了 GitHub Actions CI/CD 的核心概念、实践案例和最佳实践。通过学习和应用这些知识,您将能够构建高效、安全、可靠的自动化部署流程。