Skip to main content

Tomcat Configuration

server.xml, Connector 튜닝, JVM 옵션, 다중 인스턴스


📚 시리즈 네비게이션

이전현재다음
Nginx + TomcatTomcat Configuration-

시리즈 목차


🏗️ server.xml 구조

전체 계층

<Server>                          <!-- Tomcat 인스턴스 -->
<Listener /> <!-- 이벤트 리스너 -->
<GlobalNamingResources /> <!-- 전역 JNDI 리소스 -->

<Service> <!-- 서비스 (Connector + Engine) -->
<Connector /> <!-- 클라이언트 연결 (HTTP, AJP) -->
<Connector />

<Engine> <!-- 요청 처리 엔진 -->
<Realm /> <!-- 인증/권한 -->

<Host> <!-- 가상 호스트 -->
<Valve /> <!-- 요청/응답 처리 -->
<Context /> <!-- 웹 애플리케이션 -->
</Host>
</Engine>
</Service>
</Server>

요청 처리 흐름


🔌 Connector 상세 설정

HTTP Connector

<Connector port="8080"
protocol="HTTP/1.1"

<!-- 연결 관련 -->
connectionTimeout="20000"
maxConnections="8192"
acceptCount="100"

<!-- 스레드 풀 -->
maxThreads="200"
minSpareThreads="10"

<!-- 요청 처리 -->
maxPostSize="2097152"
URIEncoding="UTF-8"

<!-- Keep-Alive -->
keepAliveTimeout="20000"
maxKeepAliveRequests="100"

<!-- 압축 -->
compression="on"
compressionMinSize="2048"
compressibleMimeType="text/html,text/xml,text/plain,text/css,
text/javascript,application/javascript,
application/json,application/xml"

redirectPort="8443" />

주요 속성

연결 관련

속성기본값설명
port8080리스닝 포트
connectionTimeout20000연결 타임아웃 (ms)
maxConnections8192 (NIO)최대 동시 연결 수
acceptCount100대기 큐 크기

스레드 풀

속성기본값설명
maxThreads200최대 처리 스레드
minSpareThreads10최소 유휴 스레드
maxQueueSizeInteger.MAX대기 작업 큐 크기

요청 처리

속성기본값설명
maxPostSize2097152 (2MB)최대 POST 크기
maxParameterCount10000최대 파라미터 수
URIEncodingISO-8859-1URI 인코딩

Connector Protocol

<!-- BIO (Blocking I/O) - Tomcat 8.5+에서 제거됨 -->
<Connector protocol="HTTP/1.1" />

<!-- NIO (Non-blocking I/O) - 기본값 -->
<Connector protocol="org.apache.coyote.http11.Http11NioProtocol" />

<!-- NIO2 -->
<Connector protocol="org.apache.coyote.http11.Http11Nio2Protocol" />

<!-- APR (Apache Portable Runtime) - 네이티브 라이브러리 필요 -->
<Connector protocol="org.apache.coyote.http11.Http11AprProtocol" />
Protocol특징권장
NIOJava NIO 기반, 적은 스레드로 많은 연결
NIO2Java NIO2 기반, NIO보다 약간 나음
APR네이티브 라이브러리, SSL 성능 좋음

Executor (공유 스레드 풀)

여러 Connector가 스레드 풀을 공유함:

<Service name="Catalina">

<!-- 공유 스레드 풀 정의 -->
<Executor name="tomcatThreadPool"
namePrefix="catalina-exec-"
maxThreads="300"
minSpareThreads="20"
maxQueueSize="100" />

<!-- Connector에서 Executor 사용 -->
<Connector port="8080"
protocol="HTTP/1.1"
executor="tomcatThreadPool"
connectionTimeout="20000" />

<Connector port="8009"
protocol="AJP/1.3"
executor="tomcatThreadPool" />

</Service>

🌐 Host 설정

기본 Host

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

<!-- 접근 로그 -->
<Valve className="org.apache.catalina.valves.AccessLogValve"
directory="logs"
prefix="localhost_access_log"
suffix=".txt"
pattern="%h %l %u %t &quot;%r&quot; %s %b %D" />

</Host>
속성설명
name호스트명 (도메인)
appBase애플리케이션 디렉토리
unpackWARsWAR 자동 압축 해제
autoDeploy자동 배포 (개발용, 운영에서는 false)

가상 호스트 추가

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

<!-- 기본 호스트 -->
<Host name="localhost" appBase="webapps">
...
</Host>

<!-- 추가 가상 호스트 -->
<Host name="example.com" appBase="/var/www/example">
<Alias>www.example.com</Alias>

<Valve className="org.apache.catalina.valves.AccessLogValve"
directory="logs"
prefix="example_access_log"
suffix=".txt"
pattern="%h %l %u %t &quot;%r&quot; %s %b" />
</Host>

<Host name="api.example.com" appBase="/var/www/api">
...
</Host>

</Engine>

📦 Context 설정

Context 정의 위치

위치우선순위용도
$CATALINA_HOME/conf/server.xml1서버 레벨 (권장 안 함)
$CATALINA_HOME/conf/Catalina/localhost/*.xml2호스트별 설정
webapps/myapp/META-INF/context.xml3앱 내장
$CATALINA_HOME/conf/context.xml4전역 기본값

Context 설정 예시

<!-- $CATALINA_HOME/conf/Catalina/localhost/myapp.xml -->
<Context docBase="/opt/apps/myapp" path="/myapp" reloadable="false">

<!-- DataSource -->
<Resource name="jdbc/mydb"
auth="Container"
type="javax.sql.DataSource"
driverClassName="com.mysql.cj.jdbc.Driver"
url="jdbc:mysql://localhost:3306/mydb?useSSL=false"
username="appuser"
password="password"
maxTotal="50"
maxIdle="20"
minIdle="5"
maxWaitMillis="10000"
validationQuery="SELECT 1"
testOnBorrow="true" />

<!-- 환경 변수 -->
<Environment name="appEnv" type="java.lang.String" value="production" />

</Context>

ROOT 애플리케이션 설정

<!-- $CATALINA_HOME/conf/Catalina/localhost/ROOT.xml -->
<Context docBase="/opt/apps/myapp" path="">
...
</Context>

☕ JVM 옵션 설정

setenv.sh 생성

#!/bin/bash
# $CATALINA_HOME/bin/setenv.sh

# 힙 메모리
CATALINA_OPTS="$CATALINA_OPTS -Xms1g"
CATALINA_OPTS="$CATALINA_OPTS -Xmx2g"

# Metaspace (Java 8+)
CATALINA_OPTS="$CATALINA_OPTS -XX:MetaspaceSize=256m"
CATALINA_OPTS="$CATALINA_OPTS -XX:MaxMetaspaceSize=512m"

# GC 설정 (G1GC 권장)
CATALINA_OPTS="$CATALINA_OPTS -XX:+UseG1GC"
CATALINA_OPTS="$CATALINA_OPTS -XX:MaxGCPauseMillis=200"
CATALINA_OPTS="$CATALINA_OPTS -XX:+ParallelRefProcEnabled"

# GC 로그 (Java 11+)
CATALINA_OPTS="$CATALINA_OPTS -Xlog:gc*:file=$CATALINA_HOME/logs/gc.log:time,uptime:filecount=5,filesize=10M"

# 인코딩
CATALINA_OPTS="$CATALINA_OPTS -Dfile.encoding=UTF-8"
CATALINA_OPTS="$CATALINA_OPTS -Dsun.jnu.encoding=UTF-8"

# 타임존
CATALINA_OPTS="$CATALINA_OPTS -Duser.timezone=Asia/Seoul"

# HeapDump (OOM 발생 시)
CATALINA_OPTS="$CATALINA_OPTS -XX:+HeapDumpOnOutOfMemoryError"
CATALINA_OPTS="$CATALINA_OPTS -XX:HeapDumpPath=$CATALINA_HOME/logs/"

# JMX 원격 모니터링 (선택)
#CATALINA_OPTS="$CATALINA_OPTS -Dcom.sun.management.jmxremote"
#CATALINA_OPTS="$CATALINA_OPTS -Dcom.sun.management.jmxremote.port=9090"
#CATALINA_OPTS="$CATALINA_OPTS -Dcom.sun.management.jmxremote.ssl=false"
#CATALINA_OPTS="$CATALINA_OPTS -Dcom.sun.management.jmxremote.authenticate=false"

export CATALINA_OPTS

JVM 메모리 가이드

용도XmsXmx참고
개발256m512m가볍게
소규모512m1g트래픽 적음
중규모1g2g일반적
대규모2g4g+트래픽 많음

: Xms와 Xmx를 같게 설정하면 힙 리사이징 오버헤드 감소.

GC 선택 가이드

GC특징권장 환경
G1GC균형잡힌 성능, 예측 가능한 지연대부분의 경우 (기본)
ZGC초저지연 (<1ms), Java 11+저지연 필수
Shenandoah저지연, Red Hat저지연 필요
ParallelGC높은 처리량, 긴 GC 시간배치 작업
# G1GC (권장)
-XX:+UseG1GC

# ZGC (Java 15+에서 프로덕션 준비)
-XX:+UseZGC

# Shenandoah (Java 12+)
-XX:+UseShenandoahGC

📊 Valve (밸브) 설정

Access Log Valve

<Valve className="org.apache.catalina.valves.AccessLogValve"
directory="logs"
prefix="access"
suffix=".log"
rotatable="true"
fileDateFormat="yyyy-MM-dd"
pattern="%h %l %u %t &quot;%r&quot; %s %b %D %{X-Forwarded-For}i" />

패턴 문자:

패턴설명
%h원격 호스트 (IP)
%l원격 로그인 이름 (보통 -)
%u인증된 사용자
%t시간
%r요청 첫 줄 (메서드, URI, 프로토콜)
%sHTTP 상태 코드
%b응답 바이트
%D처리 시간 (밀리초)
%T처리 시간 (초)
%{header}i요청 헤더

Remote IP Valve

프록시 뒤에서 실제 클라이언트 IP 인식:

<Valve className="org.apache.catalina.valves.RemoteIpValve"
remoteIpHeader="X-Forwarded-For"
protocolHeader="X-Forwarded-Proto"
internalProxies="192\.168\.1\.\d+|127\.0\.0\.1" />

Error Report Valve

에러 페이지 커스터마이징:

<Valve className="org.apache.catalina.valves.ErrorReportValve"
showReport="false"
showServerInfo="false" />

Stuck Thread Detection Valve

멈춘 스레드 감지:

<Valve className="org.apache.catalina.valves.StuckThreadDetectionValve"
threshold="600"
interruptThreadThreshold="0" />

🔐 보안 설정

불필요한 앱 제거

# 기본 앱 제거 (운영 환경)
rm -rf $CATALINA_HOME/webapps/docs
rm -rf $CATALINA_HOME/webapps/examples
rm -rf $CATALINA_HOME/webapps/host-manager
# rm -rf $CATALINA_HOME/webapps/manager # 필요하면 유지
rm -rf $CATALINA_HOME/webapps/ROOT # 기본 페이지

Manager 앱 접근 제한

<!-- $CATALINA_HOME/webapps/manager/META-INF/context.xml -->
<Context antiResourceLocking="false" privileged="true">
<Valve className="org.apache.catalina.valves.RemoteAddrValve"
allow="127\.0\.0\.1|192\.168\.1\.\d+" />
</Context>

서버 정보 숨기기

<!-- server.xml의 Connector -->
<Connector port="8080"
server="Server"
... />
<!-- Host 내부에 ErrorReportValve -->
<Valve className="org.apache.catalina.valves.ErrorReportValve"
showReport="false"
showServerInfo="false" />

AJP 보안 (Tomcat 9.0.31+)

<Connector port="8009"
protocol="AJP/1.3"
address="127.0.0.1"
secretRequired="true"
secret="your-secret-key" />

Shutdown 포트 보안

<!-- 복잡한 문자열 사용 또는 비활성화 -->
<Server port="8005" shutdown="COMPLEX_SHUTDOWN_STRING">

<!-- 또는 완전 비활성화 -->
<Server port="-1" shutdown="SHUTDOWN">

🗂️ 다중 인스턴스 구성

CATALINA_HOME vs CATALINA_BASE

변수설명공유
CATALINA_HOMETomcat 설치 디렉토리 (바이너리)공유 가능
CATALINA_BASE인스턴스별 설정/데이터인스턴스별 분리

디렉토리 구조

/opt/tomcat/                      # CATALINA_HOME (공유)
├── bin/
├── lib/
└── ...

/var/tomcat/instance1/ # CATALINA_BASE (인스턴스 1)
├── conf/
│ ├── server.xml # 포트: 8080, 8005, 8009
│ └── ...
├── logs/
├── temp/
├── webapps/
└── work/

/var/tomcat/instance2/ # CATALINA_BASE (인스턴스 2)
├── conf/
│ ├── server.xml # 포트: 8081, 8006, 8010
│ └── ...
├── logs/
├── temp/
├── webapps/
└── work/

인스턴스 생성 스크립트

#!/bin/bash
# create-instance.sh

CATALINA_HOME=/opt/tomcat
INSTANCE_NAME=$1
INSTANCE_BASE=/var/tomcat/$INSTANCE_NAME

# 디렉토리 생성
mkdir -p $INSTANCE_BASE/{conf,logs,temp,webapps,work}

# 설정 파일 복사
cp -r $CATALINA_HOME/conf/* $INSTANCE_BASE/conf/

# 권한 설정
chown -R tomcat:tomcat $INSTANCE_BASE

echo "Instance created at $INSTANCE_BASE"
echo "Edit $INSTANCE_BASE/conf/server.xml to change ports"

인스턴스 실행

#!/bin/bash
# instance1-start.sh

export CATALINA_HOME=/opt/tomcat
export CATALINA_BASE=/var/tomcat/instance1

$CATALINA_HOME/bin/startup.sh
#!/bin/bash
# instance1-stop.sh

export CATALINA_HOME=/opt/tomcat
export CATALINA_BASE=/var/tomcat/instance1

$CATALINA_HOME/bin/shutdown.sh

Systemd 서비스 (다중 인스턴스)

# /etc/systemd/system/tomcat@.service

[Unit]
Description=Apache Tomcat %i
After=network.target

[Service]
Type=forking

Environment="CATALINA_HOME=/opt/tomcat"
Environment="CATALINA_BASE=/var/tomcat/%i"
Environment="CATALINA_PID=/var/tomcat/%i/tomcat.pid"

ExecStart=/opt/tomcat/bin/startup.sh
ExecStop=/opt/tomcat/bin/shutdown.sh

User=tomcat
Group=tomcat

[Install]
WantedBy=multi-user.target
# 사용법
systemctl start tomcat@instance1
systemctl start tomcat@instance2
systemctl enable tomcat@instance1

📋 튜닝 체크리스트

성능

항목확인
Connector maxThreads 조정
Connector maxConnections 조정
JVM 힙 메모리 설정
GC 알고리즘 선택 (G1GC)
Keep-Alive 설정
압축 (compression) 설정

보안

항목확인
불필요한 기본 앱 제거
Manager 앱 접근 제한
서버 정보 숨기기
Shutdown 포트 보안
AJP 보안 설정 (필요시)

운영

항목확인
Access 로그 설정
GC 로그 설정
HeapDump 설정
로그 로테이션
autoDeploy=false (운영)

🔗 시리즈 네비게이션

이전다음
Nginx + Tomcat Integration-

시리즈 목차로 돌아가기


🔗 참고 자료