본문으로 건너뛰기

mod_jk 로드밸런싱: AJP 기반 Tomcat 클러스터

mod_jk는 Apache HTTPD와 Tomcat을 AJP 프로토콜로 연결하는 모듈로, 가장 성숙하고 안정적인 Tomcat 클러스터링 방식 중 하나입니다. workers.properties 파일에서 클러스터를 정의하고, 가중치(lbfactor), 라우팅, 세션 고정까지 정밀하게 제어할 수 있습니다.


mod_jk 설치

# Ubuntu/Debian
sudo apt-get install libapache2-mod-jk

# CentOS/RHEL
sudo yum install mod_jk

# 설치 확인
apache2ctl -M | grep jk
# 출력: jk_module (shared)

workers.properties 완전 가이드

workers.properties는 mod_jk의 핵심 설정 파일입니다. Tomcat 인스턴스(worker)와 로드밸런서를 정의합니다.

파일 위치

# 기본 위치
/etc/libapache2-mod-jk/workers.properties

# 또는 직접 지정 (apache2.conf 또는 jk.conf)
JkWorkersFile /etc/apache2/workers.properties

기본 클러스터 설정 (workers.properties)

# ========================
# Worker 목록 정의
# ========================
worker.list=loadbalancer,status

# ========================
# Tomcat 인스턴스 1 (jvm1)
# ========================
worker.jvm1.type=ajp13
worker.jvm1.host=10.0.0.1
worker.jvm1.port=8009
worker.jvm1.lbfactor=3 # 가중치: 높을수록 요청 많이 받음
worker.jvm1.connection_pool_size=20
worker.jvm1.connection_pool_timeout=600
worker.jvm1.socket_timeout=10
worker.jvm1.socket_keepalive=True
worker.jvm1.ping_timeout=10000 # ms
worker.jvm1.ping_mode=A # A=항상 핑, C=연결 시, I=간격별

# ========================
# Tomcat 인스턴스 2 (jvm2)
# ========================
worker.jvm2.type=ajp13
worker.jvm2.host=10.0.0.2
worker.jvm2.port=8009
worker.jvm2.lbfactor=3
worker.jvm2.connection_pool_size=20
worker.jvm2.connection_pool_timeout=600
worker.jvm2.socket_timeout=10
worker.jvm2.socket_keepalive=True
worker.jvm2.ping_mode=A

# ========================
# Tomcat 인스턴스 3 (jvm3, 예비 서버)
# ========================
worker.jvm3.type=ajp13
worker.jvm3.host=10.0.0.3
worker.jvm3.port=8009
worker.jvm3.lbfactor=1
worker.jvm3.connection_pool_size=10
worker.jvm3.socket_timeout=10

# ========================
# 로드밸런서 정의
# ========================
worker.loadbalancer.type=lb
worker.loadbalancer.balance_workers=jvm1,jvm2,jvm3
worker.loadbalancer.sticky_session=True # 세션 고정 활성화
worker.loadbalancer.sticky_session_force=False # 세션 서버 죽으면 다른 서버로 이동
worker.loadbalancer.method=R # R=요청 수, T=트래픽, B=부하(bybusyness)
worker.loadbalancer.lock=O # O=낙관적 락킹 (성능 우선)

# ========================
# 상태 모니터링 Worker
# ========================
worker.status.type=status

lbfactor (가중치) 동작 원리

jvm1: lbfactor=3  → 전체의 3/7 처리 (약 43%)
jvm2: lbfactor=3 → 전체의 3/7 처리 (약 43%)
jvm3: lbfactor=1 → 전체의 1/7 처리 (약 14%)

총합이 중요한 것이 아니라 비율이 중요합니다. 3:3:130:30:10은 동일하게 동작합니다.


method (로드밸런싱 알고리즘)

worker.loadbalancer.method로 분산 알고리즘을 선택합니다.

이름설명
RRequests요청 수 기반 Round Robin (기본값)
TTraffic처리 바이트 기반 분산
BBusyness현재 처리 중인 요청 수 기반 (Least Connection)
SSessions세션 수 기반 분산
NNext다음 순번 (순수 Round Robin)
# 최소 연결 방식 (API 서버에 권장)
worker.loadbalancer.method=B

Tomcat server.xml과의 연동: jvmRoute

Sticky Session이 동작하려면 Tomcat의 jvmRouteworkers.properties의 worker 이름이 일치해야 합니다.

Tomcat 1 (10.0.0.1) server.xml 설정

<!-- /opt/tomcat/conf/server.xml -->
<Engine name="Catalina" defaultHost="localhost" jvmRoute="jvm1">
<!-- jvmRoute는 worker 이름과 동일하게 설정 -->
</Engine>

Tomcat 2 (10.0.0.2) server.xml 설정

<Engine name="Catalina" defaultHost="localhost" jvmRoute="jvm2">
</Engine>

이렇게 설정하면 Tomcat이 세션 ID에 라우팅 정보를 추가합니다:

Set-Cookie: JSESSIONID=1A2B3C4D5E6F.jvm1; Path=/; HttpOnly

Apache mod_jk는 .jvm1 부분을 읽어 해당 worker로 라우팅합니다.


Apache 설정: JkMount

httpd.conf 또는 VirtualHost에서 mod_jk 설정을 합니다.

# /etc/apache2/conf-available/jk.conf

# workers.properties 파일 위치
JkWorkersFile /etc/apache2/workers.properties

# 로그 설정
JkLogFile /var/log/apache2/mod_jk.log
JkLogLevel info # error, warn, info, debug, trace 중 선택
JkLogStampFormat "[%a %b %d %H:%M:%S %Y]"

# 상태 페이지 (내부 접근만)
JkMountCopy All
# /etc/apache2/sites-available/app.conf

<VirtualHost *:80>
ServerName example.com

# 정적 파일: Apache가 직접 처리
DocumentRoot /var/www/html

# 동적 요청: Tomcat 클러스터로 전달
JkMount /app/* loadbalancer
JkMount /api/* loadbalancer
JkMount /*.jsp loadbalancer
JkMount /*.do loadbalancer

# 정적 파일은 제외 (Apache가 처리)
JkUnMount /static/* loadbalancer
JkUnMount /images/* loadbalancer
JkUnMount *.css loadbalancer
JkUnMount *.js loadbalancer

# 상태 페이지
<Location "/jk-status">
JkMount /jk-status status
Require ip 127.0.0.1 10.0.0.0/8
</Location>

CustomLog /var/log/apache2/access.log combined
ErrorLog /var/log/apache2/error.log
</VirtualHost>

고급 설정: 서버 상태 제어

workers.properties에서 서버 비활성화

# 특정 서버 일시 중지 (점검 중)
worker.jvm3.activation=d # d=disabled, a=active, f=forced

# 또는 lbfactor를 0으로
worker.jvm3.lbfactor=0

jk-status 페이지로 런타임 제어

# jk-status URL 접속 후 웹 UI에서 제어
http://localhost/jk-status

# CLI로 서버 비활성화
curl "http://localhost/jk-status?cmd=update&mime=txt&w=loadbalancer&sw=jvm3&wa=d"

# CLI로 서버 활성화
curl "http://localhost/jk-status?cmd=update&mime=txt&w=loadbalancer&sw=jvm3&wa=a"

# 현재 상태 확인 (텍스트 형식)
curl "http://localhost/jk-status?cmd=show&mime=txt"

실전 전체 구성 예시

실제 운영 환경에서 사용하는 완전한 설정입니다.

workers.properties (완전 버전)

# 전체 worker 목록
worker.list=loadbalancer,status

# ─── Tomcat 1 ───────────────────────────────────
worker.jvm1.type=ajp13
worker.jvm1.host=10.0.0.1
worker.jvm1.port=8009
worker.jvm1.secret=myAjpSecret # AJP 시크릿 (보안 필수)
worker.jvm1.lbfactor=2
worker.jvm1.connection_pool_size=25
worker.jvm1.connection_pool_timeout=600
worker.jvm1.socket_keepalive=True
worker.jvm1.ping_mode=A
worker.jvm1.ping_timeout=5000
worker.jvm1.prepost_timeout=5000
worker.jvm1.recover_time=60 # 장애 후 재시도 간격(초)

# ─── Tomcat 2 ───────────────────────────────────
worker.jvm2.type=ajp13
worker.jvm2.host=10.0.0.2
worker.jvm2.port=8009
worker.jvm2.secret=myAjpSecret
worker.jvm2.lbfactor=2
worker.jvm2.connection_pool_size=25
worker.jvm2.connection_pool_timeout=600
worker.jvm2.socket_keepalive=True
worker.jvm2.ping_mode=A
worker.jvm2.ping_timeout=5000
worker.jvm2.prepost_timeout=5000
worker.jvm2.recover_time=60

# ─── Hot Standby Tomcat ──────────────────────────
worker.jvm3.type=ajp13
worker.jvm3.host=10.0.0.3
worker.jvm3.port=8009
worker.jvm3.secret=myAjpSecret
worker.jvm3.lbfactor=1
worker.jvm3.activation=f # f=forced standby (평시엔 사용 안 함)
worker.jvm3.connection_pool_size=5
worker.jvm3.recover_time=60

# ─── 로드밸런서 ──────────────────────────────────
worker.loadbalancer.type=lb
worker.loadbalancer.balance_workers=jvm1,jvm2,jvm3
worker.loadbalancer.sticky_session=True
worker.loadbalancer.sticky_session_force=False # 장애 시 다른 서버로 페일오버 허용
worker.loadbalancer.method=B # 최소 부하 방식
worker.loadbalancer.lock=O
worker.loadbalancer.error_escalation_time=0
worker.loadbalancer.max_reply_timeouts=0

# ─── 상태 Worker ──────────────────────────────────
worker.status.type=status
worker.status.read_only=True # 읽기 전용 (보안)

Tomcat AJP 커넥터 설정 (server.xml)

mod_jk의 secret과 반드시 일치해야 합니다:

<!-- Tomcat 1: /opt/tomcat/conf/server.xml -->
<Connector protocol="AJP/1.3"
address="0.0.0.0"
port="8009"
redirectPort="8443"
secretRequired="true"
secret="myAjpSecret"
maxThreads="200"
connectionTimeout="20000" />

동작 확인 및 디버깅

# mod_jk 로그 레벨 높이기 (디버깅)
JkLogLevel debug

# 로그 확인
tail -f /var/log/apache2/mod_jk.log

# 주요 로그 패턴
grep "ajp_send_request" /var/log/apache2/mod_jk.log | tail -20
grep "ERROR" /var/log/apache2/mod_jk.log

# 각 Tomcat의 세션 분포 확인 (jk-status)
curl "http://localhost/jk-status?cmd=show&mime=txt" | grep -E "Sessions|Errors|lbfactor"

# 특정 Tomcat으로 강제 라우팅 테스트
curl -b "JSESSIONID=TEST.jvm1" http://localhost/app/test
curl -b "JSESSIONID=TEST.jvm2" http://localhost/app/test

mod_jk vs mod_proxy_ajp 비교

항목mod_jkmod_proxy_ajp
관리 UIjk-status 페이지 제공balancer-manager
설정 파일별도 workers.propertieshttpd.conf 내 inline
알고리즘R/T/B/S/Nbyrequests/bytraffic/bybusyness
Sticky SessionjvmRoute 자동 연동stickysession 수동 설정
성숙도매우 높음 (오래된 모듈)높음
설정 복잡도복잡 (별도 파일)상대적으로 단순
Apache 설정과 통합JkMount 별도 문법ProxyPass 동일 문법

권장: 새 프로젝트에서는 mod_proxy_ajp 또는 mod_proxy_http 사용. 기존 mod_jk 레거시 환경은 유지.

다음 페이지에서는 헬스체크 설정으로 장애 노드를 자동 감지하고 제거하는 방법을 알아봅니다.