How to config multiple web applications with virtual hosts in one Tomcat server
1. Introduction
Sometimes we need to run multiple web applications with the corresponding virtual hosts in one Tomcat server. Actually, this is not simple to config and there is more than one solution. For example, you can install each web application in separated Tomcat servers.
2. Requirement
Assumed that you have:
- Tomcat: /opt/tomcat-dev/
- Apache 2: /etc/httpd/
- Registered two sub-domains:
api.mobabel.net
manager.mobabel.net
The domains DNS has pointed to your server
And the main domain mobabel.net is available. - Two web applications
nova-api.war
nova-manager.war - CentOS or other Linux
3. Use Case
3-1. 1. Config single web application with a virtual host ( with port + context path)
We use web application nova-api as an example.
1. Copy nova-api.war into /opt/tomcat-dev/webapps
Start Tomcat and this application nova-api can be found in Tomcat Web Application Manager (http://www.mobabel.net:8080/manager/html/), check whether http://www.mobabel.net:8080/nova-api/ is working or not.
2. Edit /opt/tomcat-dev/conf/server.xml
1 2 3 4 5 6 |
<Server> <Service> <Connector port="8080" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" /> |
Restart Tomcat server.
3. Active Port 8080 in the firewall
1 2 |
# firewall-cmd --zone=public --permanent --add-port=8080/tcp # firewall-cmd --reload |
On some servers from the server supplier, you need active special ports in their admin control panel.
4. Set the reverse proxy in Apache
Insert this virtual host declaration in /etc/httpd/conf/httpd.conf or other custom config file:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
<VirtualHost *:80> ServerName api.mobabel.net ProxyRequests Off ProxyVia Off ProxyPreserveHost Off <Proxy *> Order Deny,Allow Allow from all </Proxy> ErrorLog logs/api.mobabel.net-error_log CustomLog logs/api.mobabel.net-access_log common ProxyPass /nova-api/ http://localhost:8080/nova-api/ ProxyPassReverse /nova-api/ http://localhost:8080/nova-api/ </VirtualHost> |
Restart Apache server.
5. Test http://api.mobabel.net:8080/nova-api/
It should work.
Conclusion
This solution is fit for Continuous Integration CI, because Jenkins can deploy war into Tomcat server via Tomcat txt manager remotely.
But this web URL has port 8080 and context path /nova-api/. This is inconvenient for users.
I will go further to remove the port and context path
3-2. 2. Config single web application with a virtual host ( without port + context path)
1. Keep the previous settings.
2. Edit /opt/tomcat-dev/conf/server.xml
Please make sure you are using same host name “localhost“(or your custom host name) in Connector, Engine and Host node.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
<Server> <Service> <Connector port="8080" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" address="localhost" proxyName="api.mobabel.net" proxyPort="80" scheme="http" /> <Connector port="8009" protocol="AJP/1.3" redirectPort="8443" /> ...... <Engine name="Catalina" defaultHost="localhost"> ...... <Host name="localhost" appBase="webapps" unpackWARs="true" autoDeploy="true"> <Context path="" docBase="/opt/tomcat-dev/webapps/nova-api" reloadable="false" useHttpOnly="true"> <Manager pathname=""/> </Context> <Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs" prefix="api.mobabel.net_access_log" suffix=".txt" pattern="%h %l %u %t "%r" %s %b" /> </Host> ...... |
Restart Tomcat server.
3. Set the reverse proxy in Apache
Edit this virtual host declaration in /etc/httpd/conf/httpd.conf or other custom config file:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
<VirtualHost *:80> ServerName api.mobabel.net ProxyRequests Off ProxyVia Off ProxyPreserveHost Off <Proxy *> Order Deny,Allow Allow from all </Proxy> ErrorLog logs/api.mobabel.net-error_log CustomLog logs/api.mobabel.net-access_log common ProxyPass / http://localhost:8080/ ProxyPassReverse / http://localhost:8080/ </VirtualHost> |
Restart Apache server.
4. Test http://api.mobabel.net/ The boring port and context path is gone!
Conclusion
This solution is fit for Continuous Integration CI, because Jenkins can deploy war into Tomcat server via Tomcat txt manager remotely.
But this solution will make your Tomcat only work for one web application with the domain.
3-3. 3. Config multiple web applications with the virtual hosts ( without port + context path)
1. Keep the previous settings for nova-api
2. create a new webapps container for nova-manager and grant permission. Assume that your tomcat-dev is running under group tomcat and with owner tomcat-dev.
1 2 3 |
mkdir /opt/tomcat-dev/webapps-manager chown -hR tomcat-dev:tomcat /opt/tomcat-dev/webapps-manager |
3. Copy nova-manager.war into /opt/tomcat-dev/webapps-manager/
4. Edit /opt/tomcat-dev/conf/server.xml
Please make sure you are using same host name “localhost“(or your custom host name) in Connector, Engine and Host node.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 |
<Server> <Service> <Connector port="8080" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" address="localhost" proxyName="api.mobabel.net" proxyPort="80" scheme="http" /> <Connector port="8009" protocol="AJP/1.3" redirectPort="8443" /> ...... <Engine name="Catalina" defaultHost="localhost"> ...... <Host name="localhost" appBase="webapps" unpackWARs="true" autoDeploy="true"> <Context path="" docBase="/opt/tomcat-dev/webapps/nova-api" reloadable="false" useHttpOnly="true"> <Manager pathname=""/> </Context> <Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs" prefix="api.mobabel.net_access_log" suffix=".txt" pattern="%h %l %u %t "%r" %s %b" /> </Host> ...... </Service> <!--- seperate Service for nova-manager application --> <Service name="DevNovaManager"> <Connector port="8083" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8444" address="localhost" proxyName="manager.mobabel.net" proxyPort="80" scheme="http" /> <Connector port="8013" protocol="AJP/1.3" redirectPort="8444" /> <Engine name="DevNovaManager" defaultHost="localhost"> <Host name="localhost" appBase="webapps-manager" unpackWARs="true" autoDeploy="true"> <Context path="" docBase="/opt/tomcat-dev/webapps-manager/nova-manager" reloadable="false" useHttpOnly="true"> <Manager pathname=""/> </Context> <Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs" prefix="manager.mobabel.net_access_log" suffix=".txt" pattern="%h %l %u %t "%r" %s %b" /> </Host> </Engine> </Service> </Server> |
Please use different port “8013” for connector AJP and “8444” for redirectPort. Otherwise, you could not start Tomcat server because of the port conflict.
Restart Tomcat server.
You can use netstat command to check the port usage.
1 |
netstat -plntu |
5. Set the reverse proxy in Apache
Add this virtual host declaration in /etc/httpd/conf/httpd.conf or other custom config file:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
<VirtualHost *:80> ServerName manager.mobabel.net ProxyRequests Off ProxyVia Off ProxyPreserveHost Off <Proxy *> Order Deny,Allow Allow from all </Proxy> ErrorLog logs/manager.mobabel.net-error_log CustomLog logs/manager.mobabel.net-access_log common ProxyPass / http://localhost:8083/ ProxyPassReverse / http://localhost:8083/ </VirtualHost> |
Restart Apache server.
6. Test http://api.mobabel.net/ and http://manager.mobabel.net/
Conclusion
Jenkins can not deploy war into Tomcat server via Tomcat txt manager directly. But Jenkins can use FTP protocol to upload war remotely.
3-4. 4. Add another application in case 3
You can repeat step 1 to 5 to add one new web application, for example: nova-other
In step 2 a new webapps-other is created, and nova-other.war is copied into /opt/tomcat-dev/webapps-other/. It seems correct and everything works fine.
3-4-1. BUT
When nova-other.war is moved to /opt/tomcat-dev/webapps, everything still works fine!!!
That means multiple web applications with different port and virtual domain can be put into one webapps directory!
This is the last modified server.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 |
<Server> <Service> <Connector port="8080" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" address="localhost" proxyName="api.mobabel.net" proxyPort="80" scheme="http" /> <Connector port="8009" protocol="AJP/1.3" redirectPort="8443" /> ...... <Engine name="Catalina" defaultHost="localhost"> ...... <Host name="localhost" appBase="webapps" unpackWARs="true" autoDeploy="true"> <Context path="" docBase="/opt/tomcat-dev/webapps/nova-api" reloadable="false" useHttpOnly="true"> <Manager pathname=""/> </Context> <Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs" prefix="api.mobabel.net_access_log" suffix=".txt" pattern="%h %l %u %t "%r" %s %b" /> </Host> ...... </Service> <!--- seperate Service for nova-manager application --> <Service name="DevNovaManager"> <Connector port="8083" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8444" address="localhost" proxyName="manager.mobabel.net" proxyPort="80" scheme="http" /> <Connector port="8013" protocol="AJP/1.3" redirectPort="8444" /> <Engine name="DevNovaManager" defaultHost="localhost"> <Host name="localhost" appBase="webapps" unpackWARs="true" autoDeploy="true"> <Context path="" docBase="/opt/tomcat-dev/webapps/nova-manager" reloadable="false" useHttpOnly="true"> <Manager pathname=""/> </Context> <Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs" prefix="manager.mobabel.net_access_log" suffix=".txt" pattern="%h %l %u %t "%r" %s %b" /> </Host> </Engine> </Service> <!--- seperate Service for nova-other application --> <Service name="DevNovaOther"> <Connector port="8084" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8445" address="localhost" proxyName="other.mobabel.net" proxyPort="80" scheme="http" /> <Connector port="8014" protocol="AJP/1.3" redirectPort="8445" /> <Engine name="DevNovaOther" defaultHost="localhost"> <Host name="localhost" appBase="webapps" unpackWARs="true" autoDeploy="true"> <Context path="" docBase="/opt/tomcat-dev/webapps/nova-other" reloadable="false" useHttpOnly="true"> <Manager pathname=""/> </Context> <Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs" prefix="other.mobabel.net_access_log" suffix=".txt" pattern="%h %l %u %t "%r" %s %b" /> </Host> </Engine> </Service> </Server> |
Conclusion
You can still use separate appBase in Host depends on your request. But Jenkins can deploy war into Tomcat server via Tomcat txt manager directly if all web applications are in appBase webapps.
Further Test
Copy Tomcat’s application manager from /opt/tomcat-dev/webapps/manager to /opt/tomcat-dev/webapps-manager/manager, and give a free port and virtual host(newmanager.mobabel.net) to it.
And do not forget put Realm inside Engine.
http://newmanager.mobabel.net/manager/html works!! Jenkins can deploy war to the custom webapps directory now!
3-5. 5. Config multiple web applications with the virtual hosts in multiple instances in Single Tomcat server
https://dzone.com/articles/run-configure-multiple-instance-in-a-single-tomcat
Not yet tested.
3-6. Refer:
https://www.tecmint.com/run-deploy-multiple-web-applications-instances-tomcat-centos-ubuntu/
https://dzone.com/articles/run-configure-multiple-instance-in-a-single-tomcat