第05章 CI/CD流水线¶
📚 章节概述¶
本章将深入讲解CI/CD(持续集成/持续部署)流水线的构建,包括Jenkins、GitLab CI、GitHub Actions等主流工具的使用。通过本章学习,你将能够搭建完整的CI/CD流水线,实现自动化构建、测试和部署。
🎯 学习目标¶
完成本章后,你将能够:
- 理解CI/CD的核心概念和最佳实践
- 掌握Jenkins流水线的配置和使用
- 熟练使用GitLab CI/CD
- 掌握GitHub Actions的工作流程
- 了解蓝绿部署和金丝雀发布
- 能够搭建生产级的CI/CD流水线
5.1 CI/CD概述¶
5.1.1 什么是CI/CD¶
CI/CD是持续集成(Continuous Integration)、持续交付(Continuous Delivery)和持续部署(Continuous Deployment)的统称。
核心概念¶
- 持续集成(CI)
- 频繁地将代码集成到主干
- 自动化构建和测试
-
快速发现和修复问题
-
持续交付(CD)
- 确保代码可以随时部署
- 自动化部署到测试环境
-
人工审批后部署到生产环境
-
持续部署(CD)
- 自动化部署到生产环境
- 无需人工干预
- 快速反馈用户
5.1.2 CI/CD的价值¶
- 提高开发效率
- 自动化重复性工作
- 减少人为错误
-
加快交付速度
-
提高代码质量
- 自动化测试
- 代码审查
-
静态代码分析
-
降低风险
- 小步快跑
- 快速回滚
-
减少部署失败
-
增强团队协作
- 统一的工作流程
- 透明的构建状态
- 快速反馈
5.2 Jenkins流水线¶
5.2.1 Jenkins概述¶
Jenkins是最流行的开源CI/CD工具,支持丰富的插件生态系统。
Jenkins的核心特性¶
- 分布式构建
- 支持多Agent
- 负载均衡
-
资源隔离
-
丰富的插件
- 1000+插件
- 支持各种工具集成
-
高度可扩展
-
Pipeline as Code
- Jenkinsfile
- 版本控制
- 可重复的构建
5.2.2 安装Jenkins¶
Docker安装¶
# 拉取Jenkins镜像
docker pull jenkins/jenkins:lts
# 运行Jenkins
docker run -d \
--name jenkins \
-p 8080:8080 \
-p 50000:50000 \
-v jenkins_home:/var/jenkins_home \
jenkins/jenkins:lts
# 查看初始密码
docker exec jenkins cat /var/jenkins_home/secrets/initialAdminPassword
Kubernetes安装¶
# Jenkins Deployment 定义
apiVersion: apps/v1
kind: Deployment
metadata:
name: jenkins
namespace: devops # 部署到 devops 命名空间
spec:
replicas: 1 # 单实例部署
selector:
matchLabels:
app: jenkins
template:
metadata:
labels:
app: jenkins
spec:
containers:
- name: jenkins
image: jenkins/jenkins:lts
ports:
- containerPort: 8080 # Web UI 端口
- containerPort: 50000 # Agent 通信端口
volumeMounts:
- name: jenkins-home
mountPath: /var/jenkins_home # 挂载持久化存储目录
volumes:
- name: jenkins-home
persistentVolumeClaim:
claimName: jenkins-pvc # 引用 PVC 实现数据持久化
--- # YAML文档分隔符
# Jenkins Service 定义(对外暴露服务)
apiVersion: v1
kind: Service
metadata:
name: jenkins
namespace: devops
spec:
type: LoadBalancer # 通过负载均衡器暴露服务
ports:
- port: 8080
targetPort: 8080
name: http # Web UI 端口
- port: 50000
targetPort: 50000
name: agent # Agent 连接端口
selector:
app: jenkins
---
# 持久化存储声明(PVC)
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: jenkins-pvc
namespace: devops
spec:
accessModes:
- ReadWriteOnce # 单节点读写模式
resources:
requests:
storage: 20Gi # 申请 20GB 存储空间
5.2.3 Jenkinsfile¶
基本Pipeline¶
pipeline {
agent any // 在任意可用的 Agent 上运行
stages {
// 阶段1:代码检出
stage('Checkout') {
steps {
checkout scm
}
}
// 阶段2:编译构建
stage('Build') {
steps {
sh 'mvn clean package'
}
}
// 阶段3:运行测试
stage('Test') {
steps {
sh 'mvn test'
}
}
// 阶段4:部署到 Kubernetes
stage('Deploy') {
steps {
sh 'kubectl apply -f k8s/'
}
}
}
// 构建后处理
post {
success {
echo 'Pipeline succeeded!'
}
failure {
echo 'Pipeline failed!'
}
}
}
声明式Pipeline¶
pipeline {
agent any // 在任意可用的 Agent 上运行
// 构建参数定义,支持手动选择分支和部署环境
parameters {
string(name: 'BRANCH', defaultValue: 'main', description: 'Git branch to build')
choice(name: 'ENVIRONMENT', choices: ['dev', 'staging', 'production'], description: 'Deployment environment')
}
// 环境变量定义
environment {
DOCKER_REGISTRY = 'myregistry.com'
IMAGE_NAME = 'myapp'
IMAGE_TAG = "${BUILD_NUMBER}" // 使用构建号作为镜像标签
}
stages {
// 检出指定分支代码
stage('Checkout') {
steps {
git branch: params.BRANCH, url: 'https://github.com/myorg/myapp.git'
}
}
// 编译构建(跳过测试加速构建)
stage('Build') {
steps {
sh 'mvn clean package -DskipTests'
}
}
// 单元测试并收集报告
stage('Unit Test') {
steps {
sh 'mvn test'
junit 'target/surefire-reports/*.xml' // 发布 JUnit 测试报告
}
}
// 代码质量扫描(SonarQube)
stage('Code Quality') {
steps {
sh 'mvn sonar:sonar'
}
}
// 构建 Docker 镜像并打标签
stage('Build Docker Image') {
steps {
sh "docker build -t ${DOCKER_REGISTRY}/${IMAGE_NAME}:${IMAGE_TAG} ."
sh "docker tag ${DOCKER_REGISTRY}/${IMAGE_NAME}:${IMAGE_TAG} ${DOCKER_REGISTRY}/${IMAGE_NAME}:latest"
}
}
// 推送镜像到仓库
stage('Push Docker Image') {
steps {
// 使用凭据安全登录镜像仓库
withCredentials([usernamePassword(credentialsId: 'docker-registry', usernameVariable: 'DOCKER_USER', passwordVariable: 'DOCKER_PASS')]) {
// 使用 --password-stdin 避免密码出现在进程列表和CI日志中
sh "echo \${DOCKER_PASS} | docker login ${DOCKER_REGISTRY} -u ${DOCKER_USER} --password-stdin"
sh "docker push ${DOCKER_REGISTRY}/${IMAGE_NAME}:${IMAGE_TAG}"
sh "docker push ${DOCKER_REGISTRY}/${IMAGE_NAME}:latest"
}
}
}
// 部署到目标 Kubernetes 环境
stage('Deploy to Kubernetes') {
steps {
sh "sed -i 's|IMAGE_TAG|${IMAGE_TAG}|g' k8s/deployment.yaml" // 替换镜像标签
sh "kubectl apply -f k8s/ -n ${params.ENVIRONMENT}" // 应用 K8s 资源配置
sh "kubectl rollout status deployment/myapp -n ${params.ENVIRONMENT}" // 等待部署完成
}
}
}
// 构建后处理
post {
always {
cleanWs() // 每次构建后清理工作空间
}
// 成功时发送邮件通知
success {
echo 'Pipeline succeeded!'
emailext(
subject: "Jenkins Pipeline Success: ${JOB_NAME} - ${BUILD_NUMBER}",
body: "The pipeline ${JOB_NAME} build ${BUILD_NUMBER} completed successfully.",
to: 'team@example.com'
)
}
// 失败时发送告警邮件
failure {
echo 'Pipeline failed!'
emailext(
subject: "Jenkins Pipeline Failure: ${JOB_NAME} - ${BUILD_NUMBER}",
body: "The pipeline ${JOB_NAME} build ${BUILD_NUMBER} failed.",
to: 'team@example.com'
)
}
}
}
5.3 GitLab CI/CD¶
5.3.1 GitLab CI概述¶
GitLab CI/CD是GitLab内置的CI/CD工具,与Git仓库深度集成。
核心特性¶
- 内置集成
- 无需额外安装
- 与Git仓库无缝集成
-
可视化Pipeline
-
YAML配置
- .gitlab-ci.yml
- 简单易用
-
版本控制
-
Docker支持
- 原生Docker支持
- 自定义Runner
- 缓存和Artifacts
5.3.2 .gitlab-ci.yml¶
基本配置¶
# 定义流水线阶段顺序
stages:
- build
- test
- deploy
# 全局变量定义
variables:
DOCKER_REGISTRY: "myregistry.com"
IMAGE_NAME: "myapp"
IMAGE_TAG: "$CI_COMMIT_SHORT_SHA" # 使用 Git 短 SHA 作为镜像标签
# 构建任务:构建 Docker 镜像
build:
stage: build
image: docker:latest
services:
- docker:dind # 启用 Docker-in-Docker 服务
script:
- docker build -t $DOCKER_REGISTRY/$IMAGE_NAME:$IMAGE_TAG .
- docker tag $DOCKER_REGISTRY/$IMAGE_NAME:$IMAGE_TAG $DOCKER_REGISTRY/$IMAGE_NAME:latest
only: # 仅在指定分支上触发
- main
- develop
# 测试任务:运行测试并收集覆盖率
test:
stage: test
image: python:3.11
script:
- pip install -r requirements.txt
- pytest tests/
coverage: '/TOTAL.*\s+(\d+%)$/' # 覆盖率提取正则表达式
artifacts: # 测试产物配置
reports:
coverage_report:
coverage_format: cobertura
path: coverage.xml
# 部署到开发环境
deploy_dev:
stage: deploy
image: bitnami/kubectl:latest
script:
- kubectl config use-context dev-cluster # 切换到开发集群
- sed -i "s|IMAGE_TAG|$IMAGE_TAG|g" k8s/deployment.yaml
- kubectl apply -f k8s/ -n dev
- kubectl rollout status deployment/myapp -n dev # 等待部署完成
only:
- develop # 仅 develop 分支触发
environment:
name: dev
url: https://dev.example.com
# 部署到预发布环境
deploy_staging:
stage: deploy
image: bitnami/kubectl:latest
script:
- kubectl config use-context staging-cluster
- sed -i "s|IMAGE_TAG|$IMAGE_TAG|g" k8s/deployment.yaml
- kubectl apply -f k8s/ -n staging
- kubectl rollout status deployment/myapp -n staging
only:
- main
when: manual # 需手动触发部署
environment:
name: staging
url: https://staging.example.com
# 部署到生产环境
deploy_production:
stage: deploy
image: bitnami/kubectl:latest
script:
- kubectl config use-context prod-cluster
- sed -i "s|IMAGE_TAG|$IMAGE_TAG|g" k8s/deployment.yaml
- kubectl apply -f k8s/ -n production
- kubectl rollout status deployment/myapp -n production
only:
- tags # 仅在打 tag 时触发
when: manual # 需手动确认后部署
environment:
name: production
url: https://example.com
高级配置¶
stages:
- build
- test
- security
- deploy
variables:
DOCKER_REGISTRY: "myregistry.com"
IMAGE_NAME: "myapp"
IMAGE_TAG: "$CI_COMMIT_SHORT_SHA" # 使用 Git 短 SHA 作为镜像标签
FF_USE_FASTZIP: "true" # 启用快速压缩,提升产物传输速度
ARTIFACT_COMPRESSION_LEVEL: "fast" # 使用快速压缩级别
# 缓存配置(按分支缓存依赖目录,加速构建)
cache:
key: ${CI_COMMIT_REF_SLUG} # 按分支名称隔离缓存
paths:
- node_modules/
- .m2/
- vendor/
# 构建阶段
build:
stage: build
image: docker:latest
services:
- docker:dind
before_script:
- echo "$DOCKER_REGISTRY_PASSWORD" | docker login -u "$DOCKER_REGISTRY_USER" --password-stdin $DOCKER_REGISTRY
script:
- docker build -t $DOCKER_REGISTRY/$IMAGE_NAME:$IMAGE_TAG .
- docker tag $DOCKER_REGISTRY/$IMAGE_NAME:$IMAGE_TAG $DOCKER_REGISTRY/$IMAGE_NAME:latest
- docker push $DOCKER_REGISTRY/$IMAGE_NAME:$IMAGE_TAG
- docker push $DOCKER_REGISTRY/$IMAGE_NAME:latest
only:
- main
- develop
- merge_requests
# 单元测试
unit_test:
stage: test
image: python:3.11
services:
- postgres:13
- redis:latest
variables:
POSTGRES_DB: testdb
POSTGRES_USER: testuser
POSTGRES_PASSWORD: testpass
DATABASE_URL: "postgresql://testuser:testpass@postgres:5432/testdb"
REDIS_URL: "redis://redis:6379/0"
before_script:
- pip install -r requirements.txt
script:
- pytest tests/unit/ -v --cov=app --cov-report=xml --cov-report=html
coverage: '/TOTAL.*\s+(\d+%)$/'
artifacts:
reports:
coverage_report:
coverage_format: cobertura
path: coverage.xml
paths:
- htmlcov/
expire_in: 30 days
only:
- branches
# 集成测试
integration_test:
stage: test
image: python:3.11
services:
- postgres:13
- redis:latest
variables:
POSTGRES_DB: testdb
POSTGRES_USER: testuser
POSTGRES_PASSWORD: testpass
DATABASE_URL: "postgresql://testuser:testpass@postgres:5432/testdb"
REDIS_URL: "redis://redis:6379/0"
before_script:
- pip install -r requirements.txt
script:
- pytest tests/integration/ -v
only:
- main
- merge_requests
# 安全扫描(Trivy 镜像漏洞检测)
security_scan:
stage: security
image: aquasec/trivy:latest
script:
- trivy image --exit-code 1 --severity HIGH,CRITICAL $DOCKER_REGISTRY/$IMAGE_NAME:$IMAGE_TAG # 扫描高危和严重漏洞
allow_failure: true # 允许失败,不阻塞流水线
only:
- main
- merge_requests
# 部署到开发环境
deploy_dev:
stage: deploy
image: bitnami/kubectl:latest
environment:
name: dev
url: https://dev.example.com
on_stop: stop_dev # 关联停止操作(用于清理环境)
script:
- kubectl config use-context dev-cluster
- helm upgrade --install myapp ./helm-chart --namespace dev --set image.tag=$IMAGE_TAG --set environment=dev # 使用 Helm 部署
- kubectl rollout status deployment/myapp -n dev # 等待部署完成
only:
- develop
# 停止开发环境(清理资源)
stop_dev:
stage: deploy
image: bitnami/kubectl:latest
environment:
name: dev
action: stop # 标记为停止环境操作
script:
- kubectl config use-context dev-cluster
- helm uninstall myapp --namespace dev # 卸载 Helm Release
when: manual # 需手动触发
only:
- develop
# 部署到预发布环境
deploy_staging:
stage: deploy
image: bitnami/kubectl:latest
environment:
name: staging
url: https://staging.example.com
script:
- kubectl config use-context staging-cluster
- helm upgrade --install myapp ./helm-chart --namespace staging --set image.tag=$IMAGE_TAG --set environment=staging
- kubectl rollout status deployment/myapp -n staging
when: manual # 需手动确认后部署
only:
- main # 仅 main 分支可触发
# 部署到生产环境(最终发布)
deploy_production:
stage: deploy
image: bitnami/kubectl:latest
environment:
name: production
url: https://example.com
script:
- kubectl config use-context prod-cluster
- helm upgrade --install myapp ./helm-chart --namespace production --set image.tag=$IMAGE_TAG --set environment=production
- kubectl rollout status deployment/myapp -n production
when: manual # 需手动确认后部署
only:
- tags # 仅在打 tag 时触发(版本发布)
5.4 GitHub Actions¶
5.4.1 GitHub Actions概述¶
GitHub Actions是GitHub的CI/CD服务,与GitHub仓库深度集成。
核心特性¶
- 原生集成
- 无需额外配置
- 与GitHub无缝集成
-
可视化Workflow
-
YAML配置
- .github/workflows/*.yml
- 简单易用
-
版本控制
-
Marketplace
- 丰富的Actions
- 社区支持
- 高度可扩展
5.4.2 Workflow配置¶
基本Workflow¶
name: CI/CD Pipeline
# 触发条件
on:
push:
branches: [ main, develop ] # 推送到 main/develop 分支时触发
pull_request:
branches: [ main ] # PR 到 main 分支时触发
workflow_dispatch: # 支持手动触发
jobs:
# 构建与测试任务
build:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.12'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
- name: Run tests
run: |
pytest tests/ -v --cov=app --cov-report=xml
- name: Upload coverage # 上传覆盖率报告到 Codecov
uses: codecov/codecov-action@v5
with:
file: ./coverage.xml
# 部署任务(依赖 build 成功后执行)
deploy:
needs: build # 依赖 build 任务
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main' # 仅 main 分支触发部署
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Docker Buildx # 配置 Docker 多平台构建
uses: docker/setup-buildx-action@v3
- name: Login to Docker Registry # 登录镜像仓库
uses: docker/login-action@v3
with:
registry: myregistry.com
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Build and push # 构建并推送 Docker 镜像
uses: docker/build-push-action@v6
with:
context: .
push: true
tags: |
myregistry.com/myapp:latest
myregistry.com/myapp:${{ github.sha }}
- name: Deploy to Kubernetes # 部署到 K8s 集群
uses: azure/k8s-deploy@v4
with:
manifests: |
k8s/deployment.yaml
k8s/service.yaml
images: |
myregistry.com/myapp:${{ github.sha }}
kubeconfig: ${{ secrets.KUBE_CONFIG }}
高级Workflow¶
name: Advanced CI/CD Pipeline
# 触发条件
on:
push:
branches: [ main, develop ] # 推送到 main/develop 时触发
pull_request:
branches: [ main ] # PR 到 main 时触发
workflow_dispatch: # 手动触发,可选择部署环境
inputs:
environment:
description: 'Deployment environment'
required: true
default: 'staging'
type: choice
options:
- dev
- staging
- production
# 全局环境变量
env:
DOCKER_REGISTRY: myregistry.com
IMAGE_NAME: myapp
jobs:
# 代码规范检查(Lint)
lint:
name: Code Linting
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.12'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install flake8 black isort
- name: Run flake8
run: |
flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
- name: Run black
run: black --check .
- name: Run isort
run: isort --check-only .
# 测试任务
test:
name: Run Tests
runs-on: ubuntu-latest
needs: lint # 依赖 lint 通过后执行
# 服务容器(测试依赖的数据库和缓存)
services: # services定义各个服务容器
postgres:
image: postgres:13
env:
POSTGRES_DB: testdb
POSTGRES_USER: testuser
POSTGRES_PASSWORD: testpass
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
ports:
- 5432:5432
redis:
image: redis:latest
options: >-
--health-cmd "redis-cli ping"
--health-interval 10s
--health-timeout 5s
--health-retries 5
ports:
- 6379:6379
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.12'
cache: 'pip'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
- name: Run unit tests
env:
DATABASE_URL: postgresql://testuser:testpass@localhost:5432/testdb
REDIS_URL: redis://localhost:6379/0
run: |
pytest tests/unit/ -v --cov=app --cov-report=xml --cov-report=html
- name: Run integration tests
env:
DATABASE_URL: postgresql://testuser:testpass@localhost:5432/testdb
REDIS_URL: redis://localhost:6379/0
run: |
pytest tests/integration/ -v
- name: Upload coverage reports
uses: codecov/codecov-action@v5
with:
file: ./coverage.xml
flags: unittests
- name: Archive coverage reports
uses: actions/upload-artifact@v4
with:
name: coverage-report
path: htmlcov/
# 安全扫描
security:
name: Security Scan
runs-on: ubuntu-latest
needs: test # 依赖测试通过后执行
steps:
- uses: actions/checkout@v4
- name: Run Trivy vulnerability scanner
uses: aquasecurity/trivy-action@0.28.0
with:
scan-type: 'fs'
scan-ref: '.'
format: 'sarif'
output: 'trivy-results.sarif'
- name: Upload Trivy results to GitHub Security tab
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: 'trivy-results.sarif'
# 构建 Docker 镜像
build:
name: Build Docker Image
runs-on: ubuntu-latest
needs: [test, security] # 依赖测试和安全扫描通过
# 输出镜像信息供后续部署任务使用
outputs:
image-tag: ${{ steps.meta.outputs.tags }}
image-digest: ${{ steps.build.outputs.digest }}
steps:
- uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Log in to Docker Registry
uses: docker/login-action@v3
with:
registry: ${{ env.DOCKER_REGISTRY }}
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Extract metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.DOCKER_REGISTRY }}/${{ env.IMAGE_NAME }}
tags: |
type=ref,event=branch
type=ref,event=pr
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=sha,prefix=
- name: Build and push Docker image
id: build
uses: docker/build-push-action@v6
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha # 使用 GitHub Actions 缓存加速构建
cache-to: type=gha,mode=max # 缓存所有构建层
- name: Generate SBOM # 生成软件物料清单(供应链安全)
uses: anchore/sbom-action@v0
with:
image: ${{ env.DOCKER_REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }}
format: spdx-json
output-file: sbom.json
- name: Upload SBOM
uses: actions/upload-artifact@v4
with:
name: sbom
path: sbom.json
# 部署到开发环境
deploy_dev:
name: Deploy to Development
runs-on: ubuntu-latest
needs: build
if: github.ref == 'refs/heads/develop' # 仅 develop 分支触发
environment:
name: development
url: https://dev.example.com
steps:
- uses: actions/checkout@v4
- name: Configure kubectl
uses: azure/k8s-set-context@v3
with:
method: kubeconfig
kubeconfig: ${{ secrets.KUBE_CONFIG_DEV }}
- name: Deploy to Kubernetes
uses: azure/k8s-deploy@v4
with:
manifests: |
k8s/deployment.yaml
k8s/service.yaml
images: |
${{ needs.build.outputs.image-tag }}
namespace: dev
# 部署到预发布环境
deploy_staging:
name: Deploy to Staging
runs-on: ubuntu-latest
needs: build
if: github.ref == 'refs/heads/main' # 仅 main 分支触发
environment:
name: staging
url: https://staging.example.com
steps:
- uses: actions/checkout@v4
- name: Configure kubectl
uses: azure/k8s-set-context@v3
with:
method: kubeconfig
kubeconfig: ${{ secrets.KUBE_CONFIG_STAGING }}
- name: Deploy to Kubernetes
uses: azure/k8s-deploy@v4
with:
manifests: |
k8s/deployment.yaml
k8s/service.yaml
images: |
${{ needs.build.outputs.image-tag }}
namespace: staging
# 部署到生产环境(需手动触发且选择 production)
deploy_production:
name: Deploy to Production
runs-on: ubuntu-latest
needs: deploy_staging # 依赖预发布部署成功
if: github.event_name == 'workflow_dispatch' && github.event.inputs.environment == 'production'
environment:
name: production
url: https://example.com
steps:
- uses: actions/checkout@v4
- name: Configure kubectl
uses: azure/k8s-set-context@v3
with:
method: kubeconfig
kubeconfig: ${{ secrets.KUBE_CONFIG_PROD }}
- name: Deploy to Kubernetes
uses: azure/k8s-deploy@v4
with:
manifests: |
k8s/deployment.yaml
k8s/service.yaml
images: |
${{ needs.build.outputs.image-tag }}
namespace: production
- name: Verify deployment # 验证部署状态
run: |
kubectl rollout status deployment/myapp -n production
kubectl get pods -n production
5.5 部署策略¶
5.5.1 蓝绿部署¶
蓝绿部署通过维护两个相同的生产环境(蓝和绿),实现零停机部署。
实现方式¶
# Argo Rollouts 蓝绿部署配置
apiVersion: argoproj.io/v1alpha1
kind: Rollout
metadata:
name: myapp
spec:
replicas: 5
strategy:
blueGreen:
activeService: myapp-active # 当前活跃服务(接收生产流量)
previewService: myapp-preview # 预览服务(新版本验证)
autoPromotionEnabled: false # 禁用自动晋升,需手动确认切换
scaleDownDelaySeconds: 30 # 切换后旧版本保留 30 秒(便于快速回滚)
prePromotionAnalysis: # 晋升前自动分析
templates:
- templateName: success-rate
args:
- name: service-name
value: myapp-preview # 验证预览版本的成功率
postPromotionAnalysis: # 晋升后自动分析
templates:
- templateName: success-rate
args:
- name: service-name
value: myapp-active # 验证活跃版本的成功率
selector:
matchLabels:
app: myapp
template:
metadata:
labels:
app: myapp
spec:
containers:
- name: myapp
image: myregistry.com/myapp:v1.0
ports:
- containerPort: 8080
5.5.2 金丝雀发布¶
金丝雀发布逐步将新版本发布给一小部分用户,验证后再全量发布。
实现方式¶
# Argo Rollouts 金丝雀发布配置
apiVersion: argoproj.io/v1alpha1 # apiVersion指定K8s API版本
kind: Rollout # kind指定资源类型
metadata:
name: myapp
spec: # spec定义资源的期望状态
replicas: 10
strategy:
canary:
canaryService: myapp-canary # 金丝雀服务(新版本)
stableService: myapp-stable # 稳定版本服务
trafficRouting: # 通过 Istio 进行流量路由
istio:
virtualService:
name: myapp-vsvc
steps: # 发布步骤:逐步增加流量比例
- setWeight: 10 # 第1步:10% 流量到新版本
- pause: {duration: 10m} # 暂停观察 10 分钟
- analysis: # 自动分析金丝雀版本指标
templates:
- templateName: success-rate
args:
- name: service-name
value: myapp-canary
- setWeight: 20 # 第2步:增加到 20%
- pause: {duration: 10m}
- analysis: # 再次验证成功率
templates:
- templateName: success-rate
args:
- name: service-name
value: myapp-canary
- setWeight: 40 # 第3步:增加到 40%
- pause: {duration: 10m}
- setWeight: 60 # 第4步:增加到 60%
- pause: {duration: 10m}
- setWeight: 80 # 第5步:增加到 80%,之后全量发布
- pause: {duration: 10m}
selector:
matchLabels:
app: myapp
template:
metadata:
labels:
app: myapp
spec:
containers:
- name: myapp
image: myregistry.com/myapp:v1.0
ports:
- containerPort: 8080
5.6 练习题¶
基础题¶
- 选择题
-
CI/CD中的CI代表什么?
- A. Continuous Integration
- B. Continuous Delivery
- C. Continuous Deployment
- D. Continuous Infrastructure
-
简答题
- 解释CI、CD的区别和联系。
- 说明Jenkins、GitLab CI、GitHub Actions的优缺点。
进阶题¶
- 实践题
- 搭建一个完整的Jenkins流水线。
- 配置GitLab CI/CD实现自动化部署。
-
使用GitHub Actions实现多环境部署。
-
设计题
- 设计一个生产级的CI/CD流水线。
- 设计一个蓝绿部署方案。
答案¶
1. 选择题答案¶
- A(CI代表Continuous Integration,持续集成)
2. 简答题答案¶
CI、CD的区别和联系: - CI(持续集成):频繁集成代码,自动化构建和测试 - CD(持续交付/部署):自动化部署,确保代码可以随时部署 - 联系:CI是CD的基础,CD是CI的延伸
Jenkins、GitLab CI、GitHub Actions的优缺点: - Jenkins:插件丰富,但配置复杂 - GitLab CI:集成度高,但功能相对有限 - GitHub Actions:简单易用,但生态较小
3. 实践题答案¶
参见5.2-5.4节的示例。
4. 设计题答案¶
参见5.5-5.6节的示例。
5.7 面试准备¶
大厂面试题¶
字节跳动¶
- 解释CI/CD的流程和价值。
- 如何实现零停机部署?
- Jenkins Pipeline的类型有哪些?
- 如何优化CI/CD流水线性能?
腾讯¶
- GitLab CI/CD的缓存机制是什么?
- 如何实现金丝雀发布?
- GitHub Actions的Secrets如何管理?
- 如何处理CI/CD中的失败?
阿里云¶
- 如何设计多环境部署策略?
- CI/CD中的安全最佳实践是什么?
- 如何实现自动化测试集成?
- 如何监控CI/CD流水线?
📚 参考资料¶
- Jenkins官方文档:https://www.jenkins.io/doc/
- GitLab CI/CD文档:https://docs.gitlab.com/ee/ci/
- GitHub Actions文档:https://docs.github.com/en/actions
- Argo Rollouts:https://argoproj.github.io/argo-rollouts/
- 《持续交付》
- 《Jenkins权威指南》
🎯 本章小结¶
本章深入讲解了CI/CD流水线技术,包括:
- CI/CD的核心概念和价值
- Jenkins流水线的配置和使用
- GitLab CI/CD的工作流程
- GitHub Actions的实践
- 蓝绿部署和金丝雀发布
- 完整的实战案例
通过本章学习,你掌握了CI/CD的核心技术,能够搭建生产级的自动化流水线。下一章将深入学习监控告警系统。