Tomcat 고가용성 구성 테스트 노트
Tomcat 6.0을 가지고 고가용성, 즉 클러스터링을 따라해 보았습니다.
시나리오.
- tomcat 인스턴스는 weblogic, oc4j와 비슷하게 제품 바이너리와 인스턴스를 분리해 본다.
- cluster 구성으로 생성한다.
- 애플리케이션 배포 시나리오를 생각해 본다.
- 테스트 애플리케이션 준비
- Apache mod_proxy_jk 세팅
톰캣 바이너리는 app/apache/apache-tomcat-6.0.32 경로에 있습니다. 인스턴스 경로는 아래와 같은 구성을 시도해봅니다.
app/apache/user_projects - 톰캣 인스턴스묶음용? 경로
app/apache/user_projects/cluster_domain - 톰캣이 만들어질 도메인?
app/apache/user_projects/cluster_domain/bin - 도메인용 스크립트 디렉토리 ( startup.sh, shutdown.sh 존재 )
app/apache/user_projects/cluster_domain/servers - 톰캣 인스턴스용 디렉토리
app/apache/user_projects/cluster_domain/servers/tomcat1 - 'tomcat1'이란 이름의 톰캣 인스턴스
app/apache/user_projects/cluster_domain/servers/tomcat1/bin,conf,logs,temp,webapps,work - 'tomcat1'인스턴스 구성요소
app/apache/user_projects/cluster_domain/servers/tomcat2 - 'tomcat2'이란 이름의 톰캣 인스턴스
app/apache/user_projects/cluster_domain/servers/tomcat2/bin,conf,logs,temp,webapps,work - 'tomcat2'인스턴스 구성요소
app/apache/user_projects/cluster_domain/webapps - 이 도메인에서 사용할 애플리케이션 준비 디렉토리
인스턴스 구성. bin 경로와 conf 경로를 준비합니다.
# DOMAIN_HOME=~/app/apache/user_projects/cluster_domain
# cd $DOMAIN_HOME
# cd servers/tomcat1
# cd bin
# cp ../../../../apache-tomcat-6.0.32/bin/*.sh .
# cd ../conf
# cp -R ../../../../apache-tomcat-6.0.32/conf/* .
인스턴스 구성중 우선 catalina.sh 구성을 수정합니다.
JAVA_HOME=~/app/sun/java/jdk1.6.0_27
JAVA_OPTS="-Djava.security.egd=file:/dev/./urandom -Djava.awt.headless=true -XX:MaxPermSize=256m"
CATALINA_HOME=~/app/apache/apache-tomcat-6.0.32
CATALINA_BASE=~/app/apache/user_projects/cluster_domain/servers/tomcat1
위 구성을 통해 tomcat/lib의 바이너리를 사용하면서, tomcat1경로를 기준으로한 톰캣을 사용할 수 있습니다.
인스턴스 구성중 server.xml 구성을 변경합니다. server.xml의 경우 포트, cluster 항목, 애플리케이션 경로를 구성합니다. 기본 포트에 tomcat1은 10000번을 더하고, tomcat2는 20000번을 더하여 포트를 정했습니다.(18080, 18443, 18009, 28080, 28443, 28009 )
<?xml version='1.0' encoding='utf-8'?>
<Server port="18005" shutdown="SHUTDOWN">
<Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="on" />
<Listener className="org.apache.catalina.core.JasperListener" />
<Listener className="org.apache.catalina.core.JreMemoryLeakPreventionListener" />
<Listener className="org.apache.catalina.mbeans.ServerLifecycleListener" />
<Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener" />
<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">
<Executor name="tomcatThreadPool" namePrefix="catalina-exec-"
maxThreads="150" minSpareThreads="4"/>
<Connector executor="tomcatThreadPool"
port="18080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="18443" />
<Connector port="18443" maxThreads="200"
scheme="https" secure="true" SSLEnabled="true"
keystoreFile="./conf/.keystore" keystorePass="changeit"
clientAuth="false" sslProtocol="TLS"/>
<Connector port="18009" protocol="AJP/1.3" redirectPort="18443" />
<Engine name="Catalina" defaultHost="localhost" jvmRoute="jvm1">
<Host name="localhost" appBase="webapps"
unpackWARs="true" autoDeploy="true"
xmlValidation="false" xmlNamespaceAware="false">
<Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
prefix="localhost_access_log." suffix=".log" pattern="common" resolveHosts="false"/>
</Host>
</Engine>
</Service>
</Server>
이 구성을 tomcat1, tomcat2에 맞게 각각 작성하면, 두개의 인스턴스를 구동하고 cluster를 확인할 수 있습니다.
톰캣에서 기본으로 제공하고 유일한 멀티캐스트 기반 TCP 클러스터를 사용해 봅니다.
server.xml에서 Engine항목에 구성을 추가합니다.
<!-- 생략 -->
<Engine name="Catalina" defaultHost="localhost" jvmRoute="jvm1">
<!-- 생략 -->
<Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster"/>
<!-- 생략 -->
</Engine>
<!-- 생략 -->
지금까지의 구성을 통해서는 tomcat1, tomcat2 인스턴스에 각각 webapps 경로에 애플리케이션을 배포해야 합니다.
만약 두개의 애플리케이션이 동일한 애플리케이션을 사용하고 같이 배포되어야 한다면, 앞서 준비한 cluster_domain/webapps 에 애플리케이션을 넣어주고, server.xml의 Host항목에 appBase를 “../../webapps” 형태로 해주면 됩니다.
단, tomcat의 manager같은 경우 앞단의 load balancer를 타고 들어갈 때, 구성이 좀 까다로워지는 관계로 저는 인스턴스별로 애플리케이션이 배포되도록 했습니다.
단, 형태를 domain/webapps에 원본을 두고, 인스턴스는 symbolic link를 사용하는 꼼수를 좀 사용했구요.
클러스터링을 활용하는 애플리케이션은 아래 두 파일을 가지는 간단한 예제로 확인할 수 있습니다.
index.jsp
<%@page contentType="text/html; charset=UTF-8" %>
<%
if ( request.getParameter("name") != null ) {
session.setAttribute("name", request.getParameter("name"));
}
%>
<%=session.getAttribute("name") %>
<form method="POST">
<input type="text" name="name" />
<input type="submit" />
</form>
web.xml
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee">
<distributable />
</web-app>
distributable 항목을 넣어야 cluster환경에서 쓸 수 있습니다. 그리고 물론 session에 주입하는 객체는 serializable을 구현하도록해야 하고.
이 앱을 준비한 후, http://localhost:18080/test/index.jsp http://localhost:28080/test/index.jsp 형태로 호출하여 세션이 공유되는 것을 확인할 수 있습니다.
클러스터된 애플리케이션에 아파치가 밸런싱하여 가는 구성과 각각의 서버로 가능 구성은 각각 아래와 같습니다.
# 로드 밸런싱 구성
<Proxy balancer://cluster>
BalancerMember ajp://localhost:18009 loadfactor=1
BalancerMember ajp://localhost:28009 loadfactor=1
ProxySet lbmethod=bytraffic
</Proxy>
# 로드 밸런서로 보내기
ProxyPass /clusteredapp balancer://cluster/clusteredapp
ProxyPassReverse /clusteredapp balancer://cluster/clusteredapp
# 개별 서버로 보내기
ProxyPass /tomcat1-manager ajp://localhost:18009/tomcat1-manager
ProxyPassReverse /tomcat1-manager ajp://localhost:18009/tomcat1-manager
ProxyPass /tomcat2-manager ajp://localhost:28009/tomcat2-manager
ProxyPassReverse /tomcat2-manager ajp://localhost:28009/tomcat2-manager