Deploy .war on tomcat Docker container

I’m trying to automatize the process of deploying tomcat webapps’ .war on docker containers. To this goal, I devleoped a sample spring boot application to use as a test. Since my final goal is to apply the process to old existing war applications, I configured the application to produce a war file instead of a jar with tomcat embedded.

The sample application is just a sample Spring MVC application with a /greeting?id=<id> endpoint that just returns a page with a “Hello ” message, where is a string read from a table on a mysql db. The application is deployed on a Docker container based on the base tomcat image, while the database is deployed on a separate mysql container.

  • How to open a shell while/after running a Docker container without overriding existing CMD?
  • How to make changes to httpd.conf of apache running inside DOCKER container and restart apache
  • Dockerfile: create ENV variable that a USER can see?
  • How does one remove an image in Docker?
  • How to expose a Docker network to the host machine?
  • Installing MySQL in Docker fails with error message “Can't connect to local MySQL server through socket”
  • When launching the two containers via a docker-compose script, the deployment seems to have success: The two containers are running, the tomcat instance is active, no error message is visible in cotainer logs, and I can see the webapp deployed and marked as running in the tomcat manager interface at http://<IP>:8082/manager/html. The problem is that at http://<IP>:8082/myapp-sample-webapp/greeting?id=<id> I get a 404 error. I tested putting a test.html static file inside the web app, and this is visible at http://<IP>:8082/myapp-sample-webapp/test.html.

    Note also that if I configure the application as a tomcat-embedded jar application (of course changing also Dockerfile configuration accordingly) I can access the /greeting?id=<id> endpoint without problems.

    A side problem is that I cannot see any application generated log (neither on the docker container’s log with docker logs ..., nor in the /usr/local/tomcat/logs/myapp-sample-webapp.log log file that I configure for the app logging (see application.properties below), and this is preventing me to check whether the problems are somehow related to failing the connection to the mysql database.

    This is the pom.xml file for the application is the following:

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
    
        <groupId>eu.myapp</groupId>
        <artifactId>myapp-sample-webapp</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <packaging>war</packaging>
    
        <name>myapp-sample-webapp</name>
        <description>Project created for testing Docker packaging of a .war web application</description>
    
        <parent>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>1.4.0.RELEASE</version>
            <relativePath /> <!-- lookup parent from repository -->
        </parent>
    
        <properties>
            <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
            <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
            <java.version>1.8</java.version>
            <docker.image.prefix>myapp-h2020</docker.image.prefix>
        </properties>
    
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-data-jpa</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <scope>runtime</scope>
            </dependency>
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <version>1.16.10</version>
                <scope>provided</scope>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-data-rest</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-thymeleaf</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-tomcat</artifactId>
                <scope>provided</scope>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-devtools</artifactId>
                <optional>true</optional>
            </dependency>
        </dependencies>
    
        <build>
            <finalName>${project.artifactId}</finalName>
            <plugins>
                <plugin>
                    <groupId>org.codehaus.mojo</groupId>
                    <artifactId>exec-maven-plugin</artifactId>
                    <version>1.2.1</version>
                    <configuration>
                        <mainClass>${main.class}</mainClass>
                    </configuration>
                </plugin>
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                </plugin>
                <plugin>
                    <groupId>com.spotify</groupId>
                    <artifactId>docker-maven-plugin</artifactId>
                    <version>0.2.3</version>
                    <configuration>
                        <imageName>${docker.image.prefix}/${project.artifactId}:${project.version}</imageName>
                        <dockerDirectory>src/main/docker</dockerDirectory>
                        <resources>
                            <resource>
                                <targetPath>/</targetPath>
                                <directory>${project.build.directory}</directory>
                                <include>${project.build.finalName}.war</include>
                            </resource>
                        </resources>
                    </configuration>
                </plugin>
            </plugins>
        </build>
    
        <repositories>
            <repository>
                <id>spring-snapshots</id>
                <name>Spring Snapshots</name>
                <url>https://repo.spring.io/snapshot</url>
                <snapshots>
                    <enabled>true</enabled>
                </snapshots>
            </repository>
            <repository>
                <id>spring-milestones</id>
                <name>Spring Milestones</name>
                <url>https://repo.spring.io/milestone</url>
                <snapshots>
                    <enabled>false</enabled>
                </snapshots>
            </repository>
        </repositories>
    
    </project>
    

    This is the main controller class:

    @Controller
    public class GreetingController {
    
        @Autowired
        private RecordRepository repository;
    
        @RequestMapping("/greeting")
        public String greeting(@RequestParam(value="id", required=true ) Integer id, Model model) {
            Record record = repository.findById(id);
            String name = ( record == null ? "World" : record.getName() );
            model.addAttribute("name", name);
            return "greeting" ;
        }
    
    }
    

    This is the main application (the commandline runner was written when initially testing the application in tomcat embedded mode):

    edit: I updated the class as suggested by Alex’s comment

    @SpringBootApplication
    public class MyAppSampleWebappApplication extends SpringBootServletInitializer  {
    
        private static final Logger log = LoggerFactory.getLogger(MyAppSampleWebappApplication.class);
    
        // JAR
        public static void main(String[] args) {
            System.out.println("Starting MyAppSampleWebappApplication (JAR)...");
            log.debug("Starting MyAppSampleWebappApplication (JAR)...");
            SpringApplication.run(MyAppSampleWebappApplication.class, args);
        }
    
        //WAR
            @Override
            protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
                System.out.println("Starting MyAppSampleWebappApplication (WAR)...");
                log.debug("Starting MyAppSampleWebappApplication (WAR)...");
            return application.sources(MyAppSampleWebappApplication.class);
            }
    }
    

    This is the application.properties for my file:

    # datasource
    spring.jpa.database-platform=org.hibernate.dialect.MySQL5InnoDBDialect
    spring.datasource.driverClassName=com.mysql.jdbc.Driver
    
    # for the moment I hardcoded references to mysql database, to replace with info from
    # environment variable on "real" version
    spring.datasource.url=jdbc:mysql://<IP OF HOST MACHINE>:3306/myapp-demo
    spring.datasource.username=myapp
    spring.datasource.password=myapp
    
    spring.jpa.hibernate.ddl-auto=update
    spring.data.jpa.repositories.enabled=true
    spring.jpa.show-sql=true
    
    logging.level.=DEBUG
    logging.file=/usr/local/tomcat/logs/myapp-sample-webapp.log
    

    This is the Dockerfile used through mvn build:docker to generate the image for my application:

    FROM tomcat
    ADD tomcat-users.xml /usr/local/tomcat/conf
    ADD myapp-sample-webapp.war /usr/local/tomcat/webapps/
    CMD ["catalina.sh", "run"]
    

    And finally, the docker-compose file launching the two containers:

    # container for an external mysql service
    
    sample-mysql:
      image: mysql:latest
      environment:
        MYSQL_ROOT_PASSWORD: p4SSW0rd
        MYSQL_DATABASE: myapp-demo
        MYSQL_USER: myapp
        MYSQL_PASSWORD: myapp
      ports:
        - 3306:3306
    
    # myapp-sample-webapp web application container:
    
    myapp-sample-webapp:
      image: myapp-h2020/myapp-sample-webapp:0.0.1-SNAPSHOT
      ports:
        - 8082:8080
    

  • How to reach the service running in docker container(overlay) externally from different hosts
  • docker how can I get the list of dependent child images?
  • Docker: Access mysql in docker container using --net=host option from host
  • Cannot stop or restart a docker container
  • Different Docker 1.9 networks talk to each other?
  • Service name for Docker Compose remote interpreter in PyCharm 5.1 Beta 2
  • 2 Solutions collect form web for “Deploy .war on tomcat Docker container”

    I think I’ve found the solution:

    In my Dockerfile, I built my application’s image on the tomcat base image ( “FROM tomcat” )

    I found out that this image is based on JRE 7, while in my pom.xml, I had version 1.8 as Java version (<java.version>1.8</java.version>)

    By replacing the first line of the Dockerfile with a more specific version “FROM tomcat:7-jre8“, I can now see the page generated by my controller.

    I’m not 100% sure without looking into log file.

    How your MVC’s view layer is configured? I guess that Spring cannot find view’s representation for “greeting”. Do you have any JSP, Velocity, etc template?

    Alternatively try to tamper with @ResponseBody on the method.

    Docker will be the best open platform for developers and sysadmins to build, ship, and run distributed applications.