Skip to content

Latest commit

 

History

History
170 lines (123 loc) · 4.75 KB

08-pitfalls.md

File metadata and controls

170 lines (123 loc) · 4.75 KB

08. 자주 만나는 함정

운영 환경에 적용하기 전 한 번씩 점검할 것.

① JkUnMount를 적용했는데 효과 없음

가장 흔한 사고. Rewrite가 JkUnMount보다 먼저 동작해 경로를 갈아낀 케이스.

해결:

RewriteCond %{REQUEST_URI} !^/resources
RewriteCond %{REQUEST_URI} !^/files
RewriteRule "^(.*)$" "/your-app/$1" [R,L]

자세히 → 03-rewrite-priority.md

② AJP executor 공유 후에도 병목

maxThreads=800이라도 acceptCount가 작으면 OS 대기 큐에서 거부될 수 있다.

<Connector protocol="AJP/1.3"
           executor="tomcatThreadPool"
           acceptCount="200"           <!-- 큐 크기 -->
           maxConnections="2000"       <!-- 최대 동시 연결 -->
           .../>

acceptCount 기본은 100. 부하 큰 환경에선 200~500.

③ swapoff -a 도중 시스템 hang

RAM 가용 메모리 < 스왑 사용량이면 swapoff가 멈춘다.

# 사전 점검
AVAILABLE=$(free -m | awk 'NR==2 {print $7}')
SWAP_USED=$(free -m | awk 'NR==3 {print $3}')

if [ "$SWAP_USED" -gt "$AVAILABLE" ]; then
    echo "위험: swapoff 금지"
    exit 1
fi

sudo swapoff -a && sudo swapon -a

자동화: scripts/swap-check.sh

④ Apache 직접 서빙으로 바꿨더니 404

Alias 경로의 디렉토리 권한·소유권 확인:

# Apache 데몬 사용자가 읽을 수 있어야 함
ls -la /srv/webapp/your-app/static
# drwxr-xr-x. daemon daemon ...

# 안 맞으면
sudo chown -R daemon:daemon /srv/webapp/your-app/static
sudo chmod -R 755 /srv/webapp/your-app/static

SELinux가 enforcing인 경우:

sudo semanage fcontext -a -t httpd_sys_content_t "/srv/webapp/your-app/static(/.*)?"
sudo restorecon -Rv /srv/webapp/your-app/static

# 확인
ls -Z /srv/webapp/your-app/static

⑤ 캐시 헤더 누락으로 재방문이 무거움

Apache 직접 서빙으로 바꿨으니 mod_expires로 캐시 정책 별도 적용. 안 하면:

  • 첫 호출: 빠름 (Apache가 직접 처리)
  • 재방문: 같은 자원을 매번 다운로드 (304도 안 옴) → 무거움
LoadModule expires_module modules/mod_expires.so

<IfModule expires_module>
    ExpiresActive On
    ExpiresByType image/*       "access plus 30 days"
    ExpiresByType text/css      "access plus 30 days"
    ExpiresByType application/javascript "access plus 30 days"
    ExpiresByType font/*        "access plus 1 year"
</IfModule>

검증:

curl -I https://your-domain.example/resources/css/main.css
# Cache-Control: max-age=2592000   ← 30일
# Expires: ...

⑥ AJP Connector를 외부에 노출

<!-- ❌ 위험 -->
<Connector protocol="AJP/1.3"
           address="0.0.0.0"           <!-- 모든 인터페이스 -->
           secretRequired="false"
           port="8009"/>

<!-- ✅ 안전 -->
<Connector protocol="AJP/1.3"
           address="127.0.0.1"         <!-- localhost만 -->
           secretRequired="true"
           secret="<long-random>"
           port="8009"/>

CVE-2020-1938 (Ghostcat) 회피.

⑦ Rewrite 무한 루프

# ❌ 무한 루프
RewriteCond %{REQUEST_URI} !^/your-app
RewriteRule "^(.*)$" "/your-app/$1" [R,L]

# 처음: /resources → /your-app/resources (Redirect)
# 두 번째 요청: /your-app/resources → 위 조건에 안 걸림 → 통과
# 정상 동작이지만, 만약 RewriteCond를 빼면:
# /resources → /your-app/resources → /your-app/your-app/resources → ... 무한

!^/your-app 같은 자기 자신 제외 조건을 항상 포함.

또는 [R,L] 대신 [L]만 써서 내부 리라이트(클라이언트 리다이렉트 없음)로 처리:

RewriteRule "^(.*)$" "/your-app/$1" [L]

내부 리라이트면 클라이언트는 모르지만, JkMount 평가는 새 경로 기준이라 본 가이드 패턴과 호환된다.

⑧ AJP Connector 변경 후 mod_jk와 secret 불일치

<!-- Tomcat -->
<Connector protocol="AJP/1.3"
           secretRequired="true"
           secret="abc123def456"
           .../>
# mod_jk workers.properties - 같은 값이어야 함
worker.worker1.secret=abc123def456

불일치 시 mod_jk가 Tomcat에 연결 시도하지만 거부됨. Apache error_log에 secret required 류 에러.

종합 — 적용 후 확인 체크리스트

  • httpd -t (Apache 설정 문법 OK)
  • catalina.sh configtest (Tomcat OK)
  • 정적 리소스 응답에 Server: Apache (Tomcat 흔적 없음)
  • 캐시 헤더(Cache-Control) 포함
  • AJP 포트가 외부에서 접근 불가 (telnet <external-ip> 8009 실패)
  • 스왑 사용량 1GB 이하 유지
  • 24시간 후 같은 시간대 재측정 안정성 확인