Java Docker多级构建-为什么要在Docker中构建?

Java Docker多级构建-为什么要在Docker中构建?,java,docker,maven,Java,Docker,Maven,这里是Docker/java新手。 在我看来: 使用多级构建。例如,您可以使用maven映像 构建Java应用程序,然后重置为tomcat映像并复制 Java工件 我知道我们使用容器化来确保应用程序的运行时环境完全符合需要,但是为什么我们也需要在容器中运行构建呢?如果有一个CI/CD管道 如果需要/何时/何地清理生成缓存 只是再次运行构建 使用新工件创建docker图像? 如果您的构建构件是可移植的,并且您不认为宿主构建环境繁琐,那么按照您描述的方式做绝对没有错。如果你看看周围的Java Doc

这里是Docker/java新手。 在我看来:

使用多级构建。例如,您可以使用maven映像 构建Java应用程序,然后重置为tomcat映像并复制 Java工件

我知道我们使用容器化来确保应用程序的运行时环境完全符合需要,但是为什么我们也需要在容器中运行构建呢?如果有一个CI/CD管道

如果需要/何时/何地清理生成缓存 只是再次运行构建 使用新工件创建docker图像?

如果您的构建构件是可移植的,并且您不认为宿主构建环境繁琐,那么按照您描述的方式做绝对没有错。如果你看看周围的Java Docker问题,几乎所有的问题都有DockerFile,比如

FROM openjdk:8-jre
COPY myapp.jar /
CMD ["java", "-jar", "/myapp.jar"]
在两种情况下,我认为您应该更喜欢多阶段构建,而不是其他备选方案:

您使用的是编译语言,它生成不可移植二进制文件C++、GO、RISE,而不在本地Linux主机上工作;如果您在Docker内部构建应用程序,它将与最终的Docker运行时是相同的操作系统

您的应用程序需要扩展,通常用C编写,需要完整的工具链来构建,而不是运行;例如,Python MySQL接口


如果您的构建系统是相当标准化的,并且生成了一个可移植的工件,那么在主机上构建应用程序并将其复制到映像中就可以了。javajar文件非常适合这种模式;因此,通过WebPACK构建的JavaScript浏览器应用程序。

如果你的构建构件是可移植的,并且你不认为主机构建环境繁重,那么按照你描述的方式做绝对没有错。如果你看看周围的Java Docker问题,几乎所有的问题都有DockerFile,比如

FROM openjdk:8-jre
COPY myapp.jar /
CMD ["java", "-jar", "/myapp.jar"]
在两种情况下,我认为您应该更喜欢多阶段构建,而不是其他备选方案:

您使用的是编译语言,它生成不可移植二进制文件C++、GO、RISE,而不在本地Linux主机上工作;如果您在Docker内部构建应用程序,它将与最终的Docker运行时是相同的操作系统

您的应用程序需要扩展,通常用C编写,需要完整的工具链来构建,而不是运行;例如,Python MySQL接口


如果您的构建系统是相当标准化的,并且生成了一个可移植的工件,那么在主机上构建应用程序并将其复制到映像中就可以了。javajar文件非常适合这种模式;通过Webpack构建的Javascript浏览器应用程序也是如此。

一切如代码是一种理念,它建议我们将所有内容都检查到git中,以便在发生灾难时,比如说一场大流行席卷了你的劳动力。。。,有朝一日,您可以从git repo和一些保存您使用的库的二进制存储库中重建所有内容。所谓“一切”,我指的是源代码、构建系统、所有管道和所有基础设施

在git回购中维护源代码是不够的,因为如果不清楚应该如何构建源代码,那么它就没有什么价值。例如,一个250kloc千行代码的java系统可能看起来像是用java-8编译的,但是可能有一些逻辑只在java-6下工作。您假设它是java-8,因为250kloc的代码太多,无法阅读和理解,所以您构建并运行了它,它在生产中崩溃了,因为您使用了错误的编译器或其他Linux发行版。如果您的整个构建环境也在代码中,并且可以很容易地复制,那么这种情况就不会发生。您只需运行docker image build-tag mycompany/myimage。或者更好的方法是,使用Makefile并运行make,然后您的应用程序将使用原始开发人员打算使用的工具重建自身

当然,我们不需要求助于大流行,这才是一项有用的技术。试图在没有详细说明的情况下构建开源项目是一件让人头疼的事,即使有了说明,也可能永远找不到合适的库和工具,下载它们,并使它们协同工作。如果您对工具集不太了解或根本不了解,这个问题就会加剧。将构建指令放在docker容器中,然后启动—您有一个可构建的项目,使用可测试和可执行的指令集构建它几乎是微不足道的

这种模式非常强大,现在我在docker中构建了所有东西,并包含一个Makefile,以确保docker命令也能按我的预期运行。键入makebuild比相关的docker命令要容易得多。而且,整个工作都与我的项目的源代码一起提交给git,因此构建系统与项目的其余部分一起进行版本控制

我们使用多阶段构建的原因是,这比使用多个docker文件更容易,虽然基本上是一样的,但开销更少,出现错误的可能性也更小 凯斯。存在多阶段生成,因此您可以在生成阶段包含编译器和类似工具,并在最终映像中丢弃它们。其目的是使最终图像尽可能小。这有助于降低存储成本并加快图像在网络上的传输,但对大多数人来说,可能更重要的是,这也减少了图像的攻击面。更少的组件意味着更少的漏洞。如果你不打算在生产中编译任何东西,为什么在你的生产映像中有一个编译器呢?编译器可能存在漏洞。您可能正在修补漏洞,但您是否真的希望修补并向生产部门发布一个新服务,因为您从未在生产部门实际使用过编译器?当然不是。因此,mutli阶段构建通过使用不同的工具和配置创建多个图像、阶段来帮助我们,但只有最后一个被标记并发布;其余的都被丢弃了

这种方法的许多好处之一是,我们只需指定一次构建过程,它就可以在任何地方工作。当与持续集成CI工具集成时,这使我们不必学习如何在该特定工具中构建我们的应用程序,也避免了在CI工具使用与开发人员构建方式不同的方法构建时可能引入的错误。如果您使用容器化构建,则可以保证开发人员和CI平台使用相同的工具以相同的方式完成相同的工作,这有助于避免“在我的机器上工作”类错误。它还加快了管道的开发速度,因为您只需将管道配置为运行docker build或“make”,如前所述。这种方法确保了构建的“可重复性”,或者说,如果做得对,它应该确保这一点——仍然很容易出错


我强烈建议您尽快采用这种方法。这是我最近工作过的所有大公司(主要是欧洲大银行)的法定方法。一旦你习惯了这样做,以前的方法在最好的情况下似乎是狭隘的,在最坏的情况下是错误的。这种方法将为您节省大量时间和精力,并带来更易于维护的高质量系统,尤其是在这些困难时期。

一切都是代码是一种理念,它建议我们将所有内容都检查到git中,以便在发生灾难时,比如说一场大流行会毁掉您的劳动力。。。,有朝一日,您可以从git repo和一些保存您使用的库的二进制存储库中重建所有内容。所谓“一切”,我指的是源代码、构建系统、所有管道和所有基础设施

在git回购中维护源代码是不够的,因为如果不清楚应该如何构建源代码,那么它就没有什么价值。例如,一个250kloc千行代码的java系统可能看起来像是用java-8编译的,但是可能有一些逻辑只在java-6下工作。您假设它是java-8,因为250kloc的代码太多,无法阅读和理解,所以您构建并运行了它,它在生产中崩溃了,因为您使用了错误的编译器或其他Linux发行版。如果您的整个构建环境也在代码中,并且可以很容易地复制,那么这种情况就不会发生。您只需运行docker image build-tag mycompany/myimage。或者更好的方法是,使用Makefile并运行make,然后您的应用程序将使用原始开发人员打算使用的工具重建自身

当然,我们不需要求助于大流行,这才是一项有用的技术。试图在没有详细说明的情况下构建开源项目是一件让人头疼的事,即使有了说明,也可能永远找不到合适的库和工具,下载它们,并使它们协同工作。如果您对工具集不太了解或根本不了解,这个问题就会加剧。将构建指令放在docker容器中,然后启动—您有一个可构建的项目,使用可测试和可执行的指令集构建它几乎是微不足道的

这种模式非常强大,现在我在docker中构建了所有东西,并包含一个Makefile,以确保docker命令也能按我的预期运行。键入makebuild比相关的docker命令要容易得多。而且,整个工作都与我的项目的源代码一起提交给git,因此构建系统与项目的其余部分一起进行版本控制

我们使用多阶段构建的原因是,这比使用多个docker文件更容易,虽然基本上是一样的,但开销更少,出错的机会也更少。存在多阶段生成,因此您可以在生成阶段包含编译器和类似工具,并在最终映像中丢弃它们。其目的是使最终图像尽可能小。这有助于降低存储成本并加快图像在网络上的传输,但可能更重要 对于大多数人来说,这也减少了图像的攻击面。更少的组件意味着更少的漏洞。如果你不打算在生产中编译任何东西,为什么在你的生产映像中有一个编译器呢?编译器可能存在漏洞。您可能正在修补漏洞,但您是否真的希望修补并向生产部门发布一个新服务,因为您从未在生产部门实际使用过编译器?当然不是。因此,mutli阶段构建通过使用不同的工具和配置创建多个图像、阶段来帮助我们,但只有最后一个被标记并发布;其余的都被丢弃了

这种方法的许多好处之一是,我们只需指定一次构建过程,它就可以在任何地方工作。当与持续集成CI工具集成时,这使我们不必学习如何在该特定工具中构建我们的应用程序,也避免了在CI工具使用与开发人员构建方式不同的方法构建时可能引入的错误。如果您使用容器化构建,则可以保证开发人员和CI平台使用相同的工具以相同的方式完成相同的工作,这有助于避免“在我的机器上工作”类错误。它还加快了管道的开发速度,因为您只需将管道配置为运行docker build或“make”,如前所述。这种方法确保了构建的“可重复性”,或者说,如果做得对,它应该确保这一点——仍然很容易出错


我强烈建议您尽快采用这种方法。这是我最近工作过的所有大公司(主要是欧洲大银行)的法定方法。一旦你习惯了这样做,以前的方法在最好的情况下似乎是狭隘的,在最坏的情况下是错误的。这种方法将为您节省大量时间和精力,并带来更高质量、更易于维护的系统,尤其是在当前困难时期。

感谢您提供详细的答案。请您补充一下:当您[正确配置]CI/CD管道时,为什么这种方法更好?我们假设管道配置也在repo中,例如Gitlab-CI。乐意提供帮助。完成。投票表决以供流行病参考;谢谢你的详细回答。请您补充一下:当您[正确配置]CI/CD管道时,为什么这种方法更好?我们假设管道配置也在repo中,例如Gitlab-CI。乐意提供帮助。完成。投票表决以供流行病参考;