[Tomcat] 톰켓 프로세스 종료 이슈 (shutdown 시 PID 남음)
이슈 내용
Tomcat을 종료하는 스크립트(shutdown.sh)를 실행했지만 프로세스가 죽지 않고 그대로 남는다. netstate -tnlp 상으로는 확인되지 않고, ps -ef 상으로는 PID 번호가 확인된다.
기존 프로세스를 kill하지 않으면 tomcat 재실행(startup.sh) 시, 같은 프로세스가 2개 돌게 된다.
# 톰켓 shutdown 후, ps -ef
# pid가 남아있음
ps -ef | grep Batch
> user 12345 ... 00:00:25 /usr/bin/java
-Djava.util.logging.config.file=/tomcat-path/conf/...
# 톰켓 shutdown 후, netstat -tnlp
# 애플리케이션 port는 닫힘
netstat -tnlp
> Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
tcp 0 0 127.0.0.53:53 0.0.0.0:* LISTEN -
tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN -
tcp6 0 0 :::80 :::* LISTEN -
원인
이런 상황이 발생하는 이유는 tomcat 종료 시점에 프로세스에 실행 중인 스레드가 남아있어서라고 한다. 서버에서 실행중인 포트를 확인해보면 애플리케이션은 종료됐지만, 내부에서 실행중인 스레드가 남아서 프로세스가 종료되지 않는다.
해결 방법 (2가지)
1. catalina.pid 파일 활용
해당 방법은 CATALINA_PID 변수를 .pid 파일로 뽑아내 프로세스 종료에 사용하는 방법이다. CATALINA_PID를 지정하면 tomcat이 구동될 때, PID를 파일에 기록하게 되는데 종료 시점에 이 파일을 읽어 강제 종료시킬 수 있다.
tomcat 디렉토리 내부 bin 경로에서 startup.sh, shutdown.sh에 아래 명령어를 추가하면 된다.
# startup.sh
export CATALINA_PID=/home/[tomcat 경로]/bin/catalina.pid
exec "$PRGDIR"/"$EXECUTABLE" start "$@"
# shutdown.sh
# 톰켓 종료에 -force 옵션을 꼭 넣어줘야 함
export CATALINA_PID=/home/[tomcat 경로]/bin/catalina.pid
exec "$PRGDIR"/"$EXECUTABLE" stop -force "$@"
2. catalina.sh 스크립트에 tomcat PID 검색 및 삭제 명령어 추가
이 방법은 catalina.sh 스크립트 내부에서 tomcat 종료 시점에 PID를 찾아 죽이는 명령어를 추가하는 방법이다.
아래 코드 블록을 참고해서 해당 포인트에 명령어를 추가하면 된다.
# catalina.sh
if [ ! -z "$CATALINA_PID" ]; then
`ps -ef | grep tomcat | grep java | grep -v grep | awk '{print $2}' > /home/[tomcat 경로]/bin/catalina.pid`
# echo "Using CATALINA_PID: $CATALINA_PID"
fi
추가 이슈 (logrotate와 tomcat PID 충돌)
이 부분은 나와 같이 애플리케이션 내부에 logback과 같은 라이브러리로 로깅을 처리할 때, 마주할 수 있다. 내 생각에는 tomcat이 실행될 때, logback이 실행하는 rotatelogs가 먼저 실행돼서 CATALINA_PID가 rotatelogs로 지정되는 걸로 보인다.
이럴 경우, 위 2가지 방법으로 해결이 안 되는데, 조금 극단적이지만 catalina.sh 스크립트에서 shutdown 시점에 pid를 찾아 강제로 kill 시키는 방법으로 대응했다.
# netstat -tnlp
user 5061 1 0 Oct31 ? 00:00:01 /usr/bin/rotatelogs /home/user/logs/App/%Y%m%d.log 86400 540
user 5062 5060 7 Oct31 ? 10:37:10 /usr/bin/java -Djava.util.logging.config.file=...
# catalina.sh
KILL_SLEEP_INTERVAL=5
if [ $FORCE -eq 1 ]; then
// ...
fi
# KILL PID
ps -ef | grep 톰켓 | grep -v grep | grep -v catalina.sh | awk '{print $2}' | xargs kill -9
elif [ "$1" = "configtest" ] ; then
// ...
else
마무리
가장 좋은 방법은 tomcat이 shutdown 되는 시점에 프로세스 내부에 실행되는 스레드가 남지 않게 하는 것으로 생각된다. Java의 경우 Bean 라이플 사이클 중, init-method 속성으로 destroy 처리로 해결할 수 있지 않을까 싶다.