File System과 IO
3줄 요약
- File System과 I/O는 로그, 업로드, 임시 파일, classpath resource, heap dump 같은 백엔드 작업의 실제 비용을 만든다.
- File descriptor는 파일뿐 아니라 socket, pipe, eventfd 같은 OS 자원을 가리키므로 고갈되면 네트워크 장애처럼 보일 수 있다.
- Disk I/O 병목은 CPU 사용률보다 iowait, latency, queue, file descriptor, 로그 쓰기 패턴으로 확인해야 한다.
핵심 정리
- File descriptor는 process가 열린 파일과 socket을 참조하는 번호이며 OS limit의 영향을 받는다.
- Buffered I/O는 system call 횟수를 줄이지만 flush와 sync 시점이 지연과 데이터 유실 가능성을 만든다.
- 로그는 단순 문자열 출력이 아니라 파일 쓰기, rotation, 압축, disk quota, stdout 수집 비용을 가진다.
- Java File I/O는 try-with-resources, stream close, temp file 정리, multipart 설정과 연결된다.
- I/O 병목은 애플리케이션 thread가 WAITING이 아니라 RUNNABLE 또는 native 상태로 보일 수 있어 OS 지표가 필요하다.
헷갈리는 지점
- 파일을 닫지 않아도 GC가 처리한다고 생각하기 쉽다.
- File descriptor는 OS 자원이므로 close를 명시적으로 보장해야 한다.
- GC timing에 의존하면 고부하에서 descriptor가 먼저 고갈될 수 있다.
- 로그는 성능에 영향이 작다고 보기 쉽다.
- 동기 로그, 큰 stack trace, 과도한 debug 로그는 disk와 CPU를 동시에 압박한다.
- 로그 수집기가 느리면 stdout 쓰기도 지연될 수 있다.
- Disk가 느리면 CPU만 낮게 나온다고 단정하기 쉽다.
- I/O wait와 thread 상태, load average가 함께 변할 수 있다.
- Application에서는 전체 응답 지연으로만 보일 수 있다.
확인 질문
- File descriptor 고갈이 API 장애로 보이는 이유는 무엇인가?
- Socket도 file descriptor를 사용하므로 새 연결이나 파일 열기가 실패할 수 있기 때문이다.
- Buffered I/O를 쓰는 이유는 무엇인가?
- 작은 write마다 system call과 disk 반영을 하지 않고 묶어서 처리해 비용을 줄이기 위해서다.
- 로그 파일 쓰기를 운영에서 조심해야 하는 이유는 무엇인가?
- 로그량, rotation, 압축, 수집 지연이 disk I/O와 애플리케이션 지연을 만들 수 있기 때문이다.