Organized 50 interview questions into 12 categories: - 01-分布式系统 (9 files): 分布式事务, 分布式锁, 一致性哈希, CAP理论, etc. - 02-数据库 (2 files): MySQL索引优化, MyBatis核心原理 - 03-缓存 (5 files): Redis数据结构, 缓存问题, LRU算法, etc. - 04-消息队列 (1 file): RocketMQ/Kafka - 05-并发编程 (4 files): 线程池, 设计模式, 限流策略, etc. - 06-JVM (1 file): JVM和垃圾回收 - 07-系统设计 (8 files): 秒杀系统, 短链接, IM, Feed流, etc. - 08-算法与数据结构 (4 files): B+树, 红黑树, 跳表, 时间轮 - 09-网络与安全 (3 files): TCP/IP, 加密安全, 性能优化 - 10-中间件 (4 files): Spring Boot, Nacos, Dubbo, Nginx - 11-运维 (4 files): Kubernetes, CI/CD, Docker, 可观测性 - 12-面试技巧 (1 file): 面试技巧和职业规划 All files renamed to Chinese for better accessibility and organized into categorized folders for easier navigation. Generated with [Claude Code](https://claude.com/claude-code) via [Happy](https://happy.engineering) Co-Authored-By: Claude <noreply@anthropic.com> Co-Authored-By: Happy <yesreply@happy.engineering>
33 KiB
33 KiB
CI/CD (持续集成/持续部署)
问题
背景:CI/CD 是现代软件开发的核心实践,通过自动化构建、测试和部署,提高软件交付速度和质量。
问题:
- 什么是 CI/CD?它解决了哪些问题?
- Jenkins Pipeline 的核心概念是什么?
- 请描述一个完整的 CI/CD 流程
- GitLab CI 和 GitHub Actions 的区别是什么?
- 如何实现蓝绿部署和金丝雀发布?
- CI/CD 中的环境变量和密钥如何管理?
- 如何实现基础设施即代码(IaC)?
- CI/CD 流水线如何集成测试(单元测试、集成测试、E2E 测试)?
- 如何回滚失败的部署?
- 在实际项目中如何设计 CI/CD 流水线?
标准答案
1. CI/CD 概述
定义:
CI (Continuous Integration,持续集成):
- 开发人员频繁提交代码到共享仓库
- 每次提交都自动触发构建和测试
- 及早发现集成错误
CD (Continuous Delivery,持续交付):
- 代码通过测试后自动部署到 staging 环境
- 随时可以部署到生产环境
- 需要手动触发生产部署
CD (Continuous Deployment,持续部署):
- 代码通过测试后自动部署到生产环境
- 无需人工干预
- 完全自动化
解决的问题:
传统开发痛点:
├─ 集成困难(大量代码合并冲突)
├─ 测试反馈慢(手动测试耗时长)
├─ 部署风险高(手动部署容易出错)
├─ 交付周期长(从开发到上线耗时数周)
└─ 回滚困难(出问题难以快速恢复)
CI/CD 解决方案:
├─ 自动化构建和测试(每次提交自动运行)
├─ 快速反馈(几分钟内知道测试结果)
├─ 自动化部署(一键部署到任何环境)
├─ 快速交付(每天多次部署)
└─ 易于回滚(保留历史版本,快速回滚)
2. Jenkins Pipeline 核心概念
Pipeline 类型:
// 1. Declarative Pipeline(声明式,推荐)
pipeline {
agent any
stages {
stage('Build') {
steps {
sh 'mvn clean package'
}
}
stage('Test') {
steps {
sh 'mvn test'
}
}
stage('Deploy') {
steps {
sh 'kubectl apply -f k8s/'
}
}
}
post {
success {
echo 'Pipeline succeeded!'
}
failure {
echo 'Pipeline failed!'
}
}
}
// 2. Scripted Pipeline(脚本式,灵活)
node {
stage('Build') {
sh 'mvn clean package'
}
stage('Test') {
sh 'mvn test'
}
stage('Deploy') {
sh 'kubectl apply -f k8s/'
}
}
核心概念:
1. Agent(代理)
// 任意可用 agent
agent any
// 指定标签
agent { label 'linux' }
// Docker agent
agent {
docker {
image 'maven:3.8.1-openjdk-11'
args '-v $HOME/.m2:/root/.m2'
}
}
// Kubernetes agent(Pod Template)
agent {
kubernetes {
yaml '''
spec:
containers:
- name: maven
image: maven:3.8.1-openjdk-11
command: ["cat"]
tty: true
'''
}
}
2. Stages(阶段)
stages {
stage('Build') {
when {
branch 'main' // 只在 main 分支执行
}
steps {
sh 'mvn clean package'
}
}
stage('Deploy to Staging') {
when {
branch 'develop'
}
steps {
sh 'kubectl apply -f k8s/staging/'
}
}
stage('Deploy to Production') {
when {
tag pattern: "v\\d+\\.\\d+\\.\\d+", comparator: "REGEXP"
}
steps {
sh 'kubectl apply -f k8s/production/'
}
}
}
3. Post(后置操作)
post {
always {
junit 'target/surefire-reports/*.xml' // 发布测试报告
}
success {
sh 'notify-success.sh' // 发送成功通知
}
failure {
sh 'notify-failure.sh' // 发送失败通知
}
unstable {
echo 'This pipeline is unstable!'
}
changed {
echo 'Pipeline status changed!'
}
}
4. Environment(环境变量)
environment {
MAVEN_HOME = '/opt/maven'
DATABASE_URL = credentials('database-url') // 从 Jenkins 凭证获取
DEPLOY_ENV = 'staging'
}
pipeline {
agent any
environment {
APP_VERSION = sh(script: 'git describe --tags --always', returnStdout: true).trim()
}
stages {
stage('Build') {
steps {
sh "mvn clean package -Dapp.version=${APP_VERSION}"
}
}
}
}
5. Parameters(参数化构建)
pipeline {
agent any
parameters {
string(name: 'DEPLOY_ENV', defaultValue: 'staging', description: 'Deploy environment')
booleanParam(name: 'RUN_TESTS', defaultValue: true, description: 'Run tests')
choice(name: 'TIER', choices: ['dev', 'staging', 'production'], description: 'Environment tier')
}
stages {
stage('Deploy') {
when {
expression { params.DEPLOY_ENV == 'production' }
}
steps {
sh "kubectl apply -f k8s/${params.DEPLOY_ENV}/"
}
}
}
}
3. 完整 CI/CD 流程
流程图:
开发者提交代码
↓
触发 CI/CD 流水线
↓
┌─────────────────────────────────────┐
│ 1. 代码检查 │
│ - 代码风格检查 (ESLint, Checkstyle)│
│ - 静态分析 (SonarQube) │
│ - 安全扫描 (Snyk, OWASP) │
└─────────────────────────────────────┘
↓
┌─────────────────────────────────────┐
│ 2. 构建 │
│ - 编译 (Maven/Gradle/npm) │
│ - 打包 (JAR/WAR/Docker镜像) │
│ - 版本打标 (git tag) │
└─────────────────────────────────────┘
↓
┌─────────────────────────────────────┐
│ 3. 测试 │
│ - 单元测试 (JUnit, pytest) │
│ - 集成测试 (TestContainers) │
│ - 代码覆盖率 (JaCoCo) │
└─────────────────────────────────────┘
↓
┌─────────────────────────────────────┐
│ 4. 构建镜像 │
│ - Docker Build │
│ - 推送到镜像仓库 (Docker Hub/ECR) │
└─────────────────────────────────────┘
↓
┌─────────────────────────────────────┐
│ 5. 部署到 Staging 环境 │
│ - Kubernetes Deploy │
│ - 数据库迁移 (Flyway/Liquibase) │
└─────────────────────────────────────┘
↓
┌─────────────────────────────────────┐
│ 6. 自动化测试 │
│ - E2E 测试 (Selenium/Cypress) │
│ - 性能测试 (JMeter/Gatling) │
│ - 安全测试 (OWASP ZAP) │
└─────────────────────────────────────┘
↓
┌─────────────────────────────────────┐
│ 7. 人工审核 (可选) │
│ - 查看测试报告 │
│ - 审核变更内容 │
└─────────────────────────────────────┘
↓
┌─────────────────────────────────────┐
│ 8. 部署到 Production 环境 │
│ - 蓝绿部署/金丝雀发布 │
│ - 监控验证 │
└─────────────────────────────────────┘
↓
┌─────────────────────────────────────┐
│ 9. 发布通知 │
│ - 钉钉/企业微信/Slack │
│ - 发布日志 │
└─────────────────────────────────────┘
Jenkinsfile 示例:
pipeline {
agent any
tools {
maven 'Maven 3.8.1'
jdk 'JDK 11'
}
environment {
IMAGE_NAME = "my-app"
IMAGE_TAG = "${env.BUILD_NUMBER}"
REGISTRY = "registry.example.com"
KUBECONFIG = credentials('kubeconfig')
}
stages {
stage('Checkout') {
steps {
git branch: 'main', url: 'https://github.com/myorg/myapp.git'
}
}
stage('Code Quality') {
steps {
sh 'mvn checkstyle:check'
sh 'mvn spotbugs:check'
script {
def scannerHome = tool 'SonarQube Scanner';
withSonarQubeEnv('MySonarQube') {
sh "${scannerHome}/bin/sonar-scanner"
}
}
}
}
stage('Build') {
steps {
sh 'mvn clean package -DskipTests'
archiveArtifacts artifacts: 'target/*.jar', fingerprint: true
}
}
stage('Unit Test') {
steps {
sh 'mvn test'
junit 'target/surefire-reports/*.xml'
}
}
stage('Integration Test') {
steps {
sh 'mvn verify -Pintegration-test'
junit 'target/failsafe-reports/*.xml'
}
}
stage('Build & Push Docker Image') {
steps {
script {
docker.withRegistry("https://${REGISTRY}", 'docker-registry-credentials') {
def image = docker.build("${IMAGE_NAME}:${IMAGE_TAG}")
image.push()
image.push('latest')
}
}
}
}
stage('Deploy to Staging') {
steps {
sh """
kubectl set image deployment/my-app \
my-app=${REGISTRY}/${IMAGE_NAME}:${IMAGE_TAG} \
--namespace=staging
"""
sh 'kubectl rollout status deployment/my-app --namespace=staging'
}
}
stage('E2E Test') {
steps {
sh 'mvn verify -Pe2e-test'
}
}
stage('Deploy to Production') {
when {
branch 'main'
}
steps {
input message: 'Deploy to Production?', ok: 'Deploy'
sh """
kubectl set image deployment/my-app \
my-app=${REGISTRY}/${IMAGE_NAME}:${IMAGE_TAG} \
--namespace=production
"""
sh 'kubectl rollout status deployment/my-app --namespace=production'
}
}
}
post {
always {
cleanWs()
}
success {
sh 'notify-success.sh'
}
failure {
sh 'notify-failure.sh'
}
}
}
4. GitLab CI vs GitHub Actions
对比表:
| 特性 | GitLab CI | GitHub Actions |
|---|---|---|
| 集成度 | GitLab 内置 | GitHub 内置 |
| 配置文件 | .gitlab-ci.yml | .github/workflows/*.yml |
| Runner 类型 | Shared/Specific/Group | Hosted/Self-hosted |
| 缓存 | artifacts/cache | actions/cache |
| 密钥管理 | Variables/Secrets | Secrets/Environments |
| 矩阵构建 | 支持 | 支持 |
| 复用性 | Include/Template | Reusable Workflows |
| 社区生态 | 丰富 | 快速增长 |
GitLab CI 示例:
# .gitlab-ci.yml
stages:
- build
- test
- deploy
variables:
MAVEN_OPTS: "-Dmaven.repo.local=$CI_PROJECT_DIR/.m2/repository"
IMAGE_NAME: registry.example.com/my-app
IMAGE_TAG: $CI_PIPELINE_ID
cache:
paths:
- .m2/repository/
# 构建
build:
stage: build
image: maven:3.8.1-openjdk-11
script:
- mvn clean package -DskipTests
artifacts:
paths:
- target/*.jar
expire_in: 1 week
# 单元测试
unit-test:
stage: test
image: maven:3.8.1-openjdk-11
script:
- mvn test
artifacts:
reports:
junit: target/surefire-reports/*.xml
# 代码质量
code-quality:
stage: test
image: sonarsource/sonar-scanner-cli
script:
- sonar-scanner
allow_failure: true
# 构建镜像
build-image:
stage: build
image: docker:20.10.7
services:
- docker:20.10.7-dind
before_script:
- docker login -u $REGISTRY_USER -p $REGISTRY_PASSWORD registry.example.com
script:
- docker build -t $IMAGE_NAME:$IMAGE_TAG .
- docker push $IMAGE_NAME:$IMAGE_TAG
- docker tag $IMAGE_NAME:$IMAGE_TAG $IMAGE_NAME:latest
- docker push $IMAGE_NAME:latest
# 部署到 Staging
deploy-staging:
stage: deploy
image: bitnami/kubectl:latest
script:
- kubectl set image deployment/my-app my-app=$IMAGE_NAME:$IMAGE_TAG --namespace=staging
- kubectl rollout status deployment/my-app --namespace=staging
environment:
name: staging
url: https://staging.example.com
only:
- develop
# 部署到 Production
deploy-production:
stage: deploy
image: bitnami/kubectl:latest
script:
- kubectl set image deployment/my-app my-app=$IMAGE_NAME:$IMAGE_TAG --namespace=production
- kubectl rollout status deployment/my-app --namespace=production
environment:
name: production
url: https://example.com
when: manual # 手动触发
only:
- main
GitHub Actions 示例:
# .github/workflows/ci-cd.yml
name: CI/CD Pipeline
on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main ]
release:
types: [ created ]
env:
REGISTRY: registry.example.com
IMAGE_NAME: my-app
jobs:
# 并行运行
build-and-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up JDK 11
uses: actions/setup-java@v3
with:
java-version: '11'
distribution: 'temurin'
cache: maven
- name: Build with Maven
run: mvn clean package -DskipTests
- name: Run Unit Tests
run: mvn test
- name: Upload Test Results
if: always()
uses: actions/upload-artifact@v3
with:
name: test-results
path: target/surefire-reports/*.xml
- name: Build Docker Image
run: |
docker build -t ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.run_number }} .
docker tag ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.run_number }} ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest
- name: Login to Registry
uses: docker/login-action@v2
with:
registry: ${{ env.REGISTRY }}
username: ${{ secrets.REGISTRY_USERNAME }}
password: ${{ secrets.REGISTRY_PASSWORD }}
- name: Push Docker Image
run: |
docker push ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.run_number }}
docker push ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest
# 依赖 build-and-test
deploy-staging:
needs: build-and-test
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/develop'
steps:
- uses: actions/checkout@v3
- name: Set up kubectl
uses: azure/setup-kubectl@v3
- name: Deploy to Staging
run: |
kubectl set image deployment/my-app my-app=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.run_number }} --namespace=staging
kubectl rollout status deployment/my-app --namespace=staging
deploy-production:
needs: build-and-test
runs-on: ubuntu-latest
if: github.event_name == 'release'
environment:
name: production
url: https://example.com
steps:
- uses: actions/checkout@v3
- name: Set up kubectl
uses: azure/setup-kubectl@v3
- name: Deploy to Production
run: |
kubectl set image deployment/my-app my-app=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.run_number }} --namespace=production
kubectl rollout status deployment/my-app --namespace=production
- name: Notify
run: |
curl -X POST ${{ secrets.SLACK_WEBHOOK }} \
-H 'Content-Type: application/json' \
-d '{"text":"Deployment to Production successful!"}'
5. 蓝绿部署和金丝雀发布
蓝绿部署(Blue-Green Deployment):
步骤:
1. 部署新版本到 Green 环境
2. 验证 Green 环境(健康检查、E2E 测试)
3. 切换流量到 Green 环境
4. 保留 Blue 环境,以便快速回滚
优势:
- 零停机部署
- 快速回滚(切换回 Blue)
- 风险低
劣势:
- 需要双倍资源
- 数据库迁移需要特殊处理
Kubernetes 实现:
# 1. 部署 Blue(当前版本)
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app-blue
spec:
replicas: 3
selector:
matchLabels:
app: my-app
version: blue
template:
metadata:
labels:
app: my-app
version: blue
spec:
containers:
- name: my-app
image: my-app:1.0.0
---
# 2. 部署 Green(新版本)
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app-green
spec:
replicas: 3
selector:
matchLabels:
app: my-app
version: green
template:
metadata:
labels:
app: my-app
version: green
spec:
containers:
- name: my-app
image: my-app:2.0.0
---
# 3. Service 指向 Blue
apiVersion: v1
kind: Service
metadata:
name: my-app
spec:
selector:
app: my-app
version: blue # 当前指向 Blue
ports:
- port: 80
targetPort: 8080
# 切换到 Green:修改 selector.version = green
Jenkins Pipeline:
stage('Blue-Green Deployment') {
steps {
script {
// 1. 部署 Green
sh "kubectl apply -f k8s/green-deployment.yaml"
// 2. 等待 Green Ready
sh 'kubectl rollout status deployment/my-app-green'
// 3. 健康检查
sh """
for i in {1..30}; do
curl -f http://my-app-green.default.svc.cluster.local/health && break || sleep 5
done
"""
// 4. 切换流量到 Green
sh "kubectl patch service my-app -p '{\"spec\":{\"selector\":{\"version\":\"green\"}}}'"
// 5. 监控 Green(如果失败,切换回 Blue)
timeout(time: 5, unit: 'MINUTES') {
input message: 'Verify Green environment. OK to proceed?', ok: 'Keep Green'
}
}
}
post {
failure {
// 回滚到 Blue
sh "kubectl patch service my-app -p '{\"spec\":{\"selector\":{\"version\":\"blue\"}}}'"
}
}
}
金丝雀发布(Canary Deployment):
步骤:
1. 部署新版本到小部分实例(如 10%)
2. 观察错误率、延迟等指标
3. 逐步增加流量(10% → 50% → 100%)
4. 如果出现问题,立即回滚
优势:
- 风险可控
- 渐进式发布
- 可以快速发现问题
劣势:
- 需要流量管理(如 Istio)
- 监控要求高
Istio 实现:
# 1. 部署 v1 和 v2
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app-v1
spec:
replicas: 9
template:
metadata:
labels:
app: my-app
version: v1
spec:
containers:
- name: my-app
image: my-app:1.0.0
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app-v2
spec:
replicas: 1
template:
metadata:
labels:
app: my-app
version: v2
spec:
containers:
- name: my-app
image: my-app:2.0.0
---
# 2. VirtualService 配置金丝雀
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
name: my-app
spec:
hosts:
- my-app
http:
- route:
- destination:
host: my-app
subset: v1
weight: 90 # 90% 流量到 v1
- destination:
host: my-app
subset: v2
weight: 10 # 10% 流量到 v2
---
# 3. DestinationRule 定义 subset
apiVersion: networking.istio.io/v1beta1
kind: DestinationRule
metadata:
name: my-app
spec:
host: my-app
subsets:
- name: v1
labels:
version: v1
- name: v2
labels:
version: v2
Jenkins Pipeline:
stage('Canary Deployment') {
steps {
script {
// 1. 部署 v2(1 个副本)
sh "kubectl apply -f k8s/v2-deployment.yaml"
// 2. 配置 10% 流量到 v2
sh "kubectl apply -f istio/10-percent-canary.yaml"
// 3. 监控 5 分钟
sleep(time: 5, unit: 'MINUTES')
// 4. 检查错误率
def errorRate = sh(
script: 'curl -s http://prometheus/api/v1/query?query=rate(requests_total{status=~"5.."}[5m]) | jq .data.result[0].value[1]',
returnStdout: true
).trim()
if (errorRate.toDouble() > 0.01) {
error "Error rate too high: ${errorRate}"
}
// 5. 逐步增加流量
sh "kubectl apply -f istio/50-percent-canary.yaml"
sleep(time: 5, unit: 'MINUTES')
// 6. 100% 流量到 v2
sh "kubectl apply -f istio/100-percent-canary.yaml"
}
}
post {
failure {
// 回滚到 v1
sh "kubectl apply -f istio/rollback-to-v1.yaml"
}
}
}
6. 环境变量和密钥管理
Jenkins 凭证管理:
// 1. 使用 Jenkins 凭证
withCredentials([
string(credentialsId: 'database-url', variable: 'DATABASE_URL'),
usernamePassword(credentialsId: 'docker-registry', usernameVariable: 'REGISTRY_USER', passwordVariable: 'REGISTRY_PASSWORD')
]) {
sh """
docker login -u $REGISTRY_USER -p $REGISTRY_PASSWORD registry.example.com
docker build -t myapp:${BUILD_NUMBER} --build-arg DATABASE_URL=$DATABASE_URL .
"""
}
// 2. 使用 Secret File
withCredentials([file(credentialsId: 'kubeconfig', variable: 'KUBECONFIG')]) {
sh 'kubectl --kubeconfig=$KUBECONFIG get pods'
}
// 3. 使用 Secret Text
withCredentials([string(credentialsId: 'slack-webhook', variable: 'SLACK_WEBHOOK')]) {
sh """
curl -X POST $SLACK_WEBHOOK \
-H 'Content-Type: application/json' \
-d '{"text":"Build successful!"}'
"""
}
GitLab CI 密钥管理:
# 在 GitLab UI 中设置 CI/CD Variables
variables:
# 普通变量
DEPLOY_ENV: production
# 使用 Masked Variables(隐藏变量)
build:
script:
- docker login -u $REGISTRY_USER -p $REGISTRY_PASSWORD
- echo $DATABASE_URL # 在日志中会被隐藏为 ****
# 使用 File 类型变量(自动保存为文件)
deploy:
script:
- kubectl --kubeconfig=$KUBECONFIG get pods
GitHub Actions 密钥管理:
# 在 GitHub UI 中设置 Secrets
env:
DATABASE_URL: ${{ secrets.DATABASE_URL }}
steps:
- name: Login to Registry
uses: docker/login-action@v2
with:
registry: registry.example.com
username: ${{ secrets.REGISTRY_USERNAME }}
password: ${{ secrets.REGISTRY_PASSWORD }}
- name: Deploy
run: |
kubectl --kubeconfig <(echo "${{ secrets.KUBECONFIG }}") get pods
Kubernetes Secrets:
# 1. 创建 Secret
apiVersion: v1
kind: Secret
metadata:
name: app-secrets
type: Opaque
data:
database-url: BASE64_ENCODED_URL
api-key: BASE64_ENCODED_KEY
# 2. 在 Pod 中使用
apiVersion: v1
kind: Pod
spec:
containers:
- name: app
image: my-app
env:
- name: DATABASE_URL
valueFrom:
secretKeyRef:
name: app-secrets
key: database-url
- name: API_KEY
valueFrom:
secretKeyRef:
name: app-secrets
key: api-key
7. 基础设施即代码(IaC)
Terraform 示例:
# main.tf
provider "aws" {
region = "us-west-2"
}
# VPC
resource "aws_vpc" "main" {
cidr_block = "10.0.0.0/16"
enable_dns_hostnames = true
enable_dns_support = true
tags = {
Name = "main-vpc"
}
}
# EKS Cluster
resource "aws_eks_cluster" "main" {
name = "main-cluster"
role_arn = aws_iam_role.eks_cluster.arn
vpc_config {
subnet_ids = aws_subnet.private[*].id
}
depends_on = [aws_iam_role_policy_attachment.eks_cluster_policy]
}
# Node Group
resource "aws_eks_node_group" "main" {
cluster_name = aws_eks_cluster.main.name
node_group_name = "main-node-group"
node_role_arn = aws_iam_role.eks_nodes.arn
subnet_ids = aws_subnet.private[*].id
scaling_config {
desired_size = 3
max_size = 5
min_size = 1
}
instance_types = ["t3.medium"]
depends_on = [aws_iam_role_policy_attachment.eks_nodes_policy]
}
# RDS Database
resource "aws_db_instance" "main" {
identifier = "main-db"
engine = "mysql"
engine_version = "8.0"
instance_class = "db.t3.micro"
allocated_storage = 20
storage_encrypted = true
db_name = "mydb"
username = var.db_username
password = var.db_password
vpc_security_group_ids = [aws_security_group.db.id]
db_subnet_group_name = aws_db_subnet_group.main.name
backup_retention_period = 7
skip_final_snapshot = false
final_snapshot_identifier = "main-db-final-snapshot"
}
# Output
output "cluster_endpoint" {
value = aws_eks_cluster.main.endpoint
}
output "db_endpoint" {
value = aws_db_instance.main.endpoint
sensitive = true
}
在 CI/CD 中使用 Terraform:
stage('Terraform Apply') {
steps {
withCredentials([
string(credentialsId: 'aws-access-key-id', variable: 'AWS_ACCESS_KEY_ID'),
string(credentialsId: 'aws-secret-access-key', variable: 'AWS_SECRET_ACCESS_KEY')
]) {
script {
dir('terraform') {
// 1. 初始化
sh 'terraform init'
// 2. 格式检查
sh 'terraform fmt -check'
// 3. 验证
sh 'terraform validate'
// 4. Plan
def plan = sh(
script: 'terraform plan -out=tfplan',
returnStdout: true
)
// 5. 人工审核
input message: "Review Terraform Plan:\n${plan}", ok: 'Apply'
// 6. Apply
sh 'terraform apply tfplan'
}
}
}
}
}
8. 集成测试
Pipeline 测试阶段:
stage('Test') {
parallel {
stage('Unit Test') {
steps {
sh 'mvn test'
junit 'target/surefire-reports/*.xml'
}
}
stage('Integration Test') {
steps {
sh 'mvn verify -Pintegration-test'
junit 'target/failsafe-reports/*.xml'
}
}
stage('Code Coverage') {
steps {
sh 'mvn jacoco:report'
jacoco execPattern: 'target/jacoco.exec', classPattern: 'target/classes', sourcePattern: 'src/main/java'
}
}
}
}
stage('E2E Test') {
steps {
sh 'mvn verify -Pe2e-test'
publishHTML([
reportDir: 'target/cypress-report',
reportFiles: 'index.html',
reportName: 'E2E Test Report'
])
}
}
TestContainers 示例:
@SpringBootTest
@Testcontainers
public class UserServiceIntegrationTest {
@Container
private static final PostgreSQL<?> postgres = new PostgreSQL<>("postgres:13");
@Container
private static final GenericContainer<?> redis = new GenericContainer<>("redis:6")
.withExposedPorts(6379);
@DynamicPropertySource
static void configureProperties(DynamicPropertyRegistry registry) {
registry.add("spring.datasource.url", postgres::getJdbcUrl);
registry.add("spring.datasource.username", postgres::getUsername);
registry.add("spring.datasource.password", postgres::getPassword);
registry.add("spring.redis.host", redis::getHost);
registry.add("spring.redis.port", () -> redis.getFirstMappedPort());
}
@Test
void shouldCreateUser() {
User user = new User("Alice", "alice@example.com");
User saved = userService.save(user);
assertNotNull(saved.getId());
}
}
9. 回滚失败的部署
Kubernetes 回滚:
# 1. 查看历史版本
kubectl rollout history deployment/my-app
# 2. 回滚到上一版本
kubectl rollout undo deployment/my-app
# 3. 回滚到指定版本
kubectl rollout undo deployment/my-app --to-revision=3
# 4. 暂停部署(出现问题)
kubectl rollout pause deployment/my-app
# 5. 恢复部署
kubectl rollout resume deployment/my-app
Jenkins Pipeline 回滚:
stage('Deploy') {
steps {
script {
// 1. 记录当前版本
def currentVersion = sh(
script: 'kubectl get deployment my-app -o jsonpath="{.spec.template.spec.containers[0].image}"',
returnStdout: true
).trim()
// 2. 部署新版本
sh "kubectl set image deployment/my-app my-app=${IMAGE}:${TAG}"
// 3. 等待部署完成
timeout(time: 5, unit: 'MINUTES') {
sh 'kubectl rollout status deployment/my-app'
}
// 4. 健康检查
sh """
for i in {1..30}; do
curl -f http://my-app.default.svc.cluster.local/health && break || sleep 5
done
"""
// 5. 监控
sleep(time: 2, unit: 'MINUTES')
// 6. 检查错误率
def errorRate = sh(
script: 'curl -s http://prometheus/api/v1/query?query=rate(requests_total{status=~"5.."}[5m]) | jq .data.result[0].value[1]',
returnStdout: true
).trim()
if (errorRate.toDouble() > 0.05) {
error "Error rate too high: ${errorRate}, rolling back..."
}
}
}
post {
failure {
script {
// 回滚到上一版本
sh 'kubectl rollout undo deployment/my-app'
echo 'Rolled back to previous version'
}
}
}
}
10. 实际项目经验
场景 1:电商系统 CI/CD 流水线
需求:
- 代码提交后自动构建、测试
- 通过测试后自动部署到 Staging
- Staging 通过 E2E 测试后,手动部署到 Production
- 生产环境支持蓝绿部署
方案:
1. GitHub Actions 构建 Docker 镜像
2. 推送到私有镜像仓库
3. 部署到 Kubernetes Staging 环境
4. 运行 Cypress E2E 测试
5. 人工审核后部署到 Production
6. 使用 Istio 实现蓝绿切换
场景 2:数据库迁移自动化
# 使用 Flyway 自动迁移数据库
migrate:
stage: migrate
image: flyway/flyway:7
script:
- flyway migrate -url=$DATABASE_URL -user=$DATABASE_USER -password=$DATABASE_PASSWORD
only:
- main
场景 3:多环境配置管理
# 使用 Helm Charts 实现多环境部署
deploy-staging:
script:
- helm upgrade --install my-app ./helm-chart --namespace staging --values helm-chart/values-staging.yaml
deploy-production:
script:
- helm upgrade --install my-app ./helm-chart --namespace production --values helm-chart/values-production.yaml
11. 阿里 P7 加分项
架构设计能力:
- 设计过企业级 CI/CD 平台(支持多语言、多环境)
- 实现过自建 Runner 集群(Kubernetes Executors)
- 有多租户、多团队的 CI/CD 隔离经验
深度理解:
- 熟悉 Jenkins/GitLab CI 源码和插件开发
- 理解分布式缓存和构建加速(Build Cache、Docker Layer Cache)
- 有 GitOps 实践经验(ArgoCD、Flux)
性能优化:
- 优化过构建时间(并行构建、增量构建、缓存策略)
- 实现过分布式构建(Build Farm)
- 优化过 Runner 资源利用率(动态扩缩容)
安全实践:
- 实现过 CI/CD 安全扫描(SAST、DAST、依赖扫描)
- 有签名和验证经验(容器镜像签名、Commit Signing)
- 实现过密钥轮换和凭证管理
监控和可观测性:
- 集成过 CI/CD 监控(构建成功率、构建时间、部署频率)
- 实现过部署追踪(Deployment Tracking、Change Log)
- 设计过性能测试自动化(K6、JMeter 集成)
DevSecOps:
- 实现过安全左移(Pre-commit Hooks、PR Check)
- 集成过合规性检查(PCI-DSS、GDPR)
- 实现过供应链安全(SBOM、漏洞扫描)