本文目录导读:
我来介绍全栈项目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 }}
最佳实践
- 分阶段构建:利用Docker多阶段构建减小镜像体积
- 缓存策略:使用GitHub Actions缓存和Docker缓存层
- 安全扫描:集成Trivy或Snyk进行安全扫描
- 灰度发布:使用蓝绿部署或金丝雀发布策略
- 回滚机制:保留历史版本镜像,支持快速回滚
这套配置覆盖了全栈项目的CI/CD关键流程,可以根据具体需求调整。
标签: 全栈项目