본문으로 건너뛰기

Tomcat 인메모리 클러스터링

Tomcat은 SimpleTcpCluster를 통해 서버 간 세션을 실시간으로 복제하는 인메모리 클러스터링을 내장 지원합니다. 별도 외부 저장소 없이 Tomcat 설정만으로 세션 공유를 구현할 수 있습니다.


클러스터링 아키텍처

Tomcat 클러스터는 멀티캐스트 또는 유니캐스트로 멤버를 자동 발견하고, 세션 변경이 발생할 때마다 클러스터 내 다른 서버에 복제합니다.

[App1: 10.0.0.1]
세션 A 변경

├──▶ [App2: 10.0.0.2] 세션 A 복제
└──▶ [App3: 10.0.0.3] 세션 A 복제

DeltaManager vs BackupManager

구분DeltaManagerBackupManager
복제 방식전체 복제 (All-to-All)백업 복제 (Primary + Backup)
복제 대상모든 노드에 복제1개 백업 노드에만 복제
적합한 규모소규모 (2~4대)중규모 (4대 이상)
장애 시 복구어느 서버든 즉시 접수Primary 장애 시 Backup이 접수
메모리·네트워크 부하높음낮음

DeltaManager 설정

모든 서버에 세션을 복제합니다. 가장 단순하고 신뢰성이 높습니다.

server.xml 설정 (각 Tomcat 동일하게 적용)

<!-- /opt/tomcat/conf/server.xml -->

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

<Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster"
channelSendOptions="8">

<!-- 세션 복제 매니저 -->
<Manager className="org.apache.catalina.ha.session.DeltaManager"
expireSessionsOnShutdown="false"
notifyListenersOnReplication="true"/>

<!-- 채널 구성: 멤버 발견 + 데이터 전송 -->
<Channel className="org.apache.catalina.tribes.group.GroupChannel">

<!-- 멀티캐스트로 클러스터 멤버 자동 발견 -->
<Membership className="org.apache.catalina.tribes.membership.McastService"
address="228.0.0.4"
port="45564"
frequency="500"
dropTime="3000"/>

<!-- 수신: 다른 서버에서 복제 데이터 받기 -->
<Receiver className="org.apache.catalina.tribes.transport.nio.NioReceiver"
address="10.0.0.1"
port="4000"
autoBind="100"
selectorTimeout="5000"
maxThreads="6"/>

<!-- 발신: 다른 서버로 복제 데이터 보내기 -->
<Sender className="org.apache.catalina.tribes.transport.ReplicationTransmitter">
<Transport className="org.apache.catalina.tribes.transport.nio.PooledParallelSender"/>
</Sender>

<!-- 인터셉터 -->
<Interceptor className="org.apache.catalina.tribes.group.interceptors.TcpFailureDetector"/>
<Interceptor className="org.apache.catalina.tribes.group.interceptors.MessageDispatchInterceptor"/>
</Channel>

<!-- 세션 복제 밸브: 요청 처리 후 변경된 세션 복제 -->
<Valve className="org.apache.catalina.ha.tcp.ReplicationValve"
filter=".*\.gif;.*\.js;.*\.jpeg;.*\.jpg;.*\.png;.*\.htm;.*\.html;.*\.css;.*\.txt"/>

<!-- 통계 리스너 -->
<ClusterListener className="org.apache.catalina.ha.session.ClusterSessionListener"/>
</Cluster>

<Host name="localhost" appBase="webapps" ...>
</Host>
</Engine>

Tomcat 2, 3 설정 (address만 변경)

<!-- Tomcat 2 -->
<Engine jvmRoute="server2">
<Cluster ...>
<Channel>
<Receiver address="10.0.0.2" port="4000" .../>
</Channel>
</Cluster>
</Engine>

<!-- Tomcat 3 -->
<Engine jvmRoute="server3">
<Cluster ...>
<Channel>
<Receiver address="10.0.0.3" port="4000" .../>
</Channel>
</Cluster>
</Engine>

web.xml: 분산 애플리케이션 선언 (필수)

클러스터링을 사용하는 웹 애플리케이션의 WEB-INF/web.xml<distributable/> 태그를 추가해야 합니다:

<!-- webapp/WEB-INF/web.xml -->
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://jakarta.ee/xml/ns/jakartaee"
version="6.0">

<!-- 이 태그가 있어야 세션 복제 활성화 -->
<distributable/>

<!-- 나머지 설정 -->
<session-config>
<session-timeout>30</session-timeout>
</session-config>
</web-app>

BackupManager 설정

트래픽이 많아 전체 복제 부담이 클 때 사용합니다. 세션의 Primary 서버와 Backup 서버만 유지합니다.

<Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster">

<Manager className="org.apache.catalina.ha.session.BackupManager"
expireSessionsOnShutdown="false"
notifyListenersOnReplication="true"
mapSendOptions="6"/>

<!-- Channel 설정은 DeltaManager와 동일 -->
<Channel className="org.apache.catalina.tribes.group.GroupChannel">
<Membership address="228.0.0.4" port="45564"
frequency="500" dropTime="3000"
className="org.apache.catalina.tribes.membership.McastService"/>
<Receiver address="10.0.0.1" port="4000"
className="org.apache.catalina.tribes.transport.nio.NioReceiver"
autoBind="100" selectorTimeout="5000" maxThreads="6"/>
<Sender className="org.apache.catalina.tribes.transport.ReplicationTransmitter">
<Transport className="org.apache.catalina.tribes.transport.nio.PooledParallelSender"/>
</Sender>
<Interceptor className="org.apache.catalina.tribes.group.interceptors.TcpFailureDetector"/>
<Interceptor className="org.apache.catalina.tribes.group.interceptors.MessageDispatchInterceptor"/>
</Channel>

<Valve className="org.apache.catalina.ha.tcp.ReplicationValve"
filter=".*\.(gif|js|jpeg|jpg|png|css|html|txt)"/>
<ClusterListener className="org.apache.catalina.ha.session.ClusterSessionListener"/>
</Cluster>

멀티캐스트가 막힌 환경: 유니캐스트 설정

클라우드나 일부 네트워크에서는 멀티캐스트가 차단됩니다. 이때는 정적 유니캐스트 멤버십을 사용합니다.

<Channel className="org.apache.catalina.tribes.group.GroupChannel">

<!-- Membership 대신 StaticMembershipInterceptor 사용 -->
<Interceptor className="org.apache.catalina.tribes.group.interceptors.StaticMembershipInterceptor">
<Member className="org.apache.catalina.tribes.membership.StaticMember"
host="10.0.0.2"
port="4000"
uniqueId="{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2}"/>
<Member className="org.apache.catalina.tribes.membership.StaticMember"
host="10.0.0.3"
port="4000"
uniqueId="{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3}"/>
</Interceptor>

<Receiver address="10.0.0.1" port="4000"
className="org.apache.catalina.tribes.transport.nio.NioReceiver"
autoBind="100" selectorTimeout="5000" maxThreads="6"/>
<Sender className="org.apache.catalina.tribes.transport.ReplicationTransmitter">
<Transport className="org.apache.catalina.tribes.transport.nio.PooledParallelSender"/>
</Sender>
<Interceptor className="org.apache.catalina.tribes.group.interceptors.TcpFailureDetector"/>
<Interceptor className="org.apache.catalina.tribes.group.interceptors.MessageDispatchInterceptor"/>
</Channel>

세션 직렬화: Serializable 필수

클러스터 간 세션 복제는 Java 직렬화(Serialization)를 사용합니다. 세션에 저장되는 모든 객체가 java.io.Serializable을 구현해야 합니다.

// 세션에 저장되는 클래스 — Serializable 구현 필수
public class UserSession implements java.io.Serializable {

private static final long serialVersionUID = 1L; // 버전 관리

private Long userId;
private String username;
private String role;
private LocalDateTime loginTime;

// Serializable이 아닌 필드는 transient로 제외
private transient HttpSession httpSession;

// getter/setter...
}

주의: transient로 선언된 필드는 복제에서 제외됩니다.


클러스터 동작 확인

# Tomcat 로그에서 클러스터 멤버 감지 확인
grep "McastService" /opt/tomcat/logs/catalina.out
# INFO: Sending... membership for [/10.0.0.1]
# INFO: Added member [/10.0.0.2:4000]
# INFO: Added member [/10.0.0.3:4000]

# 세션 복제 확인
grep "session" /opt/tomcat/logs/catalina.out | grep -i "replac\|replic"

# 실시간 로그 모니터링
tail -f /opt/tomcat/logs/catalina.out | grep -E "cluster|session|replac"

테스트: 세션 복제 검증

# 1. App1에 접속해 로그인 (세션 생성)
curl -c cookies.txt -X POST http://app1:8080/app/login \
-d "user=admin&pass=secret"

# 2. App2에 직접 접속해도 세션이 유지되는지 확인
curl -b cookies.txt http://app2:8080/app/profile
# 로그인 상태로 프로필 페이지 접근 가능해야 함

# 3. App1 서버 중단 후 로드밸런서를 통해 접속
sudo systemctl stop tomcat1
curl -b cookies.txt http://lb.example.com/app/profile
# 세션이 App2, App3에 복제되어 있으므로 로그인 유지

인메모리 클러스터링의 한계

서버 수 증가에 따른 복제 부하

DeltaManager는 N대의 서버에서 세션 변경 시 N-1번 복제합니다.

3대: 세션 1건 변경 → 2번 복제 (부담 작음)
10대: 세션 1건 변경 → 9번 복제 (부담 증가)
50대: 세션 1건 변경 → 49번 복제 (심각한 부담)

권장: 4대 이상에서는 BackupManager 또는 Redis 세션 공유로 전환.

네트워크 분리(Network Partition) 위험

클러스터 노드 간 네트워크가 일시 단절되면 각 노드가 독립적으로 동작해 세션 데이터가 분기됩니다. 네트워크 복구 후 충돌이 발생할 수 있습니다.

다음 페이지에서는 Redis를 이용한 외부 세션 공유로 이 한계를 해결하는 방법을 알아봅니다.