목차

Tomcat Clsutering

이강우 2024/12/11 13:51

본 문서는 Tomcat 9.x 이상에서 정상 동작함을 확인하였습니다. Tomcat 7.x 이하에서는 테스트 되지 않았습니다.

Tomcat Clustering 관련 문서는 공식문서 https://tomcat.apache.org/tomcat-9.0-doc/cluster-howto.html 를 참조하면 아주 쉽게 구성할 수 있다.

클러스터링 사전 요구사항

기본설정

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

위의 내용만 추가하면 바로 구성할 수 있다.

위 구성을 사용하면 DeltaManager를 사용 하여 전체 세션 복제를 활성화 할 수 있습니다. 모든 세션이 클러스터 의 다른 모든 노드 에 복제 됨을 의미 합니다. 소규모 클러스터에는 효과적이지만 4 개 이상의 노드와 같은 대규모 클러스터에는 권장하지 않습니다.
또한 DeltaManager를 사용할 때 Tomcat은 모든 노드, 심지어 애플리케이션이 배포되지 않은 노드까지 세션을 복제합니다 .

이 문제를 해결하려면 BackupManager를 사용하여야 합니다. BackupManager는 세션 데이터를 단지 응용 프로그램이 배포 된 노드에만 복제 합니다. 클러스터의 노드 수를 4대이상 늘릴 예정이라면 DeltaManagerBackupManager로 마이그레이션하는 것이 좋습니다.

클러스터링 기본값

다음은 몇 가지 중요한 기본값입니다.

TCP StaticMembership 클러스터링

네트워크간에 Multicast가 차단되어있어서 노드간 클러스터 확인이 되지 않는경우가 있다. 요즘 최신의 네트워크 스위치 제품들은 기본적으로 IGMP가 차단되어 있는경우가 많기때문에 이런일이 빈번하다.
해결방법은 당연히 IGMP를 차단 해제 해주고 Multicast를 이용하면 되지만 여타 여건상 안되는경우 TCP로 Static Cluster Membership을 구성하면 된다.

Tomcat 9.0.17 버전 이후부터 Membership에 StaticMembershipService 가 새롭게 추가되었다. 따라서 이후 버전부터는 StaticMember를 사용하는 방법이 2가지로 나뉜다.

차이점

항목 StaticMembershipService StaticMembershipInterceptor
사용 위치 <Membership> 요소 내에서 사용. <Interceptor> 요소 내에서 사용.
구현 클래스 org.apache.catalina.tribes.membership.StaticMembershipService org.apache.catalina.tribes.group.interceptors.StaticMembershipInterceptor
로컬 멤버 자동 식별 지원 (로컬 멤버를 자동으로 식별). 지원하지 않음 (로컬 멤버를 명시적으로 정의해야 함).
설정 방식 <Member> 또는 <LocalMember> 요소 사용. <Member> 요소 사용.
설정 난이도 약간 복잡함. 상대적으로 단순함.
멀티캐스트 탐색 비활성화. 비활성화.

새로운 StaticMembershipService 를 이용하는 방법 (권장)

  1. McastService Membership을 제거하고 대신 StaticMembershipService 구성한다.
  2. TCPPingInterceptor 구성 - StaticMember 구성시에는 반드시 활성화 하여야 한다.
      <Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster"
               channelSendOptions="6">

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

        <Channel className="org.apache.catalina.tribes.group.GroupChannel">
          <!-- multicast membership -->
          <!--
          <Membership className="org.apache.catalina.tribes.membership.McastService"
                      address="${tomcat.cluster.member.address}"
                      port="${tomcat.cluster.member.port}"
                      frequency="500"
                      dropTime="3000"
                      />
          -->
		  
          <!-- static membership
          9.0.17 이후 버전부터 새롭게 추가된 StaticMembershipService 정의부분이다.
          Interceptor를 사용하지 않고 모든 클러스터 멤버를 수동으로 정의 해주면 된다.
          -->
          <Membership className="org.apache.catalina.tribes.membership.StaticMembershipService">
            <Member className="org.apache.catalina.tribes.membership.StaticMember"
                       port="5011"
                       host="10.33.0.51"
                       uniqueId="{1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,1}"/>
            <Member className="org.apache.catalina.tribes.membership.StaticMember"
                       port="5011"
                       host="10.33.0.52"
                       uniqueId="{1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,2}"/>
          </Membership>

          <Receiver className="org.apache.catalina.tribes.transport.nio.NioReceiver"
                    address="${tomcat.cluster.receiver.address}"
                    port="${tomcat.cluster.receiver.port}"
                    autoBind="100"
                    selectorTimeout="5000"
                    maxThreads="6"
                    />

          <Sender className="org.apache.catalina.tribes.transport.ReplicationTransmitter">
            <Transport className="org.apache.catalina.tribes.transport.nio.PooledParallelSender"/>
          </Sender>
          <!-- if use StaticMembership enable TcpPingInterceptor -->
          <Interceptor className="org.apache.catalina.tribes.group.interceptors.TcpPingInterceptor"/>
          <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=""/>
        <Valve className="org.apache.catalina.ha.session.JvmRouteBinderValve"/>
        <!--
        <Deployer className="org.apache.catalina.ha.deploy.FarmWarDeployer"
                  tempDir="/tmp/war-temp/"
                  deployDir="/tmp/war-deploy/"
                  watchDir="/tmp/war-listen/"
                  watchEnabled="false"/>
         -->

        <ClusterListener className="org.apache.catalina.ha.session.ClusterSessionListener"/>
      </Cluster>

예전방식 StaticMembershipInterceptor 를 이용하는 방법 (9.0.17이전버전 - 비권장)

  1. McastService Membership을 제거한다.
  2. TCPPingInterceptor 구성 - StaticMember 구성시에는 반드시 활성화 하여야 한다.
  3. StaticMembershipInterceptor 구성

다음은 StaticMembershipInterceptor 를 이용한 TCP StaticMember 클러스터 구성 예제 입니다.

        <!-- channelSendOptions 값이 6:동기 방식 8:비동기 방식
        8:비동기 방식 사용시 Receiver의 selectorTimeout을 5000(5초) 이상으로 설정 권장
        channelStartOptions 은 멀티캐스트 멤버쉽의 경우에만 사용하거나 가급적이면 해당 속성은 설정하지 않는다.
        -->
        <Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster"
                 channelSendOptions="6">

          <!-- Delta Manager -->
          <Manager className="org.apache.catalina.ha.session.DeltaManager"
                   expireSessionsOnShutdown="false"
                   notifyListenersOnReplication="true"/>

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

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

            <!-- Multicast Member -->
            <!--
            <Membership className="org.apache.catalina.tribes.membership.McastService"
                        address="${tomcat.cluster.member.address}"
                        port="${tomcat.cluster.member.port}"
                        frequency="500"
                        dropTime="3000"/>
            -->

            <Receiver className="org.apache.catalina.tribes.transport.nio.NioReceiver"
                      address="${tomcat.cluster.receiver.address}"
                      port="${tomcat.cluster.receiver.port}"
                      selectorTimeout="100"
                      maxThreads="6"/>

            <Sender className="org.apache.catalina.tribes.transport.ReplicationTransmitter">
              <Transport className="org.apache.catalina.tribes.transport.nio.PooledParallelSender"/>
            </Sender>

            <!-- 이 인터셉터는 모든 노드에 핑으로 체크를 해주는 인터셉터이다.
                 이 인터셉터는 다른 노드가 클러스터를 떠났을 때 모든 노드가 인식 할 수 있도록 다른 노드를 ping 체크한다.
                 이 클래스가 없으면 클러스터가 제대로 작동하는 것처럼 보일 수 있지만 노드를 제거하고 다시 도입하면 세션 복제가 중단 될 수 있다.
                 TcpFailureDetector보다 위쪽에 위치하여야 한다. -->
            <Interceptor className="org.apache.catalina.tribes.group.interceptors.TcpPingInterceptor"/>
            
            <!-- 멤버간 데이터 통신 오류 또는 시간 초과등의 문제가 발생하였을시 감지하는 인터셉터 -->
            <Interceptor className="org.apache.catalina.tribes.group.interceptors.TcpFailureDetector"/>
            <Interceptor className="org.apache.catalina.tribes.group.interceptors.MessageDispatchInterceptor"/>
            <Interceptor className="org.apache.catalina.tribes.group.interceptors.ThroughputInterceptor"/>

            <!-- StaticMember 인터셉터는 멀티캐스팅멤버 대신에 고정값으로 사용할때 선언한다. -->
            <Interceptor className="org.apache.catalina.tribes.group.interceptors.StaticMembershipInterceptor">
              <!-- 자기자신 선언
                   참고: Tomcat 9.0.17 이상 버전에서는 <LocalMember> 요소를 사용하여 로컬 멤버를 명시적으로 구성할 필요가 없습니다.
                   로컬 멤버를 포함한 모든 클러스터 멤버는 <Member> 요소를 사용하여 정의할 수 있으며, Tomcat이 로컬 멤버를 자동으로 식별합니다.
                   따라서 아래 LocalMember 부분은 정의하지 않습니다.
                   https://tomcat.apache.org/tomcat-9.0-doc/config/cluster-membership.html#Setting
              -->
              <!--
              <LocalMember className="org.apache.catalina.tribes.membership.StaticMember"
                           domain="staging-cluster"
                           uniqueId="{1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,1}"/>
              -->

              <!-- 고정멤버로 선언할 다른 노드 정보 -->
              <Member className="org.apache.catalina.tribes.membership.StaticMember"
                      port="4000"
                      securePort="-1"
                      host="tomcat2"
                      domain="staging-cluster"
                      uniqueId="{1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,2}"/>
            </Interceptor>

          </Channel>

          <!-- 해당 필터에 포함되는 요청은 세션데이터 갱신에서 제외한다 -->
          <Valve className="org.apache.catalina.ha.tcp.ReplicationValve"
                 filter=".*\.gif|.*\.js|.*\.jpeg|.*\.jpg|.*\.png|.*\.htm|.*\.html|.*\.css|.*\.txt"/>

          <!-- route 변경시 현재 jvmRoute를 변경해주는 밸브.
               이게 없는경우 route가 변경되어도 jvmRoute값이 유지되어 failback효과를 유도한다 -->
          <Valve className="org.apache.catalina.ha.session.JvmRouteBinderValve"/>

          <!-- 클러스터 노드간 자동배포를 위한 디플로이어 일반적으로 사용하지 않으며 <Host 엘리먼트에 넣어야 정상 동작함
          <Deployer className="org.apache.catalina.ha.deploy.FarmWarDeployer"
                    tempDir="/tmp/war-temp/"
                    deployDir="/tmp/war-deploy/"
                    watchDir="/tmp/war-listen/"
                    watchEnabled="false"/>
          -->

          <!-- DeltaManager 사용시 필요함 -->
          <ClusterListener className="org.apache.catalina.ha.session.ClusterSessionListener"/>
        </Cluster>

참조링크