Nginx + Tomcat Integration
Nginx 리버스 프록시 기반 Tomcat 연동, 로드밸런싱, SSL
📚 시리즈 네비게이션
| 이전 | 현재 | 다음 |
|---|---|---|
| Apache + Tomcat | Nginx + Tomcat | Tomcat 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은 클라이언트 정보를 알 수 없음:
| 정보 | 헤더 없이 | 헤더 있으면 |
|---|---|---|
| 클라이언트 IP | Nginx 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()→ 실제 클라이언트 IPrequest.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 Integration | Tomcat Configuration |