Java 如何使用docker volume在Tomcat中部署war/jar

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/51259892/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-11 00:03:11  来源:igfitidea点击:

How to use docker volume to deploy war / jar in Tomcat

javadockertomcatdocker-compose

提问by Denis Stephanov

Is it possible to deploy some java war or jar file in Tomcat? I looking for a lot of tutorials and the only solution I found is copy project war file into /usr/local/tomcat/webapps/.

是否可以在 Tomcat 中部署一些 java war 或 jar 文件?我寻找了很多教程,我找到的唯一解决方案是将项目 war 文件复制到/usr/local/tomcat/webapps/.

I actually used that solution but I would like to improve my dockerisation. My primary goal is when I run my 2 images (application in tomcat and db image) with docker-compose I want to use my local war file of target folder in tomcat, and, when I build war again after the code changed, that change will be reflected without stopping containers, removing, and rebuilding. Can you help to do that? My attempts failed. I want it just for development purpose.

我实际上使用了该解决方案,但我想改进我的 dockerisation。我的主要目标是当我使用 docker-compose 运行我的 2 个图像(tomcat 和 db 图像中的应用程序)时,我想在 tomcat 中使用目标文件夹的本地 war 文件,并且当我在代码更改后再次构建 war 时,该更改将在不停止容器、移除和重建的情况下反映。你能帮忙做吗?我的尝试失败了。我想要它只是为了开发目的。

Here is my docker-compose.yml

这是我的 docker-compose.yml

version: '3'

services:

  tomcat-service:
    build:
      context: ../
      dockerfile: docker/app/Dockerfile
    volumes:
      - D:\myproj\target\app.war:/usr/local/tomcat/webapps/ROOT.war
    ports:
      - "8080:8080"
    depends_on:
      - "db-service"

  db-service:
    build: ./database
    ports:
      - "5433:5432"

and Dockerfile for that tomcat

和那个 tomcat 的 Dockerfile

FROM tomcat:8.0-jre8
RUN rm -rvf /usr/local/tomcat/webapps/ROOT
COPY ./docker/app/context.xml /usr/local/tomcat/conf/
# with following copy command it works, but when I rebuild war file, I need stop docker-compose and build and run it again .. I want use volume instead of copy war
#COPY ./pnp-web/target/pnp.war /usr/local/tomcat/webapps/ROOT.war
EXPOSE 8080
CMD ["catalina.sh", "run"]

With configuration above applicaton starts, but when I run mvn clean packageapplication is not loaded anymore

上面的配置启动了应用程序,但是当我运行mvn clean package应用程序时不再加载

EDIT

编辑

I checked log of tomcat container and I found this error:

我检查了tomcat容器的日志,发现了这个错误:

tomcat-cont       | 10-Jul-2018 08:20:36.754 INFO [localhost-startStop-1] org.apache.catalina.startup.HostConfig.deployWAR Deploying web application archive /usr/local/tomcat/webapps/ROOT.war
tomcat-cont       | 10-Jul-2018 08:20:36.858 SEVERE [localhost-startStop-1] org.apache.catalina.core.ContainerBase.addChildInternal ContainerBase.addChild: start:
tomcat-cont       |  org.apache.catalina.LifecycleException: Failed to start component [StandardEngine[Catalina].StandardHost[localhost].StandardContext[]]
tomcat-cont       |     at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:162)
tomcat-cont       |     at org.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase.java:755)
tomcat-cont       |     at org.apache.catalina.core.ContainerBase.addChild(ContainerBase.java:731)
tomcat-cont       |     at org.apache.catalina.core.StandardHost.addChild(StandardHost.java:717)
tomcat-cont       |     at org.apache.catalina.startup.HostConfig.deployWAR(HostConfig.java:973)
tomcat-cont       |     at org.apache.catalina.startup.HostConfig$DeployWar.run(HostConfig.java:1850)
tomcat-cont       |     at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
tomcat-cont       |     at java.util.concurrent.FutureTask.run(FutureTask.java:266)
tomcat-cont       |     at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
tomcat-cont       |     at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
tomcat-cont       |     at java.lang.Thread.run(Thread.java:748)
tomcat-cont       | Caused by: org.apache.catalina.LifecycleException: Failed to start component [org.apache.catalina.webresources.StandardRoot@51f50cb1]
tomcat-cont       |     at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:162)
tomcat-cont       |     at org.apache.catalina.core.StandardContext.resourcesStart(StandardContext.java:5016)
tomcat-cont       |     at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5149)
tomcat-cont       |     at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:145)
tomcat-cont       |     ... 10 more
tomcat-cont       | Caused by: org.apache.catalina.LifecycleException: Failed to initialize component [org.apache.catalina.webresources.JarResourceSet@20e48a4a]
tomcat-cont       |     at org.apache.catalina.util.LifecycleBase.init(LifecycleBase.java:107)
tomcat-cont       |     at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:135)
tomcat-cont       |     at org.apache.catalina.webresources.StandardRoot.startInternal(StandardRoot.java:722)
tomcat-cont       |     at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:145)
tomcat-cont       |     ... 13 more
tomcat-cont       | Caused by: java.lang.IllegalArgumentException: java.util.zip.ZipException: error in opening zip file
tomcat-cont       |     at org.apache.catalina.webresources.AbstractSingleArchiveResourceSet.initInternal(AbstractSingleArchiveResourceSet.java:142)
tomcat-cont       |     at org.apache.catalina.util.LifecycleBase.init(LifecycleBase.java:102)
tomcat-cont       |     ... 16 more
tomcat-cont       | Caused by: java.util.zip.ZipException: error in opening zip file
tomcat-cont       |     at java.util.zip.ZipFile.open(Native Method)
tomcat-cont       |     at java.util.zip.ZipFile.<init>(ZipFile.java:225)
tomcat-cont       |     at java.util.zip.ZipFile.<init>(ZipFile.java:155)
tomcat-cont       |     at java.util.jar.JarFile.<init>(JarFile.java:166)
tomcat-cont       |     at java.util.jar.JarFile.<init>(JarFile.java:130)
tomcat-cont       |     at org.apache.tomcat.util.compat.JreCompat.jarFileNewInstance(JreCompat.java:170)
tomcat-cont       |     at org.apache.tomcat.util.compat.JreCompat.jarFileNewInstance(JreCompat.java:155)
tomcat-cont       |     at org.apache.catalina.webresources.AbstractSingleArchiveResourceSet.initInternal(AbstractSingleArchiveResourceSet.java:139)
tomcat-cont       |     ... 17 more
tomcat-cont       |
tomcat-cont       | 10-Jul-2018 08:20:36.859 SEVERE [localhost-startStop-1] org.apache.catalina.startup.HostConfig.deployWAR Error deploying web application archive /usr/local/tomcat/webapps/ROOT.war
tomcat-cont       |  java.lang.IllegalStateException: ContainerBase.addChild: start: org.apache.catalina.LifecycleException: Failed to start component [StandardEngine[Catalina].StandardHost[localhost].Stand
ardContext[]]
tomcat-cont       |     at org.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase.java:759)
tomcat-cont       |     at org.apache.catalina.core.ContainerBase.addChild(ContainerBase.java:731)
tomcat-cont       |     at org.apache.catalina.core.StandardHost.addChild(StandardHost.java:717)
tomcat-cont       |     at org.apache.catalina.startup.HostConfig.deployWAR(HostConfig.java:973)
tomcat-cont       |     at org.apache.catalina.startup.HostConfig$DeployWar.run(HostConfig.java:1850)
tomcat-cont       |     at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
tomcat-cont       |     at java.util.concurrent.FutureTask.run(FutureTask.java:266)
tomcat-cont       |     at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
tomcat-cont       |     at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
tomcat-cont       |     at java.lang.Thread.run(Thread.java:748)
tomcat-cont       |
tomcat-cont       | 10-Jul-2018 08:20:36.860 INFO [localhost-startStop-1] org.apache.catalina.startup.HostConfig.deployWAR Deployment of web application archive /usr/local/tomcat/webapps/ROOT.war has finish
ed in 105 ms

this error happened when I wanted to try restart container when new war is builded.

当我想在构建新战争时尝试重新启动容器时发生此错误。

回答by Mikael Gueck

You have two separate problems:

您有两个不同的问题:

  1. Depending on what command you use, Maven might well remove and recreate your targetdirectory, which will leave the old, removed targetdirectory still opened for the volume mount, by the Docker process. Your old file will be removed, and the new file created into a new directory which Docker has no idea about.

  2. When Maven builds a new WAR ZIP file, your servlet runner might notice the new file mid-build, and try to open a half-baked WAR, which would of course end in failure.

  1. 根据您使用的命令,Maven 可能会删除并重新创建您的target目录,这将使旧的、删除的target目录仍然由 Docker 进程打开以进行卷挂载。您的旧文件将被删除,并将新文件创建到一个 Docker 不知道的新目录中。

  2. 当 Maven 构建一个新的 WAR ZIP 文件时,您的 servlet 运行器可能会在构建过程中注意到这个新文件,并尝试打开一个半生不熟的 WAR,这当然会以失败告终。

I suggest that you create a separate, at least semi-permanent directory, not in the targettree, for the purpose of being mounted by Docker. Create a new Maven profile in your pom.xmlfile, and add a build target which copies your WAR file, after it's done being built, into that new directory, which you mount as Tomcat's webappsinside your container.

我建议您创建一个单独的,至少是半永久的目录,而不是在target树中,以便被 Docker 挂载。在您的pom.xml文件中创建一个新的 Maven 配置文件,并添加一个构建目标,该目标将您的 WAR 文件在构建完成后复制到该新目录中,您将其作为 Tomcat 挂载webapps到您的容器中。

Edit:Here's a solution which doesn't depend on the particular manner in which the virtualization system used by Docker might implement volume file transfer on any particular platform.

编辑:这是一个解决方案,它不依赖于 Docker 使用的虚拟化系统可能在任何特定平台上实现卷文件传输的特定方式。

https://git.mikael.io/mikaelhg/docker-tomcat-war-deploy-poc

https://git.mikael.io/mikaelhg/docker-tomcat-war-deploy-poc

pom.xmlsnippet:

pom.xml片段:

        <plugin>
            <groupId>org.codehaus.cargo</groupId>
            <artifactId>cargo-maven2-plugin</artifactId>
            <version>1.6.8</version>
            <configuration>
                <container>
                    <containerId>tomcat8x</containerId>
                    <type>remote</type>
                </container>
                <configuration>
                    <type>runtime</type>
                    <properties>
                        <cargo.protocol>http</cargo.protocol>
                        <cargo.hostname>localhost</cargo.hostname>
                        <cargo.servlet.port>8080</cargo.servlet.port>
                        <cargo.remote.username>admin</cargo.remote.username>
                        <cargo.remote.password>admin</cargo.remote.password>
                    </properties>
                </configuration>
                <deployer>
                    <type>remote</type>
                </deployer>
                <deployables>
                    <deployable>
                        <groupId>${project.groupId}</groupId>
                        <artifactId>${project.artifactId}</artifactId>
                        <type>${project.packaging}</type>
                        <properties>
                            <context>/app</context>
                        </properties>
                    </deployable>
                </deployables>
            </configuration>
        </plugin>

docker-compose.ymlsnippet:

docker-compose.yml片段:

tomcat:
  image: tomcat:8
  volumes:
    - ./tomcat-users.xml:/usr/local/tomcat/conf/tomcat-users.xml
    - ./manager-context.xml:/usr/local/tomcat/webapps/manager/META-INF/context.xml
  ports:
    - "8080:8080"
  depends_on:
    - db

tomcat-users.xml:

tomcat-users.xml

<?xml version="1.0" encoding="UTF-8"?>
<tomcat-users>
    <role rolename="manager-gui"/>
    <role rolename="manager-script"/>
    <user username="admin" password="admin" roles="manager-gui,manager-script"/>
</tomcat-users>

manager-context.xml:

manager-context.xml

<?xml version="1.0" encoding="UTF-8"?>
<Context antiResourceLocking="false" privileged="true">
    <Manager sessionAttributeValueClassNameFilter="java\.lang\.(?:Boolean|Integer|Long|Number|String)|org\.apache\.catalina\.filters\.CsrfPreventionFilter$LruCache(?:$1)?|java\.util\.(?:Linked)?HashMap"/>
</Context>

Then:

然后:

mvn package

mvn cargo:redeploy

Edit 2:As a response to "... is possible to do that without anz additional plugin?" in the comments:

编辑 2:作为对“......可以在没有 anz 附加插件的情况下做到这一点的回应?” 在评论中:

Yes. If you:

是的。如果你:

  1. Are running Windows on the host, and a Tomcat Docker image inside a virtual machine.

  2. Want to accomplish this through the use of volumes and no extra plugins.

  1. 在主机上运行 Windows,在虚拟机中运行 Tomcat Docker 映像。

  2. 想要通过使用卷而不使用额外的插件来实现这一点。

... you can go about it like this:

...你可以这样做:

  1. Mount, for example, C:/example/warsto the Docker containers' /tmp/example/wars.

  2. Run mvn package.

  3. Copy the WAR file, say with a script which does the whole thing, from the build to the deploy, to the directory C:/example/wars. We are only taking this step because you might run mvn cleanwhich will remove the targetdirectory, and if you've mounted it directly, Docker might not notice the new targetdirectory created by mvn.

  4. Look up your container name with docker ps.

  5. Run the command, again from your deploy script, docker exec $CONTAINER mv /tmp/example/wars/*.war /usr/local/tomcat/webapps/which will copy, inside the Docker container, inside the virtual machine, the complete, non-broken WAR ZIP file into the deployment directory.

  1. 例如,挂载C:/example/wars到 Docker 容器的/tmp/example/wars.

  2. 运行mvn package

  3. 将 WAR 文件复制到目录C:/example/wars. 我们之所以采取这一步,是因为您可能运行mvn cleanwhich 会删除target目录,如果您直接挂载它,Docker 可能不会注意到targetmvn.

  4. 使用 查找您的容器名称docker ps

  5. 再次从您的部署脚本运行该命令,docker exec $CONTAINER mv /tmp/example/wars/*.war /usr/local/tomcat/webapps/它将在 Docker 容器内、虚拟机内将完整的、未损坏的 WAR ZIP 文件复制到部署目录中。

回答by Victor

Your problem seems simpler than it looks. First of all, yes you can do that, a pretty good and production grade example here: https://hub.docker.com/r/esystemstech/liferay

你的问题看起来比看起来简单。首先,是的,您可以这样做,这里是一个非常好的生产级示例:https: //hub.docker.com/r/esystemstech/liferay

About your docker file, this line does nothing for you, besides hiding the files under a layer:

关于您的 docker 文件,除了将文件隐藏在层下之外,这一行对您没有任何作用:

RUN rm -rvf /usr/local/tomcat/webapps/ROOT

which means this is not even saving space. Now, consider mounting the folder as your volume, instead of the file. You can also leave the context inside the file. and finally just check if the timing is correct, I mean, if you are rebooting after the packing operation. I am saying that because if:

这意味着这甚至没有节省空间。现在,考虑将文件夹安装为您的卷,而不是文件。您还可以将上下文保留在文件中。最后只是检查时间是否正确,我的意思是,如果您在打包操作后重新启动。我这么说是因为如果:

Caused by: java.util.zip.ZipException: error in opening zip file

For what I am seeing in your examples, you do not even need a Dockerfile, you can replace the buildwith an imagein your docker compose file, and just mount the volume. Unless you are doing extra stuff inside the docker file.

对于我在你的例子我看到,你甚至不需要一个Dockerfile,可以更换buildimage在你的搬运工撰写的文件,刚刚安装卷。除非你在 docker 文件中做额外的事情。

回答by JoeG

While this does NOT answer your question exactly, there is an alternative to consider that does not require you to have a different development configuration than for test or production.

虽然这不能完全回答您的问题,但有一种替代方案可供考虑,它不需要您拥有与测试或生产不同的开发配置。

Just build your war file locally and then docker cpit:

只需在本地构建您的战争文件,然后docker cp它:

docker cp D:\myproj\target\app.war My_Tomcat_Container:/usr/local/tomcat/webapps/ROOT.war