Docker_CI_CD

一、CI/CD概述

1.1 CI/CD概念

1.1.1 CI/CD定义

CI/CD核心概念:

┌─────────────────────────────────────────┐
│         CI/CD流程                        │
├─────────────────────────────────────────┤
│                                         │
│  ┌─────────────────────────────────┐   │
│  │   CI (持续集成)                 │   │
│  │   ├─ 代码提交                   │   │
│  │   ├─ 自动构建                   │   │
│  │   ├─ 自动测试                   │   │
│  │   └─ 代码质量检查               │   │
│  └─────────────────────────────────┘   │
│                 ↓                       │
│  ┌─────────────────────────────────┐   │
│  │   CD (持续交付)                 │   │
│  │   ├─ 自动部署到测试环境         │   │
│  │   ├─ 自动部署到预发布环境       │   │
│  │   └─ 手动部署到生产环境         │   │
│  └─────────────────────────────────┘   │
│                 ↓                       │
│  ┌─────────────────────────────────┐   │
│  │   CD (持续部署)                 │   │
│  │   └─ 自动部署到生产环境         │   │
│  └─────────────────────────────────┘   │
└─────────────────────────────────────────┘

东巴文理解:

CI/CD对比:
┌──────────────────┬──────────────┬──────────────┐
│     阶段         │   CI         │   CD         │
├──────────────────┼──────────────┼──────────────┤
│  持续集成       │ 自动构建测试  │ -            │
│  持续交付       │ -            │ 手动部署生产 │
│  持续部署       │ -            │ 自动部署生产 │
└──────────────────┴──────────────┴──────────────┘

CI/CD优势:
├─ 提高开发效率
├─ 减少人为错误
├─ 快速反馈问题
├─ 加快交付速度
├─ 提高代码质量
└─ 降低发布风险

1.1.2 CI/CD工具

常见CI/CD工具:

CI/CD工具对比:
┌──────────────────┬──────────────┬──────────────┐
│     工具         │   特点       │   适用场景   │
├──────────────────┼──────────────┼──────────────┤
│  Jenkins        │ 插件丰富      │ 企业级应用   │
│  GitLab CI/CD   │ 集成GitLab    │ GitLab用户   │
│  GitHub Actions │ 集成GitHub    │ GitHub用户   │
│  Travis CI      │ 配置简单      │ 开源项目     │
│  CircleCI       │ 云原生        │ 云原生应用   │
│  TeamCity       │ JetBrains     │ JetBrains用户│
└──────────────────┴──────────────┴──────────────┘

工具选择建议:
✅ 已有GitLab: 使用GitLab CI/CD
✅ 已有GitHub: 使用GitHub Actions
✅ 企业环境: 使用Jenkins
✅ 开源项目: 使用Travis CI
✅ 云原生应用: 使用CircleCI

1.2 Docker在CI/CD中的角色

1.2.1 Docker在CI/CD中的应用

Docker在CI/CD中的作用:

Docker在CI/CD中的应用:
┌──────────────────┬──────────────┬──────────────┐
│     阶段         │   Docker作用 │   优势       │
├──────────────────┼──────────────┼──────────────┤
│  构建阶段       │ 构建环境标准化│ 环境一致     │
│  测试阶段       │ 测试环境隔离  │ 并行测试     │
│  部署阶段       │ 部署包标准化  │ 快速部署     │
│  运行阶段       │ 运行环境一致  │ 环境统一     │
└──────────────────┴──────────────┴──────────────┘

Docker CI/CD流程:
1. 代码提交 → 触发CI
2. 拉取代码 → 构建Docker镜像
3. 运行测试 → 在Docker容器中测试
4. 推送镜像 → 推送到镜像仓库
5. 部署应用 → 使用Docker部署

二、Jenkins集成Docker

2.1 Jenkins安装配置

2.1.1 安装Jenkins

使用Docker安装Jenkins:

# 创建Jenkins数据目录
mkdir -p /var/jenkins_home

# 启动Jenkins容器
docker run -d \
  --name jenkins \
  -p 8080:8080 \
  -p 50000:50000 \
  -v /var/jenkins_home:/var/jenkins_home \
  -v /var/run/docker.sock:/var/run/docker.sock \
  jenkins/jenkins:lts

# 查看初始密码
docker exec jenkins cat /var/jenkins_home/secrets/initialAdminPassword

# 输出
a1b2c3d4e5f6g7h8i9j0

# 访问Jenkins
# http://localhost:8080

Jenkins Docker配置:

# 给jenkins用户docker权限
docker exec -it -u root jenkins bash

# 在容器内执行
groupadd -g 999 docker
usermod -aG docker jenkins

# 退出容器
exit

# 重启Jenkins
docker restart jenkins

2.1.2 安装Docker插件

安装必要插件:

Jenkins插件安装:
1. 登录Jenkins
2. 系统管理 → 插件管理
3. 安装以下插件:
   ├─ Docker Pipeline
   ├─ Docker
   ├─ Git
   ├─ Pipeline
   └─ Blue Ocean

插件配置:
1. 系统管理 → 系统配置
2. 配置Docker:
   ├─ Docker URL: unix:///var/run/docker.sock
   └─ 测试连接

2.2 Jenkins Pipeline

2.2.1 Pipeline基本概念

Pipeline结构:

// Jenkinsfile
pipeline {
    agent any
    
    environment {
        IMAGE_NAME = 'myapp'
        IMAGE_TAG = "${env.BUILD_ID}"
    }
    
    stages {
        stage('Checkout') {
            steps {
                checkout scm
            }
        }
        
        stage('Build') {
            steps {
                script {
                    docker.build("${IMAGE_NAME}:${IMAGE_TAG}")
                }
            }
        }
        
        stage('Test') {
            steps {
                script {
                    docker.image("${IMAGE_NAME}:${IMAGE_TAG}").inside {
                        sh 'npm test'
                    }
                }
            }
        }
        
        stage('Push') {
            steps {
                script {
                    docker.withRegistry('https://registry.example.com', 'docker-registry-credentials') {
                        docker.image("${IMAGE_NAME}:${IMAGE_TAG}").push()
                    }
                }
            }
        }
        
        stage('Deploy') {
            steps {
                sh 'docker-compose -f docker-compose.prod.yml up -d'
            }
        }
    }
    
    post {
        always {
            cleanWs()
        }
        success {
            echo 'Pipeline succeeded!'
        }
        failure {
            echo 'Pipeline failed!'
        }
    }
}

Pipeline语法说明:

Pipeline语法:
┌──────────────────┬──────────────┬──────────────┐
│     部分         │   说明       │   示例       │
├──────────────────┼──────────────┼──────────────┤
│  agent          │ 执行节点      │ any/docker   │
│  environment    │ 环境变量      │ IMAGE_NAME   │
│  stages         │ 阶段集合      │ Build/Test   │
│  stage          │ 单个阶段      │ Build        │
│  steps          │ 执行步骤      │ sh/script    │
│  post           │ 后置处理      │ always       │
└──────────────────┴──────────────┴──────────────┘

Agent类型:
├─ any: 任意可用节点
├─ none: 不指定节点
├─ label: 指定标签节点
├─ docker: 使用Docker容器
└─ kubernetes: 使用Kubernetes Pod

2.2.2 Docker Pipeline示例

完整的Docker Pipeline:

// Jenkinsfile
pipeline {
    agent {
        docker {
            image 'node:18-alpine'
            args '-v /var/run/docker.sock:/var/run/docker.sock'
        }
    }
    
    environment {
        REGISTRY = 'registry.example.com'
        IMAGE_NAME = 'myapp'
        IMAGE_TAG = "${env.BRANCH_NAME}-${env.BUILD_NUMBER}"
    }
    
    stages {
        stage('Checkout') {
            steps {
                checkout scm
                sh 'git rev-parse --short HEAD > commit'
                script {
                    env.COMMIT_ID = readFile('commit').trim()
                }
            }
        }
        
        stage('Install Dependencies') {
            steps {
                sh 'npm ci'
            }
        }
        
        stage('Run Tests') {
            steps {
                sh 'npm test'
            }
            post {
                always {
                    junit 'test-results/*.xml'
                }
            }
        }
        
        stage('Build Image') {
            steps {
                script {
                    docker.build("${REGISTRY}/${IMAGE_NAME}:${IMAGE_TAG}", "--build-arg COMMIT_ID=${env.COMMIT_ID} .")
                }
            }
        }
        
        stage('Scan Image') {
            steps {
                script {
                    sh "docker scan ${REGISTRY}/${IMAGE_NAME}:${IMAGE_TAG}"
                }
            }
        }
        
        stage('Push Image') {
            steps {
                script {
                    docker.withRegistry("https://${REGISTRY}", 'docker-registry-credentials') {
                        docker.image("${REGISTRY}/${IMAGE_NAME}:${IMAGE_TAG}").push()
                        docker.image("${REGISTRY}/${IMAGE_NAME}:${IMAGE_TAG}").push('latest')
                    }
                }
            }
        }
        
        stage('Deploy to Dev') {
            when {
                branch 'develop'
            }
            steps {
                sh """
                    docker-compose -f docker-compose.dev.yml pull
                    docker-compose -f docker-compose.dev.yml up -d
                """
            }
        }
        
        stage('Deploy to Prod') {
            when {
                branch 'main'
            }
            steps {
                input 'Deploy to production?'
                sh """
                    docker-compose -f docker-compose.prod.yml pull
                    docker-compose -f docker-compose.prod.yml up -d
                """
            }
        }
    }
    
    post {
        always {
            cleanWs()
            sh 'docker image prune -f'
        }
        success {
            mail to: 'team@example.com',
                 subject: "Pipeline Success: ${env.JOB_NAME} #${env.BUILD_NUMBER}",
                 body: "Pipeline succeeded!\n\nBuild URL: ${env.BUILD_URL}"
        }
        failure {
            mail to: 'team@example.com',
                 subject: "Pipeline Failed: ${env.JOB_NAME} #${env.BUILD_NUMBER}",
                 body: "Pipeline failed!\n\nBuild URL: ${env.BUILD_URL}"
        }
    }
}

2.3 Jenkins多环境部署

2.3.1 多环境配置

多环境Pipeline:

// Jenkinsfile
pipeline {
    agent any
    
    environment {
        REGISTRY = 'registry.example.com'
        IMAGE_NAME = 'myapp'
    }
    
    stages {
        stage('Build') {
            steps {
                script {
                    docker.build("${REGISTRY}/${IMAGE_NAME}:${env.BRANCH_NAME}-${env.BUILD_NUMBER}")
                }
            }
        }
        
        stage('Test') {
            steps {
                script {
                    docker.image("${REGISTRY}/${IMAGE_NAME}:${env.BRANCH_NAME}-${env.BUILD_NUMBER}").inside {
                        sh 'npm test'
                    }
                }
            }
        }
        
        stage('Deploy to Dev') {
            when {
                branch 'develop'
            }
            steps {
                script {
                    deployEnvironment('dev')
                }
            }
        }
        
        stage('Deploy to Test') {
            when {
                branch 'release/*'
            }
            steps {
                script {
                    deployEnvironment('test')
                }
            }
        }
        
        stage('Deploy to Prod') {
            when {
                branch 'main'
            }
            steps {
                input 'Deploy to production?'
                script {
                    deployEnvironment('prod')
                }
            }
        }
    }
}

def deployEnvironment(String env) {
    def composeFile = "docker-compose.${env}.yml"
    def imageTag = "${env.BRANCH_NAME}-${env.BUILD_NUMBER}"
    
    sh """
        docker-compose -f ${composeFile} pull
        docker-compose -f ${composeFile} up -d
        docker-compose -f ${composeFile} ps
    """
}

多环境Docker Compose:

# docker-compose.dev.yml
version: '3.8'
services:
  app:
    image: registry.example.com/myapp:${BRANCH_NAME}-${BUILD_NUMBER}
    environment:
      - NODE_ENV=development
      - DB_HOST=db
    depends_on:
      - db
    ports:
      - "3000:3000"
  
  db:
    image: postgres:15-alpine
    environment:
      - POSTGRES_DB=myapp_dev
      - POSTGRES_PASSWORD=dev123
    volumes:
      - db_dev_data:/var/lib/postgresql/data

volumes:
  db_dev_data:

---
# docker-compose.prod.yml
version: '3.8'
services:
  app:
    image: registry.example.com/myapp:${BRANCH_NAME}-${BUILD_NUMBER}
    environment:
      - NODE_ENV=production
      - DB_HOST=db
    depends_on:
      - db
    ports:
      - "80:3000"
    restart: always
  
  db:
    image: postgres:15-alpine
    environment:
      - POSTGRES_DB=myapp_prod
      - POSTGRES_PASSWORD_FILE=/run/secrets/db_password
    volumes:
      - db_prod_data:/var/lib/postgresql/data
    restart: always

volumes:
  db_prod_data:

三、GitLab CI/CD

3.1 GitLab CI/CD配置

3.1.1 GitLab Runner安装

安装GitLab Runner:

# 使用Docker安装GitLab Runner
docker run -d \
  --name gitlab-runner \
  --restart always \
  -v /var/run/docker.sock:/var/run/docker.sock \
  -v /srv/gitlab-runner/config:/etc/gitlab-runner \
  gitlab/gitlab-runner:latest

# 注册Runner
docker exec -it gitlab-runner gitlab-runner register

# 输入信息
Enter the GitLab instance URL:
https://gitlab.com

Enter the registration token:
<your-registration-token>

Enter a description for the runner:
my-runner

Enter tags for the runner (comma-separated):
docker,production

Enter optional maintenance note for the runner:

Whether to run untagged jobs [true/false]:
true

Whether to lock Runner to current project [true/false]:
false

Enter the executor:
docker

Enter the default Docker image:
alpine:latest

# 查看Runner状态
docker exec gitlab-runner gitlab-runner list

Runner配置:

# /etc/gitlab-runner/config.toml
concurrent = 4

[[runners]]
  name = "my-runner"
  url = "https://gitlab.com"
  token = "your-token"
  executor = "docker"
  [runners.docker]
    image = "alpine:latest"
    privileged = true
    volumes = ["/cache", "/var/run/docker.sock:/var/run/docker.sock"]

3.1.2 .gitlab-ci.yml配置

基本配置:

# .gitlab-ci.yml
stages:
  - build
  - test
  - deploy

variables:
  IMAGE_NAME: registry.example.com/myapp
  IMAGE_TAG: $CI_COMMIT_SHORT_SHA

before_script:
  - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY

build:
  stage: build
  script:
    - docker build -t $IMAGE_NAME:$IMAGE_TAG .
    - docker push $IMAGE_NAME:$IMAGE_TAG
  tags:
    - docker

test:
  stage: test
  script:
    - docker pull $IMAGE_NAME:$IMAGE_TAG
    - docker run --rm $IMAGE_NAME:$IMAGE_TAG npm test
  tags:
    - docker

deploy_dev:
  stage: deploy
  script:
    - docker-compose -f docker-compose.dev.yml pull
    - docker-compose -f docker-compose.dev.yml up -d
  environment:
    name: development
    url: https://dev.example.com
  only:
    - develop
  tags:
    - docker

deploy_prod:
  stage: deploy
  script:
    - docker-compose -f docker-compose.prod.yml pull
    - docker-compose -f docker-compose.prod.yml up -d
  environment:
    name: production
    url: https://www.example.com
  only:
    - main
  when: manual
  tags:
    - docker

3.2 GitLab CI/CD高级配置

3.2.1 多环境部署

多环境配置:

# .gitlab-ci.yml
stages:
  - build
  - test
  - security
  - deploy

variables:
  IMAGE_NAME: registry.example.com/myapp
  DOCKER_DRIVER: overlay2

# 构建阶段
build:
  stage: build
  script:
    - docker build --cache-from $IMAGE_NAME:latest -t $IMAGE_NAME:$CI_COMMIT_SHA -t $IMAGE_NAME:latest .
    - docker push $IMAGE_NAME:$CI_COMMIT_SHA
    - docker push $IMAGE_NAME:latest
  tags:
    - docker

# 测试阶段
test:
  stage: test
  script:
    - docker pull $IMAGE_NAME:$CI_COMMIT_SHA
    - docker run --rm $IMAGE_NAME:$CI_COMMIT_SHA npm test
  coverage: '/Lines\s*:\s*(\d+\.?\d*)%/'
  artifacts:
    reports:
      junit: test-results/*.xml
      coverage_report:
        coverage_format: cobertura
        path: coverage/cobertura-coverage.xml
  tags:
    - docker

# 安全扫描
security_scan:
  stage: security
  script:
    - docker pull $IMAGE_NAME:$CI_COMMIT_SHA
    - docker scan $IMAGE_NAME:$CI_COMMIT_SHA
  allow_failure: true
  tags:
    - docker

# 部署到开发环境
deploy_dev:
  stage: deploy
  script:
    - |
      cat <<EOF > .env
      IMAGE_TAG=$CI_COMMIT_SHA
      ENVIRONMENT=development
      EOF
    - docker-compose -f docker-compose.dev.yml pull
    - docker-compose -f docker-compose.dev.yml up -d
  environment:
    name: development
    url: https://dev.example.com
    on_stop: stop_dev
  only:
    - develop
  tags:
    - docker

stop_dev:
  stage: deploy
  script:
    - docker-compose -f docker-compose.dev.yml down
  when: manual
  environment:
    name: development
    action: stop
  only:
    - develop
  tags:
    - docker

# 部署到测试环境
deploy_test:
  stage: deploy
  script:
    - |
      cat <<EOF > .env
      IMAGE_TAG=$CI_COMMIT_SHA
      ENVIRONMENT=test
      EOF
    - docker-compose -f docker-compose.test.yml pull
    - docker-compose -f docker-compose.test.yml up -d
  environment:
    name: test
    url: https://test.example.com
  only:
    - /^release\/.*$/
  tags:
    - docker

# 部署到生产环境
deploy_prod:
  stage: deploy
  script:
    - |
      cat <<EOF > .env
      IMAGE_TAG=$CI_COMMIT_SHA
      ENVIRONMENT=production
      EOF
    - docker-compose -f docker-compose.prod.yml pull
    - docker-compose -f docker-compose.prod.yml up -d
  environment:
    name: production
    url: https://www.example.com
  only:
    - main
  when: manual
  tags:
    - docker

3.2.2 缓存和制品

配置缓存和制品:

# .gitlab-ci.yml
stages:
  - install
  - build
  - test
  - deploy

# 缓存配置
cache:
  key: ${CI_COMMIT_REF_SLUG}
  paths:
    - node_modules/
    - .npm/

variables:
  npm_config_cache: "$CI_PROJECT_DIR/.npm"

# 安装依赖
install:
  stage: install
  image: node:18-alpine
  script:
    - npm ci --cache .npm --prefer-offline
  artifacts:
    paths:
      - node_modules/
    expire_in: 1 hour

# 构建
build:
  stage: build
  image: node:18-alpine
  script:
    - npm run build
  artifacts:
    paths:
      - dist/
    expire_in: 1 week

# 测试
test:
  stage: test
  image: node:18-alpine
  script:
    - npm test
  coverage: '/Lines\s*:\s*(\d+\.?\d*)%/'
  artifacts:
    reports:
      junit: test-results/*.xml
      coverage_report:
        coverage_format: cobertura
        path: coverage/cobertura-coverage.xml

# Docker构建
docker_build:
  stage: build
  image: docker:latest
  services:
    - docker:dind
  script:
    - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
    - docker build -t $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA .
    - docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
  only:
    - main
    - develop

四、自动化部署流程

4.1 部署策略

4.1.1 滚动更新

滚动更新配置:

# docker-compose.yml
version: '3.8'
services:
  app:
    image: registry.example.com/myapp:latest
    deploy:
      replicas: 3
      update_config:
        parallelism: 1
        delay: 10s
        failure_action: rollback
      rollback_config:
        parallelism: 1
        delay: 10s
      restart_policy:
        condition: on-failure
        delay: 5s
        max_attempts: 3
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
      interval: 10s
      timeout: 3s
      retries: 3
      start_period: 30s

滚动更新脚本:

#!/bin/bash
# rolling-update.sh

IMAGE_NAME="registry.example.com/myapp"
NEW_TAG=$1
REPLICAS=3

# 拉取新镜像
docker pull ${IMAGE_NAME}:${NEW_TAG}

# 更新服务
docker service update \
  --image ${IMAGE_NAME}:${NEW_TAG} \
  --update-parallelism 1 \
  --update-delay 10s \
  --update-failure-action rollback \
  myapp_service

# 检查服务状态
docker service ps myapp_service

# 等待更新完成
while [ $(docker service ps myapp_service --filter "desired-state=running" | grep -c "Running") -lt $REPLICAS ]; do
  echo "Waiting for all replicas to be running..."
  sleep 5
done

echo "Rolling update completed!"

4.1.2 蓝绿部署

蓝绿部署配置:

# docker-compose.blue.yml
version: '3.8'
services:
  app_blue:
    image: registry.example.com/myapp:${IMAGE_TAG}
    environment:
      - NODE_ENV=production
    ports:
      - "3001:3000"
    labels:
      - "deployment=blue"

---
# docker-compose.green.yml
version: '3.8'
services:
  app_green:
    image: registry.example.com/myapp:${IMAGE_TAG}
    environment:
      - NODE_ENV=production
    ports:
      - "3002:3000"
    labels:
      - "deployment=green"

蓝绿部署脚本:

#!/bin/bash
# blue-green-deploy.sh

IMAGE_TAG=$1
CURRENT=$(cat /tmp/current_deployment 2>/dev/null || echo "blue")

if [ "$CURRENT" == "blue" ]; then
    NEW="green"
    NEW_PORT=3002
    OLD_PORT=3001
else
    NEW="blue"
    NEW_PORT=3001
    OLD_PORT=3002
fi

echo "Current deployment: $CURRENT"
echo "New deployment: $NEW"

# 启动新版本
docker-compose -f docker-compose.${NEW}.yml pull
docker-compose -f docker-compose.${NEW}.yml up -d

# 等待新版本启动
sleep 10

# 健康检查
if curl -f http://localhost:${NEW_PORT}/health; then
    echo "Health check passed!"
    
    # 切换流量
    # 更新负载均衡器配置
    
    # 停止旧版本
    docker-compose -f docker-compose.${CURRENT}.yml down
    
    # 记录当前部署
    echo $NEW > /tmp/current_deployment
    
    echo "Blue-green deployment completed!"
else
    echo "Health check failed! Rolling back..."
    docker-compose -f docker-compose.${NEW}.yml down
    exit 1
fi

4.2 监控与回滚

4.2.1 部署监控

部署监控脚本:

#!/bin/bash
# deploy-monitor.sh

CONTAINER_NAME=$1
TIMEOUT=300
INTERVAL=5
ELAPSED=0

echo "Monitoring deployment: $CONTAINER_NAME"

while [ $ELAPSED -lt $TIMEOUT ]; do
    # 检查容器状态
    STATUS=$(docker inspect --format='{{.State.Status}}' $CONTAINER_NAME 2>/dev/null)
    
    if [ "$STATUS" == "running" ]; then
        # 检查健康状态
        HEALTH=$(docker inspect --format='{{.State.Health.Status}}' $CONTAINER_NAME 2>/dev/null)
        
        if [ "$HEALTH" == "healthy" ]; then
            echo "Container is healthy!"
            exit 0
        elif [ "$HEALTH" == "unhealthy" ]; then
            echo "Container is unhealthy!"
            exit 1
        fi
    elif [ "$STATUS" == "exited" ]; then
        echo "Container exited unexpectedly!"
        exit 1
    fi
    
    echo "Waiting for container to be healthy... ($ELAPSED/$TIMEOUT)"
    sleep $INTERVAL
    ELAPSED=$((ELAPSED + INTERVAL))
done

echo "Timeout reached!"
exit 1

4.2.2 自动回滚

自动回滚配置:

// Jenkinsfile
pipeline {
    agent any
    
    environment {
        IMAGE_NAME = 'registry.example.com/myapp'
        PREVIOUS_TAG = ''
    }
    
    stages {
        stage('Get Previous Version') {
            steps {
                script {
                    PREVIOUS_TAG = sh(
                        script: "docker images ${IMAGE_NAME} --format '{{.Tag}}' | head -n 2 | tail -n 1",
                        returnStdout: true
                    ).trim()
                }
            }
        }
        
        stage('Deploy') {
            steps {
                script {
                    sh """
                        docker-compose pull
                        docker-compose up -d
                    """
                }
            }
        }
        
        stage('Health Check') {
            steps {
                script {
                    timeout(time: 5, unit: 'MINUTES') {
                        waitUntil {
                            def r = sh script: 'curl -f http://localhost:3000/health', returnStatus: true
                            return (r == 0);
                        }
                    }
                }
            }
        }
    }
    
    post {
        failure {
            script {
                echo "Deployment failed! Rolling back..."
                sh """
                    docker-compose down
                    docker tag ${IMAGE_NAME}:${PREVIOUS_TAG} ${IMAGE_NAME}:latest
                    docker-compose pull
                    docker-compose up -d
                """
            }
        }
    }
}

五、本章小结

5.1 核心概念总结

5.1.1 CI/CD要点

CI/CD核心概念:
├─ CI(持续集成): 自动构建和测试
├─ CD(持续交付): 自动部署到测试环境
└─ CD(持续部署): 自动部署到生产环境

Jenkins集成Docker:
├─ 安装Docker插件
├─ 配置Docker环境
├─ 使用Pipeline构建镜像
└─ 自动化部署流程

GitLab CI/CD:
├─ 安装GitLab Runner
├─ 配置.gitlab-ci.yml
├─ 多环境部署
└─ 缓存和制品管理

自动化部署:
├─ 滚动更新: 逐步替换
├─ 蓝绿部署: 快速切换
├─ 监控: 实时监控
└─ 回滚: 快速回滚

5.1.2 CI/CD流程

标准CI/CD流程:
1. 代码提交 → 触发CI
2. 拉取代码 → 构建Docker镜像
3. 运行测试 → 在Docker容器中测试
4. 安全扫描 → 扫描镜像漏洞
5. 推送镜像 → 推送到镜像仓库
6. 部署应用 → 使用Docker部署
7. 监控应用 → 监控应用状态
8. 自动回滚 → 失败时回滚

5.2 下一章预告

下一章: Docker实战案例

将学习:

  • 🎯 Web应用部署
  • 📊 数据库部署
  • 🔧 微服务架构
  • 🚀 生产环境实践

📝 练习题

基础题

  1. Jenkins安装: 使用Docker安装Jenkins并配置Docker环境。

  2. GitLab Runner: 安装GitLab Runner并注册到GitLab。

  3. 简单Pipeline: 编写一个简单的Jenkins Pipeline,构建Docker镜像。

进阶题

  1. 多环境部署: 配置Jenkins Pipeline实现多环境部署。

  2. GitLab CI/CD: 配置GitLab CI/CD实现自动化部署。

  3. 蓝绿部署: 实现蓝绿部署策略。

实践题

  1. 完整CI/CD流程: 搭建完整的CI/CD流程,要求:

    • Jenkins或GitLab CI/CD
    • 自动构建Docker镜像
    • 自动测试
    • 自动部署到多环境
    • 监控和回滚
  2. CI/CD文档: 编写一份CI/CD文档,包含:

    • 流程说明
    • 配置步骤
    • 部署策略
    • 监控方案