【问题标题】:Using Docker in development for Java EE applications在 Java EE 应用程序开发中使用 Docker
【发布时间】:2015-02-05 04:09:53
【问题描述】:

我会加 300 分作为赏金

我最近开始仔细研究 Docker,以及如何使用它来更快地让团队的新成员在开发环境中运行,以及将新版本的软件交付到生产环境中。

我有一些关于应该如何以及在什么阶段将 Java EE 应用程序添加到容器的问题。在我看来,有多种方法可以做到这一点。

这是 Docker 之前的典型工作流程(在我的团队中):

  1. 开发人员编写代码
  2. 开发人员使用 Maven 构建代码并生成 WAR
  3. 开发人员在 JBoss 管理控制台/或使用 Maven 插件上传 WAR

现在,在 Docker 出现之后,我有点困惑是否应该创建我需要的镜像并配置它们,以便在运行 JBoss Wildfly 容器时剩下要做的就是通过管理控制台部署应用程序在网上。还是应该在每次在 Maven 中构建应用程序时创建一个新容器,并使用 Dockerfile 中的ADD 命令添加它,然后在启动后只运行容器而不部署到它?

在生产中,我猜最后一种方法是它所偏爱的?如果我错了,请纠正我。 但是在开发中应该怎么做呢?还有其他工作流程吗?

【问题讨论】:

    标签: java maven jakarta-ee jboss docker


    【解决方案1】:

    我已经在 Glassfish 中广泛使用 Docker 已经很长时间了,不久前还写了一篇关于这个主题的博客 here

    它是 JavaEE 开发的绝佳工具。

    对于您的生产映像,我更喜欢将所有内容捆绑在一起,构建静态基础映像并在新的 WAR 中分层。我喜欢使用 CI 服务器来完成工作,并为生产分支进行 CI 配置,它将获取一个基础,在发布版本中分层,然后发布工件。通常,我们手动部署到生产环境中,但如果您真的想变得花哨,您甚至可以通过将 CI 服务器部署到生产环境中并使用代理服务器来确保新会话获得更新版本来实现自动化。

    在开发过程中,我喜欢在签入代码之前在本地运行任何依赖容器的程序(例如 Arquillian 集成测试)时采用相同的方法。这使环境尽可能接近生产环境,我认为这在测试方面很重要。这是我反对使用嵌入式容器进行测试但部署到非嵌入式容器等方法的一个重要原因。我见过很多测试将在嵌入式环境中通过而在生产/非嵌入式环境中失败的情况。

    在开发/部署/手动测试周期中,在提交代码之前,我认为部署到容器(它是基础映像的一部分)的方法在开发速度方面更具成本效益。循环与每次都在您的 WAR 中构建。如果您的开发环境使用 JRebel 或 XRebel 之类的工具,您可以在其中热部署代码并简单地刷新浏览器以查看更改,这也是一种更好的方法。

    【讨论】:

    • 谢谢。因此,您要说的是,每个开发人员都将拥有一个未嵌入应用程序的环境,而在提交代码之后,是进行 QA、用户测试、生产等的时候了,您可以通过将应用程序添加到图像中来将应用程序捆绑到容器中以 CI 服务器为例。如何将应用程序添加到图像中?您是否从 CI 服务器上的文件夹中复制它并使用 ADD 命令?
    【解决方案2】:

    通常使用 Docker 部署任何东西的方式是在平台基础映像之上生成一个新映像。这样你就可以遵循 Docker 依赖捆绑的理念。

    就 Maven 而言,您可以生成一个 tarball 程序集(假设它称为 jars.tar),然后在 Dockerfile 中调用 ADD jars.tar /app/lib。你也可以实现一个 Maven 插件来生成一个 Dockerfile。

    这是当今 Docker 最明智的方法,其他方法,例如构建映像 FROM scratch 不太适用于 Java 应用程序。

    另见Java JVM on Docker/CoreOS

    【讨论】:

      【解决方案3】:

      您可能想看看rhuss/docker-maven-plugin。它允许无缝集成使用 docker 作为部署单元:

      • 使用标准 Maven 程序集描述符来构建带有 docker:build 的映像,这样您就可以轻松地生成 WAR 文件或将您的微服务添加到 Docker 映像中。
      • 您可以使用docker:push推送创建的图像
      • 使用docker:startdocker:stop,您可以在单元测试期间使用您的图像。

      这个插件附带了一个全面的文档,如果有任何未解决的问题,请打开一个问题。

      您可能已经注意到,我是这个插件的作者 ;-)。坦率地说,还有其他 docker-maven-plugins,它们的重点略有不同。对于一个简单的检查,您可以查看shootout-docker-maven,它提供了四个最活跃的 maven-docker-plugins 的示例配置。

      然后,工作流只是将工件边界从 WAR/EAR 文件转移到 Docker 映像。 mvn docker:push 将它们移动到 Docker 注册表中,在持续交付管道中使用的各个测试阶段从那里提取它。

      【讨论】:

        【解决方案4】:

        Arun Gupta 关于使用 Docker 设置 JRebel 的博文在这里可能会很方便:http://blog.arungupta.me/configure-jrebel-docker-containers/

        【讨论】:

          【解决方案5】:

          我尝试了一个类似的场景来使用 docker 来运行我的应用程序。在我的情况下,我想用 tomcat 启动 docker 来运行战争。然后在 maven 的集成测试阶段,在 docker 上启动 cucumber/phantomjs 集成测试。 示例实现记录在https://github.com/abroer/cucumber-integration-test。当测试成功时,您可以扩展此示例以将 docker 映像推送到您的私有仓库。推送的镜像可以在从开发到生产的任何环境中使用。

          【讨论】:

            【解决方案6】:

            使用最新版本的 Docker,您可以使用 Docker Links、Docker Volume 和 Docker Compose 轻松实现这一目标。来自 Docker 站点的有关这些工具的更多信息。

            回到您提到的工作流程:对于任何典型的 Java EE 应用程序,都需要一个应用程序服务器和一个数据库服务器。由于您在帖子中没有提及如何设置数据库,因此我假设您的开发环境将为每个开发人员提供单独的数据库服务器。

            考虑到所有这些,我可以建议以下工作流程:

            • 从官方镜像构建基础 Wildfly 应用服务器。您可以通过以下方式实现:“docker pull”命令
            • 运行基础应用服务器:

            docker run -d -it -p 8080:8080 -p 9990:9990 --name baseWildfly jboss/wildfly

            应用程序服务器现在正在运行,您需要对其进行配置以连接到您的数据库服务器,并在必要时配置数据源设置和其他配置以启动您的 Java EE 应用程序。 为此,您需要登录到 Jboss 容器的 bash 终端:

            docker exec -i -t baseWildfly /bin/bash/

            你现在在集装箱的终端。您可以像在任何 linux 环境中一样配置应用程序服务器。

            您可以通过手动将 WAR 文件部署到 Wildfly 来测试配置。正如您所说,这可以使用管理控制台、maven 插件或 ADD 命令轻松完成。我通常用管理控制台来做这件事,只是为了快速测试。当您验证配置工作时,您可以删除 WAR 文件并创建容器的快照:

            docker commit --change "添加基本设置和配置" baseWildfly yourRepository:tag

            您现在可以将创建的图像推送到您的私有存储库并与您的开发团队共享。他们现在可以拉取镜像并立即运行应用服务器进行部署。

            我们不想使用管理控制台为每个 Maven 构建部署 WAR 文件,因为这太麻烦了,所以下一个任务是使用 Docker Volume 自动化它。

            假设您已配置 Maven 将 WAR 文件构建到“../your_project/deployments/”,您可以将其链接到 Jboss 容器的部署目录,如下所示:

            docker run -d -p 8080:8080 -v ../your_project/deployments:/opt/jboss/wildfly/standalone/deployments

            现在,每次您使用 Maven 重建应用程序时,应用程序服务器都会扫描更改并重新部署您的 WAR 文件。

            给每个开发者单独的数据库服务器也是很成问题的,因为他们必须在容器中自己配置它,因为他们可能有不同的设置(例如数据库的 url、用户名、密码等)。所以,最终将其 dockerize 是件好事。

            假设您使用 Postgres 作为您的数据库服务器,您可以从 postgres 官方存储库中提取它。准备好镜像后,就可以运行数据库服务器了:

            docker run -d -p 5432:5432 -t --name postgresDB postgres

            或使用链接的“数据”目录运行数据库服务器:

            docker run -d -p 5432:5432 -v ../your_postgres/data:/var/lib/postgresql -t --name postgresDB postgres

            第一个命令会将您的数据保存在容器中,而后一个命令会将您的数据保存在主机环境中。

            现在您可以将数据库容器与 Wildfly 链接:

            docker run -d -p 8080:8080 --link postgresDB:database -t baseWildfly

            以下是链接的输出:

            现在您可以为开发人员团队中的所有成员提供相同的环境,他们可以用最少的设置开始编码。

            生产环境也可以使用相同的基础镜像,所以每当你想发布新版本时,你只需要将WAR文件复制到主机的“your_deployment”文件夹中。

            将应用服务器和数据库服务器 docker 化的好处是,您可以在未来轻松地将其集群化以扩展它或应用高可用性。

            【讨论】:

              【解决方案7】:

              对于我当前的部署过程,我使用 glassfish 和这个技巧,效果非常好。

              <plugin>
                          <groupId>org.codehaus.mojo</groupId>
                          <artifactId>exec-maven-plugin</artifactId>
                          <version>${plugin.exec.version}</version>
                          <executions>
                              <execution>
                                  <id>docker</id>
                                  <phase>package</phase>
                                  <goals>
                                      <goal>exec</goal>
                                  </goals>
                              </execution>
                          </executions>
                          <configuration>
                              <executable>docker</executable>
                              <arguments>
                                  <argument>cp</argument>
                                  <argument>${project.build.directory}/${project.build.finalName}</argument>
                                  <argument>glassfish:/glassfish4/glassfish/domains/domain1/autodeploy</argument>
                              </arguments>
                          </configuration>
                      </plugin>
              

              一旦你运行:mvn clean package,容器就会启动并开始部署最新的战争。

              【讨论】: