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:1과 30:30:10은 동일하게 동작합니다.
method (로드밸런싱 알고리즘)
worker.loadbalancer.method로 분산 알고리즘을 선택합니다.
| 값 | 이름 | 설명 |
|---|---|---|
R | Requests | 요청 수 기반 Round Robin (기본값) |
T | Traffic | 처리 바이트 기반 분산 |
B | Busyness | 현재 처리 중인 요청 수 기반 (Least Connection) |
S | Sessions | 세션 수 기반 분산 |
N | Next | 다음 순번 (순수 Round Robin) |
# 최소 연결 방식 (API 서버에 권장)
worker.loadbalancer.method=B
Tomcat server.xml과의 연동: jvmRoute
Sticky Session이 동작하려면 Tomcat의 jvmRoute와 workers.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_jk | mod_proxy_ajp |
|---|---|---|
| 관리 UI | jk-status 페이지 제공 | balancer-manager |
| 설정 파일 | 별도 workers.properties | httpd.conf 내 inline |
| 알고리즘 | R/T/B/S/N | byrequests/bytraffic/bybusyness |
| Sticky Session | jvmRoute 자동 연동 | stickysession 수동 설정 |
| 성숙도 | 매우 높음 (오래된 모듈) | 높음 |
| 설정 복잡도 | 복잡 (별도 파일) | 상대적으로 단순 |
| Apache 설정과 통합 | JkMount 별도 문법 | ProxyPass 동일 문법 |
권장: 새 프로젝트에서는 mod_proxy_ajp 또는 mod_proxy_http 사용. 기존 mod_jk 레거시 환경은 유지.
다음 페이지에서는 헬스체크 설정으로 장애 노드를 자동 감지하고 제거하는 방법을 알아봅니다.