Tomcat 인스턴스와 JVM 옵션
2. 핵심 원리
2.1 CATALINA_HOME 과 CATALINA_BASE
CATALINA_HOME 은 Tomcat 을 압축 해제한 실행 파일 위치(bin·lib)이고, CATALINA_BASE 는 인스턴스별 설정과 로그가 있는 위치입니다. 지정하지 않으면 두 값이 같아 인스턴스가 하나뿐인 구성이 됩니다.
| 구분 | CATALINA_HOME | CATALINA_BASE |
|---|---|---|
| 위치 | $BASE/tomcat/current |
$BASE/tomcat-inst/<이름> |
| 내용 | bin·lib(공유 실행 파일) | conf·logs·temp·webapps·work |
| 인스턴스 수 | 서버당 1개 | 서버당 여러 개 가능 |
| 업그레이드 | current 링크만 전환 | 손대지 않음 |
2.2 인스턴스 폴더 구조
CATALINA_BASE 는 다섯 폴더로 이뤄집니다. 없는 폴더가 있으면 Tomcat 이 기동 시 자동으로 만들지만, 미리 만들어 권한을 통제하는 편이 안전합니다.
| 폴더 | 용도 |
|---|---|
| conf | server.xml·context.xml·web.xml |
| logs | catalina.out·access 로그 |
| temp | 임시 파일·catalina.pid |
| webapps | war·배포된 앱 |
| work | JSP 컴파일 산출물 |
2.3 로그를 심볼릭 링크로 중앙화
인스턴스가 늘어나면 로그가 tomcat-inst/<이름>/logs 아래 흩어져 백업 스크립트가 복잡해집니다. $BASE/logs/<이름> 을 실제 위치로 두고 인스턴스의 logs 를 그리로 심볼릭 링크(symlink)하면 로그 백업 경로가 한 곳으로 모입니다.
catalina.out 백업은 logrotate 의 copytruncate 방식으로 압축·삭제합니다(리눅스 운영 06 참조).
2.4 포트 겹침 피하기
한 서버에서 인스턴스를 여러 개 띄우면 server.xml 의 HTTP 포트와 종료 포트가 겹치지 않아야 합니다. 겹치면 두 번째 인스턴스가 기동 시 포트 바인딩에 실패합니다.
| 인스턴스 | HTTP 포트 | 종료 포트 |
|---|---|---|
| app1 | 8080 | 8005 |
| app2 | 8180 | 8105 |
| app3(추가 시) | 8280 | 8205 |
2.5 server.xml 은 인스턴스마다 별도 파일
CATALINA_HOME 의 conf 는 손대지 않고, 각 CATALINA_BASE 의 conf/server.xml 을 인스턴스별로 관리합니다. Engine·Host 이름은 인스턴스끼리 같아도 되고, 앱을 구분하는 것은 포트와 appBase 경로입니다.
2.6 setenv.sh 는 CATALINA_BASE 기준으로 읽힌다
catalina.sh 는 시작할 때 $CATALINA_BASE/bin/setenv.sh 를 먼저 찾고, 없으면 $CATALINA_HOME/bin/setenv.sh 를 씁니다. 인스턴스마다 다른 JVM 옵션을 주려면 반드시 CATALINA_BASE 쪽에 파일을 둬야 합니다.
01-tomcat-install 레슨의 setenv.sh 는 CATALINA_HOME 공용 파일로 JAVA_HOME·CATALINA_PID 만 최소로 뒀습니다. 이번 레슨은 인스턴스별 setenv.sh 에 힙·GC 등 JVM 튜닝 옵션을 넣습니다.
2.7 힙 크기는 Xms=Xmx
-Xms(초기 힙)와 -Xmx(최대 힙)를 같게 두면 런타임 중 힙을 늘리고 줄이는 리사이즈가 없어져 지연이 줄어듭니다. 서버용 JVM 에서는 사실상 표준 설정입니다.
2.8 GC 옵션 명시
JDK 21 은 기본 GC 가 G1 이지만, -XX:+UseG1GC 를 명시하면 JDK 버전이 올라가 기본값이 바뀌어도 동작이 그대로 유지됩니다. 지연에 민감한 앱은 별도로 ZGC 등을 검토합니다.
2.9 시간대와 인코딩 시스템 프로퍼티
-Duser.timezone=Asia/Seoul, -Dfile.encoding=UTF-8 을 인스턴스마다 넣어둡니다. OS 로케일이 다른 이미지에서 옮겨와도 로그 시각과 한글 처리가 흔들리지 않습니다.
2.10 OOM 발생 시 힙 덤프
-XX:+HeapDumpOnOutOfMemoryError 와 -XX:HeapDumpPath=... 를 함께 지정하면 메모리 부족(OOM)이 터졌을 때 원인 분석용 덤프 파일이 남습니다. 경로를 지정하지 않으면 프로세스의 현재 작업 디렉터리에 떨어져 못 찾기 쉽습니다.
2.11 JNDI DataSource 와 context.xml
context.xml 에 Resource 를 선언하면 애플리케이션은 JDBC URL·비밀번호를 몰라도 JNDI 이름(java:comp/env/jdbc/AppDB)만으로 커넥션 풀을 얻습니다. 비밀번호를 평문으로 두지 않는 방법은 실무 확장 13 암호화를 참고합니다.
드라이버 jar 는 CATALINA_BASE/lib(인스턴스 전용)에 둡니다. CATALINA_HOME/lib(공용)에 두면 인스턴스마다 다른 드라이버 버전이 필요할 때 충돌합니다.
2.12 systemd 템플릿 유닛
[email protected] 는 %i 로 인스턴스 이름을 받는 템플릿 유닛입니다. systemctl enable --now tomcat@app1 처럼 인스턴스 이름을 붙여 기동하며, 인스턴스마다 유닛 파일을 복붙할 필요가 없습니다.