目錄
1、引語
2、jdk與tomcat的安裝
3、tomcat多實例配置與測試
4、tengine安裝與負載均衡配置
5、整體測試
6、思考和優化tomcat配置流程
7、總結
1、引語
有沒有這樣一種情況,你在一臺服務器跑了一個tomcat實例,當有一天你發現不管你怎麼優化tomcat,它的併發能力處理能力始終上不去了,而你服務器的硬件資源還有一部份剩餘時,這時你就得采用tomcat啓用多實例的方式,讓剩下的硬件資源也一起利用起來,讓用戶的請求分攤到多個實例上來處理,這樣只要硬件資源允許能大大提升tomcat的整體性能,利用前端的反向代理軟件還可實例tomcat實例級別的高可用和負載均衡。前端的反向代理軟件選擇比較多,可以用LVS、Haproxy、Apache、Nginx,比較常用的是Apache和Nginx。Apache可以實現http和AJP方式與tomcat結合,而Nginx只能選擇http的方式。關於apache與tomcat整合請見http://zhaochj.blog.51cto.com/368705/1641939。
接下來我們在tomcat上實現多實例,並利用nginx以反向代理的方式與tomcat整合實現負載均衡。
2、jdk與tomcat的安裝
這次測試所使用的系統環境與軟件如下所示:
[root@nod3 tomcat]# cat /etc/issue CentOS release 6.4 (Final) Kernel \r on an \m [root@nod3 tomcat]# uname -r 2.6.32-358.el6.x86_64 [root@nod3 tomcat]# pwd /root/tomcat [root@nod3 tomcat]# ls apache-tomcat-7.0.62.tar.gz jdk-7u9-linux-x64.rpm tengine-2.1.0-1.el6.x86_64.rpm
安裝JDK:
[root@nod3 tomcat]# rpm -ivh jdk-7u9-linux-x64.rpm Preparing... ########################################### [100%] 1:jdk warning: /etc/init.d/jexec saved as /etc/init.d/jexec.rpmorig #之前我這系統好像裝過jdk ########################################### [100%] Unpacking JAR files... rt.jar... Error: Could not open input file: /usr/java/jdk1.7.0_09/jre/lib/rt.pack jsse.jar... Error: Could not open input file: /usr/java/jdk1.7.0_09/jre/lib/jsse.pack charsets.jar... Error: Could not open input file: /usr/java/jdk1.7.0_09/jre/lib/charsets.pack tools.jar... Error: Could not open input file: /usr/java/jdk1.7.0_09/lib/tools.pack localedata.jar... Error: Could not open input file: /usr/java/jdk1.7.0_09/jre/lib/ext/localedata.pack #上邊的錯誤可忽略,不影響jdk的安裝和使用 [root@nod3 tomcat]# vim /etc/profile.d/java.sh #導出二進制文件路徑: export PATH=$PATH:/usr/java/latest/bin [root@nod3 tomcat]# source /etc/profile.d/java.sh [root@nod3 tomcat]# java -version java version "1.7.0_09" Java(TM) SE Runtime Environment (build 1.7.0_09-b05) Java HotSpot(TM) 64-Bit Server VM (build 23.5-b02, mixed mode) #能夠正常顯示java的版本信息
tomcat安裝:
[root@nod3 tomcat]# tar xf apache-tomcat-7.0.62.tar.gz -C /usr/local/ [root@nod3 local]# ln -sv apache-tomcat-7.0.62 tomcat-7 `tomcat-7' -> `apache-tomcat-7.0.62' [root@nod3 local]# vim /etc/profile.d/tomcat.sh #導出tomcat的二進制文件目錄 export PATH=$PATH:/usr/local/tomcat-7/bin [root@nod3 local]# source /etc/profile.d/tomcat.sh [root@nod3 local]# catalina.sh version Using CATALINA_BASE: /usr/local/tomcat-7 Using CATALINA_HOME: /usr/local/tomcat-7 Using CATALINA_TMPDIR: /usr/local/tomcat-7/temp Using JRE_HOME: /usr Using CLASSPATH: /usr/local/tomcat-7/bin/bootstrap.jar:/usr/local/tomcat-7/bin/tomcat-juli.jar Server version: Apache Tomcat/7.0.62 Server built: May 7 2015 17:14:55 UTC Server number: 7.0.62.0 OS Name: Linux OS Version: 2.6.32-358.el6.x86_64 Architecture: x86_64 JVM Version: 1.5.0 JVM Vendor: Free Software Foundation, Inc. #上邊正確顯示出tomcat版本信息
注:在以前的博文中寫到在安裝好JDK與tomcat後都全配置一些環境變量,但這裏不需要去配置全局的環境變量,因爲在多實例運行環境下對每個實例的管理都需要用腳本,所在那些環境變量都配置在各自的腳本中,以接下來的內容中有所體現。
3、tomcat多實例配置與測試
在進行多實例環境部署時先要有個總體的規劃,各個實例存放的目錄在哪裏?你的webapp程序放在哪裏?我這裏是這樣的規劃的,把各個實例都存放在“/usr/local/tomcat_instances/”目錄下,webapp程序就存放在“/usr/local/tomcat_instances/站點名稱/webapps/”目錄下。
創建實例目錄:
[root@nod3 local]# mkdir -pv /usr/local/tomcat_instances/{nod1.test.com-instance-1,nod1.test.com-instance-2} mkdir: created directory `/usr/local/tomcat_instances' mkdir: created directory `/usr/local/tomcat_instances/nod1.test.com-instance-1' mkdir: created directory `/usr/local/tomcat_instances/nod1.test.com-instance-2'
拷貝配置文件到各個實例目錄並創建相應的工作目錄:
[root@nod3 local]# cp -r /usr/local/tomcat-7/conf /usr/local/tomcat_instances/nod1.test.com-instance-1/ [root@nod3 local]# cp -r /usr/local/tomcat-7/conf /usr/local/tomcat_instances/nod1.test.com-instance-2/ [root@nod3 local]# mkdir -pv /usr/local/tomcat_instances/nod1.test.com-instance-1/{logs,temp,webapps/webpages,work} [root@nod3 local]# mkdir -pv /usr/local/tomcat_instances/nod1.test.com-instance-2/{logs,temp,webapps/webpages,work} [root@nod3 local]# tree /usr/local/tomcat_instances/nod1.test.com-instance-1/ /usr/local/tomcat_instances/nod1.test.com-instance-1/ ├── conf │ ├── catalina.policy │ ├── catalina.properties │ ├── context.xml │ ├── logging.properties │ ├── server.xml │ ├── tomcat-users.xml │ └── web.xml ├── logs ├── temp ├── webapps │ └── webpages └── work 6 directories, 7 files [root@nod3 local]# tree /usr/local/tomcat_instances/nod1.test.com-instance-2/ /usr/local/tomcat_instances/nod1.test.com-instance-2/ ├── conf │ ├── catalina.policy │ ├── catalina.properties │ ├── context.xml │ ├── logging.properties │ ├── server.xml │ ├── tomcat-users.xml │ └── web.xml ├── logs ├── temp ├── webapps │ └── webpages └── work 6 directories, 7 files
修改各個實例的監聽端口站點目錄:
第一個實例:
[root@nod3 local]# vim /usr/local/tomcat_instances/nod1.test.com-instance-1/conf/server.xml ............ <!-- 在現在高版本的tomcat中也只是允許本地連接此端口,也是比較安全的 --> <Server port="8005" shutdown="SHUTDOWN"> ........... <!-- http連接器監聽的端口,第一個實例我不修改此值 --> <Connector port="8080" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" /> .......... <!-- AJP連接器監聽的端口,只有當使用apache作爲反向代理時才支持AJP協議,我這裏採用nginx作爲反應代理,所以禁用 --> <!-- <Connector port="8009" protocol="AJP/1.3" redirectPort="8443" /> --> ........ <!-- 定義host容器,主要是定義appBase,host name可以不作修改,因爲前端會用nginx作反應代理,tomcat並不直接面向用戶,再增加了<Context />容器 --> <Host name="localhost" appBase="/usr/local/tomcat_instances/nod1.test.com-instance-1/webapps" unpackWARs="true" autoDeploy="true"> <Context path="" docBase="webpages" /> ....... <!-- 修改日誌文件的prefix --> <Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs" prefix="nod1.test.com_access_log." suffix=".txt" pattern="%h %l %u %t "%r" %s %b" />
第二個實例:
[root@nod3 local]# vim /usr/local/tomcat_instances/nod1.test.com-instance-2/conf/server.xml ........ <!-- 第2個實例監聽端口更換成8006,只要不與其他實例監聽的端口相沖突即可--> <Server port="8006" shutdown="SHUTDOWN"> ...... <!-- http監聽端口更換爲8081端口,只要不與其他實例中監聽的端口相沖突--> <Connector port="8081" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" /> ............ <!-- <Connector port="8009" protocol="AJP/1.3" redirectPort="8443" /> --> ......... <Host name="localhost" appBase="/usr/local/tomcat_instances/nod1.test.com-instance-2/webapps" unpackWARs="true" autoDeploy="true"> <Context path="" docBase="webpages" /> ........ <Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs" prefix="nod1.test.com_access_log." suffix=".txt" pattern="%h %l %u %t "%r" %s %b" /> .......
爲兩個實例提供測試頁面:
[root@nod3 local]# vim /usr/local/tomcat_instances/nod1.test.com-instance-1/webapps/webpages/index.jsp <html><body><center> <h1>This is "nod1.test.com-instance-1"</h1> </br> Now time is: <%=new java.util.Date()%> </center> </body></html> [root@nod3 local]# vim /usr/local/tomcat_instances/nod1.test.com-instance-2/webapps/webpages/index.jsp <html><body><center> <h1>This is "nod1.test.com-instance-2"</h1> </br> Now time is: <%=new java.util.Date()%> </center> </body></html>
編寫腳本啓動各個實例:
[root@nod3 local]# vim /usr/local/tomcat_instances/nod1.test.com-instance-1/tomcat.sh #!/bin/bash #Program: manager tomcat instance #Author: zhaochj #Date: 2015-6-18 #Version 1.0 # source /etc/rc.d/init.d/functions JAVA_HOME=/usr/java/latest CATALINA_HOME=/usr/local/tomcat-7 CATALINA_BASE=$PWD export JAVA_HOME CATALINA_HOME CATALINA_BASE export PATH=$PATH:$JAVA_HOME/bin:$CATALINA_HOME/bin case "$1" in start) $CATALINA_HOME/bin/startup.sh && exit 0 ;; stop) $CATALINA_HOME/bin/shutdown.sh && exit 0 ;; *) echo "Usage: $0{start|stop}" && exit 1 ;; esac
[root@nod3 local]# chmod +x /usr/local/tomcat_instances/nod1.test.com-instance-1/tomcat.sh [root@nod3 local]# cd /usr/local/tomcat_instances/nod1.test.com-instance-1/ #切換到實例目錄 [root@nod3 nod1.test.com-instance-1]# sh tomcat.sh start Using CATALINA_BASE: /usr/local/tomcat_instances/nod1.test.com-instance-1 Using CATALINA_HOME: /usr/local/tomcat-7 Using CATALINA_TMPDIR: /usr/local/tomcat_instances/nod1.test.com-instance-1/temp Using JRE_HOME: /usr/java/latest Using CLASSPATH: /usr/local/tomcat-7/bin/bootstrap.jar:/usr/local/tomcat-7/bin/tomcat-juli.jar Tomcat started.
注意:利用這個腳本來啓動實例時一定要切換到實例的目錄纔可以,因爲腳本中“CATALINA_BASE=$PWD”是把當前目錄當作tomcat的工作目錄,當然也可以寫成絕對的路徑,像這樣“CATALINA_BASE=/usr/local/tomcat_instances/nod1.test.com-instance-1”,這樣定義後使用此腳本就沒有一定要切換到實例的目錄的限制。
[root@nod3 nod1.test.com-instance-1]# ss -tnlp | grep java LISTEN 0 100 :::8080 :::* users:(("java",2119,42)) LISTEN 0 1 ::ffff:127.0.0.1:8005 :::* users:(("java",2119,46)) #兩個端口已正常監聽
再用瀏覽器測試,如下:
[root@nod3 nod1.test.com-instance-1]# cp tomcat.sh ../nod1.test.com-instance-2/ #把腳本文件拷貝到另一個實例 [root@nod3 nod1.test.com-instance-1]# cd ../nod1.test.com-instance-2/ [root@nod3 nod1.test.com-instance-2]# pwd /usr/local/tomcat_instances/nod1.test.com-instance-2 [root@nod3 nod1.test.com-instance-2]# ls conf logs temp tomcat.sh webapps work [root@nod3 nod1.test.com-instance-2]# sh tomcat.sh start Using CATALINA_BASE: /usr/local/tomcat_instances/nod1.test.com-instance-2 Using CATALINA_HOME: /usr/local/tomcat-7 Using CATALINA_TMPDIR: /usr/local/tomcat_instances/nod1.test.com-instance-2/temp Using JRE_HOME: /usr/java/latest Using CLASSPATH: /usr/local/tomcat-7/bin/bootstrap.jar:/usr/local/tomcat-7/bin/tomcat-juli.jar Tomcat started. [root@nod3 nod1.test.com-instance-2]# ss -tnlp | grep java LISTEN 0 100 :::8080 :::* users:(("java",2187,42)) LISTEN 0 100 :::8081 :::* users:(("java",2216,42)) LISTEN 0 1 ::ffff:127.0.0.1:8005 :::* users:(("java",2187,46)) LISTEN 0 1 ::ffff:127.0.0.1:8006 :::* users:(("java",2216,46)) #兩個實例各自監聽的端口都處於LISTEN狀態
打開瀏覽器測試一下第二個實例,如下:
4、tengine安裝與負載均衡配置
tengine是taobao在官方nginx上的基礎上二次發行的一個版本,tengine繼承了nginx的特性,完全兼容nginx,taobao還在nginx基礎上增加了一些新的特性,比如能動態加載模塊等,詳細請見http://tengine.taobao.org/。
[root@nod3 tomcat]# pwd /root/tomcat [root@nod3 tomcat]# ls apache-tomcat-7.0.62.tar.gz jdk-7u9-linux-x64.rpm tengine-2.1.0-1.el6.x86_64.rpm [root@nod3 tomcat]# rpm -ivh tengine-2.1.0-1.el6.x86_64.rpm #安裝tengine Preparing... ########################################### [100%] 1:tengine ########################################### [100%] [root@nod3 tomcat]# cp /etc/tengine/nginx.conf{,.back} #備份原有配置文件 [root@nod3 tomcat]# ls /etc/tengine/nginx.conf* /etc/tengine/nginx.conf /etc/tengine/nginx.conf.back /etc/tengine/nginx.conf.default
修改配置文件,滿足我們的需求:
[root@nod3 tomcat]# vim /etc/tengine/nginx.conf #user nobody; worker_processes 1; #error_log logs/error.log; #error_log logs/error.log notice; #error_log logs/error.log info; #pid logs/nginx.pid; events { worker_connections 1024; } # load modules compiled as Dynamic Shared Object (DSO) # #dso { # load ngx_http_fastcgi_module.so; # load ngx_http_rewrite_module.so; #} http { include mime.types; default_type application/octet-stream; #log_format main '$remote_addr - $remote_user [$time_local] "$request" ' # '$status $body_bytes_sent "$http_referer" ' # '"$http_user_agent" "$http_x_forwarded_for"'; #access_log logs/access.log main; sendfile on; #tcp_nopush on; #keepalive_timeout 0; keepalive_timeout 65; #gzip on; upstream tomcat_backend { #定義一個upstream # ip_hash; #調度算法,在測試時可禁用 server 127.0.0.1:8080; #定義一個後端tomcat實例 server 127.0.0.1:8081; #定義另一個後端tomcat實例 server 127.0.0.1:9111 backup; #定義一個backup,當後端的所有tomcat實例down後向這個備份站點調度 check interval=3000 rise=2 fall=5 timeout=1000 type=http; #定義對後端服務器的健康檢測機制 check_http_send "HEAD / HTTP/1.0\r\n\r\n"; #定義健康檢測方法,以http的方式檢測 check_http_expect_alive http_2xx http_3xx; } server { #定義backup的虛擬主機 listen 9111; server_name localhost; location / { root html; index index.html; } } server { #定義對外提供服務的虛擬主機 listen 80; server_name nod3.test.com; #charset koi8-r; #access_log logs/host.access.log main; location / { proxy_pass http://tomcat_backend; #定義向後端轉發 } location /status { #定義健康檢測的監控頁面 auth_basic "nginx check health status"; #定義進行健康頁面的認證方式 auth_basic_user_file /etc/tengine/.htpasswd; #認證用戶名及密碼在存放路徑 check_status; #開啓健康檢測 access_log off; #對後端健康檢測的行爲不記錄到日誌 allow 192.168.0.0/24; #允許訪問檢測頁面的網段 deny all; #拒絕其他訪問 } .....略....
修改nginx的備份虛擬主機的主頁面:
[root@nod3 tomcat]# cp /usr/local/tengine/html/index.html{,.back} [root@nod3 tomcat]# vim /usr/local/tengine/html/index.html <h1>Sorry, you visit the website is currently under maintenance!</h1> 創建訪問健康檢測頁面的用戶名及密碼: [root@nod3 tomcat]# htpasswd -c -m /etc/tengine/.htpasswd tengine New password: Re-type new password: Adding password for user tengine [root@nod3 tomcat]# cat /etc/tengine/.htpasswd tengine:$apr1$W9U8oBI3$Ae2Q7FpSWGJw9CEcNdqWK0
啓動nginx,測試健康檢測頁面:
[root@nod3 tomcat]# service nginx start Starting nginx: [ OK ] [root@nod3 tomcat]# ss -tnlp | grep nginx LISTEN 0 128 *:80 *:* users:(("nginx",2360,7),("nginx",2361,7)) LISTEN 0 128 *:9111 *:* users:(("nginx",2360,6),("nginx",2361,6)) #兩個虛擬主機都在監聽狀態
打開瀏覽器訪問健康檢測頁面:
後端的兩個tomcat實例還沒有啓動,所以狀態是down的。
分別切換到兩個tomcat實例目錄,啓動相應的實例:
[root@nod3 tomcat]# cd /usr/local/tomcat_instances/nod1.test.com-instance-1/ #切換到第一個實例目錄 [root@nod3 nod1.test.com-instance-1]# sh tomcat.sh start #啓動第一個實例 Using CATALINA_BASE: /usr/local/tomcat_instances/nod1.test.com-instance-1 Using CATALINA_HOME: /usr/local/tomcat-7 Using CATALINA_TMPDIR: /usr/local/tomcat_instances/nod1.test.com-instance-1/temp Using JRE_HOME: /usr/java/latest Using CLASSPATH: /usr/local/tomcat-7/bin/bootstrap.jar:/usr/local/tomcat-7/bin/tomcat-juli.jar Tomcat started. [root@nod3 nod1.test.com-instance-1]# cd ../nod1.test.com-instance-2/ #切換到第二個實例 [root@nod3 nod1.test.com-instance-2]# sh tomcat.sh start #啓動第二個實例 Using CATALINA_BASE: /usr/local/tomcat_instances/nod1.test.com-instance-2 Using CATALINA_HOME: /usr/local/tomcat-7 Using CATALINA_TMPDIR: /usr/local/tomcat_instances/nod1.test.com-instance-2/temp Using JRE_HOME: /usr/java/latest Using CLASSPATH: /usr/local/tomcat-7/bin/bootstrap.jar:/usr/local/tomcat-7/bin/tomcat-juli.jar Tomcat started. [root@nod3 nod1.test.com-instance-2]# ss -tnl State Recv-Q Send-Q Local Address:Port Peer Address:Port LISTEN 0 100 :::8080 :::* LISTEN 0 128 *:80 *:* LISTEN 0 100 :::8081 :::* LISTEN 0 128 :::22 :::* LISTEN 0 128 *:22 *:* LISTEN 0 128 *:9111 *:* LISTEN 0 100 ::1:25 :::* LISTEN 0 100 127.0.0.1:25 *:* LISTEN 0 1 ::ffff:127.0.0.1:8005 :::* LISTEN 0 1 ::ffff:127.0.0.1:8006 :::* #nginx、tomcat各自監聽的端口已全部處於LISTEN狀態 [root@nod3 nod1.test.com-instance-2]# jps 2389 Bootstrap 2462 Jps 2435 Bootstrap #兩個tomcat實例已在線
刷新一下健康狀態監控頁面,可見backend server已全部在線,如下圖:
5、整體測試
測試主要有以下幾項,第一:在後端tomcat各實例都正常工作時,看用戶訪問能不能根據調度算法調度到各個實例上;第二:讓後端的一個實例處理維護狀態,測試前端的調度器能否切換到正常工作的實例上;第三:把後端各個實例都處理維護狀態,測試調度器能否把用戶的訪問調度到backup服務器上,返回給用戶一個網站在維護狀態的頁面;
先來測試第一項,打開瀏覽器直接訪問前端nginx對外提供服務的地址“nod3.test.com”:
不斷的刷新頁面,會在後端兩個tomcat實例上輪詢訪問相應的站點,因爲我們在nginx.conf中沒有指定調度算法,默認時就是round-robin算法,第一項測試通過。
再來測試第二項,先編輯nginx.conf配置文件,讓後端的一個實例處理維護狀態,如下:
[root@nod3 nod1.test.com-instance-2]# vim /etc/tengine/nginx.conf ..... upstream tomcat_backend { # ip_hash; server 127.0.0.1:8080 down; #在server後加上down就表示讓後端的這個實例下線,調度算法不再往這個實例上進行調度 server 127.0.0.1:8081; server 127.0.0.1:9111 backup; check interval=3000 rise=2 fall=5 timeout=1000 type=http; check_http_send "HEAD / HTTP/1.0\r\n\r\n"; check_http_expect_alive http_2xx http_3xx; } [root@nod3 nod1.test.com-instance-2]# service nginx reload #重新載入配置文件 the configuration file /etc/tengine/nginx.conf syntax is ok configuration file /etc/tengine/nginx.conf test is successful Reloading nginx: [ OK ]
刷新健康狀態監控頁面,看有什麼變化,如下圖:
刷新後,Name爲“127.0.0.1:8080”的這個實例已被踢出,不再是集羣的一部份,這正是我們所要的,再在主頁的訪問頁面上刷新觀察變化,如下圖:
刷新後頁面一直訪問的是tomcat第二個實例提供的頁面內容,所以調度器已把所有的請求都調度到了此實例上。至此,第二項測試通過!
接下來開始第三項測試,也讓第二個實例處理維護狀態,和上邊同樣操作:
[root@nod3 nod1.test.com-instance-2]# vim /etc/tengine/nginx.conf ..... upstream tomcat_backend { # ip_hash; server 127.0.0.1:8080 down; #在server後加上down就表示讓後端的這個實例下線,調度算法不再往這個實例上進行調度 server 127.0.0.1:8081 down; #同樣加上down,讓實例處理維護狀態 server 127.0.0.1:9111 backup; check interval=3000 rise=2 fall=5 timeout=1000 type=http; check_http_send "HEAD / HTTP/1.0\r\n\r\n"; check_http_expect_alive http_2xx http_3xx; } [root@nod3 nod1.test.com-instance-2]# service nginx reload #重新載入配置文件 the configuration file /etc/tengine/nginx.conf syntax is ok configuration file /etc/tengine/nginx.conf test is successful Reloading nginx: [ OK ]
再刷新健康狀態監控頁面,如下圖:
上圖中後端的兩個實例都不在了,再看一下主頁訪問頁面,如下圖:
刷新後被調度到了nginx自己的虛擬主機上,給用戶提供了一個友好的說明信息。至此,第三項測試也通過!
6、思考和優化tomcat配置流程
至此,以nginx反向代理多實例tomcat實現負載均衡已能正常的工作,但不得不還要思考以下幾個問題:
第一:安全問題,以上的測試都是基於root用戶來啓動tomcat的各個實例的,以root權限來運行一個服務是不可取的,應該以一個權限較低的用戶來啓動tomcat實例;
[root@nod3 ~]# ps ax -o user,command | grep java #下邊的輸出信息可見運行tomcat實例的用戶是“root” root /usr/java/latest/bin/java -Djava.util.logging.config.file=/usr/local/tomcat_instances/nod1.test.com-instance-1/conf/logging.properties -Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager -Djava.endorsed.dirs=/usr/local/tomcat-7/endorsed -classpath /usr/local/tomcat-7/bin/bootstrap.jar:/usr/local/tomcat-7/bin/tomcat-juli.jar -Dcatalina.base=/usr/local/tomcat_instances/nod1.test.com-instance-1 -Dcatalina.home=/usr/local/tomcat-7 -Djava.io.tmpdir=/usr/local/tomcat_instances/nod1.test.com-instance-1/temp org.apache.catalina.startup.Bootstrap start root /usr/java/latest/bin/java -Djava.util.logging.config.file=/usr/local/tomcat_instances/nod1.test.com-instance-2/conf/logging.properties -Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager -Djava.endorsed.dirs=/usr/local/tomcat-7/endorsed -classpath /usr/local/tomcat-7/bin/bootstrap.jar:/usr/local/tomcat-7/bin/tomcat-juli.jar -Dcatalina.base=/usr/local/tomcat_instances/nod1.test.com-instance-2 -Dcatalina.home=/usr/local/tomcat-7 -Djava.io.tmpdir=/usr/local/tomcat_instances/nod1.test.com-instance-2/temp org.apache.catalina.startup.Bootstrap start
第二:tomcat多實例配置過程比較繁瑣,如果能以一個腳本來完成配置,腳本運行後運維人員只需要提供好應用的源碼,再修改相應的配置文件,這樣一個實例很快就可配置好;
第三:當服務器運行的實例較多時,怎樣對全部實例進行統一的管理,比如我需要所所有的實例都給停掉,再把所有的實例啓動起來,這也需要一個腳本來對所有的實例進行統一的管理;
接下來我們就來解決上邊的三個問題,第一個問題與第二個問題可以統一起來解決,在配置多實例時把手動創建目錄,複製文件,修改權限操作全部在腳本中完成,並在啓動實例時su到一個普通帳號就可實現,腳本如下:
[root@nod3 tomcat]# pwd /root/tomcat [root@nod3 tomcat]# ls apache-tomcat-7.0.62.tar.gz jdk-7u9-linux-x64.rpm tengine-2.1.0-1.el6.x86_64.rpm tomcat_instances_config.sh [root@nod3 tomcat]# vim tomcat_instances_config.sh #!/bin/bash #Program: tomcat multiple instances configuration #Author: zhaochj #Date: 2015-6-18 #Version 1.0 # source /etc/rc.d/init.d/functions JAVA_HOME=/usr/java/latest CATALINA_HOME=/usr/local/tomcat-7 INSTANCE_DIR=/usr/local/tomcat_instances INSTANCE_NAME=nod1.test.com-instance-3 TOMCAT_USER=tomcat #創建實例目錄 if [ -d $INSTANCE_DIR ];then mkdir $INSTANCE_DIR/$INSTANCE_NAME else echo 'Plase check the $INSTANCE_DIR' && exit 1 fi #複製所需文件及創建相應的工作目錄 if [ -d $INSTANCE_DIR/$INSTANCE_NAME ];then cp -r $CATALINA_HOME/conf $INSTANCE_DIR/$INSTANCE_NAME/ > /dev/null 2>&1 mkdir -pv $INSTANCE_DIR/$INSTANCE_NAME/{logs,temp,webapps/ROOT,work} > /dev/null 2>&1 fi #創建用於啓動tomcat實例的用戶並修改實例目錄的權限 id $TOMCAT_USER > /dev/null 2>&1 || useradd -r $TOMCAT_USER chown -R $TOMCAT_USER:$TOMCAT_USER $INSTANCE_DIR/$INSTANCE_NAME #定義一個控制tomcat啓動關閉的腳本 cat > $INSTANCE_DIR/$INSTANCE_NAME/tomcat.sh << EOF #!/bin/bash #Program: manager tomcat instance start or stop #Author: zhaochj #Date: 2015-6-18 #Version 1.0 # source /etc/rc.d/init.d/functions JAVA_HOME=/usr/java/latest #定義tomcat啓動參數 JAVA_OPTS="-Xms128M -Xmx128M" CATALINA_HOME=/usr/local/tomcat-7 CATALINA_BASE=\$PWD TOMCAT_USER=tomcat export JAVA_HOME JAVA_OPTS CATALINA_HOME CATALINA_BASE #定義函數 start() { su \$TOMCAT_USER \$CATALINA_HOME/bin/startup.sh } stop() { su \$TOMCAT_USER \$CATALINA_HOME/bin/shutdown.sh } restart() { stop sleep 3 start } #接受傳遞的參數做相應的操作 case "\$1" in start) \$1 ;; stop) \$1 ;; restart) \$1 ;; *) echo "Usage: \$0{start|stop|restart}" && exit 1 ;; esac EOF #修改腳本權限 chmod ug+x $INSTANCE_DIR/$INSTANCE_NAME/tomcat.sh
[root@nod3 tomcat]# chmod +x tomcat_instances_config.sh
接下來測試腳本的可用性:
[root@nod3 tomcat]# ls /usr/local/tomcat_instances/ #存放tomcat各實例目錄裏只有兩個目錄 nod1.test.com-instance-1 nod1.test.com-instance-2 [root@nod3 tomcat]# sh tomcat_instances_config.sh #運行腳本 [root@nod3 tomcat]# ls /usr/local/tomcat_instances/ -l total 12 drwxr-xr-x 7 root root 4096 Jun 18 22:28 nod1.test.com-instance-1 drwxr-xr-x 7 root root 4096 Jun 18 22:42 nod1.test.com-instance-2 drwxr-xr-x 7 tomcat tomcat 4096 Jun 19 15:15 nod1.test.com-instance-3 #第三個實例目錄生成,並且屬主與屬組已是tomcat [root@nod3 tomcat]# ls /usr/local/tomcat_instances/nod1.test.com-instance-3/ -l #只有腳本的屬主、屬組是root,而且其他用戶沒有可執行權限 total 24 drwxr-xr-x 2 tomcat tomcat 4096 Jun 19 15:15 conf drwxr-xr-x 2 tomcat tomcat 4096 Jun 19 15:15 logs drwxr-xr-x 2 tomcat tomcat 4096 Jun 19 15:15 temp -rwxr-xr-- 1 root root 717 Jun 19 15:15 tomcat.sh drwxr-xr-x 3 tomcat tomcat 4096 Jun 19 15:15 webapps drwxr-xr-x 2 tomcat tomcat 4096 Jun 19 15:15 work
提供一個測試主頁做測試:
[root@nod3 tomcat]# vim /usr/local/tomcat_instances/nod1.test.com-instance-3/webapps/ROOT/index.jsp <html><body><center> <h1>This is a new tomcat instance!</h1> </br> Now time is: <%=new java.util.Date()%> </center> </body></html>
啓動第三個實例:
[root@nod3 tomcat]# cd /usr/local/tomcat_instances/nod1.test.com-instance-3/ #不要忘記切換到實例目錄 [root@nod3 nod1.test.com-instance-3]# ./tomcat.sh start #啓動實例 Using CATALINA_BASE: /usr/local/tomcat_instances/nod1.test.com-instance-3 Using CATALINA_HOME: /usr/local/tomcat-7 Using CATALINA_TMPDIR: /usr/local/tomcat_instances/nod1.test.com-instance-3/temp Using JRE_HOME: /usr/java/latest Using CLASSPATH: /usr/local/tomcat-7/bin/bootstrap.jar:/usr/local/tomcat-7/bin/tomcat-juli.jar Tomcat started. [root@nod3 nod1.test.com-instance-3]# ss -tnlp | grep java LISTEN 0 100 :::8080 :::* users:(("java",3592,42)) LISTEN 0 1 ::ffff:127.0.0.1:8005 :::* users:(("java",3592,47)) LISTEN 0 100 :::8009 :::* users:(("java",3592,43)) #默認的配置文件監聽的三個端口都處於LISTEN狀態
[root@nod3 nod1.test.com-instance-3]# ps ax -o user,command | grep java #查看java進程 tomcat /usr/java/latest/bin/java -Djava.util.logging.config.file=/usr/local/tomcat_instances/nod1.test.com-instance-3/conf/logging.properties -Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager -Xms128M -Xmx128M -Djava.endorsed.dirs=/usr/local/tomcat-7/endorsed -classpath /usr/local/tomcat-7/bin/bootstrap.jar:/usr/local/tomcat-7/bin/tomcat-juli.jar -Dcatalina.base=/usr/local/tomcat_instances/nod1.test.com-instance-3 -Dcatalina.home=/usr/local/tomcat-7 -Djava.io.tmpdir=/usr/local/tomcat_instances/nod1.test.com-instance-3/temp org.apache.catalina.startup.Bootstrap start root grep java #上邊的輸出結果可見運行java進程的用戶不再是root,而是tomcat用戶
而用瀏覽器訪問此實例提供的服務也是正常的,見下圖:
再測試一下tomcat.sh腳本的stop、restart功能,這裏就不略過。至此用腳本的方式來部署tomcat多實例就順利完成。下邊再來寫一個能管理全部實例的腳本。
腳本內容如下:
[root@nod3 tomcat]# vim tomcat_instances_manager.sh #!/bin/bash #Describe: Unified management tomcat instances #Author: zhaochj #Date: 2015-6-19 #Version: 1.0 # source /etc/init.d/functions JAVA_HOME=/usr/java/latest CATALINA_HOME=/usr/local/tomcat-7 INSTANCE_DIR=/usr/local/tomcat_instances TOMCAT_USER=tomcat SCRIPT_NAME=tomcat.sh startall() { for instance_name in `ls $INSTANCE_DIR`;do cd $INSTANCE_DIR/$instance_name sh $SCRIPT_NAME start > /dev/null 2>&1 && echo "$instance_name start ok!" || echo "$instance_name unable to start." sleep 3 done } stopall() { for instance_name in `ls $INSTANCE_DIR`;do cd $INSTANCE_DIR/$instance_name sh $SCRIPT_NAME stop > /dev/null 2>&1 && echo "$instance_name stoped." || echo "$instance_name unable to stop." sleep 3 done } #直接殺掉進程 killall() { for jvmpid in `$JAVA_HOME/bin/jps | grep -i bootstrap | awk '{print $1}'`;do kill -9 $jvmpid done } case "$1" in startall) $1 ;; stopall) $1 ;; killall) $1 ;; *) echo "Usage: $0{startall|stopall|killall}" && exit 1 ;; esac [root@nod3 tomcat]# chmod +x tomcat_instances_manager.sh
爲了方便測試能統一管理全部實例的腳本,先把先前部署的第一個和第二個實例目錄的屬主與屬組修改成tomcat,並把啓動實例的腳本更換爲實例三中的腳本,即如下操作:
[root@nod3 tomcat]# chown -R tomcat:tomcat /usr/local/tomcat_instances/nod1.test.com-instance-1/ [root@nod3 tomcat]# chown -R tomcat:tomcat /usr/local/tomcat_instances/nod1.test.com-instance-2/ [root@nod3 tomcat]# cp /usr/local/tomcat_instances/nod1.test.com-instance-3/tomcat.sh /usr/local/tomcat_instances/nod1.test.com-instance-1/ cp: overwrite `/usr/local/tomcat_instances/nod1.test.com-instance-1/tomcat.sh'? y [root@nod3 tomcat]# cp /usr/local/tomcat_instances/nod1.test.com-instance-3/tomcat.sh /usr/local/tomcat_instances/nod1.test.com-instance-2/ cp: overwrite `/usr/local/tomcat_instances/nod1.test.com-instance-2/tomcat.sh'? y [root@nod3 tomcat]# ll /usr/local/tomcat_instances/nod1.test.com-instance-1/ total 24 drwxr-xr-x 3 tomcat tomcat 4096 Jun 18 22:34 conf drwxr-xr-x 2 tomcat tomcat 4096 Jun 19 11:02 logs drwxr-xr-x 2 tomcat tomcat 4096 Jun 18 21:16 temp -rwxr-xr-x 1 tomcat tomcat 717 Jun 19 16:05 tomcat.sh drwxr-xr-x 3 tomcat tomcat 4096 Jun 18 21:34 webapps drwxr-xr-x 3 tomcat tomcat 4096 Jun 18 22:14 work [root@nod3 tomcat]# ll /usr/local/tomcat_instances/nod1.test.com-instance-2/ total 24 drwxr-xr-x 3 tomcat tomcat 4096 Jun 18 22:48 conf drwxr-xr-x 2 tomcat tomcat 4096 Jun 19 11:03 logs drwxr-xr-x 2 tomcat tomcat 4096 Jun 18 21:16 temp -rwxr-xr-x 1 tomcat tomcat 717 Jun 19 16:06 tomcat.sh drwxr-xr-x 3 tomcat tomcat 4096 Jun 18 21:35 webapps drwxr-xr-x 3 tomcat tomcat 4096 Jun 18 22:47 work
注意:第三個實例監聽的端口是與第一個實例相沖突的,不要忘記修改,把<Server />容器中的"prot=8005"修改成"prot=8007",註釋掉AJP連接器,http連接器的端口修改爲8082,修改過程不再列出,可以按照上邊修改第二個實例的方法來修改。
用腳本啓動所有的實例:
[root@nod3 tomcat]# sh tomcat_instances_manager.sh startall nod1.test.com-instance-1 start ok! nod1.test.com-instance-2 start ok! nod1.test.com-instance-3 start ok! [root@nod3 tomcat]# jps 5269 Bootstrap 5241 Bootstrap 5294 Bootstrap 5308 Jps
等一會後查看端口的監聽狀態,如下:
[root@nod3 tomcat]# ss -tnl State Recv-Q Send-Q Local Address:Port Peer Address:Port LISTEN 0 100 :::8080 :::* LISTEN 0 100 :::8081 :::* LISTEN 0 100 :::8082 :::* LISTEN 0 128 :::22 :::* LISTEN 0 128 *:22 *:* LISTEN 0 100 ::1:25 :::* LISTEN 0 100 127.0.0.1:25 *:* LISTEN 0 1 ::ffff:127.0.0.1:8005 :::* LISTEN 0 1 ::ffff:127.0.0.1:8006 :::* LISTEN 0 1 ::ffff:127.0.0.1:8007 :::* #各個實例的監聽端口都是LISTEN狀態
再關閉所有實例,如下:
[root@nod3 tomcat]# sh tomcat_instances_manager.sh stopall nod1.test.com-instance-1 stoped. nod1.test.com-instance-2 stoped. nod1.test.com-instance-3 stoped. [root@nod3 tomcat]# jps 5439 Jps #如果stopall不能全部關閉實例,那直接傳遞killall參數。
7、總結
至此,一個多實例的tomcat配置就完成了,並利用tengine實現了實例級別的負載均衡系統。tomcat的運行也是用root用戶運行,在安全性在得到了一定的保障,在配置多實例時還利用腳本完成配置操作,在對多實例的統一管理上也實現了用腳本來統一管理,這大大節省了部署的時間,提高了生產率,但在生產環境下,在tomcat上線前還得做一些基本安全加固和優化操作,下一次再開一博來嘮嘮。
兩個tomcat多實例的管理腳本請在這裏下載:https://github.com/zhaochj/myrepository/tree/master/shellscripts