1. 介绍
在工作中, 所有应用原本是使用IBM Websphere
容器进行部署的, 然而IBM全家桶毕竟是收费的, 而且技术支持依赖人家. 所以有了改成tomcat
容器部署的方案. 但在改造过程中, 项目组将改造的五六个应用分别构建成war包, 每个应用war包使用一个tomcat容器, 分配各自不同的http端口, 这样的方式也是没什么问题的.
但就在2021年, 日志实现框架log4j2被爆出重量级的漏洞, 最终解决方案是升级官方发布的jar包版本. 那如果tomcat也出现这样的问题, 我们需要对每个应用的tomcat容器进行升级, 运维工作量可想而知. 有没有方案可以让这些应用共享一个tomcat容器, 并分配不同的http端口, 遇到问题只需要升级一个tomcat容器即可呢? 答案肯定是有的, 下面就给干货.
解决思想就是只放置一个tomcat容器, 让多个子实例共享这个tomcat容器(主要是里面的lib
), 然后在多个子实例中编写自己启动/关闭脚本, 实际还是调用父级tomcat的启动/关闭脚本; 以及编写自己的conf配置信息.
2. 环境准备
2.1 JDK8安装
将下载的jdk8压缩包解压到指定的安装目录.
# 解压jdk
tar -xvf jdk-8u141-linux-x64.tar.gz /usr/tomcat
# 修改jdk-8u141-linux-x64名称为jdk1.8.0.222
mv jdk-8u141-linux-x64 jdk1.8.0.222
配置jdk环境变量, vi /etc/profile
export JAVA_HOME=/usr/tomcat/jdk1.8.0.222
export PATH=$JAVA_HOME/bin:$PATH
export CLASSPATH=.:$JAVA_HOME/lib/dt.jar::$JAVA_HOME/lib/tools.jar
生效配置
source /etc/profile
2.2 父容器tomcat准备
首先下载一个tomcat, 解压到指定的安装目录.
# 解压tomcat
tar -xvf apache-tomcat-9.0.16.tar.gz /usr/tomcat/
# 修改apache-tomcat-9.0.16名称为base_tomcat
mv apache-tomcat-9.0.16 base_tomcat
进入base_tomcat, 可以看到里面的结构就是原本tomcat该有的那些目录, bin, lib, webapp, conf等, 我们后面要共享的就是base_tomcat这个父容器的lib, 且脚本也是指向的父容器bin里面的shell脚本进行启动和关闭. 父容器的conf里面的配置我们不做任何更改, 而是复制一份在各个子应用实例中进行配置修改.
查看conf目录下的配置文件项
3. 子应用实例准备
我先改造一个实例iems
展示, 首先需要复制一份tomcat到父级容器的同级目录.
# # 解压tomcat
tar -xvf apache-tomcat-9.0.16.tar.gz /usr/tomcat/
# 修改apache-tomcat-9.0.16名称为iems
mv apache-tomcat-9.0.16 iems
因为后面是共享父容器的lib, 启动/关闭脚本, 所以需要将子实例中的lib
下的jar包都删除, bin
下面的shell脚本都删除.
进入iems, 按照上面步骤删除不需要的lib和shell脚本.
cd iems
# 删除lib
rm -rf lib
# 删除shell脚本
cd bin
rm -rf *.sh
因为删除了子实例的脚本, 如果这样部署, 应用是无法调用到tomcat的启动脚本的. 需要我们自定义脚本来调用父容器tomcat的脚本. 我这里自定义了三个脚本,分别为 setenv.sh
,startup.sh
, shutdown.sh
.
touch setenv.sh
touch shutdown.sh
touch startup.sh
编辑 setenv.sh, 主要是环境变量, JVM相关的参数配置.
编辑 startup.sh, 指向父tomcat的启动脚本.
CATALINA_HOME是父容器tomcat的目录, /usr/tomcat/base_tomcat
CATALINA_BASE就是当前子实例的目录, /usr/tomcat/iems
编辑shutdown.sh, 指向父tomcat的关闭脚本.
4. 子应用实例配置修改
4.1 修改server.xml
<?xml version="1.0" encoding="UTF-8"?>
<Server port="9067" shutdown="SHUTDOWN"> <!--修改shutdown端口-->
<Listener className="org.apache.catalina.startup.VersionLoggerListener" />
<Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="on" />
<Listener className="org.apache.catalina.core.JreMemoryLeakPreventionListener" />
<Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener" />
<Listener className="org.apache.catalina.core.ThreadLocalLeakPreventionListener" />
<GlobalNamingResources>
<Resource name="UserDatabase" auth="Container"
type="org.apache.catalina.UserDatabase"
description="User database that can be updated and saved"
factory="org.apache.catalina.users.MemoryUserDatabaseFactory"
pathname="conf/tomcat-users.xml" />
</GlobalNamingResources>
<Service name="Catalina">
<!--http端口配置-->
<Connector port="9087" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8467" />
<Connector port="8009" protocol="AJP/1.3" redirectPort="8467" />
<Engine name="Catalina" defaultHost="localhost">
<Realm className="org.apache.catalina.realm.LockOutRealm">
<Realm className="org.apache.catalina.realm.UserDatabaseRealm"
resourceName="UserDatabase"/>
</Realm>
<Host name="localhost" appBase="webapps"
unpackWARs="true" autoDeploy="true">
<Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
prefix="localhost_access_log" suffix=".txt"
pattern="%h %l %u %t "%r" %s %b" />
</Host>
</Engine>
</Service>
</Server>
http端口配置内容, http端口修改为9087
4.2 修改tomcat-users.xml
tomcat控制台的应用管理界面访问需要用户/密码, 这里可以配置添加用户/密码. tomcat/123456
<?xml version="1.0" encoding="UTF-8"?>
<tomcat-users xmlns="http://tomcat.apache.org/xml"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://tomcat.apache.org/xml tomcat-users.xsd"
version="1.0">
<role rolename="manager-gui"/>
<role rolename="manager-script"/>
<role rolename="manager-status"/>
<role rolename="manager-jmx"/>
<role rolename="admin-gui"/>
<role rolename="admin-script"/>
<user username="tomcat" password="123456" roles="manager-gui,manager-script,manager-status,manager-jmx,admin-gui,admin-script"/>
</tomcat-users>
4.3 修改catalina.properties
一些属性配置可以添加在这个文件中, 比如数据库连接信息, 共享库设置, 配置环境指定等.
package.access=sun.,org.apache.catalina.,org.apache.coyote.,org.apache.jasper.,org.apache.tomcat.
package.definition=sun.,java.,org.apache.catalina.,org.apache.coyote.,\
org.apache.jasper.,org.apache.naming.,org.apache.tomcat.
common.loader="${catalina.base}/lib","${catalina.base}/lib/*.jar","${catalina.home}/lib","${catalina.home}/lib/*.jar"
# application-{profile}.properties中的配置都可以在这里配置,且优先级高于应用里的配置.
# 设置配置环境参数, 启动应用将会读取application-{profile}.properties文件的配置
spring.profiles.active=uat
server.loader=
# 共享库lib配置,这个是应用要使用到的jar包目录,不需要可以注释掉
# 如果将war包中的lib抽取到公共目录下面,可以在这里配置读取
shared.loader=/usr/tomcat/sharelib/iemslib/*.jar
tomcat.util.scan.StandardJarScanFilter.jarsToSkip=\
annotations-api.jar,\
ant-junit*.jar,\
ant-launcher.jar,\
ant.jar,\
asm-*.jar,\
aspectj*.jar,\
bootstrap.jar,\
catalina-ant.jar,\
catalina-ha.jar,\
catalina-jmx-remote.jar,\
catalina-storeconfig.jar,\
catalina-tribes.jar,\
catalina-ws.jar,\
catalina.jar,\
cglib-*.jar,\
cobertura-*.jar,\
commons-beanutils*.jar,\
commons-codec*.jar,\
commons-collections*.jar,\
commons-daemon.jar,\
commons-dbcp*.jar,\
commons-digester*.jar,\
commons-fileupload*.jar,\
commons-httpclient*.jar,\
commons-io*.jar,\
commons-lang*.jar,\
commons-logging*.jar,\
commons-math*.jar,\
commons-pool*.jar,\
dom4j-*.jar,\
easymock-*.jar,\
ecj-*.jar,\
el-api.jar,\
geronimo-spec-jaxrpc*.jar,\
h2*.jar,\
hamcrest-*.jar,\
hibernate*.jar,\
httpclient*.jar,\
icu4j-*.jar,\
jasper-el.jar,\
jasper.jar,\
jaspic-api.jar,\
jaxb-*.jar,\
jaxen-*.jar,\
jdom-*.jar,\
jetty-*.jar,\
jmx-tools.jar,\
jmx.jar,\
jsp-api.jar,\
jstl.jar,\
jta*.jar,\
junit-*.jar,\
junit.jar,\
log4j*.jar,\
mail*.jar,\
objenesis-*.jar,\
oraclepki.jar,\
oro-*.jar,\
servlet-api-*.jar,\
servlet-api.jar,\
slf4j*.jar,\
taglibs-standard-spec-*.jar,\
tagsoup-*.jar,\
tomcat-api.jar,\
tomcat-coyote.jar,\
tomcat-dbcp.jar,\
tomcat-i18n-cs.jar,\
tomcat-i18n-de.jar,\
tomcat-i18n-en.jar,\
tomcat-i18n-es.jar,\
tomcat-i18n-fr.jar,\
tomcat-i18n-ja.jar,\
tomcat-i18n-ko.jar,\
tomcat-i18n-pt-BR.jar,\
tomcat-i18n-ru.jar,\
tomcat-i18n-zh-CN.jar,\
tomcat-jdbc.jar,\
tomcat-jni.jar,\
tomcat-juli-adapters.jar,\
tomcat-juli.jar,\
tomcat-util-scan.jar,\
tomcat-util.jar,\
tomcat-websocket.jar,\
tools.jar,\
websocket-api.jar,\
wsdl4j*.jar,\
xercesImpl.jar,\
xml-apis.jar,\
xmlParserAPIs-*.jar,\
xmlParserAPIs.jar,\
xom-*.jar
tomcat.util.scan.StandardJarScanFilter.jarsToScan=\
log4j-taglib*.jar,\
log4j-web*.jar,\
log4javascript*.jar,\
slf4j-taglib*.jar
# String cache configuration.
tomcat.util.buf.StringCache.byte.enabled=true
#tomcat.util.buf.StringCache.char.enabled=true
#tomcat.util.buf.StringCache.trainThreshold=500000
#tomcat.util.buf.StringCache.cacheSize=5000
4.4 部署应用
将构建好的war包(iems.war)放到子实例的webapps目录即可.
然后进入bin目录启动子实例iems.
cd /usr/tomcat/iems/bin
./startup.sh
# 查看启动日志, 也可以看自己应用的logback中指定的日志文件
tail -f /usr/tomcat/iems/logs/catalina.out
启动成功后, 进入iems的控制台查看. 浏览器访问http://{ip}:{port}/
使用用户名/密码登录进入应用管理界面(Manager App), 查看应用部署情况. tomcat/123456
输入用户密码后, 登录进入应用发布界面, 可以看到我们的bmws_iems应用已经启动成功, 这个界面着实很丑.
下面还有读取本地war包发布的功能.
5. tomcat监控插件
上面提到tomcat控制台的应用发布界面真的很丑, 功能也很少. 我从网上找到了一款tomcat监控插件, 对tomcat管理应用扩展了很多功能. Tomcat监控工具之probe
直接用起来, 在GitHub下载 probe , 下载完解压到tomcat子实例的webapps目录下即可, 不用修改任何配置, 即插即用. 现在我们重新启动iems子实例看看. 浏览器访问http://{ip}:{port}/probe
我们来看看proble扩展了那些好用的功能.
应用卸载, 停止, 重启
发布或更新应用, 并可以设置上下文根context名称, 启动解压后的目录名称就是上下文根context的名称.
查看子实例的系统信息, 支持手动垃圾回收.
QPS监控
支持语言切换
6. 遇到的问题
项目上线前, 在准备验证报文的过程中, 发现浏览器直发报文请求应用会报错, 但是使用postman等测试工具发送请求正常. 由于生产环境没有提供postman这类测试工具给我们验证, 所以必须解决这个问题. 经过查阅资料, 需要修改一些配置才能支持url自动encoding编码.
在catalina.properties文件中加入编码配置
tomcat.util.http.parser.HttpParser.requestTargetAllow=|{}
org.apache.tomcat.util.buf.UDecoder.ALLOW_ENCODED_SLASH=true
在server.xml文件的
<Connector port="9087" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8467"
relaxedPathChars="|{}[],"
relaxedQueryChars="|{}[],"
/>