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月前
|
Kubernetes Cloud Native Docker
云原生时代的容器化实践:Docker和Kubernetes入门
【10月更文挑战第37天】在数字化转型的浪潮中,云原生技术成为企业提升敏捷性和效率的关键。本篇文章将引导读者了解如何利用Docker进行容器化打包及部署,以及Kubernetes集群管理的基础操作,帮助初学者快速入门云原生的世界。通过实际案例分析,我们将深入探讨这些技术在现代IT架构中的应用与影响。
99 2
|
7天前
|
人工智能 数据可视化 JavaScript
NodeTool:AI 工作流可视化构建器,通过拖放节点设计复杂的工作流,集成 OpenAI 等多个平台
NodeTool 是一个开源的 AI 工作流可视化构建器,通过拖放节点的方式设计复杂的工作流,无需编码即可快速原型设计和测试。它支持本地 GPU 运行 AI 模型,并与 Hugging Face、OpenAI 等平台集成,提供模型访问能力。
59 14
NodeTool:AI 工作流可视化构建器,通过拖放节点设计复杂的工作流,集成 OpenAI 等多个平台
|
9天前
|
Ubuntu 应用服务中间件 nginx
docker入门-快速学会docker
本文介绍了Docker的基本概念,包括镜像、容器、tar文件、Dockerfile和仓库,并通过实际操作演示了如何使用Docker。从拉取Nginx镜像、运行容器、修改容器内容、保存容器为新镜像,到使用Dockerfile构建自定义镜像,最后讲解了如何保存和恢复镜像。文中还推荐了一个在线实践平台Play with Docker,方便读者快速上手Docker。
44 4
docker入门-快速学会docker
|
14天前
|
DataWorks 数据挖掘 大数据
方案实践测评 | DataWorks集成Hologres构建一站式高性能的OLAP数据分析
DataWorks在任务开发便捷性、任务运行速度、产品使用门槛等方面都表现出色。在数据处理场景方面仍有改进和扩展的空间,通过引入更多的智能技术、扩展数据源支持、优化任务调度和可视化功能以及提升团队协作效率,DataWorks将能够为企业提供更全面、更高效的数据处理解决方案。
|
29天前
|
机器学习/深度学习 算法 Python
随机森林算法是一种强大的集成学习方法,通过构建多个决策树并综合其结果进行预测。
随机森林算法是一种强大的集成学习方法,通过构建多个决策树并综合其结果进行预测。本文详细介绍了随机森林的工作原理、性能优势、影响因素及调优方法,并提供了Python实现示例。适用于分类、回归及特征选择等多种应用场景。
56 7
|
29天前
|
关系型数据库 MySQL PHP
PHP与MySQL的无缝集成:构建动态网站的艺术####
本文将深入探讨PHP与MySQL如何携手合作,为开发者提供一套强大的工具集,以构建高效、动态且用户友好的网站。不同于传统的摘要概述,本文将以一个生动的案例引入,逐步揭示两者结合的魅力所在,最终展示如何通过简单几步实现数据驱动的Web应用开发。 ####
|
28天前
|
消息中间件 Java Kafka
Spring Boot 与 Apache Kafka 集成详解:构建高效消息驱动应用
Spring Boot 与 Apache Kafka 集成详解:构建高效消息驱动应用
43 1
|
1月前
|
运维 Kubernetes Cloud Native
云原生技术入门:Kubernetes和Docker的协同工作
【10月更文挑战第43天】在云计算时代,云原生技术成为推动现代软件部署和运行的关键力量。本篇文章将带你了解云原生的基本概念,重点探讨Kubernetes和Docker如何协同工作以支持容器化应用的生命周期管理。通过实际代码示例,我们将展示如何在Kubernetes集群中部署和管理Docker容器,从而为初学者提供一条清晰的学习路径。
|
1月前
|
Cloud Native 持续交付 Docker
Docker容器化技术:从入门到实践
Docker容器化技术:从入门到实践
|
1月前
|
运维 持续交付 虚拟化
docker入门详解!!!
本文介绍了容器技术的发展历程,从物理机到虚拟化再到容器化,重点讲解了Docker的诞生及其优势。Docker通过轻量级的容器技术,实现了资源的高效利用、快速启动、环境一致性、持续交付和部署等优点。文章还详细解析了Docker的架构和工作原理,包括Docker Daemon、REST接口、Docker Client等组件,以及容器与虚拟机的差异。
84 2