본문 바로가기

서버운영 (TA, ADMIN)/미들웨어

[톰캣] 아파치톰캣 알아보기(1)

Apache HTTP Server를 배치하는 경우 Tomcat Connector(mod_jk)를 통해 연결하는 것을 전제로 한다. nginx와 같은 웹서버 제품과 연결하여 구성할 수도 있다.

 

Tomcat은 웹 애플리케이션 서버이면서 하나의 자바 애플리케이션이다. Tomcat을 기동하기 위해서는 JRE(Java Runtime Environment)환경에서 JVM상 o.a.catalina.startup.Bootstrap 클래스를 실행해야 한다.

 

컨트롤러 기기란?

 

yum(Yellowdog updater, Modified): 레드햇 계열 리눅스 패키지 관리 도구

apt(Advanced Packaging Tool): BSD 계열 리눅스 등에 포팅된 패키징 툴

 

32bit 자바의 최대 사용 가능 메모리를 2^32(4GB)이다. 따라서 수 GB의 메모리가 필요한 자바 애플리케이션은 메모리 한계 때문에 32bit 자바보다는 64bit 자바를 사용해야 한다. 64bit 자바는 32bit에 비해 내부 데이터 Type이 차지하는 공간이 커서 캐시 효율이 떨어진다. 

 

자바 애플리케이션 실행 과정에서 핵심적인 역할을 하는 것은 bytecode이다. java 확장자의 소스 코드를 컴파일하면 class 확장자의 파일이 생성되는데 이 파일은 하드웨어적 특성이나 OS 특성에 독립적인 파일이다. JVM은 이 클래스 파일, 즉 bytecode를 인터프리터하여 실행한다.

 

JVM Stack 처리 중 발생할 수 있는 메모리 오류는 StackOverflowError와 OutOfMemoryError 등이 있다. 각 Thread가 사용 가능한 Stack 크기는 정해져있는데 만일 특정 Thread가 최대 Stack 크기 이상으로 메서드를 생성, 호출하면 StackOverflowError가 발생한다. 또한 여유 메모리가 충분하지 않으면 Stack 자체를 생성하지 못하면서 OutOfMemoryError가 발생하기도 한다.

 

클래스 파일은 자바 실행 파일이기때문에 자바 가상 머신인 JVM상에서 실행해야 한다. JDK가 기본 제공하고 있는 Java Class File Disassembler인 javap가 존재한다.

 

Constant Pool? javap의 -verbose 옵션은 Constant Pool 정보, Flag, Stack, Local Variable, parameter 등 추가적인 정보를 제공한다. local variable 값은 Local Variable 배열 안에 담겨있다. 그러면 각 local variable의 이름은 javac의 -g 혹은 -g:vars 옵션을 사용하여 컴파일하면 추가 디버그 옵션을 포함하게 되는데, 이렇게 컴파일된 클래스 파일을 javap -l 옵션으로 역어셈블하면 LocalVariableTable 정보를 얻을 수 있다.

 


 

각 클래스 파일은 다음 아이템들을 포함하고 있다.

 

 아이템 분류

 클래스 파일 내 아이템

 크기

 Header

 magic

 4byte

 minor_version

 2byte

 major_version

 2byte

 Constant Pool

 constant_pool_count

 2byte

 constant_pool

 배열(길이: constant_pool_count - 1)

 Class

 access_flag

 2byte

 this_class

 2byte

 super_class

 2byte

 interfaces_count

 2byte

 interfaces

 배열(길이: interfaces_count)

 Field

 fields_count

 2byte

 fields

 배열(길이: fields_count)

 Method

 methods_count

 2byte

 methods

 배열(길이: methods_count)

 Attribute

 attributes_count

 2byte

 attributes

 배열(길이: attributes_count)

 


 

톰캣의 기동과 중에 사용하는 파일은 startup과 shutdown이다.

 

-rwxr-x---  1 tomcat tomcat  21793 Sep 28  2017 backup_catalina.sh

-rw-r-----  1 tomcat tomcat  34813 Sep 28  2017 bootstrap.jar

-rw-r-----  1 tomcat tomcat  14505 Sep 28  2017 catalina.bat

lrwxrwxrwx  1 tomcat tomcat     32 Feb 18 11:09 catalina.sh

-rw-r-----  1 tomcat tomcat   1664 Sep 28  2017 catalina-tasks.xml

-rw-r-----  1 tomcat tomcat  24283 Sep 28  2017 commons-daemon.jar

-rw-r-----  1 tomcat tomcat 204944 Sep 28  2017 commons-daemon-native.tar.gz

-rw-r-----  1 tomcat tomcat   2040 Sep 28  2017 configtest.bat

-rwxr-x---  1 tomcat tomcat   1922 Sep 28  2017 configtest.sh

-rwxr-x---  1 tomcat tomcat   8004 Sep 28  2017 daemon.sh

-rw-r-----  1 tomcat tomcat   2091 Sep 28  2017 digest.bat

-rwxr-x---  1 tomcat tomcat   1965 Sep 28  2017 digest.sh

-rw-r-----  1 tomcat tomcat   3147 Sep 28  2017 setclasspath.bat

-rwxr-x---  1 tomcat tomcat   3306 Sep 28  2017 setclasspath.sh

-rw-r-----  1 tomcat tomcat   2020 Sep 28  2017 shutdown.bat

-rwxr-x---  1 tomcat tomcat   1902 Sep 28  2017 shutdown.sh

-rw-r-----  1 tomcat tomcat   2022 Sep 28  2017 startup.bat

-rwxr-x---  1 tomcat tomcat   1904 Sep 28  2017 startup.sh

-rw-r-----  1 tomcat tomcat  48422 Sep 28  2017 tomcat-juli.jar

drwxr-xr-x  8 tomcat tomcat   4096 Aug 29  2017 tomcat-native-1.2.14-src

-rw-r-----  1 tomcat tomcat 404159 Sep 28  2017 tomcat-native.tar.gz

-rw-r-----  1 tomcat tomcat   3621 Sep 28  2017 tool-wrapper.bat

-rwxr-x---  1 tomcat tomcat   4579 Sep 28  2017 tool-wrapper.sh

-rw-r-----  1 tomcat tomcat   2026 Sep 28  2017 version.bat

-rwxr-x---  1 tomcat tomcat   1908 Sep 28  2017 version.sh

 
톰캣은 기동시 CATALINA_BASE, CATALINA_HOME, CATALINA_TMPDIR, JRE_HOME, CLASSPATH 등의 환경변수를 출력한다. 환경 변수를 명시적으로 설정하지 않았더라도 Tomcat이 환경 분석 후 자동으로 설정한다. 

 

로그 파일은 logs 디렉터리 하위에 위치한다. 만약 각 관리 애플리케이션을 사용하지 않는다면 $CATALINA_BASE/conf/logging.properties 내 해당 로그 항목을 제거하여 로그를 남기지 않도록 할 수 있다. catalina.out 파일은 Console 로그이다.

 

 

[Shutdown 포트]

Tomcat은 중지 시에 Shutdown 포트를 사용한다. Shutdown 포트는 오직 중지 요청만 Listen한다. 그런데 이 기능을 악용하여 Tomcat을 임의로 중지할 수 있다.

 

Tomcat은 기본적으로 localhost의 Shutdown 포트만 Listen한다. 따라서 localhost 외 원격 사용자는 Shutdown 포트에 진입할 수 없다. tomcat 계정으로 Tomcat을 기동했다면 shutdown.sh를 통한 중지 작업은 오직 tomcat 계정만 가능하다. 그러나 Shutdown 포트를 이용한 중지 작업은 네트워크 통신이기 때문에 OS 계정 권한과는 상관없이 수행 가능하다.

 

telnet localhost 8005

 

INFO: A valid shutdown command was received via the shutdown port. Stopping the Server instance.

 

만일 Tomcat 기동 계정이 아닌 다른 계정으로 shutdown.sh를 실행하면 $CATALINA_BASE/conf/server.xml 파일을 조회할 수 있는권한이 없기 때문에 Tomcat을 중지할 수 없다.

 

만약 Tomcat 기동 계정이 아닌 다른 계정이 server.xml 파일을 조회할 수 있는 권한이 있고 shutdown.sh 파일을 실행할 수 있는 권한이 이싸면 호스트 내 어느 계정이나 shutdown.sh을 통해 Tomcat을 중지할 수 있다. 따라서 $CATALINA_BASE/conf 하위 설정 파일의 권한 관리가 필요하다.

 

$CATALINA_BASE/con/server.xxml 내 Shutdown 포트가 -1으로 설정되어 있기 때문에 Tomcat은 SIGTERM 시그널을 통해 중지하게 될 것이다. 하지만 SIGTERM 시그널을 받을 Tomcat Process 정보가 어디에도 없다. 이 문제의 해결을 위해 $CATALINA_PID 환경 변수를 이용하여 Tomcat Process 번호를 포함하고 있는 PID 파일을 생성한다.

 

CATALINA_BASE=/app/apache-tomcat-7.0.64

export CATALINA_BASE

CATALINA_PID=$CATALINA_BASE/tomcat.CATALINA_PID

export CATALINA_PID

./startup.sh

 

$CATALINA_PID 환경 변수 설정 후 Tomcat을 기동하면 기동 메시지에 "Using CATALINA_PID:"가 추가된다. 환경 변수 중 $CATALINA_PID가 설정되어 있으면 catalina.sh가 그값을 출력하기 때문이다.

 

Shutdown 포트가 -1로 설정되어 있고, $CATALINA_PID 환경 변수를 통해 PID 파일이 생성되어 있는 상태라면 Tomcat을 정상 중지 할 수 있게 된다.

 

스크립트를 보면 리턴 값이 0이 아니고 CATALINA_PID 변수가 설정되어 있을때 kill -15를 사용하여 중지하고 있다. 15 시그널인 SIGTERM은 9인 SIGKILL에 비해 좀 더 안전한 중지 방법니다.

# stop failed. Shutdown port disabled? Try a normal kill.
if [ $? != 0 ]; then
	if [ ! -z "$CATALINA_PID" ]; then
		echo "The stop command failed. Attempting to signal the process to stop through OS signal."
		kill -15 `cat "$CATALINA_PID"` >/dev/null 2>&1
	fi
fi

 


 

catalina.sh

 

catalina.sh의 argument 종류는 jpda/debug/run/start/stop/version 등이 존재한다.

 

org.apache.catalina.startup.Bootstrap "$@" start

org.apache.catalina.startup.Bootstrap "$@" stop

 

실질적인 기동/중지 역할을 하는 것은 Bootstrap 클래스다. o.a.catalina.startup.Bootstrap 클래스의 main 메서드를 확인해보겠다.

public static void main(String args[]) {
	try {
		String command = "start";
		if (args.length > 0) {
			command = args[args.length - 1];
		}

		if  (command.equals("startd")) {
			args[args.length - 1] = "start";
			daemon.load(args);
			daemon.start();
		} else if (command.equals("stopd")) {
			args[args.length - 1] = "stop";
			daemon.stop();
		} else if (command.equals("start")) {
			daemon.setAwait(true);
			daemon.load(args);
			daemon.start();
		} else if (command.equals("stop")) {
			daemon.stopServer(args);
		} else if (command.equals("configtest")) {
			daemon.load(args);
			if (null == daemon.getServer()) {
				System.exit(1);
			}
			System.exit(0);
		} else {
			log.warn("Bootstrap: command \"" + command + "\" does not exist.");
		}
	}
}

 


 

환경 변수

 

Tomcat의 주요 환경 변수에 대해 알아보겠다. 주로 $CATALINA_BASE/bin 혹은 $CATALINA_BASE/conf 디렉토리 내 실행 및 설정 파일 내에서 환경 변수를 사용한다.

 

Catalina는 과거 Tomcat 코드명이었고, 지금은 Tomcat 코어 엔진을 뜻하는 단어이다. Tomcat에서 가장 중요한 환경 변수 또한 $CATALINA_HOME과 $CATALINA_BASE이다.

 

먼저 $CATALINA_HOME은 Tomcat 설치 디렉토리로 흔히 Tomcat 엔진 위치로 통한다.

 

$CATALINA_BASE는 엔진 하위의 각 Tomcat 인스턴스 디렉토리로 기동/중지 파일 등 실행 관련 파일(bin), 설정 파일(conf), 라이브러리 파일(lib), 그리고 로그 파일(logs) 디렉터리 등이 있다. 만약 인스턴스가 오직 1개라면 $CATALINA_HOME = $CATALINA_BASE와 같이 구성할 수 있다. 또한 명시적으로 $CATALINA_BASE 환경 변수를 설정하지 않으면 $CATALINA_HOME = $CATALINA_BASE가 된다.

# Copy CATALINA_BASE from CATALINA_HOME if not already set
[ -z "$CATALINA_BASE" ] && CATALINA_BASE="$CATALINA_HOME"

 

LOGGING_CONFIG(로그 설정 파일을 설정)와 CATALINA_OPTS 등은 start인 경우에만 사용된다. 종종 JAVA_OPTS와 CATALINA_OPTS를 구별하지 않고 사용하는 경우가 있다. 하지만 Tomcat을 사용할때는 문제가 될수 있다. GC 로그 파일 설정이 대표적인 예다. CATALINA_OPTS가 아닌 JAVA_OPTS에 넣으면 Tomcat을 중지할 경우 기동시 생성하여 기록 중이던 GC 로그 파일을 overwrite하기 때문에 GC 파일 내용이 모두 사라지게 된다.

 

JNI(Java Native Interface)를 통해 사용하는 Native 라이브러리 경로이다. Tomcat Native를 사용한다면 LD_LIBRARY_PATH 내에 Native 라이브러리 경로를 추가해야 한다. 기본 Native 경로는 $CATALINA_BASE/lib 디렉토리 이다.

if [ "$1" = "debug" ] ; then
	if $os400; then
		echo "Debug command not available on OS400"
		exit 1
	else
		shift
		if [ "$1" = "-security" ] ; then
			if [ $have_tty -eq 1 ]; then
				echo "Using Security Manager"
			fi
			shift
			exec "$_RUNJDB" "$LOGGING_CONFIG" $LOGGING_MANAGER $JAVA_OPTS $CATALINA_OPTS \
				-Djava.endorsed.dirs="$JAVA_ENDORSED_DIRS" -classpath "$CLASSPATH" \
				-sourcepath "$CATALINA_HOME"/../..java \
				-Djava.security.manager \
				-Djava.security.policy="$CATALINA_BASE"/conf/catalina.policy \
				-Dcatalina.base="$CATALINA_BASE" \
				-Dcatalina.home="$CATALINA_HOME" \
				-Djava.io.tmpdir="$CATALINA_TMPDIR" \
				org.apache.catalina.startup.Bootstrap "$@" start
		else
			exec "$_RUNJDB" "$LOGGING_CONFIG" $LOGGING_MANAGER $JAVA_OPTS $CATALINA_OPTS \
				-Djava.endorsed.dirs="$JAVA_ENDORSED_DIRS" -classpath "$CLASSPATH" \
				-sourcepath "$CATALINA_HOME"/../..java \
				-Dcatalina.base="$CATALINA_BASE" \
				-Dcatalina.home="$CATALINA_HOME" \
				-Djava.io.tmpdir="$CATALINA_TMPDIR" \
				org.apache.catalina.startup.Bootstrap "$@" start
		fi
fi

 

jdb를 통해 현재 실행중인 Tomcat에 접근하여 내부 정보를 확인할 수도 있으며, 특정 Thread의 Stack trace도 확인 가능하다.

 

jdb -connect sun.jvm.hotspot.jdi.SAPIDAttachingConnector:pid=8595

 

 


 

Tomcat을 포함한 자바 기반의 대부분 WAS는 Multi Thread 관리를 위하여 Thread Pool 방식을 이용한다. Multip Thread는 여러 Thread가 몇몇 메모리 영역을 공유한다. 각 Process가 독립적인 메모리 영역을 가지고 있는 Multi Process 방식에 비해 메모리 공간을 덜 사용한다는 장점이 있다. 또한 Thread Pool 방식을 통해 Thread 생성과 소멸에 필요한 많은 자원 소비를 줄일 수도 있다. 이외에도 Pool 내 최대 Thread 수를 제한할 수 있기 때문에 애플리케이션 오류에 의해 발생할 수 있는 과다한 Thread 생성을 방지하고, 처리 가능한 수 이상의 요청 유입을 방지할 수 있다.

 

Thread 사이에 자원 경합이 발생할 수도 있고, back-end의 처리 지연에 의해 Thread 수행 시간이 길어질 수도 있다. 그결과 Thread Pool 내에 할당 가능한 여유 Thread 관련 여러 문제가 발생할 수 있는데 얼마나 빨리 원인을 파악하고 조치하냐가 시스템의 신뢰도를 좌우한다.

 

 

[Thread 덤프]

과거에는 주로 Tomcat OS Process에 SIGQUIT 시그널을 보내 Thread 덤프를 생성하였으며 최근에는 JDK가 제공하는 jstack을 이용하는 경우가 많다. 단, JDK 버전에 따라 jstack(JDK 6 이상)이 제공되지 않을 수도 있으므로 확인이 필요하다.

 

Process에 시그널을 보내기 위해서는 kill 명령어를 사용한다. kill 명령어는 이름과 같이 Process를 종료하기 위해 태어난 명령어지만 이후 Process에 여러 시그널을 보내는 명령어로 진화했다. Thread 덤프 생성을 위한시그널은 SIGQUIT 혹은 3이다. 또한 앞서 Tomcat 중지를 위하여 SIGTERM 혹은 15 시그널을 사용한 바 있다. 한편 SIGKILL 혹은 9 시그널은 SIGTERM에 비해 덜 안전하지만 좀 더 빠르게 중지한다.

 

#define SIGHUP 1

#define SIGINT 2

#define SIGQUIT 3

#define SIGILL 4

#define SIGTRAP 5

#define SIGABRT 6

#define SIGIOT 6

#define SIGBUS 7

#define SIGFPE 8

#define SIGKILL 9

#define SIGUSR1 10

#define SIGSEGV 11

#define SIGUSR2 12

 

Thread 덤프를 수 초 단위로 몇 차례 생성한 후 이들을 모두 확인하는 것이 가장 기본적인 분석 방법이다.

 

 

[jstack]

jstack을 통한 Thread 덤프 생성을 위해서는 $jstack [PID] 형식과 같이 Process 번호를 넣어 실행한다. 추가로 -l 옵션을 사용하면 각 Thread의 Lock 정보까지 획득할 수 있다.

 

http-bio-8080-exec-109는 Thread 이름에서는 BIO 방식 HTTP Connector를 통해 요청이 처리되고 있음을 유추할 수 있다. Tomcat 내부 자바 Thread 중 main(o.a.catalina.startup.Bootstrap.main) 이외의 Thread는 거의 Daemon Thread에 속한다.

 

o.a.tomcat.util.threads.TaskThreadFactory 클래스를 통해 Daemon Thread 설정을 확인할 수 있다.

public Thread newThread(Runnable r) {
	TaskThread t = new TaskThread(group, r, namePrefix + threadNumber.getAndIncrement());
	t.setDaemon(daemon);
	t.setPriority(threadPriority);
	return t;
}

 

prio는 Thread priority로 1~10 사이의 값을 가진다. 이 중 10이 가장 높다. tid는 Thread의 메모리 address, nid는 Native Thread ID이다.

 

JDK 버전에 따라서는 jcmd 유틸리티를 통해서도 Thread Dump를 생성할 수 있다.

 

$ jcmd [PID] Thread.print

 

 

[Stack]

각 Thread가 사용하는 Stack 크기는 -Xss 옵션을 통해 설정할 수 있다. -XX:ThreadStackSize 옵션도 동일한 역할을 하지만 크기 단위가 기본 kbyte라는 점이 다른다. StackOverflowError 같은 Stack 부족 현상이 발생한다면 Thread의 Stack 크기를 늘려야 한다. 정상 코드 수행 중 StackOverflowError가 발생했다면 크기를 늘려야 하지만, 코드 오류 등에 의한 일시적 원인 때문이라면 근본적인 원인을 수정해야 한다. -Xss를 늘리면 Tomcat이 사용하는 전체 메모리 사용량도 늘어나기 때문이다.

 

 

[Thread 수 산정]

processor 수에 비해 너무 많은 Thread를 동시에 실행하면 빈번한 Context Switching이 발생하게 되며, 여유 Thread 관리를 위한 오버 헤드가 증가하여 성능하락이 발생할 수가 있다. Thread Pool의 용량을 늘릴 때는 back-end의 DB가 충분히 버틸수 있는지 여부도 확인해야 한다. 동시 처리 Thread 증가로 인해 DB 한계점을 넘어서게 되어 오히려 TPS가 하락할 수 있다.

 

JVM 메모리 구조 중 Heap 영역은 Tomcat 등 WAS 운영 관리에 가장 밀접한 영역이다. Heap 메모리 구조, GC 메커니즘, 그리고 관리 방법에 대해서도 알아두자.

 


 

OutOfMemoryError

 

Tomcat과 같은 WAS를 운영관리 할때 가장 흔히 접하게 되는 오류 중 하나가 OOM 또는 OOME라고 부르는 OutOfMemory이다.

 

 

[Java heap space]

Heap 영역 내에 객체 생성을 위한 충분한 메모리가 없을때 발생한다.

 

[Requested array size exceeds VM limit]

현재 메모리 공간에서 처리할 수 없을 만큼 큰 배열을 생성하는 경우에 발생한다.

 

[PerGen space]

자바 1.7 이하 버전에서 너무 많은 클래스 정보를 로딩하거나 ClassLoader의 누수가 발생하면 Permanent Generation 부족에 따른 OutOfMemoryError가 발생할 수 있다. 또한 Permanent Generation과 마찬가지로 Metaspace 부족으로 인해 OutOfMemoryError가 발생할 수 있다.

 

[unable to create new native thread]

OutOfMemoryError의 원인이 메모리가 아닌 OS 자원에 있을 가능성이 크다.

 

 

OutOfMemoryError의 경우 먼저 할당한 Heap 메모리가 적기때문에 일어나게 된다. 이때는 -Xmx 옵션을 통해 Heap 최대값을 늘려야 한다. GC 로그를 모니터링하며 Heap 크기의 최적값을 찾아야 한다.

 

대량 데이터 조회/입력으로 인하여 OutOfMemoryError가 발생하는 경우도 많다. 조회 처리 작업의 경우 DB로부터 수만~수십만 검 이상의 데이터를 패치한 후 Heap 영역 내 Collections 객체에 로드하게 되는데, 주로 기간 이력 조회나 리포트 생성, 엑셀 다운로드 등의 작업을 수행하는 관리자 시스템에서 많이 사용하는 기능이다. 

 


 

Native는 Tomcat I/O 성능 향상을 위해 OS Natvie Resource를 사용하는 추가적인 모듈로 tcnative라고도 부른다. 처리량이 많거나 I/O가 많이 발생하거나 혹은 HTTPS 통신을 하는 시스템인 경우에 주로 사용한다. Tomcat Native를 사용하기 위해서는 APR(Apache Portable Runtime) 라이브러리가 필요한데, APR은 Apache 산하 프로젝트 중 하나로 플랫폼 비종속적인 API를 제공한다.

 

Native 사용시 Connector는 ARP 방식으로 자동설정 된다.

 

org.apache.coyote.AbstractProtocol start INFO: Starting ProtocolHandler ["http-apr-8080"]

org.apache.coyote.AbstractProtocol start INFO: Starting ProtocolHandler ["ajp-apr-8009"]

 


 

server.xml

 

$CATALINA_BASE/conf/sever.xml 파일에선 <Connector>를 포함하여 <Server>, <Service>, <Engine>, <Host> 등의 구성요소를 확인할 수 있다.

 

 

Server(org.apache.catalina.Server)

최상위 Element인 <Server>는 <Service> 모음으로, Shutdown 요청 처리를 위한 address와 port 속성을 가지고 있다. 각각 Shutdown 요청을 받기 위해 listen하는 IP address와 포트를 설정하며 기본값은 localhost와 8005이다.

<Server port="8005" shutdown="SHUTDOWN">

 

만약 port 속성을 -1로 설정하면 Shutdown 포트 기능을 사용하지 않는다. 한편 shutdown 속성은 Shutdown 명령어를 가진다. 기본 설정 값은 'SHUTDOWN'인데 보안상 변경하는 것이 좋다.

 

 

Service(org.apache.catalina.Service)

Service Element는 <Server> 하위에 있으며 <Connector> 모음이다. <Service>의 속성은 className과 name 단 2개이다.

<Service name="Catalina">

 

 

Engine(org.apache.catalina.Engine)

Engine Container의 기본 클래스는 o.a.catalina.StandardEngine이다. name 속성은 <Engine>의 이름이다. defaultHost 속성은 <Engine> 하위에 속한 <Host> 가운데 하나이며 어떤 <Host>도 처리하지 않은 요청을 처리한다. jvmRoute 속성은 Apache HTTP Server 등 front-end에 위치한 Load Balancer가 여러 Tomcat 인스턴스를 구분하기 위해 사용한다. 특히 클러스터 그룹 내의 jvmRoute는 각 인스턴스마다 고유해야 한다. jvmRoute 속성이 아닌 SystemProperty 방식으로 설정하거나 $CATALINA_BASE/conf/catalina.properties 파일을 통해서도 설정할 수 있다.

<Engine name="Catalina" defaultHost="localhost">

 

 

Host(org.apache.catalina.Host)

Host Container는 가상 호스트 기능을 제공하며 기본 클래스는 o.a.catalina.StandardHost이다.

<Host name="localhost" appBase="webapps" unpackWARs="true" autoDeploy="true">

 

각 <Host>의 이름은 name 속성을 통해 설정한다. 만약 할당된 URL이 있다면 URL로 설정한다. 상위 <Engine> 내에 2개 이상의 <Host>가 구성되어 있다면 그 중 1개 <Host>가 <Engine>의 defaultHost 값이 되어야 한다.

 

appBase는 <Host>의 애플리케이션 디렉토리다. '/'을 포함한 절대 경로, 혹은 $CATALINA_BASE의 상대 경로로 설정하며 기본은 webapps이다. xmlBase는 <Host>의 애플리케이션 Context XML 파일을 포함하는 디렉터리다. appBase와 마찬가지로 절대 경로 혹은 $CATALINA_BASE의 상대 경로로 설정하며, 기본은 $CATALINA_BASE/conf/<Engine>/<Host>이다.

 

deployXML은 애플리케이션의 META-INF/context.xml 파일을 반영할지 여부를 결정하는 속성으로 기본 ture이다. deployXML가 false이면 애플리케이션의 META-INF/context.xml을 무시하기 때문에 반드시 xmlBase내에 외부 Context 파일이 존재해야 한다. 그렇지 않으면 애플리케이션 배포시 오류가 발생한다.

 

copyXML은 애플리케이션의 META-INF/context.xml 파일을 xmlBase 디렉토리로 복사할지 여부를 결정하는 속성으로 기본 false이다. true로 설정한다면 예를 들어 ROOT 애플리케이션의 META-INF/context.xml 파일은 xmlBase 내에 ROOT.xml 파일로 복사된다. 아래는 o.a.catalina.startup.HostConfig 클래스의 deployWAR 메서드 일부이다.

boolean copyThisXml = false;
if (deployXML) {
	if (host instanceof StandardHost) {
		copyThisXml = ((StandardHost) host).isCopyXML();
	}

	if (!copyThisXml && context instanceof StandardHost) {
		copyThisXml = ((StandardContext) context).getCopyXML();
	}

	if (xmlInWar && copyThisXml) {
		xml = new File(configBase(), cn.getBaseName() + ".xml");
		entry = null;
		...
	}
}

 

copyXML은 deployXML이 true일때만 유효하다. 또한 한번 복사한 이후에는 애플리케이션 내 META-INF/context.xml 파일에 변경된다 하더라도 다시 복사하지 않는다.

 

한편 autoDeploy 속성을 통해 appBase와 xmlBase 내 변경 사항을 주기적으로 확인할 수 있다. 하지만 운영 환경이라면 가급적 false로 설정하는 것이 좋다. unpackWARs는 WAR(Web Application Archive) 파일을 풀어서 사용할지 여부를 설정하는 속성으로 기본 ture이다.

 

o.a.catalina.core.StandardHost 클래스를 통해 기본값을 확인할 수 있다.

private String appBase = "webapps";
private String xmlBase = null;
private boolean autoDeploy = true;
private boolean unpackWARs = true;

 

workDir 속성은 <Host> 내 애플리케이션이 사용하는 Scratch 디렉토리를 설정한다. Scratch 디렉터리는 JSP 컴파일 결과 생성된 *.java, *.class 파일과 세션 파일 등을 저장한다.

 

 

Context

Context는 <Host> 내에 배포된 애플리케이션이다. 기본 클래스는 o.a.catalina.core.StandardContext이다.  Context 설정 방법에는 다음과 같은 종류가 있다.

 

1. 각 애플리케이션의 META-INF/context.xml을 통해 설정한다. 만일 <Host> copyXML 속성이 true라면 context.xml 파일을 자동으로 $CATALINA_BASE/conf/<Engine>/<Host> 디렉토리 아래의 [애플리케이션].xml 파일로 복사한다.

2. $CATALINA_BASE/conf/<Engine>/<Host> 디렉토리 하위에 [애플리케이션].xml을 만들어 설정한다. 이때 [애플리케이션]명은 context path와 같다.

3. $CATALINA_BASE/conf/server.xml의 <Host> 안에 설정한다.

 

$CATALINA_BASE/conf/context.xml 파일은 Global Context 파일이기 때문에 Tomcat 내의 모든 애플리케이션이 공통으로 사용한다. 또 $CATALINA_BASE/conf/<Engine>/<Host>/context.xml.default 파일은 <Host>의 Global Context 파일이기 때문에 <Host> 하위 모든 애플리케이션이 사용한다.

 

logEffectiveWebXml은 해당 애플리케이션에 적용하는 실제 web.xml 정보를 보여주는 속성이다. 기본은 false이며, true로 설정한다면 INFO 로그 레벨에 의해 기록된다. o.a.catalina.startup.ContextConfig 클래스의 webConfig 메서드 일부를 확인해보겠다.

String mergedWebXml = webXml.toXml();
sContext.setAttribute(
	org.apache.tomcat.util.scan.Constants.MERGED_WEB_XML,
	mergedWebXml
);
if (context.getLogEffectiveWebXml()) {
	log.info("web.xml:\n" + mergedWebXml);
}

 

reloadable 속성은 WEB-INF/classes 및 WEB-INF/lib 디렉토리에 변경이 발생할때 자동 반영 여부를 결정하는 속성으로 기본 false이다. true로 설정하면 빈번한 Tomcat 재기동을 피할 수 있어 개발 시에는 유용하지만 운영 시에는 적지 않은 오버 헤드를 동반하므로 적용에 신중해야 한다.

 

copyXML과 workDir은 <Host>(o.a.catalina.core.StandardHost) 내 copyXML, workDir 속성과 동일한 역할을 하지만 <Context> 단위의 속성으로 <Host> 속성보다 우선한다.