Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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/.

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.

Here is my 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

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 package application is not loaded anymore

EDIT

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

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.

like image 662
Denis Stephanov Avatar asked Jul 10 '18 07:07

Denis Stephanov


People also ask

Can we deploy jar file in Docker?

This Dockerfile is very simple, but it is all you need to run a Spring Boot app with no frills: just Java and a JAR file. The build creates a spring user and a spring group to run the application. It is then copied (by the COPY command) the project JAR file into the container as app.

How can I deploy WAR file in Tomcat?

Perhaps the simplest way to deploy a WAR file to Tomcat is to copy the file to Tomcat's webapps directory. Copy and paste WAR files into Tomcat's webapps directory to deploy them. Tomcat monitors this webapps directory for changes, and if it finds a new file there, it will attempt to deploy it.

How do I run Tomcat in Docker?

option -p has argument hostPort:containerPort so in your case port 8080 (inside your docker image) is mapped to port 8888 on the host (your windows box). This means you should be able to find the tomcat on your Windows box by appending :8888 to your URL, just like you did with curl.


3 Answers

You have two separate problems:

  1. Depending on what command you use, Maven might well remove and recreate your target directory, which will leave the old, removed target directory 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.

I suggest that you create a separate, at least semi-permanent directory, not in the target tree, for the purpose of being mounted by Docker. Create a new Maven profile in your pom.xml file, 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 webapps inside your container.

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.

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

pom.xml snippet:

        <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.yml snippet:

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:

<?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:

<?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:

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.

... you can go about it like this:

  1. Mount, for example, C:/example/wars to 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 clean which will remove the target directory, and if you've mounted it directly, Docker might not notice the new target directory 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.

like image 83
Mikael Gueck Avatar answered Sep 22 '22 21:09

Mikael Gueck


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 cp it:

docker cp D:\myproj\target\app.war My_Tomcat_Container:/usr/local/tomcat/webapps/ROOT.war
like image 37
JoeG Avatar answered Sep 20 '22 21:09

JoeG


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

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

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 build with an image in your docker compose file, and just mount the volume. Unless you are doing extra stuff inside the docker file.

like image 38
Victor Avatar answered Sep 21 '22 21:09

Victor