全栈项目CI/CD如何配置?

访客 全栈框架 1

本文目录导读:

  1. 项目结构
  2. GitHub Actions配置示例
  3. Docker配置
  4. 环境变量管理
  5. 自动化部署脚本
  6. 监控和通知
  7. 最佳实践

我来介绍全栈项目CI/CD的标准配置方案,以前后端分离的React + Node.js项目为例。

项目结构

project/
├── frontend/          # React前端
├── backend/           # Node.js后端
├── .github/           # GitHub Actions配置
│   └── workflows/
│       └── deploy.yml
├── docker-compose.yml # 容器编排
├── Dockerfile.frontend
├── Dockerfile.backend
└── nginx.conf

GitHub Actions配置示例

# .github/workflows/deploy.yml
name: Full Stack CI/CD
on:
  push:
    branches: [ main, develop ]
  pull_request:
    branches: [ main ]
env:
  REGISTRY: ghcr.io
  IMAGE_NAME: ${{ github.repository }}
jobs:
  # 1. 测试阶段
  test:
    runs-on: ubuntu-latest
    services:
      postgres:
        image: postgres:14
        env:
          POSTGRES_USER: test
          POSTGRES_PASSWORD: test
          POSTGRES_DB: test_db
        ports:
          - 5432:5432
        options: >-
          --health-cmd pg_isready
          --health-interval 10s
          --health-timeout 5s
          --health-retries 5
    steps:
      - uses: actions/checkout@v3
      - name: Setup Node.js
        uses: actions/setup-node@v3
        with:
          node-version: '18'
          cache: 'npm'
      # 前端测试
      - name: Install frontend dependencies
        working-directory: ./frontend
        run: npm ci
      - name: Run frontend tests
        working-directory: ./frontend
        run: npm test -- --coverage
        env:
          CI: true
      - name: Run frontend lint
        working-directory: ./frontend
        run: npm run lint
      # 后端测试
      - name: Install backend dependencies
        working-directory: ./backend
        run: npm ci
      - name: Run backend tests
        working-directory: ./backend
        run: npm test -- --coverage
        env:
          CI: true
          DATABASE_URL: postgresql://test:test@localhost:5432/test_db
      - name: Run backend lint
        working-directory: ./backend
        run: npm run lint
  # 2. 构建和发布阶段
  build-and-deploy:
    needs: test
    runs-on: ubuntu-latest
    if: github.ref == 'refs/heads/main'
    steps:
      - uses: actions/checkout@v3
      # 登录容器仓库
      - name: Login to Container Registry
        uses: docker/login-action@v2
        with:
          registry: ${{ env.REGISTRY }}
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}
      # 构建前端镜像
      - name: Build and push frontend
        uses: docker/build-push-action@v4
        with:
          context: ./frontend
          file: ./Dockerfile.frontend
          push: true
          tags: |
            ${{ env.REGISTRY }}/${{ github.repository }}/frontend:latest
            ${{ env.REGISTRY }}/${{ github.repository }}/frontend:${{ github.sha }}
          cache-from: type=gha
          cache-to: type=gha,mode=max
      # 构建后端镜像
      - name: Build and push backend
        uses: docker/build-push-action@v4
        with:
          context: ./backend
          file: ./Dockerfile.backend
          push: true
          tags: |
            ${{ env.REGISTRY }}/${{ github.repository }}/backend:latest
            ${{ env.REGISTRY }}/${{ github.repository }}/backend:${{ github.sha }}
          cache-from: type=gha
          cache-to: type=gha,mode=max
      # 部署到服务器
      - name: Deploy to production
        uses: appleboy/ssh-action@v0.1.5
        with:
          host: ${{ secrets.SERVER_HOST }}
          username: ${{ secrets.SERVER_USER }}
          key: ${{ secrets.SSH_PRIVATE_KEY }}
          script: |
            cd /app/deployment
            # 拉取最新配置
            git pull origin main
            # 更新docker-compose配置
            sed -i "s|FRONTEND_TAG:latest|FRONTEND_TAG:${{ github.sha }}|g" docker-compose.yml
            sed -i "s|BACKEND_TAG:latest|BACKEND_TAG:${{ github.sha }}|g" docker-compose.yml
            # 重新部署
            docker-compose down
            docker-compose pull
            docker-compose up -d
            # 清理旧镜像
            docker image prune -f

Docker配置

前端Dockerfile

# Dockerfile.frontend
# 构建阶段
FROM node:18-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
RUN npm run build
# 生产阶段
FROM nginx:alpine
COPY --from=builder /app/build /usr/share/nginx/html
COPY nginx.conf /etc/nginx/conf.d/default.conf
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]

后端Dockerfile

# Dockerfile.backend
FROM node:18-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
# 生产阶段
FROM node:18-alpine
WORKDIR /app
COPY --from=builder /app .
EXPOSE 3001
CMD ["node", "src/index.js"]

Docker Compose

# docker-compose.yml
version: '3.8'
services:
  backend:
    image: ghcr.io/your-org/project/backend:latest
    container_name: backend
    restart: always
    environment:
      NODE_ENV: production
      DATABASE_URL: postgresql://user:password@db:5432/production_db
      JWT_SECRET: ${JWT_SECRET}
      PORT: 3001
    ports:
      - "3001:3001"
    depends_on:
      - db
      - redis
    networks:
      - app-network
  frontend:
    image: ghcr.io/your-org/project/frontend:latest
    container_name: frontend
    restart: always
    ports:
      - "80:80"
    depends_on:
      - backend
    networks:
      - app-network
  db:
    image: postgres:14-alpine
    container_name: database
    restart: always
    environment:
      POSTGRES_USER: user
      POSTGRES_PASSWORD: ${DB_PASSWORD}
      POSTGRES_DB: production_db
    volumes:
      - postgres_data:/var/lib/postgresql/data
    networks:
      - app-network
  redis:
    image: redis:7-alpine
    container_name: cache
    restart: always
    volumes:
      - redis_data:/data
    networks:
      - app-network
  nginx:
    image: nginx:alpine
    container_name: reverse-proxy
    restart: always
    ports:
      - "443:443"
    volumes:
      - ./nginx.conf:/etc/nginx/conf.d/default.conf
      - /etc/letsencrypt:/etc/letsencrypt
    depends_on:
      - frontend
      - backend
    networks:
      - app-network
networks:
  app-network:
    driver: bridge
volumes:
  postgres_data:
  redis_data:

Nginx配置

# nginx.conf
upstream backend {
    server backend:3001;
}
upstream frontend {
    server frontend:80;
}
server {
    listen 80;
    server_name yourdomain.com;
    return 301 https://$server_name$request_uri;
}
server {
    listen 443 ssl;
    server_name yourdomain.com;
    ssl_certificate /etc/letsencrypt/live/yourdomain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/yourdomain.com/privkey.pem;
    # 前端静态文件
    location / {
        proxy_pass http://frontend;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
    # API代理
    location /api {
        proxy_pass http://backend;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
    # WebSocket支持
    location /ws {
        proxy_pass http://backend;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
    }
}

环境变量管理

项目配置文件

# backend/.env.example
NODE_ENV=development
PORT=3001
DATABASE_URL=postgresql://user:password@localhost:5432/dev_db
JWT_SECRET=development_secret_key
REDIS_URL=redis://localhost:6379

CI/CD Secrets配置

在GitHub仓库Settings中添加:

SERVER_HOST=your-server-ip
SERVER_USER=deploy-user
SSH_PRIVATE_KEY=your-ssh-key
DB_PASSWORD=production-password
JWT_SECRET=production-secret

自动化部署脚本

# deploy.sh (服务器端)
#!/bin/bash
set -e
echo "Starting deployment..."
# 备份数据库(可选)
docker exec database pg_dump -U user production_db > backup_$(date +%Y%m%d_%H%M%S).sql
# 拉取最新代码
cd /app/deployment
git pull origin main
# 更新环境变量
source .env.production
# 构建和重启服务
docker-compose down
docker-compose pull
docker-compose up -d
# 健康检查
echo "Checking health..."
sleep 10
curl -f http://localhost:3001/health || exit 1
# 清理旧资源
docker system prune -f
echo "Deployment completed successfully!"

监控和通知

# CI/CD中添加通知
- name: Send deployment notification
  if: always()
  uses: slackapi/slack-github-action@v1.23.0
  with:
    payload: |
      {
        "text": "Deployment ${{ job.status }} - ${{ github.repository }}@${{ github.sha }}"
      }
  env:
    SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK }}

最佳实践

  1. 分阶段构建:利用Docker多阶段构建减小镜像体积
  2. 缓存策略:使用GitHub Actions缓存和Docker缓存层
  3. 安全扫描:集成Trivy或Snyk进行安全扫描
  4. 灰度发布:使用蓝绿部署或金丝雀发布策略
  5. 回滚机制:保留历史版本镜像,支持快速回滚

这套配置覆盖了全栈项目的CI/CD关键流程,可以根据具体需求调整。

标签: 全栈项目

抱歉,评论功能暂时关闭!