공공부하자개발 · 영어 학습 노트
리눅스
리눅스 운영RHEL 에서 배포·운영하기0/13 완료
  • 01셸 기본과 파일 시스템
  • 02텍스트 처리 (grep·sed·awk)
  • 03프로세스·서비스 관리 (systemd)
  • 04자바 애플리케이션 배포
  • 05Tomcat·nginx 배포·재기동
  • 06로그·디스크 운영 (logrotate·cron)
  • 07네트워크 진단 (ss·curl·firewalld)
  • 08셸 스크립트 (운영 자동화)
  • 09장애 대응 (jstack·jmap·OOM killer·FD)
  • 10보안 기초 (ssh 강화·SELinux·감사 로그)
  • 11컨테이너 기초 (podman)
  • 12백업·복구 (설정·릴리스·DB 덤프)
  • 13Docker 실무
사이트 소개개인정보처리방침연락처
© 2026 공부하자
홈 › 리눅스 운영 › 05 / 13

Tomcat·nginx 배포·재기동

섹션 7진행 0 / 13
1왜 배우는가2핵심 원리3코드 예제4응용 변형 예제5자주 하는 실수 (Tip)6연습 문제7정리‹ 이전다음 ›

2. 핵심 원리

2.1 Tomcat 폴더 구조

폴더 내용
bin/ startup.sh shutdown.sh catalina.sh setenv.sh
conf/ server.xml context.xml
webapps/ war 와 풀린 폴더
work/ JSP 컴파일 캐시
logs/ catalina.out 접근 로그
temp/ 임시 파일

CATALINA_HOME 은 설치 폴더, CATALINA_BASE 는 인스턴스 폴더입니다. 하나만 쓰면 둘이 같습니다. 여러 인스턴스를 띄울 때 BASE 를 나눠 conf/·webapps/·logs/ 를 분리합니다.

2.2 war 배포 방식

war 를 webapps/ 에 놓으면 Tomcat 이 자동으로 풀어 같은 이름의 폴더를 만듭니다. ROOT.war 는 / 경로, shop.war 는 /shop 경로입니다.

방식 장점 단점
실행 중 war 복사 정지 없음 옛 클래스 잔존 위험
정지 → 교체 → 시작 깨끗함 수십 초 중단
인스턴스 2개 교대 무중단 nginx 조합 필요

이 레슨은 두 번째 방식을 기본으로 합니다. 실행 중 교체는 autoDeploy 가 풀린 폴더를 지우지 못하는 경우가 있어 운영에서는 피합니다.

2.3 깨끗한 교체 순서

옛 결과물을 전부 지워야 새 war 만 남습니다. 지울 것은 세 가지입니다.

bash
rm -rf webapps/ROOT webapps/ROOT.war        # 풀린 폴더와 war
rm -rf work/Catalina/localhost/ROOT         # JSP 컴파일 캐시
cp new.war webapps/ROOT.war

work/ 를 안 지우면 JSP 를 고쳤는데 옛 화면이 나옵니다. temp/ 는 지우지 않아도 되지만 배포 전 backup/ 에 이전 war 를 복사해 두면 롤백이 cp 한 번입니다.

2.4 catalina.sh 와 setenv.sh

startup.sh 는 catalina.sh start, shutdown.sh 는 catalina.sh stop 의 줄임입니다. JVM 옵션은 bin/setenv.sh 에 둡니다.

bash
# bin/setenv.sh
export CATALINA_OPTS="-Xms1g -Xmx1g -Duser.timezone=Asia/Seoul"
export JAVA_HOME=/usr/lib/jvm/java-21-openjdk

CATALINA_OPTS 는 서버 실행에만, JAVA_OPTS 는 shutdown.sh 같은 도구에도 적용됩니다. 힙처럼 서버 전용 값은 CATALINA_OPTS 에 넣습니다. catalina.sh run 은 포그라운드로 떠서 systemd 의 Type=simple 에 맞습니다.

2.5 shutdown 이 안 끝날 때

shutdown.sh 는 8005 포트로 정지 명령을 보냅니다. 스레드가 끝나지 않으면 JVM 이 남아 있고, 그 상태에서 startup.sh 를 치면 8080 포트 충돌이 납니다.

bash
shutdown.sh
for _ in $(seq 1 30); do
  pgrep -f "catalina.base=$CATALINA_BASE" >/dev/null || break
  sleep 1
done
pkill -9 -f "catalina.base=$CATALINA_BASE"   # 30초 뒤에도 남았으면

catalina.base= 문자열로 찾으면 같은 서버의 다른 Tomcat 인스턴스를 건드리지 않습니다. pkill -f java 는 절대 쓰지 않습니다.

2.6 Tomcat systemd 유닛

03 레슨과 같은 형태로 Tomcat 도 서비스로 올립니다. catalina.sh run 을 쓰면 Type=simple 로 충분합니다.

ini
[Service]
Type=simple
User=tomcat
Environment=CATALINA_HOME=/opt/tomcat
ExecStart=/opt/tomcat/bin/catalina.sh run
ExecStop=/opt/tomcat/bin/shutdown.sh
SuccessExitStatus=143
Restart=on-failure

배포 스크립트는 systemctl 유닛이 있으면 그것을 쓰고, 없으면 startup.sh·shutdown.sh 를 씁니다. 두 방식을 섞어 쓰면 systemd 가 모르는 프로세스가 생겨 상태가 어긋납니다.

2.7 nginx 의 역할

nginx 는 앞단에서 세 가지 일을 합니다. 80·443 을 받아 8080 으로 넘기는 리버스 프록시, 정적 파일 직접 서비스, 여러 인스턴스로 분배입니다.

nginx
upstream myapp { server 127.0.0.1:8080; }
server {
    listen 80;
    location /static/ { root /opt/myapp/public; }
    location / { proxy_pass http://myapp; }
}

설정은 /etc/nginx/nginx.conf 가 conf.d/*.conf 를 읽어 들이는 구조입니다. 앱마다 conf.d/앱이름.conf 하나를 두면 다른 앱 설정을 건드리지 않습니다.

2.8 proxy 헤더

프록시를 거치면 자바 앱은 클라이언트 IP 대신 127.0.0.1 을 봅니다. 원래 정보를 헤더로 넘겨야 로그와 리다이렉트가 맞습니다.

헤더 값 용도
Host $host 가상 호스트 판별
X-Real-IP $remote_addr 클라이언트 IP
X-Forwarded-For $proxy_add_x_forwarded_for 프록시 경유 IP 목록
X-Forwarded-Proto $scheme http/https 구분

Spring Boot 는 server.forward-headers-strategy=native 를 켜면 이 헤더를 읽어 request.getRemoteAddr() 와 리다이렉트 URL 을 바로잡습니다.

2.9 nginx -t 와 reload

설정을 고친 뒤 순서는 검사, reload 입니다. reload 는 마스터가 새 설정을 읽고 워커를 하나씩 교체하므로 기존 연결이 끊기지 않습니다.

명령 동작 연결
nginx -t 문법·파일 존재 검사 영향 없음
systemctl reload nginx 설정 다시 읽기 유지
systemctl restart nginx 완전 재시작 끊김
nginx -s reload reload 와 같음 유지

restart 는 바이너리 업그레이드나 reload 로 안 바뀌는 항목을 고칠 때만 씁니다. 대부분의 설정 변경은 reload 로 충분합니다.

2.10 안전한 설정 교체

새 설정을 적용하기 전 이전 파일을 .bak 으로 남기고, 검사에 실패하면 되돌립니다. -t 가 실패한 상태로 두면 다음 사람이 restart 했을 때 nginx 가 안 뜹니다.

bash
cp conf.d/myapp.conf conf.d/myapp.conf.bak
cp new.conf conf.d/myapp.conf
nginx -t || { cp conf.d/myapp.conf.bak conf.d/myapp.conf; exit 1; }
systemctl reload nginx

nginx -t 는 root 권한이 필요합니다. 로그 파일 열기 권한까지 검사하기 때문입니다.

2.11 502·504 읽기

프록시 뒤 자바 앱이 문제일 때 nginx 가 돌려주는 코드입니다. 코드로 원인을 좁힙니다.

코드 뜻 확인할 것
502 뒷단 연결 실패 Tomcat 이 떠 있는지, 포트
504 뒷단 응답 시간 초과 proxy_read_timeout, 느린 쿼리
413 요청 본문 초과 client_max_body_size

error_log 에 connect() failed (111: Connection refused) 가 있으면 502 이고 Tomcat 이 내려간 것입니다. upstream timed out 이면 504 로 앱이 느린 것입니다.

2.12 무중단 교체와 upstream

upstream 에 인스턴스를 둘 두고 하나씩 교체하면 서비스가 끊기지 않습니다. 교체할 인스턴스를 down 으로 표시하고 reload 하면 nginx 가 그쪽으로 보내지 않습니다.

nginx
upstream myapp {
    server 127.0.0.1:8080;
    server 127.0.0.1:8081 down;     # 배포 중인 인스턴스
}

배포 뒤 down 을 빼고 다시 reload 합니다. max_fails=3 fail_timeout=10s 를 두면 죽은 인스턴스를 자동으로 제외하지만, 배포 중 실패 응답이 3번 나가므로 명시적 down 이 더 깨끗합니다.

핵심 원리
  • 2.1 Tomcat 폴더 구조
  • 2.2 war 배포 방식
  • 2.3 깨끗한 교체 순서
  • 2.4 catalina.sh 와 setenv.sh
  • 2.5 shutdown 이 안 끝날 때
  • 2.6 Tomcat systemd 유닛
  • 2.7 nginx 의 역할
  • 2.8 proxy 헤더
  • 2.9 nginx -t 와 reload
  • 2.10 안전한 설정 교체
  • 2.11 502·504 읽기
  • 2.12 무중단 교체와 upstream
이전 섹션1 왜 배우는가2 / 7다음 섹션3 코드 예제