NginX Tomcat 연동은 NginX 설정 파일에 리버스 프록시(proxy_pass) 한 줄만 추가하면 끝난다. Apache처럼 mod_jk 커넥터를 컴파일할 필요가 없어 WEB-WAS 구조를 훨씬 간결하게 만들 수 있다. 이 글에서는 RHEL 8에서 NginX 1.25와 Tomcat 10.1을 연동하고, 한발 더 나아가 Tomcat 인스턴스를 두 개로 늘려 멀티 인스턴스 로드밸런싱(upstream)까지 구성하는 과정을 구성 개요 → Tomcat 다중 인스턴스 → NginX upstream·proxy 설정 → 로드밸런싱 확인 → 정리 순서로 다룬다.
실행 환경과 구성 개요
- OS : RHEL 8
- NginX : 1.25.2
- Tomcat : 10.1.16
- Java : 11
NginX·Java 11·Tomcat 10.1은 이미 설치돼 있다고 가정한다. 전체 구조는 단순하다. 클라이언트가 80 포트로 NginX에 접속하면, NginX가 리버스 프록시로 동작해 요청을 뒤쪽 Tomcat(WAS)으로 넘긴다. 뒤에서 Tomcat을 여러 대로 늘리면 NginX가 요청을 분산하는 로드밸런서 역할까지 겸하게 된다.
- 프록시 : 다른 서버에서 리소스를 찾는 클라이언트의 요청에 대해 중개자 역할을 하는 서버
- 리버스 프록시 : 하나 이상의 백엔드 서버 앞에 위치해 각 요청을 어느 백엔드로 보낼지 결정하는 서버
즉 NginX가 리버스 프록시로서 클라이언트의 요청을 받아 대신 웹 애플리케이션 서버(Tomcat)로 전달한다. 단일 Tomcat 연동을 먼저 만든 다음, 그 위에 멀티 인스턴스 로드밸런싱을 얹는 순서로 진행한다.
1. NginX – Tomcat 단일 연동
NginX와 Tomcat 연동은 Apache보다 훨씬 간결하다. NginX 설정 파일(/etc/nginx/nginx.conf)의 server 블록 안 location /에 proxy_pass 한 줄만 추가하면 되기 때문이다. 8080은 Tomcat의 기본 HTTP 접속 포트다.
vi /etc/nginx/nginx.conf
# (중략)
server {
listen 80;
listen [::]:80;
server_name localhost;
root /usr/share/nginx/html;
include /etc/nginx/default.d/*.conf;
location / {
proxy_pass http://localhost:8080; # 이 한 줄을 추가. 8080은 Tomcat 기본 포트
}
}
# (중략)
proxy_set_header로 원본 요청 정보 전달
리버스 프록시를 거치면 Tomcat 입장에서 모든 요청이 NginX(localhost)에서 온 것처럼 보인다. 클라이언트의 실제 IP나 요청 호스트, 프로토콜(http/https) 정보가 사라져 로그·인증·리다이렉트가 어긋날 수 있다. NginX는 프록시 요청에서 기본적으로 Host를 $proxy_host 값으로 바꾸므로, 아래 proxy_set_header로 원본 정보를 함께 넘겨 주는 편이 안전하다.
location / {
proxy_pass http://localhost:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
2. NginX·Tomcat 가동 및 연동 확인
설정을 저장한 뒤 문법을 검사하고 두 서비스를 기동한다. nginx -t로 문법 오류를 먼저 걸러야 reload 실패를 피할 수 있다.
# NginX 설정 문법 검사
nginx -t
# 출력 예시
# nginx: configuration file /etc/nginx/nginx.conf test is successful
# NginX 기동(또는 재적용)
systemctl restart nginx
# Tomcat 기동
$CATALINA_HOME/bin/startup.sh
브라우저에서 서버 IP의 80 포트로 접속해 Tomcat 기본 화면이 뜨면 연동이 끝난 것이다. 80으로 들어온 요청을 NginX가 받아 내부 8080의 Tomcat으로 넘겨 응답을 돌려준다.

3. Tomcat 다중 인스턴스 구성
이제 Tomcat 인스턴스를 두 개로 늘려 부하를 분산한다. 한 서버에서 Tomcat을 여러 개 띄우려면 인스턴스마다 포트가 겹치지 않아야 한다. 기존 Tomcat이 설치된 디렉터리 아래에 tomcat1, tomcat2 디렉터리를 만들고, 각각에 Tomcat 10.1을 둔다(기존 것을 tomcat1로 쓰고, tomcat2에 동일 버전을 추가 설치).
각 인스턴스의 conf/server.xml에서 충돌하는 포트를 변경한다. 핵심은 세 곳이다. 최상단 <Server port="...">의 셧다운 포트, HTTP 요청을 받는 Connector 포트, 그리고 사용하지 않더라도 정의돼 있는 AJP Connector 포트다. 필자는 다음과 같이 나눴다.
| 인스턴스 | HTTP 포트 | 셧다운 포트 | AJP 포트 |
|---|---|---|---|
| tomcat1 | 8180 | 8105 | 8109 |
| tomcat2 | 8280 | 8205 | 8209 |
server.xml에서 바꿀 위치는 다음과 같다. tomcat1 예시이며, tomcat2는 같은 자리를 8280·8205·8209로 바꾸면 된다.
<!-- tomcat1/conf/server.xml -->
<!-- 셧다운 포트 (기본 8005 → 8105) -->
<Server port="8105" shutdown="SHUTDOWN">
<Service name="Catalina">
<!-- HTTP Connector (기본 8080 → 8180) -->
<Connector port="8180" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443" />
<!-- AJP Connector (기본 8009 → 8109) -->
<Connector protocol="AJP/1.3"
port="8109"
redirectPort="8443" />
포트를 다르게 줬으면 두 인스턴스를 각각 기동한다. 인스턴스마다 CATALINA_BASE를 분리해 실행하는 것이 정석이지만, 디렉터리를 통째로 복제한 경우 각 디렉터리의 bin/startup.sh를 실행하면 된다.
4. NginX upstream·proxy 설정
두 Tomcat을 하나의 upstream 그룹으로 묶고, proxy_pass가 그 그룹을 가리키게 하면 NginX가 요청을 두 인스턴스로 분산한다. upstream 블록은 http 컨텍스트 안, server 블록 밖에 둔다.
vi /etc/nginx/nginx.conf
# (중략) — http { } 블록 안, server 블록 밖
upstream backend { # tomcat1~2를 backend라는 단위로 묶는다
server localhost:8180; # tomcat1의 HTTP 포트
server localhost:8280; # tomcat2의 HTTP 포트
}
server {
listen 80;
location / {
proxy_pass http://backend; # 받은 요청을 backend 그룹으로 분산
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
# (중략)
로드밸런싱 알고리즘과 세션 고정
upstream에 별도 지시어가 없으면 NginX는 기본값인 라운드 로빈(Round Robin)으로 요청을 번갈아 분배한다. 워크로드에 따라 분산 방식을 바꿀 수 있다.
| 지시어 | 동작 |
|---|---|
| (없음, 기본) | 라운드 로빈 — 요청을 순서대로 번갈아 분배 |
least_conn; | 활성 연결이 가장 적은 서버로 전달 — 요청 처리 시간이 들쭉날쭉할 때 유리 |
ip_hash; | 클라이언트 IP를 해시 키로 사용 — 같은 클라이언트를 항상 같은 서버로(세션 고정) |
Tomcat 세션을 서버 간에 공유하지 않는다면 로그인 상태가 요청마다 풀릴 수 있다. 이때 ip_hash를 쓰면 같은 클라이언트의 요청이 항상 같은 인스턴스로 가서 세션이 유지된다(sticky session). 단, 해당 서버가 죽으면 다른 서버로 넘어간다.
upstream backend {
ip_hash; # 같은 클라이언트 → 같은 Tomcat (세션 고정)
server localhost:8180;
server localhost:8280;
}
5. 로드밸런싱 동작 확인
설정을 저장하고 문법 검사 후 NginX를 재적용한다.
nginx -t
systemctl reload nginx
어느 인스턴스가 응답했는지 눈으로 구별하려면 각 Tomcat의 webapps/ROOT/index.jsp 텍스트를 다르게 수정해 두면 된다. 예를 들어 tomcat1에는 "TOMCAT 1", tomcat2에는 "TOMCAT 2"를 출력하게 한 뒤 브라우저를 새로고침하면, 기본 라운드 로빈에서는 두 화면이 번갈아 뜬다. 명령줄에서도 다음처럼 확인할 수 있다.
# 80 포트로 반복 요청 — 라운드 로빈이면 응답이 번갈아 나온다
for i in 1 2 3 4; do curl -s http://localhost/ | grep -i tomcat; done
# 출력 예시
# TOMCAT 1
# TOMCAT 2
# TOMCAT 1
# TOMCAT 2
위와 같이 tomcat1·tomcat2 응답이 번갈아 나오면 로드밸런싱이 정상 동작하는 것이다.
자주 막히는 부분과 트러블슈팅
- 502 Bad Gateway : upstream의 Tomcat이 안 떠 있거나 포트가 틀렸다.
ss -tlnp | grep -E '8180|8280'로 두 포트가 LISTEN인지 확인한다. - 한쪽 인스턴스만 응답 : server.xml 포트가 겹쳐 한 인스턴스가 기동에 실패한 경우다.
logs/catalina.out의 "Address already in use" 메시지를 확인한다. - 로그인이 자꾸 풀림 : 세션이 인스턴스 간에 공유되지 않아 생긴다.
ip_hash로 세션을 고정하거나 세션 클러스터링을 구성한다. - SELinux 환경(RHEL/CentOS) : NginX가 백엔드로 프록시하지 못하면
setsebool -P httpd_can_network_connect on으로 네트워크 연결을 허용한다. - 클라이언트 IP가 모두 동일 : Tomcat 로그에 NginX IP만 찍히면
proxy_set_header X-Forwarded-For설정이 빠진 것이다.
자주 묻는 질문
AJP 커넥터를 꼭 써야 하나?
아니다. NginX는 mod_jk(AJP) 대신 HTTP 프록시(proxy_pass)로 Tomcat에 붙는다. 위 구성에서 AJP 포트는 인스턴스 간 충돌만 피하려고 분리했을 뿐 실제로는 HTTP 포트(8180·8280)만 사용한다.
인스턴스를 3개 이상으로 늘리려면?
upstream 블록에 server localhost:8380; 식으로 한 줄을 더 추가하고, 새 Tomcat의 server.xml 포트도 겹치지 않게 잡으면 된다. 특정 서버에 더 많은 트래픽을 주고 싶으면 server localhost:8180 weight=2;처럼 가중치를 줄 수도 있다.
정리
NginX Tomcat 연동은 proxy_pass 한 줄이면 충분하고, upstream 블록으로 Tomcat 여러 대를 묶으면 곧바로 로드밸런싱이 된다. 운영에서는 proxy_set_header로 클라이언트 정보를 넘기고, 세션이 풀린다면 ip_hash로 고정하는 것까지 챙기면 된다. 별도의 커넥터 컴파일이 필요했던 Apache-Tomcat 연동과 비교하면 차이가 확연하다. 같은 부하 분산을 mod_jk 기반으로 구성하는 방법은 아파치-톰캣 멀티 인스턴스 로드밸런싱 글에서 이어서 확인할 수 있다.



💬 댓글 0