아파치-톰캣 멀티 인스턴스 로드밸런싱

목차

아파치 톰캣 로드밸런싱은 Apache HTTP Server를 앞단에 두고 mod_jk의 balancer 워커로 톰캣 인스턴스 두 대에 요청을 나눠 보내는 구성이다. 톰캣 한 대만 연결했던 단일 연동과 달리, 여기서는 tomcat1·tomcat2의 포트를 분리하고 workers.properties에 두 워커를 등록해 부하를 분산한다. 이 글은 구성 개요부터 톰캣 인스턴스 분리(server.xml), Apache 밸런서 설정(mod_jk.conf·workers.properties·httpd.conf), sticky session, 동작 확인까지 순서대로 정리한다.

이 글은 아파치-톰캣 연동에 이어서 진행한다. mod_jk 컴파일과 단일 톰캣 연동이 끝나 있는 상태를 전제로 한다.

구성 개요

아파치 톰캣 로드밸런싱의 구조는 단순하다. Apache가 모든 HTTP 요청을 받아 mod_jk(AJP 프로토콜)를 통해 뒤단의 톰캣 워커들로 전달한다. workers.properties에 balancer라는 가상의 로드밸런서 워커를 정의하고, 그 아래에 실제 톰캣 인스턴스(tomcat1·tomcat2)를 멤버 워커로 묶어 두는 방식이다.

  • Apache HTTP Server : 80번 포트로 요청 수신, mod_jk로 분배
  • tomcat1 / tomcat2 : 같은 서버에 설치된 두 톰캣 인스턴스 (AJP로 통신)
  • mod_jk : Apache와 톰캣을 잇는 커넥터 모듈 (AJP 1.3)

핵심은 세 가지다. ① 두 톰캣이 포트 충돌 없이 동시에 뜨도록 server.xml에서 포트를 분리하고, ② workers.properties에 balancer 워커와 멤버 워커를 등록하며, ③ mod_jk.conf를 httpd.conf에 매핑한다. 순서대로 따라가 보자.

1. 톰캣 인스턴스 분리 — server.xml 포트 변경

두 톰캣 인스턴스가 같은 서버에서 동시에 뜨려면 포트가 겹치지 않아야 한다. 각 인스턴스의 conf/server.xml에서 HTTP·셧다운·AJP 포트를 아래와 같이 분리한다.

tomcat1

  • HTTP 포트 : 8180
  • 셧다운 포트 : 8105
  • AJP 포트 : 8109

tomcat2

  • HTTP 포트 : 8280
  • 셧다운 포트 : 8205
  • AJP 포트 : 8209

server.xml에서 바꿀 위치는 세 곳이다. 최상단 <Server>의 셧다운 포트, <Connector>의 HTTP 포트, 그리고 AJP <Connector>의 포트다. tomcat1을 예로 들면 다음과 같다.

# tomcat1/conf/server.xml

<!-- 셧다운 포트 -->
<Server port="8105" shutdown="SHUTDOWN">

<!-- HTTP 커넥터 -->
<Connector port="8180" protocol="HTTP/1.1"
           connectionTimeout="20000" redirectPort="8443" />

<!-- AJP 커넥터 (mod_jk가 이 포트로 접속) -->
<Connector port="8109" protocol="AJP/1.3" redirectPort="8443"
           secretRequired="false" />

tomcat2는 같은 자리를 각각 8205·8280·8209로 바꿔 준다. 톰캣 8.5 이상에서는 AJP 커넥터가 기본 비활성이거나 secret을 요구할 수 있으니, 실습 환경에서는 위처럼 secretRequired="false"로 풀어 두면 된다.

sticky session(아래 5번 참고)을 쓸 거라면, 이 단계에서 각 인스턴스의 <Engine>jvmRoute를 지정해 둔다. jvmRoute 값은 뒤에서 workers.properties에 등록할 워커 이름과 똑같아야 한다.

# tomcat1/conf/server.xml — Engine에 jvmRoute 추가
<Engine name="Catalina" defaultHost="localhost" jvmRoute="tomcat1">

# tomcat2/conf/server.xml
<Engine name="Catalina" defaultHost="localhost" jvmRoute="tomcat2">

2. Apache 밸런서 설정 — mod_jk.conf 작성

아파치 conf 디렉터리에 mod_jk.conf 파일을 생성한다. 아파치가 받은 모든 요청을 balancer 워커로 넘기도록 JkMount를 지정하고, workers.properties 경로와 로그 설정을 적는다.

[root@localhost conf]# vi mod_jk.conf

# 아파치가 받은 요청을 'balancer'로 넘긴다.
JkMount /* balancer

# workers.properties 파일의 경로를 지정
JkWorkersFile /usr/local/src/httpd-2.4.58/conf/workers.properties

# 연동 관련 로그 파일 설정
JkShmFile logs/mod_jk.shm
JkLogFile /usr/local/src/httpd-2.4.58/logs/mod_jk.log

# 로그 레벨 설정
JkLogLevel error

# 타임스탬프 포맷 지정
JkLogStampFormat "[%a %b %d %H:%M:%S %Y]"

# (옵션) 어느 톰캣으로 넘기는지 로그에 남긴다.
JkRequestLogFormat "%w %R %V %T %U %q"

JkMount /* balancer는 “모든 URL 패턴(/*)을 balancer 워커로 보낸다”는 뜻이다. 이 설정 파일을 생성했으면 이 파일 내용을 httpd.conf에 매핑해 주어야 한다. 매핑은 아래 4번에서 다룬다.

3. workers.properties — balancer 워커 등록

이어서 workers.properties를 작성한다. 아파치를 로드밸런서로 지정하고 tomcat1, tomcat2를 멤버 워커로 등록하는 단계다. 앞서 tomcat1, tomcat2 디렉터리에 설치한 두 인스턴스를 각각 같은 이름의 워커로 묶는다고 생각하면 된다. 이때 각 워커의 AJP 포트(8109, 8209)를 server.xml과 동일하게 맞춘다.

# workers.properties

# 사용할 워커 목록 — 로드밸런서(balancer)만 적으면 된다.
# 멤버 워커는 worker.list에 넣지 않아도 balancer가 관리한다.
worker.list=balancer

# --- 멤버 워커 1: tomcat1 ---
worker.tomcat1.type=ajp13
worker.tomcat1.host=localhost
worker.tomcat1.port=8109
worker.tomcat1.lbfactor=1

# --- 멤버 워커 2: tomcat2 ---
worker.tomcat2.type=ajp13
worker.tomcat2.host=localhost
worker.tomcat2.port=8209
worker.tomcat2.lbfactor=1

# --- 로드밸런서 워커 ---
worker.balancer.type=lb
worker.balancer.balance_workers=tomcat1,tomcat2
worker.balancer.sticky_session=true

각 지시어의 의미를 정리하면 다음과 같다.

지시어의미
worker.list=balancermod_jk가 사용할 워커 목록. 로드밸런서만 적으면 멤버는 자동 인식된다.
type=ajp13멤버 워커는 AJP 1.3 프로토콜로 톰캣과 통신한다.
host / port해당 톰캣 인스턴스의 주소와 AJP 포트(server.xml과 일치).
lbfactor부하 가중치. 값이 클수록 더 많은 요청을 받는다. 동일하면 균등 분배.
type=lb이 워커가 로드밸런서(load balancer)임을 뜻한다.
balance_workers밸런서가 관리할 멤버 워커 목록(쉼표 구분).
sticky_session같은 세션의 요청을 처음 처리한 톰캣으로 계속 보낼지 여부.

lbfactor를 두 워커 모두 1로 두면 가중 라운드로빈으로 요청이 거의 균등하게 나뉜다. 한쪽 서버 사양이 더 좋다면 그 워커의 lbfactor를 높여 더 많은 요청을 받게 할 수 있다.

4. httpd.conf 매핑

위에서 작성한 mod_jk.conf를 httpd.conf에서 불러오도록 매핑한다. httpd.conf 맨 아래에 다음 내용을 추가한다.

# mod_jk 모듈 로드 (컴파일된 모듈 경로)
LoadModule jk_module modules/mod_jk.so

<VirtualHost *:80>
        JkMount /* balancer
</VirtualHost>

# mod_jk 설정 파일 포함
Include conf/mod_jk.conf

LoadModule jk_module로 mod_jk 모듈을 적재하고, Include conf/mod_jk.conf로 앞서 만든 설정을 끌어온다. 설정을 마쳤으면 문법을 점검한 뒤 아파치를 재시작한다.

# 설정 문법 점검
apachectl configtest

# 아파치 재시작
apachectl restart

5. sticky session(세션 고정)

로드밸런싱을 하면 같은 사용자의 요청이 매번 다른 톰캣으로 갈 수 있다. 그런데 로그인 정보 같은 세션은 보통 톰캣 메모리에 저장되므로, A 인스턴스에서 만든 세션을 B 인스턴스가 모르면 로그인이 풀리는 문제가 생긴다. 이를 막는 것이 sticky session이다.

sticky session이 켜져 있으면(sticky_session=true, mod_jk 기본값) 세션 ID를 가진 요청은 그 세션을 처음 만든 톰캣으로 계속 라우팅된다. 이게 동작하려면 두 가지 이름이 일치해야 한다.

  • server.xml <Engine>jvmRoute 값 (예: tomcat1, tomcat2)
  • workers.properties의 멤버 워커 이름 (worker.tomcat1, worker.tomcat2)

jvmRoute를 지정하면 톰캣이 발급하는 세션 ID 뒤에 .tomcat1 같은 꼬리표가 붙는다(예: JSESSIONID=ABC123.tomcat1). mod_jk는 이 꼬리표를 보고 같은 인스턴스로 요청을 되돌려 보낸다. 앞서 1번에서 jvmRoute를 워커 이름과 똑같이 맞춘 이유가 바로 이것이다.

6. 로드밸런싱 동작 확인

로드밸런싱이 동작하는지 로그로 확인한다. 우선 명령창을 하나 더 띄워 mod_jk 로그를 실시간으로 본다.

tail -f logs/mod_jk.log

그리고 기존 명령창에서 다음 명령을 여러 번 연달아 입력한다. curl로 아파치(80번)에 요청을 보내고 응답에서 Tomcat 문자열만 뽑아 보는 것이다.

curl localhost | grep Tomcat
아파치 톰캣 로드밸런싱 mod_jk 로그 확인

tail -f로 보고 있던 창에 요청이 tomcat1과 tomcat2로 번갈아 분배되는 로그가 찍히면 로드밸런싱에 성공한 것이다. JkRequestLogFormat의 %w 항목에 처리한 워커 이름이 남으므로, 어느 인스턴스가 요청을 받았는지 한눈에 구분할 수 있다.

자주 막히는 부분

  • AJP 포트 불일치 — server.xml의 AJP Connector 포트와 workers.properties의 worker.*.port가 다르면 502/503이 난다. 8109·8209를 양쪽에서 똑같이 맞춘다.
  • AJP 커넥터 비활성 — 톰캣 8.5+ 기본 server.xml은 AJP 커넥터가 주석 처리돼 있다. 주석을 풀고 secretRequired="false"(또는 secret 설정)를 확인한다.
  • 한쪽으로만 쏠림 — sticky session이 켜진 상태에서 같은 세션(쿠키)으로 테스트하면 같은 인스턴스로만 간다. 분배 확인은 세션 없이(curl 단발 요청) 하거나 쿠키를 비운다.
  • mod_jk 모듈 미적재LoadModule jk_module이 주석 처리돼 있으면 JkMount가 무시된다. apachectl configtest로 먼저 점검한다.

자주 묻는 질문

mod_jk 대신 mod_proxy_balancer를 써도 되나?

가능하다. 아파치 기본 모듈인 mod_proxy_balancer를 쓰면 <Proxy balancer://>BalancerMember, ProxySet lbmethod로 비슷한 구성을 별도 컴파일 없이 만들 수 있다. 다만 이 글은 AJP 기반 mod_jk 방식을 다룬다. AJP는 HTTP보다 가볍고 mod_jk가 톰캣 워커 상태 관리에 특화돼 있어, 톰캣 클러스터에서는 여전히 널리 쓰인다.

lbfactor는 어떻게 정하나?

두 서버 사양이 같다면 둘 다 1로 둬서 균등 분배한다. 한쪽이 더 강력하면 그 워커의 lbfactor를 2~3으로 높여 더 많은 요청을 받게 한다. 값은 정수이며, 부하는 lbfactor 비율에 비례해 가중 분배된다.

정리

아파치 톰캣 로드밸런싱의 핵심은 ① 톰캣별 포트 분리(server.xml), ② workers.properties의 balancer 워커·멤버 워커 등록, ③ mod_jk.conf의 httpd.conf 매핑 세 가지다. 세션 유지가 필요하면 jvmRoute와 워커 이름을 일치시켜 sticky session을 켜면 된다. 같은 부하 분산을 NginX upstream으로 더 간단히 구성하는 방법은 NginX-Tomcat 연동 & 멀티 인스턴스 로드밸런싱 글에서 비교해 볼 수 있다.

💬 댓글 0

💬 댓글을 남기려면?
첫 댓글을 남겨보세요 ✨

이 콘텐츠가 도움이 됐나요?

누스쿨 커뮤니티에서 더 많은 커리어 전략을 나눠요