前言
Tomcat是目前比较流行的Web应用服务器,深受Java爱好者的喜爱。通常J2EE应用的产出物是一个war包,这篇文章将为你介绍如何使用Docker运行Tomcat+war包的Java Web应用,并比较其它几种方法的优缺点。
在生产环境中,我们推荐基于Tomcat的Docker容器来打包、运行您的Java应用,即在Tomcat的Docker镜像基础上构建应用镜像,Dockerfile如下所示:
FROM tomcat:8
ADD your_app.war /usr/local/tomcat/webapps/
CMD ["catalina.sh", "run"]
Hello-World
Maven是一款优秀的跨平台项目管理工具,本文将使用Maven构建一个简单Hello World Web应用,并使用docker来运行。
首先,你需要一个Maven开发环境,你可以在自己的开发环境安装Maven,或者可以使用Docker Hub上的Maven镜像作为构建环境。
创建一个Maven WebApp
创建maven-demo和maven-repo目录用作docker volume,这样Maven创建的项目会持久化在maven-demo目录下,maven的local repository持久话在maven-repo目录下。
$ mkdir maven-demo maven-repo
运行maven docker新建一个Maven项目
$ docker run -it --rm --name maven-demo -v "$PWD"/maven-demo:/usr/src/maven-demo -v "$PWD"/maven-repo/:/root/.m2/repository -w /usr/src/maven-demo maven:3 mvn -B archetype:generate -DgroupId=com.aliyun.demo -DartifactId=hello-world -DarchetypeArtifactId=maven-archetype-webapp
运行成功后,可以看到项目目录结构如下:
hello-world
|---pom.xml
|---src
|---main
|---resource
|---webapp
|---WEB-INF
| |---web.xml
|---index.jsp
构建Maven项目
$ docker run -it --rm --name maven-demo -v "$PWD"/maven-demo/:/usr/src/maven-demo -v "$PWD"/maven-repo/:/root/.m2/repository -w /usr/src/maven-demo/hello-world maven mvn package
构建成功后,hello-world目录下多了一个target目录,包含hello-world.war文件。
Docker镜像打包
编辑Dockerfile
FROM tomcat:8
ADD maven-demo/hello-world/target/hello-world.war /usr/local/tomcat/webapps/
CMD ["catalina.sh", "run"]
Build Image
$ docker build -t maven-demo-hello-world .
运行Docker镜像
$ docker run -d -p 8080:8080 maven-demo-hello-world
打开浏览器访问 http://127.0.0.1:8080/hello-world/ 将会看到 Hello World! 的输出,这表示我们编译的hello-world war 包已经成功使用 Docker 运行起来了。
版本管理
实际生产中需要特别关注的是应用镜像的版本管理,因为除了你的应用代码会更新之外,Tomcat的镜像也会因一些补丁发生版本更新。你需要及时构建应用镜像的新版本,并打上合适的tag,以便于在升级运行应用时选择合适的版本。
Docker Hub支持用户Github仓库的自动构建功能,使得这件事情变得简单很多。也就是说:只要你的Java应用代码的Github仓库中有代码更新时,会自动更新产生一个新的应用镜像。 如果你使用阿里云的容器Hub服务,同样可以支持这样的功能帮助你构建应用镜像。
更多实践方案
由于Docker功能的强大与灵活,也有用户在实践中使用其它一些方法,期望获得同样的效果,但有一些问题需要特别注意。
使用Data Volume
$ docker run -d -v /tmp/apps/your.war:/usr/local/tomcat/webapps/your.war tomcat:8
这种方法可以避免每次修改代码都会生成新的Docker应用镜像的工作,用户可以将应用包挂载到Tomcat容器的volume中。这个方法非常适合在单机上进行开发测试时使用,当你想更新应用war包时,只需要直接更新即可,无需其它额外工作。
但这个做法在生产环境上有明显的缺陷,因为生产环境中难免会遇到因为各种原因导致容器出错的情况,在这种情况发生时容器的调度系统可能会把容器调度到另一台物理机上运行,而那台机器上却没有对应的应用包volume,用户的应用就无法正常运行了。
数据容器
FROM tomcat:8
ADD your.war /usr/local/tomcat/webapps/
使用这样的Dockerfile构建出数据容器,并将它的volume与Tomcat容器共享。
$ docker build -t app-image .
$ docker create -d -v /usr/local/tomcat/webapps/ --name app app-image true
$ docker run -d --volumes-from app tomcat:8
由于系统一定会把共享volume的容器调度运行在同一台宿主机上,这样可以保证正确运行。然而,由于共享volume的容器只能被调度到同一台宿主机上,这样会限制系统的可伸缩性。