Docker Entrypoint로 실행된 Shell Script에서 Application으로 SIGTERM을 전송하는 방법

업데이트:

개요

  • Dockerfile을 만들다 보면, ENTRYPOINT1에 단순 application을 실행하는 로직 외 다양한 로직을 실행하기 위해서 Shell Script를 실행하는 경우가 있다.
  • 이 경우, Docker를 종료하려고 SIGTERM을 전송하면 Shell Script로 전송되기 때문에 Application에서는 정상적으로 SIGTERM을 전달받지 못해서 Graceful Shutdown이 수행되지 않고 SIGKILL에 의해서 서비스는 강제 종료된다.
  • 위의 상황들을 해결하기 위한 기본적인 방법인 Shell Script로 전달된 SIGTERM을 Application으로 전달하는 방법을 설명한다.

run.sh

#!/bin/sh

# Send SIGTERM to the application.
term_handler() {
  if [ $pid -ne 0 ]; then
    kill -SIGTERM "$pid"
    wait "$pid"
  fi
  exit 143 # 128(External) + 15(SIGTERM)
}

# Setup the trap for the SIGTERM.
trap 'kill ${!}; term_handler' SIGTERM

# Start application.
java org.springframework.boot.loader.launch.JarLauncher &
pid="$!"

# Wait the signal.
while true; do
  # Check if process is still running.
  kill -0 "$pid" 2>/dev/null
  if [ $? -ne 0 ]; then
    echo "Process $pid is not running. Terminating the container."
    term_handler
  fi
  # Signal confirmation interval.
  sleep 5
done
  • Apline Linux에서도 정상 동작 가능하도록 bash가 아닌 sh로 실행 가능하게 shebang2을 “/bin/sh”로 정의한다.
  • term_handler 함수는 pid가 존재하면 pid로 SIGTERM을 전송 후 완료되기 까지 pid 프로세스를 기다리는 작업을 수행하고 마지막으로 143 코드로 스크립트 실행을 종료한다.
    • 143의 의미는 외부 요청 수행을 의미하는 128에, SIGTERM을 의미하는 15를 더한 값으로 차별화 시킨 값이다.
  • tarp 명령어를 통해 SIGTERM을 받았을 때, term_handler 함수를 수행한다.
  • java 명령어로 어플리케이션을 수행하면서, pid 변수에 실행된 pid를 저장한다.
  • 5초마다 수행 프로세스가 진행 중인지를 검증하고, 프로세스 종료된 경우 스크립트 실행을 종료하여 현재 실행 중인 컨테이너 또한 종료한다.

Dockerfile

... omitted ...

# Copy run.sh and change mode to 755
COPY ./run.sh /
RUN chmod 755 /run.sh

... omitted ...

# Run application
ENTRYPOINT [ "/bin/sh", "/run.sh" ]
  • 위에서 정의한 run.sh 파일을 컨테이너 내부로 이동시킨 후, 실행에 문제 없게 755 권한을 부여한다.
  • ENTRYPOINT로 컨테이너 실행 시, run.sh 쉘 스크립트를 실행하도록 정의한다.

부가 설명

  • Docker Compose를 이용하여 종료하게 되는 경우엔 컨테이너 Graceful Shutdown을 위한 stop_grace_period3 옵션과 Spring Boot의 Graceful Shutdown4 설정을 유연하게 조절하여야 한다.

정리

  • Docker Container는 컨테이너 실행에 수행되는 CMD 혹은 ENTRYPOINT 설정으로 실행된 프로세스의 상태와 직접적인 관계가 있지만, Shell Script 등을 활용한 간접적인 Application 실행의 경우 Signal을 주요 Application에 중계해야 정상적인 상태를 동기화 할 수 있다.

Reference

※ Sample Code는 여기에서 확인 가능합니다.

댓글남기기