📚FOS Study
홈카테고리
홈카테고리

카테고리

  • AI 페이지로 이동
    • RAG 페이지로 이동
    • agents 페이지로 이동
    • langgraph 페이지로 이동
    • BMAD Method — AI 에이전트로 애자일 개발하는 방법론
    • Claude Code의 Skill 시스템 - 개발자를 위한 AI 자동화의 새로운 차원
    • Claude Code를 11일 동안 쓴 결과 — 데이터로 본 나의 사용 패턴
    • Claude Code 멀티 에이전트 — Teams
    • 하네스 엔지니어링 — 오래 실행되는 AI 에이전트를 위한 설계
    • 멀티모달 LLM (Multimodal Large Language Model)
    • AI 에이전트와 함께 MVP 만들기 — dooray-cli 사례
  • architecture 페이지로 이동
    • 캐시 설계 전략 총정리
    • 디자인 패턴
    • 분산 트랜잭션
  • css 페이지로 이동
    • FlexBox 페이지로 이동
  • database 페이지로 이동
    • mysql 페이지로 이동
    • opensearch 페이지로 이동
    • redis 페이지로 이동
    • 김영한의-실전-데이터베이스-설계 페이지로 이동
    • 커넥션 풀 크기는 얼마나 조정해야할까?
    • 인덱스 - DB 성능 최적화의 핵심
    • 역정규화 (Denormalization)
    • 데이터 베이스 정규화
  • devops 페이지로 이동
    • docker 페이지로 이동
    • k8s 페이지로 이동
    • k8s-in-action 페이지로 이동
    • monitoring 페이지로 이동
    • Envoy Proxy
    • Graceful Shutdown
  • go 페이지로 이동
    • Go 언어 기본 학습
  • http 페이지로 이동
    • HTTP Connection Pool
  • interview 페이지로 이동
    • 210812 페이지로 이동
    • 뱅크샐러드 AI Native Server Engineer
    • CJ 올리브영 커머스플랫폼유닛 Back-End 개발 지원 자료
    • 마이리얼트립 - Platform Solutions실 회원주문개발 Product Engineer
    • NHN 서비스개발센터 AI서비스개발팀
    • nhn gameenvil console backend 직무 인터뷰 준비
    • 면접을 대비해봅시다
    • 토스증권 Server Developer (Platform) 지원 자료
    • 토스증권 Server Developer (Product) 지원 자료
    • 토스뱅크 Server Developer (Product) 지원 자료
    • Tossplace Node.js Developer
    • 토스플레이스 Node.js 백엔드 컬처핏
  • java 페이지로 이동
    • jdbc 페이지로 이동
    • opentelemetry 페이지로 이동
    • spring 페이지로 이동
    • spring-batch 페이지로 이동
    • 더_자바_코드를_조작하는_다양한_방법 페이지로 이동
    • Java의 로깅 환경
    • MDC (Mapped Diagnostic Context)
    • OpenTelemetry 란 무엇인가?
    • Java StampedLock — 읽기 폭주에도 쓰기가 밀리지 않는 락
    • Virtual Thread와 Project Loom
  • javascript 페이지로 이동
    • Data_Structures_and_Algorithms 페이지로 이동
    • Heap 페이지로 이동
    • typescript 페이지로 이동
    • AbortController
    • Async Iterator와 제너레이터
    • CommonJS와 ECMAScript Modules
    • 제너레이터(Generator)
    • Http Client
    • Node.js
    • npm vs pnpm 선택기준은 무엇인가요?
    • `setImmediate()`
  • kafka 페이지로 이동
    • Kafka 기본
    • Kafka를 사용하여 **데이터 정합성**은 어떻게 유지해야 할까?
    • 메시지 전송 신뢰성
  • linux 페이지로 이동
    • fsync — 리눅스 파일 동기화 시스템 콜
    • tmux — Terminal Multiplexer
  • network 페이지로 이동
    • L2(스위치)와 L3(라우터)의 역할 차이
    • L4와 VIP(Virtual IP Address)
    • IP Subnet
  • react 페이지로 이동
    • JSX 페이지로 이동
    • VirtualDOM 페이지로 이동
    • v16 페이지로 이동
  • resume 페이지로 이동
    • CJ 올리브영 지원 문항
  • task 페이지로 이동
    • ai-service-team 페이지로 이동
    • nsc-slot 페이지로 이동
    • sb-dev-team 페이지로 이동
    • the-future-company 페이지로 이동
📚FOS Study

개발 학습 기록을 정리하는 블로그입니다.

바로가기

  • 홈
  • 카테고리

소셜

  • GitHub
  • Source Repository

© 2025 FOS Study. Built with Next.js & Tailwind CSS

목록으로 돌아가기
🚀devops

Graceful Shutdown

약 5분
2026년 4월 2일
GitHub에서 보기

Graceful Shutdown

서버를 그냥 끄면 안 되는 이유는 단순하다. 처리 중인 요청이 있다. DB 트랜잭션이 열려 있다. 커넥션 풀이 열려 있다. 이것들을 제대로 정리하지 않고 죽으면 클라이언트는 에러를 받고, 데이터는 일관성을 잃을 수 있다.

Graceful shutdown은 "받은 요청은 다 처리하고 나서 죽겠다"는 약속이다.


Linux Signal 기초

프로세스 종료는 OS가 시그널을 보내는 것으로 시작된다. 핵심은 SIGTERM과 SIGKILL의 차이다.

시그널번호의미핸들러 등록
SIGTERM15정상 종료 요청가능
SIGKILL9강제 종료불가 (커널이 직접 처리)
SIGINT2인터럽트 (Ctrl+C)가능

SIGTERM은 "이제 종료해도 된다"는 신호다. 프로세스가 핸들러를 등록해두면 받고 나서 정리 작업을 할 수 있다. SIGKILL은 다르다. 핸들러 자체가 불가능하고 커널이 즉시 프로세스를 죽인다. 그래서 graceful shutdown의 핵심은 SIGTERM을 받았을 때 무엇을 할지 정의하는 것이다.


일반 API 서버에서 신경 써야 할 것

1. 로드밸런서 / 서비스 디스커버리에서 먼저 빠지기

새 요청이 들어오지 않도록 먼저 제거되어야 한다. Kubernetes라면 Pod가 Terminating 상태가 되면 Endpoints에서 제거되기 시작하지만, 이 전파에 시간이 걸린다. preStop hook에서 sleep을 주는 이유가 이것이다.

2. in-flight 요청 처리 완료 대기

이미 들어온 요청은 끝까지 처리해야 한다. 타임아웃을 설정해서 무한정 기다리지는 않도록 한다.

3. 커넥션 드레인

HTTP Keep-Alive 커넥션, DB 커넥션 풀, gRPC 채널 등 열려 있는 커넥션을 닫아야 한다.

4. 리소스 정리

파일 핸들, 캐시 플러시, 메시지 큐 커밋 등 데이터 일관성에 영향을 주는 것들.


Spring Boot Graceful Shutdown

Spring Boot 2.3부터 내장 웹서버 수준의 graceful shutdown이 지원된다.

# application.yml
server:
  shutdown: graceful

spring:
  lifecycle:
    timeout-per-shutdown-phase: 30s  # 기본값 30s

server.shutdown=graceful을 설정하면 SIGTERM 수신 시 Tomcat(또는 Netty, Undertow)이 신규 요청 수락을 중단하고 처리 중인 요청이 완료될 때까지 대기한다. timeout-per-shutdown-phase는 최대 대기 시간이다.

/actuator/health가 OUT_OF_SERVICE로 바뀌는 걸 이용해 로드밸런서에서 먼저 제외되도록 하는 흐름도 중요하다. 헬스체크 간격이 있으니 preStop hook으로 sleep을 주는 것과 조합하면 더 안전하다.

Spring Boot + Kubernetes 조합 시 시간 예산

preStop sleep (10~15s)          → Endpoints 전파 완료 대기
+ timeout-per-shutdown-phase    → in-flight 요청 처리
= terminationGracePeriodSeconds 이내여야 함

Python 모델 서버 (gRPC)

일반 REST API 서버와 달리 모델 서버는 몇 가지 추가 고려사항이 있다.

  • 추론(inference) 시간이 길다 — 수백 ms ~ 수 초
  • GPU 메모리를 점유하고 있다 — 갑자기 죽으면 GPU 메모리 누수 가능
  • 프로세스 매니저(supervisord 등)가 중간에 있는 경우가 많다

Python signal 핸들러 등록

import signal

def serve():
    server = grpc.server(...)
    server.start()

    def handle_sigterm(signum, frame):
        print("SIGTERM received, graceful shutdown (grace=12s)...")
        server.stop(grace=12)  # 12초 내 in-flight RPC 완료 대기, 신규 요청 거부

    signal.signal(signal.SIGTERM, handle_sigterm)
    server.wait_for_termination()

server.stop(grace=N)은 두 가지를 한다:

  • 신규 RPC 요청 거부
  • 이미 처리 중인 RPC는 N초까지 완료 대기

핸들러는 server.start() 이후에 등록한다. 클로저로 server를 캡처하기 때문에 server가 이미 초기화된 이후여야 하고, Python 클로저는 호출 시점에 변수를 조회하므로 문제없다.

supervisord가 있는 경우

supervisord가 PID 1이면 SIGTERM이 supervisord에게 먼저 간다. supervisord는 stopwaitsecs 이내에 자식 프로세스가 종료되지 않으면 SIGKILL을 보낸다. 기본값이 10초라 grace period보다 짧으면 graceful stop이 완료되기 전에 강제 종료된다.

[program:grpc-server]
stopsignal=TERM
stopwaitsecs=17    # grace(12s) + 여유(5s)

Kubernetes + NCS 환경에서의 시간 예산

NHN Cloud Container Service(NCS)는 terminationGracePeriodSeconds를 30초로 고정한다. API 스펙에 해당 필드가 없어 변경할 방법이 없다.

[NCS 30초 고정 예산]
preStop sleep 15s
+ SIGTERM → server.stop(grace=12s)
= 27s  ← 30s 이내

정리

환경핵심 설정
Spring Bootserver.shutdown=graceful + timeout-per-shutdown-phase
Python gRPCsignal.signal(SIGTERM, handler) + server.stop(grace=N)
supervisordstopwaitsecs > grace 값
KubernetespreStop sleep + grace ≤ terminationGracePeriodSeconds

결국 같은 원칙이다. SIGTERM을 받으면 신규 요청을 막고, 하던 것은 끝내고, 그리고 죽는다. 어떤 스택이든 이 흐름을 명시적으로 구현해야 한다.

devops 카테고리의 다른 글 보기수정 제안하기

댓글

댓글을 불러오는 중...
  • Graceful Shutdown
  • Linux Signal 기초
  • 일반 API 서버에서 신경 써야 할 것
  • 1. 로드밸런서 / 서비스 디스커버리에서 먼저 빠지기
  • 2. in-flight 요청 처리 완료 대기
  • 3. 커넥션 드레인
  • 4. 리소스 정리
  • Spring Boot Graceful Shutdown
  • application.yml
  • Spring Boot + Kubernetes 조합 시 시간 예산
  • Python 모델 서버 (gRPC)
  • Python signal 핸들러 등록
  • supervisord가 있는 경우
  • Kubernetes + NCS 환경에서의 시간 예산
  • 정리