Skip to content

[FEAT] 비동기 작업 유실 완화를 위한 Graceful Shutdown 적용#98

Open
KoungQ wants to merge 2 commits intodevelopfrom
feat/#89/비동기-작업-유실-완화를-위한-graceful-shutdown-적용

Hidden character warning

The head ref may contain hidden characters: "feat/#89/\ube44\ub3d9\uae30-\uc791\uc5c5-\uc720\uc2e4-\uc644\ud654\ub97c-\uc704\ud55c-graceful-shutdown-\uc801\uc6a9"
Open

[FEAT] 비동기 작업 유실 완화를 위한 Graceful Shutdown 적용#98
KoungQ wants to merge 2 commits intodevelopfrom
feat/#89/비동기-작업-유실-완화를-위한-graceful-shutdown-적용

Conversation

@KoungQ
Copy link
Copy Markdown
Member

@KoungQ KoungQ commented Apr 20, 2026

📌 제목

[FEAT] 비동기 작업 유실 완화를 위한 Graceful Shutdown 적용


📢 요약

운영 환경 재시작 시 처리 중이던 요청과 비동기 작업이 즉시 중단되며 유실될 수 있는 문제를 완화하기 위해 Graceful Shutdown을 적용했습니다.

prod 프로필에서만 애플리케이션 종료 대기 시간을 설정하고, 컨테이너가 해당 시간을 실제로 보장할 수 있도록 실행 및 종료 구성을 함께 정리했습니다. 또한 readiness/liveness
probe를 활성화해 종료 시점에 새 트래픽을 받지 않도록 운영 환경과 연동할 수 있게 했습니다.

🔗 연관 이슈: Resolves #89


🚀 PR 유형

해당하는 항목에 체크해주세요.

  • ✨ 새로운 기능 추가
  • 🏗️ 빌드 및 패키지 매니저 수정

✅ PR 체크리스트

PR이 다음 요구 사항을 충족하는지 확인해주세요.

  • 🔹 커밋 메시지 컨벤션을 준수했습니다. (Commit message convention 참고)
  • 🔹 변경 사항에 대한 테스트를 수행했습니다. (버그 수정/기능 테스트)
  • 🔹 관련 문서를 업데이트했습니다. (필요한 경우)

📜 기타

리뷰어가 알면 좋을 추가 사항을 적어주세요.

  • application-prod.ymlserver.shutdown=graceful, spring.lifecycle.timeout-per-shutdown-phase=30s를 적용했습니다.
  • application-prod.yml에 readiness/liveness probe를 활성화해 /actuator/health/readiness, /actuator/health/liveness를 운영 헬스체크 경로로 사용할 수 있게 했습니다.
  • Dockerfileexec java ... 형태로 수정해 SIGTERM이 JVM에 직접 전달되도록 변경했습니다.
  • docker-compose.ymlbackend 서비스에 stop_grace_period: 45s를 추가해 앱의 30초 종료 대기 시간을 컨테이너 레벨에서 보장하도록 했습니다.
  • 애플리케이션 시작/종료 및 readiness/liveness 상태 전환 로그를 추가해 배포 시 종료 흐름을 추적할 수 있도록 했습니다.
  • 로컬 검증은 임시 DB를 사용한 prod 프로필 기동으로 수행했습니다.
  • SIGTERM 전송 시 readiness가 REFUSING_TRAFFIC으로 전환되고, 진행 중이던 15초 요청이 200 OK로 완료된 뒤 프로세스가 종료되는 것을 확인했습니다.
  • 테스트를 위해 일시적으로 사용한 전용 엔드포인트/환경은 모두 제거했고, 현재 변경분에는 배포용 설정만 남아 있습니다.
  • Graceful Shutdown 효과를 실제 운영에서 온전히 얻으려면 LB/Ingress가 /actuator/health/readiness를 기준으로 트래픽 드레이닝을 수행해야 합니다.

Summary by CodeRabbit

개선 사항

  • 안정성 개선
    • 애플리케이션 종료 시 우아한 종료 프로세스 강화
    • 헬스 체크 및 상태 모니터링 기능 추가

@KoungQ KoungQ requested a review from ydking0911 April 20, 2026 17:02
@KoungQ KoungQ self-assigned this Apr 20, 2026
@KoungQ KoungQ added the feature label Apr 20, 2026
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 20, 2026

Walkthrough

Graceful shutdown을 구현하기 위해 Docker 컨테이너, Spring Boot 애플리케이션 설정, 그리고 생명주기 모니터링을 함께 개선했어요. 프로세스 교체, 종료 대기 시간 설정, health probe 활성화, 그리고 종료 이벤트 로깅이 추가되었습니다.

Changes

Cohort / File(s) Summary
Graceful Shutdown 설정
docker-compose.yml, src/main/resources/application-prod.yml
backend 서비스의 종료 유예 시간을 45초로 설정하고, Spring Boot에서 graceful shutdown 모드 활성화, 생명주기 타임아웃을 30초로 구성했습니다.
Docker 프로세스 관리
Dockerfile
Java 프로세스 호출 시 exec을 앞에 붙여서 PID 1 프로세스를 올바르게 교체하도록 변경했습니다.
애플리케이션 생명주기 모니터링
src/main/java/com/project/dorumdorum/global/alert/ApplicationLifecycleAlertListener.java
로깅 기능을 추가하고, 애플리케이션의 준비 상태(readiness)와 활성 상태(liveness)를 모니터링하는 이벤트 리스너를 구현했습니다.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20분

설정과 코드 변경이 섞여 있고 여러 파일에 걸쳐 있지만, 각 변경의 의도가 명확하고 graceful shutdown의 전체적인 맥락을 이해하면 검토할 수 있어요. Docker, Spring Boot 설정, 그리고 자바 코드의 조화를 함께 살펴봐야 한다는 점이 검토 복잡도를 약간 높이고 있습니다.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed PR 제목은 'Graceful Shutdown 적용'으로 변경사항의 핵심을 명확하게 요약하고 있으며, 모든 변경사항(Dockerfile, docker-compose.yml, application-prod.yml, 로깅 추가)이 이 목표에 부합합니다.
Linked Issues check ✅ Passed PR 변경사항들이 Issue #89의 Graceful Shutdown 적용 요구사항을 충족합니다: application-prod.yml에서 서버 및 lifecycle 설정, Dockerfile에서 exec 추가, docker-compose.yml에서 stop_grace_period 설정, readiness/liveness 상태 로깅 추가가 모두 구현되었습니다.
Out of Scope Changes check ✅ Passed 모든 변경사항이 Graceful Shutdown 적용이라는 범위 내에 있습니다. 테스트용 임시 엔드포인트나 환경은 제거되었으며, 배포용 설정만 포함되어 있어 범위를 벗어난 변경은 없습니다.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/#89/비동기-작업-유실-완화를-위한-graceful-shutdown-적용

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@KoungQ
Copy link
Copy Markdown
Member Author

KoungQ commented Apr 21, 2026

@coderabbitai full review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 21, 2026

✅ Actions performed

Full review triggered.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
Dockerfile (1)

12-20: 🧹 Nitpick | 🔵 Trivial

런타임 이미지를 non-root 사용자로 실행해 주세요.

현재 최종 stage에 USER 지시어가 없어 애플리케이션이 root로 실행되고 있어요. graceful shutdown 목적의 exec java 변경은 좋지만, 운영 이미지에서는 전용 사용자를 만들고 USER를 지정하는 편이 보안 관점에서 더 안전합니다. docker-compose.yml에서 마운트하는 firebase-service-account.json은 선택한 UID/GID로 읽을 수 있도록 소유권을 설정해야 합니다.

🔐 권장 변경 예시
 FROM eclipse-temurin:17-jre-alpine
 
 WORKDIR /app
 
+RUN addgroup -S app && adduser -S app -G app
 COPY --from=builder /app/build/libs/*.jar app.jar
+RUN chown app:app /app/app.jar
 
 EXPOSE 8080
 
+USER app
 ENTRYPOINT ["sh", "-c", "exec java ${JAVA_OPTS} -Dspring.profiles.active=${SPRING_PROFILES_ACTIVE:-prod} -jar /app/app.jar"]
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Dockerfile` around lines 12 - 20, The final image currently runs as root; add
a non-root runtime user and switch to it by creating a dedicated user (e.g., app
user) and adding a USER directive in the final stage so ENTRYPOINT ["sh", "-c",
"exec java ... -jar /app/app.jar"] runs unprivileged; ensure files under WORKDIR
(the copied app.jar) are owned by that user (adjust COPY/ownership in the final
stage or use chown during image build) and document that the docker-compose
mount for firebase-service-account.json must set the same UID/GID (or change
ownership in the container at startup) so the non-root user can read it.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@Dockerfile`:
- Around line 12-20: The final image currently runs as root; add a non-root
runtime user and switch to it by creating a dedicated user (e.g., app user) and
adding a USER directive in the final stage so ENTRYPOINT ["sh", "-c", "exec java
... -jar /app/app.jar"] runs unprivileged; ensure files under WORKDIR (the
copied app.jar) are owned by that user (adjust COPY/ownership in the final stage
or use chown during image build) and document that the docker-compose mount for
firebase-service-account.json must set the same UID/GID (or change ownership in
the container at startup) so the non-root user can read it.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yml

Review profile: ASSERTIVE

Plan: Pro

Run ID: a4dc560a-62d5-454b-af76-2aaa3045758c

📥 Commits

Reviewing files that changed from the base of the PR and between 9008ec6 and 0c020f7.

📒 Files selected for processing (4)
  • Dockerfile
  • docker-compose.yml
  • src/main/java/com/project/dorumdorum/global/alert/ApplicationLifecycleAlertListener.java
  • src/main/resources/application-prod.yml

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[FEAT] 비동기 작업 유실 완화를 위한 Graceful Shutdown 적용

1 participant