CI/CD 流水线设计与 DevOps 实践
本文分享完整的 CI/CD 流水线设计方案,涵盖 GitLab CI、Jenkins、GitHub Actions 等主流工具的实践配置,帮助团队实现自动化交付。
CI/CD 基础概念
什么是 CI/CD
- CI (Continuous Integration):持续集成,代码频繁合并,自动构建测试
- CD (Continuous Delivery/Deployment):持续交付/部署,自动发布到环境
典型流水线阶段
代码提交 → 编译构建 → 单元测试 → 安全扫描 → 镜像打包 → 部署测试 → 集成测试 → 部署生产
GitLab CI 实践
基础配置
.gitlab-ci.yml:
stages:
- build
- test
- security
- package
- deploy
variables:
DOCKER_REGISTRY: "registry.example.com"
PROJECT_NAME: "myapp"
# 构建阶段
build:
stage: build
image: node:18-alpine
script:
- npm ci
- npm run build
artifacts:
paths:
- dist/
expire_in: 1 hour
# 测试阶段
test:
stage: test
image: node:18-alpine
script:
- npm ci
- npm run test:coverage
coverage: '/All files[^|]*\|[^|]*\s+([\d\.]+)/'
# 安全扫描
security:
stage: security
image: returntocorp/semgrep
script:
- semgrep --config=auto --json --output=report.json .
artifacts:
reports:
sast: report.json
# 镜像打包
docker-build:
stage: package
image: docker:24-dind
services:
- docker:24-dind
script:
- docker build -t $DOCKER_REGISTRY/$PROJECT_NAME:$CI_COMMIT_SHA .
- docker push $DOCKER_REGISTRY/$PROJECT_NAME:$CI_COMMIT_SHA
# 部署到 K8s
deploy-staging:
stage: deploy
image: bitnami/kubectl:latest
script:
- kubectl config use-context staging
- kubectl set image deployment/$PROJECT_NAME \
app=$DOCKER_REGISTRY/$PROJECT_NAME:$CI_COMMIT_SHA
environment:
name: staging
url: https://staging.example.com
only:
- develop
deploy-production:
stage: deploy
image: bitnami/kubectl:latest
script:
- kubectl config use-context production
- kubectl set image deployment/$PROJECT_NAME \
app=$DOCKER_REGISTRY/$PROJECT_NAME:$CI_COMMIT_SHA
environment:
name: production
url: https://example.com
only:
- main
when: manual # 需要手动触发
高级特性
并行执行:
test:unit:
stage: test
parallel: 4
script:
- npm run test:unit -- --shard=$CI_NODE_INDEX/$CI_NODE_TOTAL
缓存优化:
cache:
key: ${CI_COMMIT_REF_SLUG}
paths:
- node_modules/
- .npm/
动态流水线:
# 根据文件变更触发不同任务
workflow:
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
- if: $CI_COMMIT_BRANCH == "main"
GitHub Actions 实践
基础工作流
.github/workflows/ci.yml:
name: CI/CD Pipeline
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- 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 run test
- name: Build
run: npm run build
- name: Upload artifacts
uses: actions/upload-artifact@v4
with:
name: build-files
path: dist/
docker:
needs: build
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- uses: actions/checkout@v4
- name: Log in to Container 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 }}
- name: Build and push
uses: docker/build-push-action@v5
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
deploy:
needs: docker
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main'
steps:
- name: Deploy to production
run: |
echo "${{ secrets.KUBECONFIG }}" | base64 -d > kubeconfig
kubectl --kubeconfig=kubeconfig set image deployment/app \
app=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }}
Jenkins 实践
Pipeline 脚本
Jenkinsfile:
pipeline {
agent any
environment {
DOCKER_REGISTRY = 'registry.example.com'
PROJECT_NAME = 'myapp'
}
stages {
stage('Checkout') {
steps {
checkout scm
}
}
stage('Build') {
steps {
sh 'npm ci'
sh 'npm run build'
}
}
stage('Test') {
parallel {
stage('Unit Tests') {
steps {
sh 'npm run test:unit'
}
}
stage('Integration Tests') {
steps {
sh 'npm run test:integration'
}
}
}
}
stage('Security Scan') {
steps {
sh 'npm audit'
}
}
stage('Docker Build') {
steps {
script {
def image = docker.build("${DOCKER_REGISTRY}/${PROJECT_NAME}:${BUILD_NUMBER}")
docker.withRegistry("https://${DOCKER_REGISTRY}", 'docker-credentials') {
image.push()
image.push('latest')
}
}
}
}
stage('Deploy') {
when {
branch 'main'
}
steps {
sh """
kubectl set image deployment/${PROJECT_NAME} \
app=${DOCKER_REGISTRY}/${PROJECT_NAME}:${BUILD_NUMBER}
"""
}
}
}
post {
always {
cleanWs()
}
failure {
slackSend color: 'danger', message: "Build failed: ${env.JOB_NAME} - ${env.BUILD_NUMBER}"
}
success {
slackSend color: 'good', message: "Build succeeded: ${env.JOB_NAME} - ${env.BUILD_NUMBER}"
}
}
}
ArgoCD GitOps 实践
应用配置
application.yaml:
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: myapp
namespace: argocd
spec:
project: default
source:
repoURL: https://github.com/example/myapp.git
targetRevision: HEAD
path: k8s/overlays/production
destination:
server: https://kubernetes.default.svc
namespace: production
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=true
最佳实践
1. 镜像标签策略
# 避免使用 latest
docker build -t myapp:${GIT_COMMIT_SHA} .
docker build -t myapp:${GIT_BRANCH}-${BUILD_NUMBER} .
# 语义化版本
docker build -t myapp:v1.2.3 .
2. 多阶段构建
# 构建阶段
FROM node:18-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
# 运行阶段
FROM nginx:alpine
COPY --from=builder /app/dist /usr/share/nginx/html
3. 健康检查
# K8s deployment
livenessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
httpGet:
path: /ready
port: 8080
initialDelaySeconds: 5
periodSeconds: 5
4. 环境隔离
├── k8s/
│ ├── base/ # 基础资源
│ │ ├── deployment.yaml
│ │ ├── service.yaml
│ │ └── kustomization.yaml
│ └── overlays/
│ ├── develop/ # 开发环境
│ │ └── kustomization.yaml
│ ├── staging/ # 测试环境
│ │ └── kustomization.yaml
│ └── production/ # 生产环境
│ └── kustomization.yaml
小结
本文介绍了主流 CI/CD 工具的实践方案:
- GitLab CI:一体化 DevOps 平台
- GitHub Actions:与 GitHub 深度集成
- Jenkins:灵活的企业级方案
- ArgoCD:云原生 GitOps 工具
选择建议:
- 代码托管在 GitLab → 使用 GitLab CI
- 代码托管在 GitHub → 使用 GitHub Actions
- 需要复杂流水线编排 → 使用 Jenkins
- K8s 环境优先 → 使用 ArgoCD
参考资料: