使用 Gradle 构建 Docker 镜像

小夏 科技 更新 2024-02-03

基于 Gradle,无需安装 Docker Toolbox,即可生成 Docker 镜像。

Docker是一个开源的基于LXC的轻量级容器管理器,在Docker的帮助下,开发者只需要将应用和依赖的运行环境打包成一个可移植的容器,就可以正常运行,不受语言、框架和底层操作系统的限制,使用Docker可以缩短开发时间、探索时间和部署周期, 这样他们就可以更高效地编码,简化开发环境,消除依赖冲突等,并最终提高开发生产力。

但是,当 Windows 平台使用 Docker 技术时,需要安装和配置额外的 Docker 工具,这不可避免地会花费额外的工作时间,如果想在 Dev 环境中配置一组与生产环境一致的构建环境,则需要花费大量的精力。

为了加快开发构建速度,显著提高开发场景中的开发效率,建议使用 Gradle 进行构建,并使用 Gradle 脚本高效便捷地管理项目中的差异、依赖、编译、打包和部署过程。 本文将重点介绍 Gradle 的使用以及如何使用 Gradle 创建 Docker 镜像,如何利用 Gradle 脚本以及如何从开发人员的角度使用中央 Docker 引擎构建 Docker 镜像。

在传统的构建系统中,会使用 ant 和 m**en 作为构建工具,这里推荐的是 gradle,它不使用 xml 作为配置文件,而是使用基于 groovy 开发的 DSL,因此 gradle 的配置文件比 ant 和 m**en 的配置文件更高效、更简洁。 以下是 Gradle 和 Ant 以及 M**En 之间的主要区别

ANT是最早的构建工具,它的核心是用J**A编写的,使用XML作为构建脚本,因为它基于J**A,允许在任何环境中运行构建。 ANT基于任务链的思想,它定义了任务之间的依赖关系并形成一个序列。 缺点是使用 XML 定义构建脚本会导致脚本臃肿,而 ANT 本身不提供项目构建的指导,导致构建脚本不同,开发人员需要熟悉每个项目的脚本内容,并且不提供 ANT 生态系统中的依赖管理工具。 M**en 意识到 ANTU 的不足,采用标准的项目布局、统一的生命周期和强大的依赖管理,使用“约定胜于配置”的思想,减少构建脚本的配置,提高开发效率和管理秩序,拥有活跃的社区,可以通过合适的插件轻松扩展。 缺点是它有一个默认的结构和生命周期,有很多限制,编写插件扩展很麻烦,并且使用xml作为构建脚本,使构建脚本冗长。 Gradle 兼具 ANT 和 M**EN 的优势,具有 ANT 的强大和灵活性,并继承了 M**EN 的生命周期管理。 它使用基于 Groovy 的 DSL,并提供声明式构建语言,简单明了,使 Gradle 构建脚本更加简洁明了,并使用具有完全可配置性的标准项目布局。 您还可以扩展插件以提供默认的构建生命周期、自定义任务、单独运行任务以及定义任务之间的依赖关系。 Gradle 继承并改进了 m**en 的依赖管理,可以与 m**en 和 ivy 仓库结合使用; 同时,它与 ant 本身兼容,并有效地复用 ant 的任务; 插件实现方式多样,有很强的官方插件库; 从构建级别来看,它支持从 ant 和 m**en 的逐步迁移; 通过包装器实现跨平台的无缝兼容性。 JDK:最新的 J**A8 版本,安装和配置环境变量。 gradle 构建工具:请转到最新的完整版本。 Eclipse:Eclipse IDE for j**a EE Developers 版本,请转到 *** 最新版本。 Gradle 的核心是 Groovy,而 Groovy 的本质是 J**A,所以很明显 Gradle 环境必须依赖 JDK 和 Groovy 库,所以需要先安装 JDK 并配置环境变量。

* gradle-4.5-all,从 Gralde 中选择最新版本,然后解压到指定目录,将 gradle 的 bin 目录添加到 path 变量中,配置环境变量。 检查是否配置了gradle环境变量。 在 cmd 命令行中输入 gradle -v,如果出现类似如下消息的消息,则表示 gradle 环境配置成功。

---gradle 4.5---build time: 2018-01-24 17:04:52 utcrevision: 77d0ec90636f43669dc794ca17ef80dd65457becgroovy: 2.4.12ant: apache ant(tm) version 1.9.9 compiled on february 2 2017jvm: 1.8.0_151 (ibm corporation 2.9)os: windows 7 6.1 amd64
注意:以上信息可能因 Gradle 版本或不同环境而异。

新建一个名为 gradle project 的根目录,以多个子项目为例,所以还需要在 gradle 项目目录下创建一个子目录

gradle_project_util;gradle_project_model;gradle_project_web;
创建内部版本gradle 文件,其中包含以下内容:

清单 1根项目 gradle 配置。

subprojects jcenter } dependencies {}version = '1.0' jar }
创建设置Gradle 文件, 设置gradle 文件用于管理整个项目,声明当前项目中有哪些模块等。 具体如下:

清单 2根项目设置配置。

rootproject.name = "gradle_project"include "gradle_project_util","gradle_project_model","gradle_project_web"
创建完成后,显示如下图:

图1目录结构的示例。

在子项目中创建生成gradle 文件并添加如下内容: 清单 3子项目 gradle 脚本。

apply plugin: 'j**a' repositories dependencies /***project template***/ task createdirs sourcesets*.resources.srcdirs*.each }
在 cmd 命令行上执行 :gradle createdirs 命令行,命令执行后会创建 j**a 项目的标准目录结构。

Gradle 遵循 COC(约定重于配置)理念,默认提供与 M**en 相同的项目结构配置。 一般结构如下:

sub-project root

src main j**a (项目目录)src main resources (project resource directory)src test j**a (test source directory)src test resources (test resource directory) 将 Gradle 项目导入 Eclipse: 在 Eclipse 菜单中选择 file->import->gradle,如下图所示:

图2项目导入示例。

选择下一步后,选择 gradle 项目所在的目录,然后继续选择默认值继续下一步,直到项目结构解析完成,如下图所示:

图3Gradle 项目结构示例。

导入完成后,下图显示了 Eclipse 中 Project Explore 中显示的项目结构,并创建了与 M**en 一致的项目结构。

图4项目探索示例。

基于 gradle 的脚本的本质是 groovy 脚本,当执行一种类型的配置脚本时,会创建一个关联对象,例如,在执行构建脚本脚本时会创建一个项目对象,这个对象实际上是一个 gradle 对象。

下面介绍了 Gradle 的三个主要对象:

gradle object:这是构建初始化时创建的唯一对象,一般不建议修改此默认配置。 设置对象:每个设置gradle(gradle 的设置文件,其约定名称由 settings 定义。gradle) 将转换为设置对象,该对象在初始化阶段执行,对于多项目构建,您必须确保有设置gradle 文件,对于单项目构建设置文件是可选的,但建议您仍然需要配置它。 项目对象:每个生成Gradle 将转换为 Project 对象。 Gradle 的三种不同纪元脚本分别用于以下用途:

初始化脚本 init script(gradle)。

Gradle 对象:初始化脚本 Init 脚本 (Gradle) 与其他类型的 Gradle 脚本类似,因为它们在构建开始之前运行,主要用于为下一个构建脚本做准备。 如果我们需要编写一个初始化脚本,init 脚本,我们可以按照规则将其放置在用户家中gradle。

初始化脚本的 gradle 对象表示 gradle 的调度,我们可以通过调用项目对象的 getgradle() 方法获取 gradle 实例对象。

设置脚本设置脚本(settings)。

Settings 对象:设置实例和设置gradle 文件是一对一的对应关系,用于配置一些项目设置。 此文件通常放在项目的根目录中。

构建脚本(项目)。

在 Gradle 中,每个要编译的项目都是一个项目(每个项目的构建gradle 对应一个项目对象),每个项目都是用一系列任务构建的,其中许多任务默认由 gradle 插件支持。当我们编写 gradle 脚本时,我们大部分时间都是在编写构建脚本,因此项目和脚本对象的属性和方法等 API 非常重要。

每个项目对象和生成Gradle 是一对一的对应关系,一个项目在构建时有如程:

为当前项目创建类型设置的实例。 如果当前项目中有设置gradle 文件,请使用此文件来配置您刚刚创建的 settings 实例。 通过设置实例的配置,在项目层次结构中创建项目对象的实例。 最后,上面创建的项目对象实例执行生成。 对于每个项目gradle 脚本。 作为构建脚本的开发者,你不应该只局限于编写任务动作或配置逻辑,有时候你想在指定的生命周期事件发生时执行一个**,这里你需要了解生命周期事件。 生命周期事件可以发生在指定的生命周期之前、期间或之后,在执行阶段之后发生的生命周期事件被视为完成。 具体实施周期如下图所示

图5Gradle 生命周期图。

初始化阶段:Gradle 支持单项目和多项目构建,在初始化阶段,gradle 决定哪些项目需要添加到构建中,并为这些需要添加到构建中的项目创建项目实例,本质上是执行设置gradple 脚本。

配置阶段:配置阶段决定了整个构建的项目和任务之间的关系,它会建立一个有向图来描述任务之间的相互依赖关系,并解析构建。 添加到构建项目的每个生成gradle 脚本。

执行阶段:执行阶段是生成 gradle 来执行根项目和每个子项目下的自定义任务及其所依赖的任务,以实现最终的构建目标。

正如你所看到的,生命周期实际上可以与上面构建脚本的执行过程相关。

在多项目构造中,需要指定一个树根,树中的每个节点代表一个项目,每个项目对象都指定了一个表示树中位置的路径。 一般做法是确保有设置gradle 文件是在最初创建 gradle 项目时定义的,因此稍后将在构建脚本文件中对其进行扩展。

如前所述,gradle 构建脚本由一系列任务组成,首先要了解如何编写任务

清单 4有操作任务脚本,也没有操作任务脚本。

TaskTask 你好 }没有 Action 的任务,这是一个快捷方式,替换 Dolast,稍后解释 Task Hello <<
在上面的例子中,如果任务没有用“”执行,则任务在脚本的初始化阶段执行(即执行任何任务),如果执行,则会在gradle actiontask之后执行。 因为如果不添加 “”,则会在任务函数返回之前执行闭包,如果添加 “”,则会调用 actiontaskdolast(),所以会等到gradle actiontask执行完毕,这一点必须记住,因为在具体的使用过程中,你会发现同一个任务可能没有按照任务依赖的顺序执行,你应该考虑是否受到这种写入的影响。

通过上面这个任务的基本感觉的例子,可以发现一个构建gradle 文件定义了多个彼此无关的任务,并确定了 gradle 命令后跟的任务名称。 如果我们希望它们之间有依赖关系,先做什么,以后做什么怎么办? 让我们看一下以下内容:

清单 5任务取决于脚本。

task taskx(dependson: 'tasky')$ gradle taskx> task :taskytasky> task :taskxtaskxbuild successful in 0s2 actionable tasks: 2 executed
这是如何使用 dependson 将两个单独的任务相互连接。

您还可以在 Gradle 中使用 Groovy 创建动态任务,如下所示:

清单 6创建动态任务脚本。

4.times }
结果如下:

$ gradle task1> task :task1i'm task number 1build successful in 0s1 actionable task: 1 executed
除了在定义上述任务时指定依赖关系外,还可以通过 API 为任务添加依赖关系,如下所示:

清单 7task 表示依赖脚本。

4.times }task0.dependson task1, task1
结果如下:

$ gradle task0> task :task1i'm task number 1> task :task2i'm task number 2> task :task0i'm task number 0build successful in 0s3 actionable tasks: 3 executed
您还可以通过 API 向任务添加一些新行为,例如:

清单 8任务 dofirst,dolast 脚本。

task hello <$gradle hello> task :hellohello venushello earthhello marshello jupiterbuild successful in 0s1 actionable task: 1 executed
正如你所看到的,dofirst 和 dolast 可以多次执行,“运算符本质上是 dolast。

您还可以使用美元符号作为其他任务的属性,如下所示:

清单 9任务引用脚本。

task hello <$gradle hello> task :hellohello world!greetings from the hello task.build successful in 0s1 actionable task: 1 executed
上面脚本中使用的名称实际上是任务的默认属性,它表示当前任务的名称。

您还可以向任务添加自定义属性,如以下示例所示:

清单 10任务自定义属性脚本。

task task1 task printtaskproperties <<
结果如下:

$ gradle printtaskproperties> task :printtaskpropertiesvaluebuild successful in 0s1 actionable task: 1 executed
此外,gradle 允许您在脚本中定义一个或多个默认任务,如下所示:

清单 11任务自定义任务脚本。

defaulttasks 'clean', 'run'task clean <$gradle> task :cleandefault cleaning!> task :rundefault running!build successful in 0s2 actionable tasks: 2 executed
如前所述,gradle 在构建脚本中定义了一个项目,对于构建脚本中的每个子项目,gradle 都会创建一个要关联的项目对象,在执行构建脚本时,它会配置关联的项目对象; 生成脚本中调用的每个方法和属性都委托给当前 Project 对象。

项目对象提供了一些标准属性,这些属性便于在生成脚本中使用。

有关特定于项目的方法的详细信息,请参阅项目的文档。 对于 J**A 项目,一般需要在构建开始前清理之前的构建结果,启动构建,将构建结果复制到指定目录,将其他需要的配置、脚本等复制到指定目录,以上步骤都是构建 Docker 镜像的准备步骤。 具体如下:

清单 12Gradle 构建脚本。

apply plugin: 'j**a' repositories dependencies task cleandocker(type: delete) /docker") task copybuild(type: copy, dependson: build) -jar" into 'build/docker' } task copyscript(type: copy, dependson: copybuild) /resources/main/script") into project.file("$/docker/script")}
执行完成后,结果如下:

$ gradle copyscriptbuild successful in 1s5 actionable tasks: 5 executed
同时,可以发现在构建$projectdir的项目构建目录下有构建生成的文件、复制的配置文件和脚本文件,如下图所示

图6构建结果图。

在 J**A 项目中,一些常用的 ** 是同时被多个项目引用的,对于这部分代码需要在常用项目中提及,并编译成一个 jar 包并发布到仓库中,这样需要使用 common ** 的项目只需要根据 jar 包信息下载仓库中最新版本的 jar 包即可。

通常,您可以按如下方式添加项目依赖项:

dependencies
在 gradle 中,依赖可以组合成配置,配置就是一系列依赖,通常理解为依赖配置,可以用来声明项目的外部依赖,也可以用来声明项目的发布。 下面我们给出一些 j**a 插件中的常见配置,如下所示:

compile:用于编译对项目源码的依赖; runtime:运行时生成的类所需的依赖项,默认项,包括编译时的依赖项; testcompile:编译测试依赖,默认项,包含运行生成的类所需的依赖和编译源的依赖;testRuntime:运行测试所需的依赖,默认项,包括以上三个依赖; Gradle 可以声明许多依赖项,其中一个是外部依赖项,即当前构建之外的依赖项,通常存储在远程(例如 m**en)或本地存储库中。 下面是外部依赖项的示例:

dependencies
如您所见,引用外部依赖项需要 group、name 和 version 属性。 以上是两种不同的外部依赖书写方式,其中第二种是速记。

根据 j**a 发布 jar 包的规范,一般发布 jar 包还需要附带一个源**,供用户参考和使用。 因此,当它被打包成一个 jar 时,源代码也会被打包,如下**:

清单 13jar 打包脚本。

task sourcejar(type: jar)
此外,您还需要配置上传 jar 包和仓库信息

清单 14jar 发布脚本。

publishing }artifactory defaults }}
这里的配置是:远程仓库的URL地址、用户名、密码等信息,根据上面的gradle脚本,将发布通用项目的**,供其他项目使用。

与过去使用 war 包发布 J**A 应用类似,现在的微服务和 Web 应用大多是使用 docker 镜像发布的,本文开头已经介绍了 docker 镜像发布的便利性,这里就不深入展开了。

在本节中,我们将介绍如何确保每个开发人员发布用于测试的 Docker 镜像与生产环境中发布的镜像一致。 这里介绍中央 Docker 引擎的概念,是为了保证每个开发者都通过中央引擎构建一个 Docker 镜像,而不需要每个开发者在开发环境中配置 Docker Engine,从而消除因 Docker Toolbox 版本不同、操作系统不同而导致的 Docker Build 环境差异。 中央 Docker 引擎的概念如下图所示:

图7中央 Docker 引擎示例图。

集中式构建需要中央 Docker 引擎,配置信息基于 CentOS 73.其他操作系统也可以基于上述概念进行配置。 配置的主要思想是在 Linux Server 上安装 Docker Engine,在 Docker Engine 中配置 2375 TCP 端口,并在防火墙中释放此端口,以便其他服务可以连接远程构建。 具体步骤如下:

使用以下命令安装 docker 和 docker 引擎:

sudo yum install docker-io

sudo yum install docker-engine

安装上述命令后,还需要配置 docker。

修改 lib systemd 系统 docker服务中的以下两行是:

environmentfile=-/etc/sysconfig/docker

execstart=/usr/bin/dockerd $docker_opts

将 etc sysconfig docker 的内容修改为以下值:

docker_opts=’-h tcp: -h unix:///var/run/docker.sock’
防火墙释放端口 2375。

firewall-cmd –zone=public –add-port=2375/tcp –permanent

firewall-cmd –reload

完成上述配置后,中央 Docker 引擎就已设置完毕。

由于主要使用plugin:combmuschko.docker-remote-api 完成对远程 docker 引擎的调用,这里将介绍 gradle 插件。

其实gradle的核心主要是一个框架,所谓gradle就是简单快捷的构建,其实它是由一系列插件支持的,插件增加了新的任务。 Gradle 中一般有两种类型的插件,如下所示:

脚本插件是额外的构建脚本,用于进一步配置构建,通常在构建内部使用。 脚本插件可以从本地文件系统获取,也可以远程获取,如果从文件系统获取,则相对于项目目录,如果远程获取,则由 http url 指定。

二进制插件是实现插件接口的类,并以编程方式为构建提供一些有用的任务。

引用插件需要通过项目完成apply() 方法完成声明申请,同一个插件可以多次应用。 下面是一个示例:

脚本插件应用自:'build.gradle'二进制插件应用插件:'j**a'
插件还可以使用插件 ID,这些 ID 用作给定插件的唯一标识符,并且可以使用缩写字符 ID 进行注册以供以后使用。 例如,下面是一个示例:

由 j**a 插件的 id 引用。 

apply plugin: codecheck

在上一节中,我们谈到了如何使用 Gradle 插件,在本节中,我们将使用 Gradle 插件来构建一个依赖于 plugin:com 的 Docker 镜像bmuschko.docker-remote-api 完成 Docker 镜像的远程构建和发布。

在构建 Docker 镜像之前,需要使用 gradle 脚本完成一系列的准备工作,包括将配置文件和启动脚本复制到指定目录,构建目标 jar 包,然后使用 gradle 插件生成 dockerfile,然后根据 dockerfile 信息进行发布。

配置 Docker 引擎信息。

清单 15Docker 引擎信息配置脚本。

docker else registrycredentials }
上面的脚本会根据是 Windows 平台还是 Linux 平台使用不同的 URL,直接使用 DockerSock、Windows 平台需要通过 TCP 端口连接到 Docker Engine。

准备工作,包括清除以前构建的旧版本以及将编译结果复制到目标目录。

清单 16docker 预准备脚本。

task cleandocker(type: delete) /docker") }task copybuild(type: copy, dependson: build) -jar" into 'build/docker'}task copyscript(type: copy, dependson: copybuild) /resources/main/script") into project.file("$/docker/script")}
生成 dockerfile

清单 17生成 dockerfile 脚本。

task createdockerfile(type: dockerfile, dependson: copyscript) -jar", docker_work_home addfile 'script', "$/script" environmentvariable 'j**a_opts' , j**a_opts environmentvariable 'boot_target' , "$-$jar" runcommand "chmod +x /home/root/script/*.sh" entrypoint("sh", "-c", /home/root /script/startup.sh")}
构建 Docker 镜像、标记、发布

清单 18发布 docker 镜像脚本。

task builddockerimage(type: dockerbuildimage, dependson: createdockerfile) task builddockertagimage(type: dockertagimage, dependson: builddockerimage) repository = docker_image conventionmapping.tag = force = true}task pushimage(type: dockerpushimage, dependson: builddockertagimage) conventionmapping.tag = } artifactorypublish
最后,在 cmd 中运行命令:gradle artifactorypublish 以完成 docker 镜像构建和发布。

对于团队协作环境,与其为每个开发者单独构建一个Docker环境,不如构建一套与生产环境一致的Central Docker Engine环境,这样既方便维护,又保证了开发者生成的Docker镜像与生产环境生成的Docker镜像一致。 以上只是实际项目中的一点个人想法,如果有不足之处,希望读者能够海瀚,如果有,希望读者能够反馈,交流经验,共同进步。

相似文章

    运用智能化技术构建医院后勤服务信息管理系统

    物质医院的智能化建设一直走在行业前列,尤其是当涉及到医院业务相关的系统时,智能化程度比其他系统更强大,作为医院物流链部门,智能化改造的需求正在逐渐增加。目前,医院后勤管理部门作为医院运营的重要支撑系统,在管理层面面临诸多挑战。首先,医院物流链部门面临着越来越大的成本压力。随着医疗技术的飞速发展和患者...

    使用小程序为教育机构构建高效的管理系统

    随着互联网的飞速发展,在线教育也越来越受到关注和普及。为了满足学生和家长的需求,教育机构开发了自己的小程序管理系统。本文将详细介绍如何使用巧拓云平台一键开发自己的教育机构小程序管理系统。.进入乔拓云的背景。首先,在浏览器中搜索乔拓云,登录后台,选择教育系统,点击进入。在这里,您可以看到各种教育小程序...

    职工活动中心如何利用气穹结构建筑打造室内运动场馆?

    在职工活动中心的建设中,如何打造一个舒适实用的室内体育场馆,一直是大家关注的焦点。近年来,作为一种新型的建筑形式,空气膜结构建筑逐渐出现在体育场馆的建设中。今天,我们就来看看如何利用气穹结构建筑在职工活动中心建造室内体育场,以及其背后的优势和要点。 空气膜结构建筑在职工活动中心室内体育场的应用优势。...

    利用智能电表实现旧旧再利用,构建循环生态圈,实现数字化能源管理

    近年来,我国智能电网产业政策密集出台,国家扶持力度不断加大。随着数字智能电网的加速发展,智能电表市场和利润空间正在迅速扩大 根据中国研究院普华研究所的笔文 年中国智能电表市场深度研究报告 显示 国家电网智能电表覆盖率达 累计收款亿户,新增智能电表台,个 电表回收生态系统 随着智能电网建设规划的推进,...

    AI Analytics 构建面向未来的分析智能

    近日,由恒石科技 亚马逊云科技 一线数智联合主办的以 探索SaaS新增长,AI 分析赋能SaaS新价值 为主题的企业数字化成长研讨会第二场在上海成功举办。数十家SaaS企业服务企业CEO CIO等高管齐聚一堂,共同探讨AI新技术如何为SaaS赋能 创造新价值,以及如何通过生态合作降本增效。恒石科技深...