Jenkins入门教程(九):Jenkins与Docker集成

Docker为Jenkins提供了灵活的构建环境,本文将详细介绍如何在Jenkins中使用Docker,包括完整的配置步骤和Pipeline示例。

安装Docker插件

在插件管理中安装以下Docker相关插件:

  • Docker Pipeline:在Pipeline中使用Docker
  • Docker:Docker API集成
  • Docker Commons:Docker共享功能

配置Docker环境

确保Jenkins服务器上安装了Docker,并且Jenkins用户有权限运行Docker命令:

# 安装Docker
curl -fsSL https://get.docker.com | sh

# 将Jenkins用户添加到docker组
sudo usermod -aG docker jenkins

# 重启Jenkins服务
sudo systemctl restart jenkins

# 验证Docker权限
sudo -u jenkins docker ps

在Docker容器中构建

使用agent docker指令可以在Docker容器中执行构建:

pipeline {
    agent {
        docker {
            image 'maven:3.8-openjdk-17'
            args '-v /root/.m2:/root/.m2'  // 缓存Maven依赖
        }
    }
    
    stages {
        stage('Check Environment') {
            steps {
                sh '''
                    echo "Java Version:"
                    java -version
                    echo ""
                    echo "Maven Version:"
                    mvn -version
                '''
            }
        }
        
        stage('Build') {
            steps {
                sh 'mvn clean package -DskipTests'
            }
        }
        
        stage('Test') {
            steps {
                sh 'mvn test'
            }
        }
    }
}

执行结果:

[Pipeline] { (Check Environment)
[Pipeline] sh
+ echo 'Java Version:'
Java Version:
+ java -version
openjdk version "17.0.9" 2023-10-17
OpenJDK Runtime Environment (build 17.0.9+9)
OpenJDK 64-Bit Server VM (build 17.0.9+9, mixed mode, sharing)

+ echo 'Maven Version:'
Maven Version:
+ mvn -version
Apache Maven 3.8.7 (b89d5959fcde851dcb1c8946a785a163f14e1e29)
Maven home: /usr/share/maven
Java version: 17.0.9, vendor: Eclipse Adoptium

构建Docker镜像

在Pipeline中构建和推送Docker镜像:

pipeline {
    agent any
    
    environment {
        DOCKER_REGISTRY = 'registry.example.com'
        IMAGE_NAME = 'my-app'
        IMAGE_TAG = "${env.BUILD_NUMBER}"
    }
    
    stages {
        stage('Build Application') {
            agent {
                docker {
                    image 'maven:3.8-openjdk-17'
                    args '-v /root/.m2:/root/.m2'
                }
            }
            steps {
                sh 'mvn clean package -DskipTests'
                stash includes: 'target/*.jar', name: 'app-jar'
            }
        }
        
        stage('Build Docker Image') {
            steps {
                unstash 'app-jar'
                
                script {
                    // 使用docker.build构建镜像
                    def image = docker.build("${DOCKER_REGISTRY}/${IMAGE_NAME}:${IMAGE_TAG}")
                    
                    echo "Built image: ${image.id}"
                }
            }
        }
        
        stage('Push to Registry') {
            steps {
                script {
                    docker.withRegistry("https://${DOCKER_REGISTRY}", 'docker-registry-creds') {
                        def image = docker.image("${DOCKER_REGISTRY}/${IMAGE_NAME}:${IMAGE_TAG}")
                        image.push()
                        image.push('latest')
                    }
                }
            }
        }
    }
    
    post {
        always {
            // 清理本地镜像
            sh "docker rmi ${DOCKER_REGISTRY}/${IMAGE_NAME}:${IMAGE_TAG} || true"
        }
    }
}

示例Dockerfile

项目根目录的Dockerfile:

# 多阶段构建
FROM maven:3.8-openjdk-17 AS builder
WORKDIR /app
COPY pom.xml .
RUN mvn dependency:go-offline
COPY src ./src
RUN mvn clean package -DskipTests

# 运行时镜像
FROM eclipse-temurin:17-jre-alpine
WORKDIR /app
COPY --from=builder /app/target/*.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "app.jar"]

Docker Compose集成

对于多容器应用,使用docker-compose:

pipeline {
    agent any
    
    stages {
        stage('Start Services') {
            steps {
                sh '''
                    docker-compose -f docker-compose.test.yml up -d
                    
                    # 等待服务启动
                    sleep 10
                    
                    # 检查服务状态
                    docker-compose -f docker-compose.test.yml ps
                '''
            }
        }
        
        stage('Run Integration Tests') {
            steps {
                sh '''
                    # 执行集成测试
                    docker-compose -f docker-compose.test.yml exec -T app ./run-tests.sh
                '''
            }
        }
        
        stage('Collect Logs') {
            steps {
                sh 'docker-compose -f docker-compose.test.yml logs > docker-logs.txt'
                archiveArtifacts artifacts: 'docker-logs.txt', allowEmptyArchive: true
            }
        }
    }
    
    post {
        always {
            sh '''
                docker-compose -f docker-compose.test.yml down -v
                docker system prune -f
            '''
        }
    }
}

Docker Registry认证

配置Docker Registry凭据:

  • 在Jenkins凭据中添加用户名密码凭据,ID为docker-registry-creds
  • 在Pipeline中使用withRegistry块
// Docker Hub
docker.withRegistry('https://index.docker.io/v1/', 'dockerhub-creds') {
    def image = docker.build('myuser/myapp:latest')
    image.push()
}

// 私有Registry
docker.withRegistry('https://registry.example.com', 'private-registry-creds') {
    def image = docker.build('registry.example.com/myapp:latest')
    image.push()
}

// AWS ECR
docker.withRegistry('https://123456789.dkr.ecr.us-east-1.amazonaws.com', 'ecr:us-east-1:aws-creds') {
    def image = docker.build('123456789.dkr.ecr.us-east-1.amazonaws.com/myapp:latest')
    image.push()
}

DinD vs DooD

两种在容器中使用Docker的方式:

  • DinD (Docker in Docker):在容器内运行完整Docker守护进程,隔离性好但开销大
  • DooD (Docker outside of Docker):共享宿主机的Docker socket,更轻量(推荐)
# Docker运行Jenkins时挂载socket (DooD)
docker run -d \
  --name jenkins \
  -v /var/run/docker.sock:/var/run/docker.sock \
  -v jenkins_home:/var/jenkins_home \
  jenkins/jenkins:lts

最佳实践

  • 使用多阶段构建减小镜像体积
  • 使用.dockerignore排除不需要的文件
  • 固定基础镜像版本,避免使用latest
  • 定期清理无用镜像释放空间
  • 为生产镜像打上版本标签

总结

本文详细介绍了Jenkins与Docker的集成方法,包括在容器中构建、构建Docker镜像、使用Docker Compose等。Docker让构建环境标准化,是现代CI/CD的重要组成部分。

下一篇我们将学习使用Jenkins构建Java/Maven项目。

发表回复

后才能评论