【问题标题】:Deploy .war on tomcat Docker container在 tomcat Docker 容器上部署 .war
【发布时间】:2017-02-03 14:55:55
【问题描述】:

我正在尝试自动化在 docker 容器上部署 tomcat webapps' .war 的过程。为此,我开发了一个示例 Spring Boot 应用程序以用作测试。由于我的最终目标是将流程应用于现有的旧 war 应用程序,因此我将应用程序配置为生成一个 war 文件,而不是嵌入 tomcat 的 jar。

示例应用程序只是一个示例 Spring MVC 应用程序,带有一个 /greeting?id=<id> 端点,它只返回一个带有“Hello”消息的页面,其中是从 mysql db 上的表中读取的字符串。应用部署在基于tomcat基础镜像的Docker容器上,而数据库部署在单独的mysql容器上。

通过docker-compose 脚本启动两个容器时,部署似乎成功:两个容器正在运行,tomcat 实例处于活动状态,在 cotainer 日志中看不到错误消息,我可以看到已部署的 webapp并标记为在 http://<IP>:8082/manager/html 的 tomcat 管理器界面中运行。问题是在 http://<IP>:8082/myapp-sample-webapp/greeting?id=<id> 我收到 404 错误。我测试了在 Web 应用程序中放置一个 test.html 静态文件,这在 http://<IP>:8082/myapp-sample-webapp/test.html 中可见。

还要注意,如果我将应用程序配置为嵌入 tomcat 的 jar 应用程序(当然也相应地更改 Dockerfile 配置),我可以毫无问题地访问 /greeting?id=<id> 端点。

一个附带的问题是我看不到任何应用程序生成的日志(在带有docker logs ... 的 docker 容器的日志中,也没有在我为应用程序日志配置的/usr/local/tomcat/logs/myapp-sample-webapp.log 日志文件中(请参阅下面的application.properties),这使我无法检查问题是否与与 mysql 数据库的连接失败有关。

这是应用程序的 pom.xml 文件如下:

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

这是主控制器类:

@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" ;
    }

}

这是主应用程序(命令行运行器是在最初以tomcat嵌入模式测试应用程序时编写的):

编辑:我按照亚历克斯的评论更新了课程

@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);
        }
}

这是我的文件的 application.properties:

# 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

这是通过mvn build:docker 用于为我的应用程序生成映像的 Dockerfile:

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

最后,docker-compose 文件启动两个容器:

# 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

【问题讨论】:

  • " extends SpringBootServletInitializer" 就是答案。没有它,你的战争甚至无法在本地 tomcat 容器中运行。

标签: java tomcat docker spring-boot


【解决方案1】:

我想我已经找到了解决方案:

在我的 Dockerfile 中,我在 tomcat 基础镜像 ("FROM tomcat" ) 上构建了我的应用程序镜像

我发现这张图片是基于 JRE 7 的,而在我的 pom.xml 中,我的 Java 版本是 1.8 (&lt;java.version&gt;1.8&lt;/java.version&gt;)

通过将 Dockerfile 的第一行替换为更具体的版本“FROM tomcat:7-jre8”,我现在可以看到我的控制器生成的页面。

【讨论】:

    【解决方案2】:

    如果不查看日志文件,我不能 100% 确定。

    你的 MVC 的视图层是如何配置的?我猜想Spring找不到“问候”的视图表示。有JSP、Velocity等模板吗?

    或者尝试篡改方法上的@ResponseBody。

    【讨论】:

    • 我首先将应用程序作为独立的嵌入 tomcat 的 webapp 进行了测试,然后将配置转换为 war 文件以部署在生产 tomcat 上。在前一种情况下,一切正常,所以我很确定问题与 MVC 层配置无关。无论如何谢谢
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-02-13
    • 1970-01-01
    • 1970-01-01
    • 2010-09-13
    • 2012-07-29
    相关资源
    最近更新 更多