Spring Boot start.sh 개선 과정과 그레이스풀 리스타트의 중요성

송현진·2025년 4월 2일
0

Spring Boot

목록 보기
4/23

🛠️ 초기 상태: 단순 pkill 방식

프로젝트 초반에는 EC2에서 Spring Boot 애플리케이션을 배포할 때 아래처럼 아주 단순한 방식으로 start.sh를 작성해 사용했다.

#!/bin/bash
pkill -f 'java -jar' || echo "No app running"
nohup java -jar /home/ec2-user/app.jar > /home/ec2-user/app.log 2>&1 &

⚠️ 문제점

  • pkill -f는 애플리케이션 이름에 기반한 강제 종료이기 때문에 실행 중인 사용자 요청이 있는 상태에서도 바로 프로세스를 죽여버린다.
  • Graceful Shutdown 없이 죽이기 때문에, 서비스 중단이나 데이터 처리 중 오류가 발생할 수 있다.
  • 중복 실행, 포트 충돌 등의 문제에 대한 방어 로직이 없었다.
  • 에러가 나도 로그나 상태 확인이 어렵고 불명확했다.

🔁 개선 방향

  1. 실행 중인 프로세스를 강제로 종료하지 않고, 최대한 그레이스풀하게 종료
  2. 중복 실행 방지
  3. 포트 점유 여부 확인
  4. 로그 기록 및 쓰기 권한 확인
  5. 전체적인 예외 처리 및 에러 메시지 출력

✅ 최종 개선된 start.sh

#!/bin/bash

APP_NAME="coupangclone-0.0.1-SNAPSHOT.jar"
JAR_PATH="/home/ec2-user/$APP_NAME"
LOG_PATH="/home/ec2-user/app.log"
PORT=8080

echo "========== $(date '+%Y-%m-%d %H:%M:%S') =========="

# 1. JAR 파일 존재 여부 확인
if [ ! -f "$JAR_PATH" ]; then
  echo "[ERROR] JAR not found at $JAR_PATH"
  exit 1
fi

# 2. 기존 프로세스 확인 및 graceful 종료
PID=$(pgrep -f "$APP_NAME")

if [ -n "$PID" ]; then
  echo "[INFO] App is running (PID: $PID). Attempting graceful shutdown..."

  # graceful 종료를 위해 SIGTERM 시그널을 보냄
  kill -15 "$PID"

  # graceful 종료 대기: 최대 60초 동안 기다린다.
  for i in {1..60}; do
    if ! ps -p "$PID" > /dev/null; then
      echo "[INFO] App stopped gracefully."
      break
    fi
    echo "Waiting for process $PID to stop... ($i)"
    sleep 1
  done

  # graceful 종료가 되지 않으면 종료되지 않았다고 로그를 남기고 종료 처리
  if ps -p "$PID" > /dev/null; then
    echo "[WARN] App did not stop gracefully within 60 seconds. Aborting restart."
    exit 1
  fi
else
  echo "[INFO] No matching running process."
fi

# 3. 포트 점유 확인: 포트가 사용 중이면 실행하지 않음
if lsof -i :"$PORT" > /dev/null 2>&1; then
  echo "[WARN] Port $PORT is still in use. Aborting to avoid collision."
  exit 1
else
  echo "[INFO] Port $PORT is free."
fi

# 4. 로그 파일 접근 확인: 로그 파일에 쓸 수 있는지 확인
if ! touch "$LOG_PATH" > /dev/null 2>&1; then
  echo "[ERROR] Cannot write to log file at $LOG_PATH"
  exit 1
fi

# 5. 새 애플리케이션 실행
echo "[INFO] Starting new app"
nohup java -jar "$JAR_PATH" \
  --spring.profiles.active=prod \
  --spring.config.location=file:/home/ec2-user/application.yml,file:/home/ec2-user/application-prod.yml,file:/home/ec2-user/application-secret.yml \
  > "$LOG_PATH" 2>&1 &

NEW_PID=$!
echo "[INFO] App started with PID: $NEW_PID"

kill -9 vs kill -15 차이점 정리

명령어이름특징사용 시 주의사항
kill -15SIGTERM프로세스에 종료 요청을 보냄 → 애플리케이션이 종료를 준비하고 자원 정리를 할 기회를 가짐 (graceful shutdown)일반적으로 먼저 시도해야 하는 방식
kill -9SIGKILL즉시 프로세스를 강제로 종료 → 어떤 정리도 하지 못함, 파일이나 DB 커넥션이 꼬일 수 있음정말 종료가 안 되는 경우 최후의 수단으로 사용
  • kill -15애플리케이션이 스스로 종료할 수 있도록 기회를 주는 방식이기 때문에, Spring Boot처럼 종료 훅(shutdown hook)이 있는 서비스에는 반드시 사용해야 한다.

  • kill -9프로세스가 멈추거나 응답하지 않을 때만 사용해야 하며, 서비스 중단이나 데이터 유실 위험이 있다.

새로 알게된 용어들

  • lsof (List Open Files)
    현재 열려있는 파일 정보를 출력하는 명령어
  • -i
    인터넷 소켓을 의미 (TCP, UDP 등)

💡lsof -i :포트 - 해당 포트를 사용 중인 프로세스 확인

  • -n
    이름 해석 생략 (속도 향상)
  • /dev/null
    블랙홀 파일, 출력 무시할 때 사용
  • 2>&1
    에러 출력을 표준 출력으로 리디렉션

실행 결과

📝 배운 점

  • pkill은 간단하지만, 프로덕션 환경에서는 위험한 방법이다.
  • 애플리케이션은 가능한 한 그레이스풀하게 종료되도록 관리해야 한다.
  • pgrep + kill + lsof를 통해 중복 실행과 포트 충돌을 효과적으로 방지할 수 있다.
  • start.sh에 조건별 분기와 예외 처리를 추가하면 신뢰도 높은 자동화 배포 환경을 만들 수 있다.
profile
개발자가 되고 싶은 취준생

0개의 댓글