Apache+Tomcat 연동 실전 고수 팁
Apache+Tomcat 연동 환경에서 발생하는 실제 문제를 진단하고 해결하는 고급 노하우를 다룹니다. mod_jk 상태 페이지 활용, 연결 디버깅, 성능 튜닝, 장애 대응까지 프로덕션 운영에 필요한 기술을 정리합니다.
mod_jk 상태 페이지 활용
mod_jk의 jkstatus 핸들러를 통해 워커 상태와 로드밸런싱 통계를 실시간으로 확인할 수 있습니다.
설정
# workers.properties — 상태 워커 추가
worker.list=worker1,jkstatus
worker.jkstatus.type=status
worker.jkstatus.read_only=true # 운영 환경에서는 읽기 전용 필수
# Apache VirtualHost — 상태 페이지 경로 설정
<Location /jkstatus>
JkMount jkstatus
Require ip 127.0.0.1 10.0.0.0/8 # 내부망만 허용
</Location>
상태 페이지에서 확인할 수 있는 정보
| 항목 | 설명 |
|---|---|
| State | OK / ERR (워커 상태) |
| Elected | 총 처리 요청 수 |
| Errors | 오류 발생 횟수 |
| Sessions | 현재 처리 중인 세션 수 |
| Route | 세션 고정 라우팅 키 |
| LBF | 로드밸런싱 팩터 |
# CLI로 상태 확인
curl -s "http://localhost/jkstatus/?mime=prop" | grep worker
# 특정 워커 상태
curl -s "http://localhost/jkstatus/?cmd=show&w=worker1"
# 워커 활성화/비활성화 (read_only=false일 때만)
curl -s "http://localhost/jkstatus/?cmd=update&w=worker1&ac=0" # 비활성화
curl -s "http://localhost/jkstatus/?cmd=update&w=worker1&ac=1" # 활성화
연동 디버깅 — JkLogLevel
JkLogLevel을 높여 상세 로그를 분석합니다.
# mod_jk 로그 레벨 (심각도 순서)
JkLogLevel debug # 가장 상세 (운영 중 사용 금지 — 성능 저하)
JkLogLevel info
JkLogLevel warn # 기본값 권장
JkLogLevel error
# 로그 실시간 모니터링
tail -f /var/log/apache2/mod_jk.log
# 에러 필터링
grep -i "error\|fail\|timeout" /var/log/apache2/mod_jk.log | tail -50
# 특정 워커 관련 로그
grep "worker1" /var/log/apache2/mod_jk.log | tail -30
자주 발생하는 mod_jk 에러
| 에러 메시지 | 원인 | 해결 방법 |
|---|---|---|
Timeout waiting for a reply | Tomcat 응답 지연 | socket_timeout 증가, Tomcat 부하 확인 |
ajp_ilink_receive failed | AJP 연결 끊김 | Tomcat 재시작, 연결 풀 점검 |
No worker found for name | workers.properties 설정 오류 | worker.list 및 워커 이름 확인 |
All workers are in error state | 모든 Tomcat 다운 | Tomcat 프로세스 및 AJP 포트 확인 |
mod_proxy_http 디버깅
# Apache 에러 로그 레벨 상향 (임시)
LogLevel proxy:debug proxy_http:debug
# mod_proxy 관련 에러만 추출
grep -i "proxy\|ajp\|backend" /var/log/apache2/error.log | tail -50
# 프록시 연결 상태
curl -v http://localhost/ 2>&1 | grep -E "< HTTP|Connected|Request"
# 백엔드(Tomcat) 응답 직접 테스트
curl -v http://127.0.0.1:8080/ 2>&1 | head -30
자주 발생하는 mod_proxy 에러
| HTTP 코드 | 에러 | 원인 | 해결 방법 |
|---|---|---|---|
| 502 Bad Gateway | 백엔드 응답 없음 | Tomcat 다운, 포트 불일치 | Tomcat 상태 확인, ss -tlnp | grep 8080 |
| 503 Service Unavailable | 연결 풀 고갈 | 동시 요청 폭증 | max 속성 증가, Tomcat 스레드 확인 |
| 504 Gateway Timeout | 응답 시간 초과 | 느린 쿼리/처리 | timeout 증가, 슬로우 쿼리 분석 |
| 500 Internal Server Error | Tomcat 내부 오류 | 애플리케이션 예외 | Tomcat catalina.out 확인 |
연결 풀 최적화
mod_jk workers.properties 튜닝
# workers.properties — 연결 풀 최적화
worker.worker1.type=ajp13
worker.worker1.host=127.0.0.1
worker.worker1.port=8009
# 연결 풀 크기 = Tomcat maxThreads의 75~80%
worker.worker1.connection_pool_size=75
# 유휴 연결 타임아웃: 장시간 미사용 연결 정리
worker.worker1.connection_pool_timeout=600
# 소켓 타임아웃: Tomcat 응답 최대 대기 시간
worker.worker1.socket_timeout=60
# TCP keepalive: 방화벽이 연결을 끊는 경우 활성화
worker.worker1.socket_keepalive=true
# 재시도 설정
worker.worker1.retries=2
worker.worker1.recovery_options=7
mod_proxy_http 연결 풀 튜닝
# 글로벌 프록시 연결 설정
<IfModule mod_proxy.c>
# 총 열린 연결 수 제한
ProxyMaxForwards 10
</IfModule>
# ProxyPass 속성으로 세밀한 제어
ProxyPass / http://127.0.0.1:8080/ \
connectiontimeout=5 \ # 연결 수립 최대 5초
timeout=60 \ # 응답 최대 60초
retry=0 \ # 오류 시 즉시 재시도 (0=즉시)
acquire=3000 \ # 연결 풀 대기 최대 3초
max=100 \ # 최대 100개 연결
ttl=300 # 연결 최대 재사용 300초
# keepalive 활성화 (Tomcat과 TCP 연결 유지)
SetEnv proxy-nokeepalive 0
SetEnv proxy-initial-not-pooled 0
헤더 디버깅
클라이언트 IP, SSL 정보가 Tomcat에 올바르게 전달되는지 확인합니다.
# 디버깅용 응답 헤더에 내부 정보 노출 (개발 환경만)
<VirtualHost *:443>
Header always set X-Debug-Remote-Addr "%{REMOTE_ADDR}s"
Header always set X-Debug-Forwarded-For "%{X-Forwarded-For}e"
Header always set X-Debug-Proto "%{X-Forwarded-Proto}e"
</VirtualHost>
# Tomcat에서 수신한 헤더 확인 (Spring Boot)
# application.properties
# logging.level.org.apache.tomcat=DEBUG
# curl로 헤더 확인
curl -I -H "X-Test: hello" https://example.com/
세션 고정(Sticky Session) 트러블슈팅
# Tomcat 1에서 생성된 세션 확인
curl -c cookies.txt https://example.com/login
cat cookies.txt
# JSESSIONID=abc123.tomcat1 ← .tomcat1 부분이 라우팅 키
# 다음 요청에서 같은 Tomcat으로 라우팅 확인
curl -b cookies.txt https://example.com/dashboard
세션 고정 실패 시 점검 목록
1. Tomcat server.xml — jvmRoute 설정 확인
<Engine jvmRoute="tomcat1">
2. workers.properties — route 설정 확인
worker.tomcat1.route=tomcat1
3. JSESSIONID 쿠키에 .tomcat1 suffix 포함 여부 확인
4. sticky_session=true 설정 확인
worker.lb_worker.sticky_session=true
5. 세션 만료 또는 Tomcat 재시작으로 인한 세션 분실
→ sticky_session_force=false (다른 노드로 폴백 허용)
Apache 설정 검증 자동화
# 설정 문법 검사
sudo apache2ctl configtest
# 모듈 로드 확인
apache2ctl -M | grep -E "proxy|jk|headers|rewrite|ssl"
# VirtualHost 목록 확인
apache2ctl -S
# 실제 요청 처리 경로 추적 (Apache 2.4)
# mod_remoteip 트레이스
프록시 요청 로깅
# 프록시 요청/응답 로그 포맷
LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-agent}i\" %D" combined_timing
# 처리 시간(%D)이 3000ms(3초) 초과인 느린 요청 로깅
SetEnvIf Request_URI ".*" slow_request=0
# (커스텀 로그 분석 스크립트 사용)
CustomLog ${APACHE_LOG_DIR}/access.log combined_timing
# 느린 요청 상위 10개 (처리 시간 기준)
awk '{print $NF, $0}' /var/log/apache2/access.log | sort -rn | head -10
# 5xx 에러 요청만 추출
grep ' 5[0-9][0-9] ' /var/log/apache2/access.log | tail -50
# 시간당 요청 수
awk '{print $4}' /var/log/apache2/access.log | \
cut -d: -f2 | sort | uniq -c | sort -rn
점검 모드 (Graceful Maintenance)
# 점검 페이지 설정
<VirtualHost *:443>
ServerName example.com
# 점검 플래그 파일이 존재하면 503 반환
RewriteEngine On
RewriteCond %{DOCUMENT_ROOT}/maintenance.flag -f
RewriteCond %{REQUEST_URI} !=/maintenance.html
RewriteRule ^ /maintenance.html [R=503,L]
ErrorDocument 503 /maintenance.html
Alias /maintenance.html /var/www/maintenance.html
</VirtualHost>
# 점검 시작
touch /var/www/html/maintenance.flag
# 점검 종료
rm /var/www/html/maintenance.flag
보안 강화 체크리스트
# 서버 정보 숨기기
ServerTokens Prod # "Apache" 만 노출 (버전 숨김)
ServerSignature Off # 에러 페이지 서버 정보 제거
# 불필요한 HTTP 메서드 차단
<LimitExcept GET POST HEAD>
Require all denied
</LimitExcept>
# 디렉터리 탐색 차단
<Directory />
Options -Indexes
AllowOverride None
</Directory>
# Slowloris 방어
RequestReadTimeout header=20,MinRate=500 body=20,MinRate=500
한 눈에 보는 진단 명령어
# 1. 포트 상태 확인
ss -tlnp | grep -E "80|443|8080|8009"
# 2. Apache 설정 검증
sudo apache2ctl configtest && echo "OK"
# 3. 모듈 확인
apache2ctl -M | grep -E "proxy_http|proxy_ajp|jk"
# 4. Tomcat 연결 테스트
curl -s -o /dev/null -w "%{http_code}" http://127.0.0.1:8080/
# 5. Apache → Tomcat 프록시 경로 테스트
curl -s -o /dev/null -w "%{http_code}" http://localhost/
# 6. SSL 인증서 만료 확인
echo | openssl s_client -servername example.com -connect example.com:443 2>/dev/null \
| openssl x509 -noout -dates
# 7. 에러 로그 실시간 모니터링
sudo tail -f /var/log/apache2/error.log
Summary
| 항목 | 도구/방법 |
|---|---|
| mod_jk 상태 모니터링 | /jkstatus 핸들러 + worker.jkstatus.type=status |
| 연동 디버깅 | JkLogLevel debug / LogLevel proxy:debug |
| 연결 풀 튜닝 | connection_pool_size / max=100 ttl=300 |
| 세션 고정 확인 | JSESSIONID .tomcat1 suffix + jvmRoute |
| 설정 검증 | apache2ctl configtest + apache2ctl -M |
| 느린 요청 분석 | %D 로그 포맷 + awk 스크립트 |
| 점검 모드 | maintenance.flag 파일 + RewriteCond |
| 보안 강화 | ServerTokens Prod + RequestReadTimeout |