Skip to main content

Nginx + Tomcat Integration

Nginx 리버스 프록시 기반 Tomcat 연동, 로드밸런싱, SSL


📚 시리즈 네비게이션

이전현재다음
Apache + TomcatNginx + TomcatTomcat Configuration

시리즈 목차


🎯 왜 Nginx + Tomcat인가?

장점설명
고성능Nginx의 이벤트 기반 처리로 동시 접속 처리 우수
정적 파일Nginx가 정적 콘텐츠 직접 처리
SSL 종료Nginx에서 SSL 처리, Tomcat 부하 감소
로드밸런싱다중 Tomcat 인스턴스 분산
캐싱Nginx 캐싱으로 응답 속도 향상
보안Tomcat 직접 노출 방지

🏗️ 아키텍처

기본 구성

로드밸런싱 구성


⚙️ 기본 설정

Tomcat 설정

기본 HTTP Connector 사용:

<!-- $CATALINA_HOME/conf/server.xml -->
<Connector port="8080"
protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443" />

Nginx 기본 프록시 설정

# /etc/nginx/conf.d/tomcat.conf

server {
listen 80;
server_name example.com;

# 로그
access_log /var/log/nginx/tomcat_access.log;
error_log /var/log/nginx/tomcat_error.log;

# 모든 요청을 Tomcat으로
location / {
proxy_pass http://127.0.0.1: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;
}
}

📝 프록시 헤더 설정

왜 헤더가 필요한가?

프록시를 거치면 Tomcat은 클라이언트 정보를 알 수 없음:

정보헤더 없이헤더 있으면
클라이언트 IPNginx IP (127.0.0.1)실제 클라이언트 IP
프로토콜HTTP원래 프로토콜 (HTTPS)
호스트localhost:8080원래 도메인

주요 헤더

location / {
proxy_pass http://127.0.0.1:8080;

# 원본 Host 헤더 전달
proxy_set_header Host $host;

# 클라이언트 실제 IP
proxy_set_header X-Real-IP $remote_addr;

# 프록시 체인의 모든 IP
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

# 원래 프로토콜 (http/https)
proxy_set_header X-Forwarded-Proto $scheme;

# 원래 포트
proxy_set_header X-Forwarded-Port $server_port;
}

Tomcat에서 헤더 인식

<!-- server.xml의 Engine 또는 Host 내부 -->
<Valve className="org.apache.catalina.valves.RemoteIpValve"
remoteIpHeader="X-Forwarded-For"
protocolHeader="X-Forwarded-Proto"
internalProxies="127\.0\.0\.1" />

이제 Tomcat에서:

  • request.getRemoteAddr() → 실제 클라이언트 IP
  • request.isSecure() → HTTPS 여부 정확히 반환
  • request.getScheme() → "https" 반환

📁 정적 파일 분리

Nginx가 정적 파일 처리

server {
listen 80;
server_name example.com;

# 정적 파일 위치
root /var/www/static;

# 정적 파일 직접 처리
location /images/ {
expires 30d;
add_header Cache-Control "public, immutable";
}

location /css/ {
expires 7d;
add_header Cache-Control "public";
}

location /js/ {
expires 7d;
add_header Cache-Control "public";
}

# 정적 파일 패턴
location ~* \.(jpg|jpeg|png|gif|ico|css|js|woff2?)$ {
expires 30d;
add_header Cache-Control "public";
try_files $uri @tomcat;
}

# 나머지는 Tomcat으로
location / {
proxy_pass http://127.0.0.1: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;
}

# fallback
location @tomcat {
proxy_pass http://127.0.0.1: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;
}
}

⚖️ 로드밸런싱

upstream 블록

# /etc/nginx/conf.d/upstream.conf

upstream tomcat_cluster {
# 기본: Round Robin
server 192.168.1.10:8080;
server 192.168.1.11:8080;
server 192.168.1.12:8080;
}

로드밸런싱 알고리즘

upstream tomcat_cluster {
# 1. Round Robin (기본)
server 192.168.1.10:8080;
server 192.168.1.11:8080;

# 2. Weighted Round Robin
server 192.168.1.10:8080 weight=3;
server 192.168.1.11:8080 weight=1;

# 3. Least Connections
least_conn;
server 192.168.1.10:8080;
server 192.168.1.11:8080;

# 4. IP Hash (세션 고정)
ip_hash;
server 192.168.1.10:8080;
server 192.168.1.11:8080;

# 5. Hash (커스텀 키)
hash $request_uri consistent;
server 192.168.1.10:8080;
server 192.168.1.11:8080;
}
알고리즘설명사용 시점
Round Robin순차 분배기본값, 서버 성능 동일 시
Weight가중치 기반서버 성능 다를 때
Least Connections연결 적은 서버로요청 처리 시간 다양할 때
IP Hash클라이언트 IP 기반세션 고정 필요할 때

서버 상태 옵션

upstream tomcat_cluster {
server 192.168.1.10:8080 weight=3;
server 192.168.1.11:8080;
server 192.168.1.12:8080 backup; # 다른 서버 다운 시에만
server 192.168.1.13:8080 down; # 일시적 비활성화

server 192.168.1.14:8080 max_fails=3 fail_timeout=30s;
}
옵션설명
weight=N가중치 (기본 1)
backup백업 서버
down비활성화
max_fails=N실패 허용 횟수
fail_timeout=Ns실패 체크 간격

적용

server {
listen 80;
server_name example.com;

location / {
proxy_pass http://tomcat_cluster;
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;
}
}

🔐 SSL 설정

Nginx에서 SSL 종료

server {
listen 80;
server_name example.com;

# HTTP → HTTPS 리다이렉트
return 301 https://$server_name$request_uri;
}

server {
listen 443 ssl http2;
server_name example.com;

# SSL 인증서
ssl_certificate /etc/nginx/ssl/example.com.crt;
ssl_certificate_key /etc/nginx/ssl/example.com.key;

# SSL 설정
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256;
ssl_prefer_server_ciphers off;

# HSTS
add_header Strict-Transport-Security "max-age=31536000" always;

# Tomcat으로 프록시 (HTTP)
location / {
proxy_pass http://127.0.0.1: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 https;
proxy_set_header X-Forwarded-Port 443;
}
}

Let's Encrypt 인증서

# Certbot 설치 (Ubuntu)
apt install certbot python3-certbot-nginx

# 인증서 발급 (자동 Nginx 설정)
certbot --nginx -d example.com

# 갱신 테스트
certbot renew --dry-run

⚡ 성능 튜닝

프록시 버퍼링

location / {
proxy_pass http://127.0.0.1:8080;

# 버퍼링 설정
proxy_buffering on;
proxy_buffer_size 4k;
proxy_buffers 8 16k;
proxy_busy_buffers_size 24k;

# 타임아웃
proxy_connect_timeout 60s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
}

Keep-Alive 연결

upstream tomcat_cluster {
server 192.168.1.10:8080;
server 192.168.1.11:8080;

# Upstream Keep-Alive
keepalive 32;
}

server {
location / {
proxy_pass http://tomcat_cluster;

# HTTP/1.1 Keep-Alive
proxy_http_version 1.1;
proxy_set_header Connection "";
}
}

Gzip 압축

http {
gzip on;
gzip_types text/plain text/css application/json application/javascript text/xml;
gzip_min_length 1000;
gzip_comp_level 6;
gzip_vary on;
}

캐싱

# 캐시 존 정의
proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=tomcat_cache:10m max_size=1g inactive=60m;

server {
location / {
proxy_pass http://127.0.0.1:8080;

# 캐싱 적용
proxy_cache tomcat_cache;
proxy_cache_valid 200 10m;
proxy_cache_valid 404 1m;
proxy_cache_use_stale error timeout updating;

# 캐시 상태 헤더
add_header X-Cache-Status $upstream_cache_status;
}

# 동적 콘텐츠는 캐시 안 함
location /api/ {
proxy_pass http://127.0.0.1:8080;
proxy_cache off;
}
}

🔒 보안 설정

기본 보안 헤더

server {
# 보안 헤더
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;

# 숨기기
server_tokens off;
proxy_hide_header X-Powered-By;
}

접근 제한

server {
# 특정 경로 접근 제한
location /manager/ {
allow 192.168.1.0/24;
deny all;

proxy_pass http://127.0.0.1:8080;
}

# IP 기반 차단
location / {
deny 10.0.0.1;
allow all;

proxy_pass http://127.0.0.1:8080;
}
}

Rate Limiting

# limit_req_zone 정의
limit_req_zone $binary_remote_addr zone=api_limit:10m rate=10r/s;

server {
location /api/ {
limit_req zone=api_limit burst=20 nodelay;

proxy_pass http://127.0.0.1:8080;
}
}

🩺 트러블슈팅

502 Bad Gateway

# Tomcat 실행 확인
ps -ef | grep tomcat
netstat -tlnp | grep 8080

# Nginx 에러 로그
tail -f /var/log/nginx/error.log

# SELinux 확인 (CentOS/RHEL)
setsebool -P httpd_can_network_connect 1

504 Gateway Timeout

# 타임아웃 늘리기
location / {
proxy_pass http://127.0.0.1:8080;
proxy_connect_timeout 300;
proxy_send_timeout 300;
proxy_read_timeout 300;
}

클라이언트 IP가 127.0.0.1로 찍힘

# 헤더 설정 확인
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
<!-- Tomcat RemoteIpValve 설정 -->
<Valve className="org.apache.catalina.valves.RemoteIpValve"
remoteIpHeader="X-Forwarded-For"
protocolHeader="X-Forwarded-Proto" />

WebSocket 연결 안 됨

location /ws/ {
proxy_pass http://127.0.0.1:8080;

# WebSocket 지원
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}

📋 설정 체크리스트

항목확인
Tomcat HTTP Connector 활성화 (8080)
Nginx proxy_pass 설정
프록시 헤더 설정 (Host, X-Real-IP, X-Forwarded-*)
Tomcat RemoteIpValve 설정
SSL 설정 (필요시)
방화벽/SELinux 설정
정적 파일 분리 (선택)
로드밸런싱 설정 (선택)

🔗 시리즈 네비게이션

이전다음
Apache + Tomcat IntegrationTomcat Configuration

시리즈 목차로 돌아가기


🔗 참고 자료