프로젝트 초반에는 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
없이 죽이기 때문에, 서비스 중단이나 데이터 처리 중 오류가 발생할 수 있다.#!/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 -15 | SIGTERM | 프로세스에 종료 요청을 보냄 → 애플리케이션이 종료를 준비하고 자원 정리를 할 기회를 가짐 (graceful shutdown) | 일반적으로 먼저 시도해야 하는 방식 |
kill -9 | SIGKILL | 즉시 프로세스를 강제로 종료 → 어떤 정리도 하지 못함, 파일이나 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
에 조건별 분기와 예외 처리를 추가하면 신뢰도 높은 자동화 배포 환경을 만들 수 있다.