| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | |||
| 5 | 6 | 7 | 8 | 9 | 10 | 11 |
| 12 | 13 | 14 | 15 | 16 | 17 | 18 |
| 19 | 20 | 21 | 22 | 23 | 24 | 25 |
| 26 | 27 | 28 | 29 | 30 | 31 |
- 해결
- 자료구조
- 스타트업
- HashMap
- 배열
- 프로그래머스
- HTTP
- 구름LEVEL
- 이직
- spring
- 개발자
- dfs
- 백엔드
- 도커
- 주니어
- Linux
- 인텔리제이
- IntelliJ
- 스프링 부트
- docker
- 스프링부트
- 구현
- 인프라
- bfs
- Java
- spring boot
- 스프링
- 해시맵
- 코딩테스트
- 문자열
- Today
- Total
마이의 개발 블로그
[OS] 서브프로세스와 Graceful 종료: AI 영상 분석 시스템 개발 중 겪은 시행착오와 해결 방안 본문
배경: 서브프로세스의 도입
AI 기반 영상 분석 시스템을 개발하며, 1) CCTV RTSP 스트리밍 영상의 분할 저장, 2) 저장된 파일들에 대한 후처리 스케줄러 구동을 안정적으로 처리해야 할 필요가 있었습니다. 초기에는 모든 작업을 단일 프로세스 내에서 처리하여 개발을 진행했습니다. 그러나 단일 프로세스 내에서 Gstream을 통한 스트리밍 행위와 파일 분할 저장 행위를 동시에 수행하는 경우, 주기적으로 프로세스에 부하가 많이 걸리게 되어 그 시점마다 아래와 같은 현상들이 발생했습니다.
- 영상 저장 타이밍이 어긋남
- 저장 중인 파일이 일부 유실됨
- 타임스탬프 기반 파일 관리의 불안정성 증가
이러한 문제들을 해결하기 위해 저는 1), 2)의 기능들을 각각 별도의 서브프로세스로 분리하여 운영하는 구조를 도입하였습니다.
Graceful 종료의 도입 이유
서브프로세스를 사용하면서 또 다른 과제가 등장했습니다. 바로 프로그램 종료 시점에 남아있는 영상 녹화 파일의 후처리 미완료 문제였습니다. 녹화가 진행 중인 상태에서 프로그램이 종료 시그널을 받아 즉시 종료되면, 메인 프로세스가 서브프로세스를 바로 종료시키는 바람에 일부 .ts 파일이 완전히 쓰이지 않은 상태로 남아 데이터 손실 위험이 생깁니다. 이를 방지하기 위해, 프로그램이 종료 요청을 수신한 뒤, 안전하게 모든 후처리를 마치고 종료되도록 하는 Graceful 종료 방식을 도입하였습니다.
*Graceful 종료: 시스템이 종료 명령을 받았을 때, 즉시 종료하는 대신 현재 수행 중인 작업을 마무리한 뒤 안전하게 종료하는 방식(예: 파일 저장, 자원 정리, 연결 종료 등을 모두 수행 후 프로그램 종료)
문제 상황: 쉘 스크립트 도입 후 나타난 예기치 못한 이슈
개발 초기에는 단독 실행 및 디버깅을 하며 KeyboardInterrupt 신호만을 고려해 종료 처리를 구현했습니다. 하지만 파일 서빙을 위한 FastAPI 서버를 도입하고, 이를 백그라운드에서 띄우기 위한 쉘 스크립트 기반 구동 방식을 채택하면서 생각하지 못한 문제가 발생했습니다.
쉘 스크립트에서는 PID 파일을 기준으로 프로그램 A(영상파일 분할저장 + 저장파일 후처리)와 프로그램 B(파일서빙용 FastAPI) 시작, 종료하고 있었는데, 어느 순간부터 정상 종료 메시지가 출력되었음에도 서브프로세스가 계속 남아 CPU 자원을 점유하고 있는 현상이 발생했습니다. 더 큰 문제는, PID 파일에 저장된 프로세스가 이미 종료된 것으로 인식되어 더 이상 kill 명령어로 제어할 수 없었다는 점입니다. 간신히 기존 프로세스들을 모두 종료한 후 문제를 분석한 결과, 원인은 크게 두 가지였습니다.
1. PID 파일에는 메인 프로세스의 PID만 저장되었고, 서브프로세스들의 PID는 별도로 추적하거나 관리하고 있지 않았습니다. 서브프로세스 중 일부는 batch를 사용하여 몇 초 단위로 서브프로세스가 랜덤 PID로 발생하는 구조여서 일일이 종료할 수도 없었습니다.
2. 종료 처리 로직이 KeyboardInterrupt에만 반응하도록 구성되어 있었기에, kill 등 다른 종료 시그널에는 전혀 대응하지 못했습니다.
해결 방법
이 문제를 해결하기 위해 다음과 같은 조치를 취했습니다:
1. 서브프로세스도 종료 시그널을 감지하여 안전하게 종료할 수 있도록 코드 수정
- Python 코드 내에서 signal.SIGTERM, signal.SIGINT를 함께 처리하도록 핸들러를 추가하여, 백그라운드 실행에 대한 종료 메시지를 시스템(쉘스크립트)에서 보내더라도 정상 종료가 가능하도록 개선했습니다.
2. 종료되지 않은 서브프로세스를 처리하기 위해, 메인 프로세스 PID를 기준으로 전체 프로세스 그룹을 종료하는 방식 도입
- 쉘 스크립트에서는 kill -TERM -<PGID> 방식으로 프로세스 그룹 단위 종료를 시도하여, 서브프로세스가 남아있지 않도록 처리했습니다.
마치며
멀티프로세스를 활용한 시스템에서는 단순한 kill 혹은 CTRL+C 수준의 종료 처리로는 충분하지 않은 경우가 많습니다. 특히 영상 저장과 같이 실시간성을 요하고, 자원 정리가 중요한 시스템에서는 서브프로세스 관리 및 graceful 종료 설계가 필수적입니다. 이번 경험을 통해, 프로세스 구조 설계 시 종료 시나리오를 충분히 고려한 아키텍처의 중요성을 다시금 느낄 수 있었습니다. 이 글이 유사한 문제를 겪고 계신 분들께 도움이 되기를 바랍니다.
'개발지식 > OS' 카테고리의 다른 글
| [OS] 네이티브 라이브러리를 쓸 때 alpine 이미지를 조심하세요: 멀티코어 성능 문제 해결 과정 (0) | 2025.09.03 |
|---|---|
| [OS] 프로세스(process)와 쓰레드(thread) (0) | 2022.02.13 |