Docker多阶段构建-为什么要在Docker中构建?

时间:2020-04-02 20:23:18

标签: java docker maven

Docker / java新手在这里。 在this doc中,我看到:

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

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

的CI / CD管道还不够吗?
  • 在需要时/在需要的地方清理构建缓存
  • 再次运行构建
  • 使用新的工件创建docker映像吗?

2 个答案:

答案 0 :(得分:2)

“一切都作为代码”是一种哲学,建议我们将所有内容都检入git,以便在发生灾难时(例如,一场大流行消灭了您的劳动力...),您有一天可以从git中重建所有内容repo和一些二进制库,其中包含您使用的库。 “一切”是指源代码,构建系统,所有管道和所有基础结构。

仅在git repo中维护源代码是不够的,因为如果不清楚应如何构建,那么它就没有什么价值。例如,一个250kloc(一千行代码)的Java系统可能看起来将与Java-8一起编译,但是可能存在一些逻辑仅在Java-6下有效。您假设它是java-8,因为250kloc太多的代码难以阅读和理解,所以您构建并运行它会因为使用了错误的编译器或其他Linux发行版而在生产中崩溃。如果您的整个构建环境也都在代码中并且可以轻松复制,则不会发生这种情况。您可以只运行docker image build --tag mycompany/myimage .(或者甚至更好,使用Makefile并只运行make),然后您的应用将使用原始开发人员想要的工具重新构建自己。

当然,我们不需要大流行这种疾病就可以成为一种有用的技术。尝试在没有详细说明的情况下构建开源项目是一种PitA(痛苦中的痛苦),即使有说明,也可能要花很长时间才能找到合适的库和工具,将它们全部下载,并使它们协同工作。如果您对工具集不太了解(或根本不了解),则会加剧此问题。将构建指令放入docker容器中并繁荣起来-您拥有一个可构建项目,几乎可以用可测试和可执行的指令集进行构建。

这种模式是如此强大,以至于如今,我在docker中构建所有内容,并包含一个Makefile以确保即使docker命令也可以按照我的预期运行。键入make build比相关的docker命令要容易得多。并且,整个过程都与我的项目的源代码一起提交给git,以便将构建系统与项目的其余部分一起进行版本控制。

我们使用多阶段构建的原因是,与拥有多个docker文件相比,它更容易,尽管它基本上是同一件事,但开销更少,出错的机会更少。存在多阶段构建,因此您可以在构建阶段包括编译器和类似工具,并将其丢弃在最终映像中。目的是使最终图像尽可能小。这有助于降低存储成本并加快图像在网络上的传输,但对于大多数人而言,也许更重要的是,这还可以减少图像的受攻击面。更少的组件意味着更少的漏洞。如果您永远不会在生产中编译任何东西,为什么要在生产映像中包含编译器?编译器可能存在漏洞。您可能正在修补漏洞,但您是否真的想由于从未在生产中实际使用过的编译器而将其修补并发布到生产中?当然不是。因此,多阶段构建可以通过使用不同的工具和配置创建多个图像,阶段来帮助我们,但是只有最后一个会被标记并发布。其余的都被丢弃。

此方法的许多好处之一是,我们只需指定一次构建过程,即可在任何地方使用。与持续集成(CI)工具集成时,这使我们不必学习如何在该特定工具中构建应用程序,并且使我们免于因CI工具使用不同的开发人员构建方式构建事物而可能引入的错误。东西。如果使用容器化的版本,则可以确保开发人员和CI平台使用相同的工具以相同的方式执行相同的操作,这有助于避免出现“在我的计算机上运行”的错误。这还可以加快管道的开发速度,因为您只需配置管道即可运行docker build(或如前所述,使用“ make”)。此方法可确保构建的“可重复性”(或者,如果操作正确,则应确保此操作-仍然很容易出错)。

我强烈建议您尽快采用这种方法。这是我最近在所有大型公司(主要是欧洲大型银行)中使用的强制性方法。一旦您习惯了这一点,以前的方法充其量似乎是狭at的,而最坏的情况就是错误的。这种方法将节省您大量的时间和精力,并导致易于维护的更高质量的系统,尤其是在这些困难时期。

答案 1 :(得分:1)

如果您的构建工件是可移植的,并且您不认为主机构建环境很繁琐,那么按照您的描述进行操作绝对没有错。如果您围绕SO考察Java Docker问题,几乎所有问题都具有Dockerfile之类的

FROM openjdk:8-jre
COPY myapp.jar /
CMD ["java", "-jar", "/myapp.jar"]

确实有两种情况,我认为您应该比其他选择更喜欢多阶段构建:

  1. 您正在使用一种会生成不可移植的二进制文件(C ++,Go,Rust)的编译语言,并且无法在本机Linux主机上工作;如果您在Docker内部构建应用程序,它将与最终的Docker运行时使用相同的操作系统。

  2. 您的应用程序需要扩展,通常使用C语言编写,这些扩展需要完整的工具链来构建,但不能运行;例如Python MySQL接口。

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