Gradle从0入门到实战系列【十】集成Dockerfile构建Docker镜像

简介: 在工作中,我们会将SpringBoot程序打包成Docker镜像,这就需要在Gradle中配置Dockerfile构建程序,并且能将打包后的镜像部署并启动为一个容器,Gradle的插件能够帮我们完成这件事
本文来自于生产级别应用的实践,同时需要大家掌握前置技能Docker

环境准备

安装Docker环境

  1. Linux配置(必须)
  2. Docker安装(必须)
  3. Docker镜像测试

配置Docker仓库

  1. 安装使用harbor
  2. 使用阿里云仓库(必须)

开启Docker服务远程端口

如果要借助Jenkins、Gradle、Maven等工具将构建好的Docker image推到到某个服务器的Docker上,就必须开启该服务中Docker的远程连接端口,注意该端口尽量在内网操作,外网开放可以加入证书认证功能。

例:我现在想通过Gradle直接将镜像推送到192.168.2.195的docker服务里面,就需要配置195上的Docker,注意这里不是指私服,而是某个机器上的Docker服务。

  1. 修改Docker服务的配置文件

    $ vim /usr/lib/systemd/system/docker.service

    image.png

  2. 在[Service]部分的ExecStart处加入如下内容

    ExecStart=/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock -H tcp://0.0.0.0:2375
  3. 加入后的Service完整配置如下

    image.png

  4. 重载配置,重启服务

    $ systemctl daemon-reload && systemctl restart docker
  5. 如果开启了防火墙,则需要通过该端口

    $ firewall-cmd --zone=public --add-port=2375/tcp --permanent
    $ firewall-cmd --reload
  6. 如果是ECS等云服务器,则需要在安全组中放开2375端口
  7. 能访问到http://192.168.2.195:2375/表示配置成功,注意这里换成你服务的ip

    image.png

编写build.gradle

  1. 引入docker插件(必须)

    buildscript{
        repositories {
            maven { url 'https://maven.aliyun.com/repository/central' }
        }
    }
    plugins {
        ...
        id 'com.bmuschko.docker-remote-api' version '6.7.0'
        id 'com.bmuschko.docker-spring-boot-application' version '6.7.0'
    }

    插件文档地址

  2. 编写构建docker镜像的代码

    docker {
        springBootApplication {
            //待上传到的docker服务
            url = "tcp://192.168.2.195:2375"
            // 基础镜像
            baseImage = 'openjdk:8-alpine'
            //容器暴露的端口,我们的application.yml中配置的服务端口server.port=7000
            ports = [7000]
            //镜像名称
            images = ["it235-order:${version}"]
            //容器启动的参数
            jvmArgs = ['-Dspring.profiles.active=prod', '-Xmx256m']
        }
    }
  3. 执行./gradlew dockerBuildImage任务,我们可以查看到控制台的输出

    image.png

  4. 查看Docker上的镜像是否创建成功

    $ docker images | grep it235
  5. 通过镜像启动服务

    $ docker run -d -p 7001:7000 --name=it2351 it235-order:1.0.0-SNAPSHOT
    
    # 查看是否启动成功
    $ docker ps
    
    # 如果启动失败,需要使用日志查看命令进行查看
    $ docker logs -f -t --tail=200 容器名称或ID
  6. 通过观察build/docker目录发现,插件帮我们生成了Dockerfile

    FROM openjdk:8-alpine
    LABEL maintainer=Ron
    WORKDIR /app
    COPY libs libs/
    COPY resources resources/
    COPY classes classes/
    ENTRYPOINT ["java", "-Dspring.profiles.active=prod", "-Xmx256m", "-cp", "/app/resources:/app/classes:/app/libs/*", "com.it235.it235order.It235OrderApplication"]
    EXPOSE 7000

Dockerfile整合

  1. 编写Dockerfile脚本

    在项目根目录下新增Docker文件夹,并在其中创建Dockerfile文件,内容如下:

    FROM openjdk:8-alpine
    LABEL maintainer=junge
    
    WORKDIR /app
    
    COPY it235-order.jar  ./
    
    ENTRYPOINT ["java", "-Dspring.profiles.active=prod", "-Xmx256m" , "-jar" , "/app/it235-order.jar"]
    EXPOSE 7000
  2. 编写gradle脚本,一定要让jarDockerfile在一个目录下

    // 每次构建都清理一次build/docker目录
    task cleanDocker(type: Delete) {
        delete fileTree("${buildDir}/docker") {
            include '**/*'
        }
    }
    
    //将bootJar拷贝到build/docker目录下,并重命名
    task copyDockerJar(type: Copy, dependsOn: bootJar) {
        group = "docker"
        //拷贝前清理docker目录下所有文件
        dependsOn cleanDocker
        //将jar拷贝到build/docker目录下
        from "${project.rootDir}/Dockerfile/Dockerfile" , bootJar.archiveFile
        into "${project.buildDir}/docker"
        //改名为it235-order.jar,Dockerfile中需要使用
        rename {
            String fileName -> fileName.replace("-${version}", "")
        }
    }
    
    //指定构建镜像时的位置和服务URL
    import com.bmuschko.gradle.docker.tasks.image.*
    task buildImage(type: DockerBuildImage , dependsOn:copyDockerJar) {
        group "docker"
        url = "tcp://192.168.2.194:2375"
        //指定docker目录和Dockerfile文件
        inputDir = file("${project.buildDir}/docker")
        //指定Dockerfile
        dockerFile = project.file("${project.buildDir}/docker/Dockerfile")
        //镜像名称
        images.add("it235-order2:${version}")
    }
  3. 执行任务buildImage,观察Docker镜像列表是否已成功上传
  4. 启动容器

    docker run -d -p 7001:7000 --name=it2351 it235-order:1.0.0-SNAPSHOT
  5. 浏览器访问接口
  6. 该方式有哪些不足之处呢?是不是多个环境切换时没有办法做到很好的兼容,需要编写多个环境的Dockfile,同时如果我想使用start.sh脚本也不能做到

Dockerfile升级版

  1. 使用start.sh启动

    • 在docker目录下添加sh脚本

      #!/bin/bash
      PROFILE_ENV=$1
      java -Dspring.profiles.active=$PROFILE_ENV -Xmx256m -Djava.security.egd=file:/dev/./urandom -jar /app/it235-order.jar

      注意:切不可采用nohup或&号的形式设置为后台启动,程序一定要在前台,否则容器会直接退出

    • 改造Dockerfile

      # 注意此时需要使用/bin/bash,所以需要更换基础镜像,采用对sh支持更好的镜像
      FROM openjdk:8
      
      # sh脚本权限
      
      COPY it235-order.jar ./
      RUN sh -c 'chmod 755 start.sh'
      
      # 更改启动方式
      ENTRYPOINT ["/bin/sh","-c","/app/start.sh ${env}"]
  2. 解决多环境切换的问题

    # 增加全局扩展属性
    project.ext{
        //gradle buildImage -Penv=prod
        if (!project.hasProperty("env")) {
            env = "dev"
        }
        println "run env: ${env}"
    }
    
    
    # 在buildImage中使用buildArgs属性
    buildArgs = ["env" : "${env}"]
    
    # copyJar中,需要把start.sh脚本添加进去

    改造Dockerfile

    # 在workdir下面增加
    # 接收gradle传递的参数
    ARG env
    # 将参数设置到env中去,启动时容器内部能读到镜像中的env数据
    ENV env ${env}

推送到harbor

docker {
    url = 'tcp://192.168.2.194:2375'
    certPath = null
    registryCredentials {
        username = 'admin'
        password = 'Harbor12345'
    }
}
task copyDockerJar(type: Copy){
    group "docker"
    from "${project.rootDir}/docker/Dockerfile" ,"${project.rootDir}/docker/start.sh" , bootJar.archiveFile
    into "${project.buildDir}/docker"
    rename {
        fileName -> fileName.replace("-${version}" , "")
    }
}

import com.bmuschko.gradle.docker.tasks.image.*
task buildImage(type: DockerBuildImage , dependsOn: copyDockerJar){
    group "docker"
    url = "tcp://192.168.2.194:2375"
    images.add("192.168.2.195:8098/it235-${env}/it235-order:${version}")
    buildArgs = ["env" : "${env}"]
}

task tagImage(type: DockerTagImage,dependsOn: buildImage){
    group 'docker'
    tag = "${version}"
    repository = "192.168.2.195:8098/it235-${env}/${project.name}"
    targetImageId buildImage.getImageId()
}

// 移除镜像
task removeImage(type: DockerRemoveImage) {
    group 'docker'
    force = true
    targetImageId buildImage.imageId
}

//推送镜像
task pushImage(type: DockerPushImage , dependsOn: tagImage){
    group 'docker'
    finalizedBy removeImage
    images = buildImage.images
}

注意:如果出现HTTPS问题,请配置daemon.json文件,参照Docker中的视频配置,我这边192.168.2.194的配置如下:

$ cat /etc/docker/daemon.json
{
  "registry-mirrors" : ["http://hub-mirror.c.163.com"],
  "insecure-registries" : ["192.168.2.195:8098"],
  "live-restore": true
}

推送到云容器镜像服务

错误解决

# 问题1 connection time out
1. 镜像体积先缩小,可以FROM openjdk:8-alpine,这个打出来只有150M
2. 分步上传,先push到Docker服务,然后在Docker服务上执行push命令,2步打通之后再合并一起操作
docker push registry.cn-hangzhou.aliyuncs.com/it235-dev/it235-order:1.0.0-SNAPSHOT

# 问题2 Could not push image: denied: requested access to the resource is denied
1. registryCredentials中的用户名密码需要填写阿里云镜像服务的用户名密码
2. images的名称要能够与阿里云提供的对上
配置:images.add("registry.cn-hangzhou.aliyuncs.com/it235-${env}/it235-order:${version}")
阿里云:registry.cn-hangzhou.aliyuncs.com/it235-dev/it235-order:[镜像版本号]
3. docker服务机器上需要登录阿里云镜像仓库
docker login --username=用户名 registry.cn-hangzhou.aliyuncs.com

最终配置

project.ext{
    //gradle buildImage -Penv=prod
    if (!project.hasProperty("env")) {
        env = "dev"
    }
    println "run env: ${env}"
}

docker {
    url = "tcp://192.168.2.194:2375"
    certPath = null
    registryCredentials {
        username = 'xxxx'
        password = '阿里云镜像服务密码'
    }
}

task copyDockerJar(type: Copy){
    group "docker"
    from "${project.rootDir}/docker/Dockerfile" ,"${project.rootDir}/docker/start.sh" , bootJar.archiveFile
    into "${project.buildDir}/docker"
    rename {
        fileName -> fileName.replace("-${version}" , "")
    }
}

import com.bmuschko.gradle.docker.tasks.image.*
task buildImage(type: DockerBuildImage , dependsOn: copyDockerJar){
    group "docker"
    url = "tcp://192.168.2.194:2375"
    images.add("registry.cn-hangzhou.aliyuncs.com/it235-${env}/it235-order:${version}")
    buildArgs = ["env" : "${env}"]
}

task tagImage(type: DockerTagImage) {
    dependsOn buildImage
    repository = "registry.cn-hangzhou.aliyuncs.com/it235-${env}/it235-order"
    tag = "${version}"
    targetImageId buildImage.getImageId()
}

task removeImage(type: DockerRemoveImage) {
    targetImageId buildImage.imageId
    force = true
}

task pushImage(type: DockerPushImage) {
    group "docker"
    //构建
    dependsOn tagImage
    //推完之后,进行删除
    finalizedBy removeImage
    images = buildImage.images
}

总结

学习本篇内容需要先掌握Docker使用,在build.gradle调试的过程中你可能会遇到莫名其妙的坑,需要你有清晰的思路去排查,在后续文章中,我将给大家带来Docker系列的内容。我是君哥,关注我,这里有你意向不到的惊喜

相关文章
|
1天前
|
Prometheus 监控 Cloud Native
构建高效稳定的Docker容器监控体系
【5月更文挑战第13天】在微服务架构和容器化部署日益普及的背景下,对Docker容器的监控变得尤为重要。本文将探讨一种构建高效稳定Docker容器监控体系的方法,通过集成Prometheus和cAdvisor工具,实现对容器资源使用情况、性能指标和运行状态的实时监控。同时,结合Grafana进行数据可视化,为运维人员提供直观的分析界面,以便及时发现和解决潜在问题,保障系统的高可用性和稳定性。
15 6
|
1天前
|
运维 安全 API
构建高效自动化运维体系:Ansible与Docker的协同实践
【5月更文挑战第13天】在现代IT基础设施管理中,自动化运维已成为提升效率、确保一致性和降低人为错误的关键。本文通过深入探讨Ansible和Docker的集成实践,揭示了如何构建一个灵活、可扩展且高效的自动化运维体系。我们将从理论到实践,展示如何利用这两种技术实现自动化部署、管理和扩展应用服务,以及它们如何帮助运维团队应对快速变化的业务需求和复杂的IT环境。
12 1
|
4天前
|
Ubuntu 应用服务中间件 Shell
Docker入门
Docker入门
19 0
|
5天前
|
缓存 关系型数据库 数据库
【Docker 专栏】Docker 与容器化数据库的集成与优化
【5月更文挑战第9天】本文探讨了Docker与容器化数据库集成的优势,如快速部署、环境一致性、资源隔离和可扩展性,并列举了常见容器化数据库(如MySQL、PostgreSQL和MongoDB)。讨论了集成方法、注意事项、优化策略,包括资源调整、缓存优化和监控告警。此外,强调了数据备份、恢复测试及性能评估的重要性。未来,随着技术发展,二者的集成将更紧密,为数据管理带来更多可能性。掌握此技术将应对数字化时代的机遇与挑战。
【Docker 专栏】Docker 与容器化数据库的集成与优化
|
5天前
|
存储 安全 开发者
【Docker 专栏】Docker 与云存储服务的集成
【5月更文挑战第9天】在数字化时代,Docker和云存储服务的结合为企业和开发者提供了强大工具。Docker的高效性、可移植性和隔离性,加上云存储的扩展性、高可靠性和高可用性,通过集成可以实现数据持久化、便捷部署和资源优化。常见的集成包括AWS S3、Azure Blob Storage和Google Cloud Storage。集成时需注意安全、性能和兼容性问题,未来集成将更加紧密和智能化,助力企业创造更大价值。
【Docker 专栏】Docker 与云存储服务的集成
|
6天前
|
运维 安全 Linux
深入理解Docker自定义网络:构建高效的容器网络环境
深入理解Docker自定义网络:构建高效的容器网络环境
|
6天前
|
存储 弹性计算 运维
Docker数据集与自定义镜像:构建高效容器的关键要素
Docker数据集与自定义镜像:构建高效容器的关键要素
|
6天前
|
存储 Ubuntu Linux
Docker 从入门到实践:Docker介绍
Docker 从入门到实践:Docker介绍
|
5天前
|
存储 安全 数据安全/隐私保护
【Docker 专栏】Docker 容器化应用的备份与恢复策略
【5月更文挑战第9天】本文探讨了Docker容器化应用的备份与恢复策略,强调了备份在数据保护、业务连续性和合规要求中的关键作用。内容涵盖备份的重要性、内容及方法,推荐了Docker自带工具和第三方工具如Portainer、Velero。制定了备份策略,包括频率、存储位置和保留期限,并详细阐述了恢复流程及注意事项。文章还提及案例分析和未来发展趋势,强调了随着技术发展,备份与恢复策略将持续演进,以应对数字化时代的挑战。
【Docker 专栏】Docker 容器化应用的备份与恢复策略