Computer System 실전 가이드북
이 문서를 쓰는 이유
Computer System 문서는 CPU, thread, memory, file, socket, Linux, JVM, lock, OS 병목을 각각 설명한다. 하지만 실제 장애나 디버깅 상황에서는 한 가지 문서만 열어서는 부족하다. 느린 API 하나에도 Tomcat thread, DB connection pool, GC pause, disk I/O, socket backlog, scheduler 대기가 함께 섞일 수 있다.
이 문서는 기존 문서의 요약본이 아니다. 상황을 먼저 보고, 그 상황에서 어떤 문서를 어떤 순서로 다시 열어야 하는지 안내하는 실전 가이드북이다.
사용 방법은 단순하다.
- 지금 겪는 증상과 가장 가까운 Case를 고른다.
- 증거를 보고 CPU, memory, I/O, network, JVM, thread, lock 중 어느 축부터 볼지 가설을 세운다.
찾아볼 문서를 열어 관찰 명령, 지표 해석, 위험 신호를 확인한다.- 마지막에
가져갈 한 문장을 장애 대응 메모나 학습 노트의 기준으로 남긴다.
Computer System 학습의 목표는 명령어를 많이 외우는 것이 아니다. 애플리케이션 증상을 OS와 JVM의 실행 신호로 번역하고, 그 신호를 다시 안전한 조치로 연결하는 것이다.
커버리지 지도
| 기존 문서 | 연결 Case | 다시 봐야 하는 이유 |
|---|---|---|
| 00. Computer System 전체 지도 압축 정리 | Case 1, 15 | 요청 흐름과 병목 분류의 전체 지도를 잡기 위해 |
| 01. 백엔드 요청과 실행 환경 전체 흐름 상세 | Case 1 | HTTP 요청이 kernel, Tomcat, Spring, DB client를 거치는 흐름을 보기 위해 |
| 02. OS JVM Application 경계 상세 | Case 1, 5, 15 | 장애 증거를 OS, JVM, Application 계층으로 나누기 위해 |
| 03. CPU Memory IO Network 관찰 지점 상세 | Case 1, 10, 15 | CPU, memory, I/O, network 지표를 한 화면에서 분류하기 위해 |
| 04. 개인 프로젝트와 기업 운영 수준 비교 상세 | Case 16 | 학습 기록과 운영 문서의 수준 차이를 이해하기 위해 |
| 05. Computer System 학습 로드맵 상세 | Case 16 | 반복 학습 순서를 잡고 약한 축을 보강하기 위해 |
| 00. CPU Process Thread Scheduling 압축 정리 | Case 2 | CPU, process, thread, scheduling의 큰 구분을 잡기 위해 |
| 01. CPU Core와 실행 단위 상세 | Case 2, 11 | CPU 사용률과 core 단위 실행을 연결하기 위해 |
| 02. Process와 Thread 차이 상세 | Case 2, 13 | process와 thread의 장애 영향 범위를 구분하기 위해 |
| 03. Scheduling과 Context Switch 비용 상세 | Case 2, 3, 11 | CPU가 바쁜지 switching 비용이 큰지 판단하기 위해 |
| 04. Java Thread와 ExecutorService 상세 | Case 3, 13 | ExecutorService와 thread pool 병목을 읽기 위해 |
| 05. Thread Dump로 병목 읽기 상세 | Case 3, 13 | thread dump에서 WAITING, BLOCKED, RUNNABLE 상태를 해석하기 위해 |
| 00. Context Switching Blocking Non-blocking 압축 정리 | Case 3 | blocking과 non-blocking의 전체 판단 기준을 잡기 위해 |
| 01. Blocking이 Thread를 점유하는 방식 상세 | Case 3, 13 | 느린 I/O가 thread를 오래 붙잡는 상황을 이해하기 위해 |
| 02. Non-blocking Async Event Loop 기본 상세 | Case 3 | event loop를 막으면 어떤 증상이 생기는지 확인하기 위해 |
| 03. Java IO NIO와 서버 처리 모델 상세 | Case 3 | Java 서버 처리 모델과 I/O 방식을 연결하기 위해 |
| 04. Thread Pool Starvation 상세 | Case 3, 13 | 요청 지연과 thread pool 고갈을 직접 연결하기 위해 |
| 05. Blocking Non-blocking 실전 판단 상세 | Case 3, 13 | blocking을 줄일지 pool을 분리할지 판단하기 위해 |
| 00. Memory Stack Heap Virtual Memory 압축 정리 | Case 4 | stack, heap, native memory, virtual memory의 큰 구분을 잡기 위해 |
| 01. Stack Heap Native Memory 상세 | Case 4, 12 | heap OOM과 native memory 문제를 구분하기 위해 |
| 02. Virtual Memory Paging Swap 상세 | Case 4, 12 | swap과 memory pressure가 응답 지연을 만드는 흐름을 보기 위해 |
| 03. Memory Leak과 OOM 원인 상세 | Case 4, 12 | memory leak과 OOM의 원인 후보를 분류하기 위해 |
| 04. JVM Heap GC와 메모리 관찰 상세 | Case 4, 9, 12 | heap 사용량, GC pause, allocation rate를 해석하기 위해 |
| 05. Heap Dump와 메모리 장애 대응 상세 | Case 4, 12 | heap dump 확보와 분석 시점을 판단하기 위해 |
| 00. File System과 IO 압축 정리 | Case 5 | file descriptor, disk I/O, buffered I/O의 전체 지도를 잡기 위해 |
| 01. File Descriptor와 열린 파일 상세 | Case 5, 14 | 열린 파일과 socket이 FD limit을 공유한다는 점을 보기 위해 |
| 02. Disk IO와 Buffered IO 상세 | Case 5, 10 | disk I/O와 buffer가 API latency에 주는 영향을 확인하기 위해 |
| 03. 로그 파일 쓰기와 성능 상세 | Case 5 | 과도한 로그 쓰기가 I/O 병목을 만들 때 다시 보기 위해 |
| 04. Java File IO와 Resource 관리 상세 | Case 5, 14 | stream, file, socket resource close 누락을 점검하기 위해 |
| 05. IO 병목 관찰과 대응 상세 | Case 5, 10 | iowait, disk busy, queue length를 해석하기 위해 |
| 00. Socket과 OS Network 압축 정리 | Case 6 | socket, port, accept queue, connection state의 큰 지도를 잡기 위해 |
| 01. Socket Port File Descriptor 상세 | Case 6, 14 | socket도 file descriptor라는 점을 장애 분석에 반영하기 위해 |
| 02. Listen Backlog와 Accept Queue 상세 | Case 6, 14 | accept queue가 밀릴 때 연결 실패가 생기는 흐름을 보기 위해 |
| 03. Connection State와 TIME_WAIT 상세 | Case 6, 14 | TIME_WAIT, CLOSE_WAIT, ESTABLISHED 상태를 해석하기 위해 |
| 04. Tomcat Connector와 OS Socket 연결 상세 | Case 6, 13, 14 | Tomcat thread와 OS socket queue를 함께 보기 위해 |
| 05. ss lsof netstat로 네트워크 관찰 상세 | Case 6, 14 | ss, lsof, netstat 출력으로 network 병목을 확인하기 위해 |
| 00. Linux Process Service Log 관찰 압축 정리 | Case 7 | Linux process, service, log 관찰의 전체 기준을 잡기 위해 |
| 01. ps top htop으로 Process 보기 상세 | Case 7, 11 | process별 CPU, memory, thread 상태를 확인하기 위해 |
| 02. systemd와 Service 관리 상세 | Case 7 | service restart, status, limit, unit 설정을 확인하기 위해 |
| 03. journalctl log 파일 관찰 상세 | Case 7, 15 | 장애 타임라인과 application log를 연결하기 위해 |
| 04. Permission User Group 기본 상세 | Case 7 | 권한 문제와 실행 사용자 차이를 확인하기 위해 |
| 05. 운영 중 안전한 명령 사용 상세 | Case 7, 15 | 운영 중 명령 실행의 위험과 안전한 관찰 방식을 확인하기 위해 |
| 00. Concurrency Lock Race Condition 압축 정리 | Case 8 | race condition, lock, deadlock의 전체 구분을 잡기 위해 |
| 01. Race Condition과 Critical Section 상세 | Case 8 | 공유 상태 변경의 위험 구간을 찾기 위해 |
| 02. synchronized Lock ReentrantLock 상세 | Case 8 | lock 선택과 lock 대기 비용을 해석하기 위해 |
| 03. Deadlock Starvation Livelock 상세 | Case 8, 13 | deadlock과 starvation을 thread dump로 구분하기 위해 |
| 04. volatile Atomic Concurrent Collection 상세 | Case 8 | visibility와 atomicity를 구분하기 위해 |
| 05. Thread-safe 설계 판단 상세 | Case 8, 13 | thread-safe 설계를 코드 리뷰와 장애 분석에 연결하기 위해 |
| 00. JVM Runtime GC Thread 압축 정리 | Case 9 | JVM runtime, GC, thread의 큰 흐름을 잡기 위해 |
| 01. Class Loading JIT Runtime 상세 | Case 9 | warm-up, JIT, class loading이 지표에 미치는 영향을 보기 위해 |
| 02. JVM Memory 영역과 Metaspace 상세 | Case 9, 12 | heap, non-heap, metaspace를 구분하기 위해 |
| 03. GC 기본과 Pause 이해 상세 | Case 9, 12 | GC pause가 응답 지연으로 보이는 상황을 해석하기 위해 |
| 04. JFR JMX Actuator 관찰 상세 | Case 9, 12, 15 | JVM 내부 지표와 운영 관찰 도구를 연결하기 위해 |
| 05. JVM Option과 운영 판단 상세 | Case 9, 15 | JVM option 변경을 증거 기반으로 판단하기 위해 |
| 00. OS 관점의 성능 병목 분석 압축 정리 | Case 10 | CPU, memory, I/O, pool 병목을 OS 관점에서 묶기 위해 |
| 01. CPU Bound와 Load Average 상세 | Case 2, 10, 11 | CPU bound와 load average를 구분하기 위해 |
| 02. Memory Pressure와 Swap 상세 | Case 4, 10, 12 | memory pressure와 swap이 전체 latency를 올릴 때 확인하기 위해 |
| 03. IO Wait와 Disk 병목 상세 | Case 5, 10 | CPU가 낮아도 iowait 때문에 느린 상황을 해석하기 위해 |
| 04. Thread Pool Connection Pool 병목 연결 상세 | Case 3, 10, 13 | thread pool과 connection pool 대기를 함께 보기 위해 |
| 05. 성능 장애 원인 격리 루틴 상세 | Case 10, 15 | 증상에서 원인 후보를 좁히는 순서를 훈련하기 위해 |
| 00. Computer System 실전 플레이북 압축 정리 | Case 15 | 장애 대응형 문서의 전체 흐름을 잡기 위해 |
| 01. CPU 사용률 급등 대응 상세 | Case 11 | CPU 급등 대응의 관찰 순서와 금지 조치를 확인하기 위해 |
| 02. Memory Leak OOM 대응 상세 | Case 12 | OOM 대응에서 dump, 재시작, 원인 보존 순서를 확인하기 위해 |
| 03. Thread 고갈과 응답 지연 대응 상세 | Case 13 | thread 고갈과 응답 지연 대응 순서를 확인하기 위해 |
| 04. File Descriptor Socket 고갈 대응 상세 | Case 14 | FD와 socket 고갈 대응 절차를 확인하기 위해 |
| 05. JVM OS 장애 보고서 작성 상세 | Case 15, 16 | 장애 증거를 보고서와 학습 기록으로 남기기 위해 |
실전 Case
Case 1. 느린 API 하나를 OS, JVM, Application 경계로 나눠야 할 때
상황
GET /orders/{id} p95가 갑자기 300ms에서 2.5초로 올랐다. 코드 배포는 없었고, DB도 큰 장애 알림은 없다. 팀은 “Spring controller가 느린 것 같다”고 말하지만 아직 thread, socket, GC, DB client 대기 중 어디가 문제인지 모른다.
관찰할 증거 또는 확인할 단서
- access log latency는 높지만 application log에는 에러가 거의 없다.
- CPU 사용률은 중간인데 thread pool active가 높다.
- DB latency, GC pause, socket 상태, connection pool 대기 중 어떤 지표를 먼저 볼지 정해져 있지 않다.
- 같은 API라도 일부 instance에서만 느리다.
먼저 생각해보기
- 이 요청은 OS socket부터 Spring handler까지 어떤 계층을 지나가는가?
- 느린 증거는 Application, JVM, OS, 외부 dependency 중 어디에서 먼저 드러나는가?
- 지금 수집해야 할 최소 증거는 무엇인가?
찾아볼 문서
- 00. Computer System 전체 지도 압축 정리
- 01. 백엔드 요청과 실행 환경 전체 흐름 상세
- 02. OS JVM Application 경계 상세
- 03. CPU Memory IO Network 관찰 지점 상세
정리
느린 API는 controller 코드부터 보지 않는다. 먼저 요청 경로를 kernel socket, Tomcat thread, Spring handler, DB client, JVM heap, OS scheduler로 나누고 각 계층의 대기 신호를 확인한다. 한 계층의 지표만 보면 원인과 결과를 뒤집기 쉽다.
가져갈 한 문장
느린 API는 코드 위치가 아니라 실행 경로의 어느 계층에서 시간이 사라지는지부터 나눠야 한다.
Case 2. CPU 사용률은 높은데 원인이 계산인지 대기인지 헷갈릴 때
상황
CPU 사용률이 95%까지 올라갔고 load average도 높다. 운영자는 instance를 늘리자고 하지만, 실제로는 계산량 증가인지 context switching인지, GC나 runnable thread 폭증인지 확인되지 않았다.
관찰할 증거 또는 확인할 단서
- CPU user와 system 비율이 분리되어 있지 않다.
- runnable thread 수가 core 수보다 훨씬 많다.
- context switch 수가 평소보다 높다.
- top에서는 Java process가 높지만 어느 thread가 바쁜지 모른다.
먼저 생각해보기
- CPU core 수 대비 runnable 작업은 얼마나 많은가?
- process 하나가 CPU를 쓰는가, 여러 thread가 번갈아 쓰는가?
- CPU bound인지 scheduling 비용인지 구분할 증거는 무엇인가?
찾아볼 문서
- 00. CPU Process Thread Scheduling 압축 정리
- 01. CPU Core와 실행 단위 상세
- 02. Process와 Thread 차이 상세
- 03. Scheduling과 Context Switch 비용 상세
- 01. CPU Bound와 Load Average 상세
정리
CPU 사용률이 높다고 항상 계산이 많은 것은 아니다. core 수, runnable thread, context switch, user/system CPU, 특정 Java thread의 stack을 함께 봐야 한다. instance 증설은 완화책이 될 수 있지만, 먼저 CPU가 실제 계산에 쓰이는지 switching 비용에 쓰이는지 구분한다.
가져갈 한 문장
CPU 95%라는 숫자는 결론이 아니라 runnable 작업과 scheduling 비용을 확인하라는 신호다.
Case 3. Thread pool은 꽉 찼는데 CPU는 낮을 때
상황
API 응답이 느리고 Tomcat thread가 거의 다 active인데 CPU 사용률은 35% 수준이다. thread dump에는 WAITING과 TIMED_WAITING이 많고, 외부 API나 DB 호출이 느린 시점과 겹친다.
관찰할 증거 또는 확인할 단서
- CPU는 낮지만 active thread와 queue가 높다.
- thread dump에서 socket read, DB driver, HTTP client 대기가 반복된다.
- connection pool pending이 늘어난다.
- non-blocking으로 바꾸면 해결된다는 말만 있고 event loop blocking 위험은 검토되지 않았다.
먼저 생각해보기
- 지금 병목은 CPU가 아니라 thread 점유인가?
- blocking 작업을 별도 pool로 분리해야 하는가, timeout을 줄여야 하는가?
- non-blocking 구조를 쓰더라도 어떤 blocking 호출을 제거해야 하는가?
찾아볼 문서
- 00. Context Switching Blocking Non-blocking 압축 정리
- 01. Blocking이 Thread를 점유하는 방식 상세
- 02. Non-blocking Async Event Loop 기본 상세
- 03. Java IO NIO와 서버 처리 모델 상세
- 04. Thread Pool Starvation 상세
- 05. Blocking Non-blocking 실전 판단 상세
- 04. Java Thread와 ExecutorService 상세
- 05. Thread Dump로 병목 읽기 상세
- 04. Thread Pool Connection Pool 병목 연결 상세
정리
CPU가 낮은데 응답이 느리면 thread가 일을 못 하는 것이 아니라 기다리고 있을 수 있다. thread dump와 pool 지표를 보고 blocking 위치를 찾는다. 단순히 thread 수를 늘리면 잠시 숨통은 트일 수 있지만 downstream 대기를 더 키울 수 있다.
가져갈 한 문장
Thread 고갈은 CPU 부족보다 기다림의 위치를 찾는 문제가 더 자주 핵심이다.
Case 4. 메모리가 계속 늘고 GC가 잦아질 때
상황
배포 후 몇 시간 동안 RSS와 heap 사용량이 계속 증가한다. GC pause도 늘고, 결국 일부 instance가 OOM으로 죽었다. 재시작하면 잠시 정상화되지만 같은 패턴이 반복된다.
관찰할 증거 또는 확인할 단서
- heap used가 GC 후에도 내려가지 않는다.
- RSS가 heap보다 훨씬 크거나 native memory가 의심된다.
- swap 사용량이 늘고 전체 latency가 흔들린다.
- OOM 직전 heap dump 확보 여부가 정해져 있지 않다.
먼저 생각해보기
- heap leak인가, native memory인가, metaspace인가, OS memory pressure인가?
- 재시작 전에 어떤 dump와 지표를 남겨야 하는가?
- GC pause가 원인인지 결과인지 어떻게 확인할 것인가?
찾아볼 문서
- 00. Memory Stack Heap Virtual Memory 압축 정리
- 01. Stack Heap Native Memory 상세
- 02. Virtual Memory Paging Swap 상세
- 03. Memory Leak과 OOM 원인 상세
- 04. JVM Heap GC와 메모리 관찰 상세
- 05. Heap Dump와 메모리 장애 대응 상세
- 02. Memory Pressure와 Swap 상세
정리
OOM 대응에서 가장 위험한 것은 증거 없이 재시작만 반복하는 것이다. heap, native, metaspace, RSS, swap을 분리하고 OOM 직전의 dump와 GC log를 보존한다. GC pause는 memory pressure의 증상일 수도 있고, 과도한 allocation의 결과일 수도 있다.
가져갈 한 문장
메모리 장애는 재시작으로 숨길 수 있지만 dump와 시계열 증거 없이는 원인을 배울 수 없다.
Case 5. 로그가 많아진 뒤 API가 느려질 때
상황
디버깅을 위해 상세 로그를 켠 뒤 API p95가 상승했다. CPU는 높지 않은데 iowait가 늘고, log file size와 disk write가 급증했다. 운영자는 로그를 더 남기자고 하지만 디스크 병목 가능성이 있다.
관찰할 증거 또는 확인할 단서
- iowait와 disk busy가 평소보다 높다.
- 특정 로그 파일 write가 초당 급증한다.
- file descriptor 수가 계속 늘어난다.
- Java File I/O resource close 누락 가능성이 있다.
먼저 생각해보기
- 로그 write가 요청 thread를 얼마나 붙잡는가?
- buffered I/O와 disk flush가 latency에 어떤 영향을 주는가?
- 로그를 줄일지, 비동기화할지, sampling할지 어떤 기준으로 판단할 것인가?
찾아볼 문서
- 00. File System과 IO 압축 정리
- 01. File Descriptor와 열린 파일 상세
- 02. Disk IO와 Buffered IO 상세
- 03. 로그 파일 쓰기와 성능 상세
- 04. Java File IO와 Resource 관리 상세
- 05. IO 병목 관찰과 대응 상세
- 03. IO Wait와 Disk 병목 상세
- 02. OS JVM Application 경계 상세
정리
로그는 관찰 수단이지만 과하면 병목이 된다. 요청 thread에서 직접 쓰는 로그, flush 빈도, disk busy, iowait, file descriptor 증가를 함께 확인한다. 장애 중에는 필요한 증거와 시스템 부담 사이의 균형을 잡아야 한다.
가져갈 한 문장
관찰을 위해 켠 로그도 결국 file I/O이며, 운영 중에는 증거와 부하를 함께 계산해야 한다.
Case 6. 연결은 들어오는데 요청 처리가 시작되지 않을 때
상황
클라이언트는 connection timeout이나 간헐적인 reset을 본다. 애플리케이션 로그에는 요청이 남지 않는다. 서버 CPU와 JVM 지표만 보면 이상이 크지 않지만 accept queue나 socket 상태가 의심된다.
관찰할 증거 또는 확인할 단서
- access log에 도달하지 않은 요청이 있다.
- SYN backlog 또는 accept queue overflow가 의심된다.
- TIME_WAIT, CLOSE_WAIT, ESTABLISHED 수가 비정상적으로 많다.
- Tomcat max connections, accept count, OS backlog 설정이 맞물려 있다.
먼저 생각해보기
- 요청이 application에 들어오기 전 OS socket 단계에서 막히는가?
- socket state는 어떤 방향의 문제를 가리키는가?
- Tomcat connector 설정과 OS limit은 서로 맞는가?
찾아볼 문서
- 00. Socket과 OS Network 압축 정리
- 01. Socket Port File Descriptor 상세
- 02. Listen Backlog와 Accept Queue 상세
- 03. Connection State와 TIME_WAIT 상세
- 04. Tomcat Connector와 OS Socket 연결 상세
- 05. ss lsof netstat로 네트워크 관찰 상세
정리
애플리케이션 로그에 없다는 것은 문제가 없다는 뜻이 아니다. 요청이 OS socket queue에서 밀리면 Spring까지 도달하지 못한다. ss, lsof, socket summary, Tomcat connector 지표를 같이 보면서 application 앞단의 병목을 확인한다.
가져갈 한 문장
로그에 없는 요청은 사라진 요청이 아니라 애플리케이션 앞의 socket 경계에서 멈춘 요청일 수 있다.
Case 7. 서비스가 죽었는데 로그와 권한부터 안전하게 봐야 할 때
상황
Linux 서버에서 Spring Boot service가 갑자기 내려갔다. 급한 마음에 바로 재시작하고 싶지만, 종료 이유와 직전 로그, 실행 사용자, systemd limit을 확인하지 않으면 같은 문제가 반복될 수 있다.
관찰할 증거 또는 확인할 단서
- systemd status에는 exit code만 보인다.
- journalctl에는 OOM, permission denied, bind failed 같은 흔적이 있을 수 있다.
- 실행 사용자가 바뀌거나 파일 권한이 달라진 정황이 있다.
- 운영 중 무거운 명령을 무심코 실행할 위험이 있다.
먼저 생각해보기
- 재시작 전에 어떤 로그와 상태를 남겨야 하는가?
- service unit의 limit, user, working directory는 기대와 같은가?
- 운영 중 안전하게 실행할 수 있는 명령과 피해야 할 명령은 무엇인가?
찾아볼 문서
- 00. Linux Process Service Log 관찰 압축 정리
- 01. ps top htop으로 Process 보기 상세
- 02. systemd와 Service 관리 상세
- 03. journalctl log 파일 관찰 상세
- 04. Permission User Group 기본 상세
- 05. 운영 중 안전한 명령 사용 상세
정리
서비스 장애에서는 재시작이 완화일 수 있지만 증거를 지우는 행동이 될 수도 있다. status, journal, process, user, limit, 최근 배포와 설정 변경을 먼저 확인한다. 운영 명령은 출력 크기와 시스템 부하를 고려해 범위를 제한한다.
가져갈 한 문장
운영 중 첫 명령은 문제를 고치는 명령이 아니라 증거를 보존하면서 범위를 좁히는 명령이어야 한다.
Case 8. 간헐적으로 값이 틀리고 deadlock이 의심될 때
상황
동시에 여러 요청이 같은 재고 값을 수정할 때 간헐적으로 값이 틀어진다. 다른 시간대에는 thread가 BLOCKED 상태로 오래 멈추고, lock 순서가 꼬인 deadlock도 의심된다.
관찰할 증거 또는 확인할 단서
- 같은 요청을 순차 실행하면 문제가 재현되지 않는다.
- thread dump에 BLOCKED thread가 반복된다.
- synchronized와 ReentrantLock이 섞여 있고 lock 순서가 문서화되어 있지 않다.
- volatile을 사용했지만 복합 연산의 atomicity는 보장되지 않는다.
먼저 생각해보기
- 공유 상태의 critical section은 어디인가?
- visibility 문제인가, atomicity 문제인가, lock 순서 문제인가?
- thread-safe하게 만들 책임은 자료구조, lock, transaction 중 어디에 있는가?
찾아볼 문서
- 00. Concurrency Lock Race Condition 압축 정리
- 01. Race Condition과 Critical Section 상세
- 02. synchronized Lock ReentrantLock 상세
- 03. Deadlock Starvation Livelock 상세
- 04. volatile Atomic Concurrent Collection 상세
- 05. Thread-safe 설계 판단 상세
정리
동시성 문제는 재현이 어렵기 때문에 증거가 중요하다. 값이 틀리는 문제는 race condition과 atomicity를 보고, 멈추는 문제는 lock 대기와 deadlock을 본다. volatile은 보이는 문제를 해결할 수 있지만 복합 변경의 원자성을 보장하지 않는다.
가져갈 한 문장
동시성 장애는 빠른 수정 전에 공유 상태, 보장해야 할 불변식, lock 범위를 먼저 적어야 한다.
Case 9. JVM 지표는 많은데 무엇을 봐야 할지 모를 때
상황
Actuator, JMX, JFR, GC log를 모두 켤 수 있지만 어떤 지표가 현재 문제에 필요한지 모른다. latency가 흔들리고 heap도 움직이지만 JVM option을 바로 바꾸기에는 근거가 부족하다.
관찰할 증거 또는 확인할 단서
- GC pause와 API latency가 같은 시간대에 움직인다.
- heap, non-heap, metaspace 중 어느 영역이 증가하는지 분리되지 않았다.
- JIT warm-up이나 class loading 영향이 배포 직후에 나타날 수 있다.
- JVM option 변경 요청이 있지만 변경 전후 검증 기준이 없다.
먼저 생각해보기
- 지금 필요한 것은 heap 관찰인가, thread 관찰인가, GC pause 관찰인가?
- JVM option 변경은 어떤 증거가 있을 때만 시도해야 하는가?
- JFR, JMX, Actuator 중 어떤 도구가 가장 낮은 비용으로 필요한 증거를 주는가?
찾아볼 문서
- 00. JVM Runtime GC Thread 압축 정리
- 01. Class Loading JIT Runtime 상세
- 02. JVM Memory 영역과 Metaspace 상세
- 03. GC 기본과 Pause 이해 상세
- 04. JFR JMX Actuator 관찰 상세
- 05. JVM Option과 운영 판단 상세
- 04. JVM Heap GC와 메모리 관찰 상세
정리
JVM 관찰은 많이 켜는 것보다 필요한 증거를 낮은 비용으로 얻는 것이 중요하다. latency와 GC pause, heap/non-heap, thread state, allocation rate를 시간축으로 맞춘다. option 변경은 원인 가설과 성공 기준이 있을 때만 운영 판단이 된다.
가져갈 한 문장
JVM option은 감으로 튜닝하는 값이 아니라 관찰 지표가 만든 가설을 검증하는 변경이다.
Case 10. CPU, memory, I/O 중 어디가 병목인지 격리해야 할 때
상황
서비스 전체 latency가 올라갔지만 CPU, memory, disk, network, pool 지표가 조금씩 다 이상하다. 한 팀은 CPU를 의심하고, 다른 팀은 DB pool을 의심한다. 장애 대응 회의에서 원인 후보를 좁히는 순서가 필요하다.
관찰할 증거 또는 확인할 단서
- CPU는 높지만 iowait도 같이 높다.
- memory pressure와 swap이 보인다.
- connection pool pending과 thread pool active가 함께 증가한다.
- 어떤 지표가 원인이고 어떤 지표가 결과인지 시간축이 없다.
먼저 생각해보기
- 사용자 영향 지표와 자원 포화 지표를 시간순으로 놓으면 무엇이 먼저 움직였는가?
- CPU, memory, disk, pool 중 포화된 자원은 무엇인가?
- 완화 조치가 어느 지표를 낮춰야 성공인가?
찾아볼 문서
- 00. OS 관점의 성능 병목 분석 압축 정리
- 01. CPU Bound와 Load Average 상세
- 02. Memory Pressure와 Swap 상세
- 03. IO Wait와 Disk 병목 상세
- 04. Thread Pool Connection Pool 병목 연결 상세
- 05. 성능 장애 원인 격리 루틴 상세
- 03. CPU Memory IO Network 관찰 지점 상세
정리
성능 장애 격리는 모든 지표를 한꺼번에 보는 일이 아니라 증상, 포화, 대기, 변경 이력을 시간순으로 놓는 일이다. CPU, memory, I/O, pool을 각각 후보로 두고 배제 근거를 남긴다. 성공 기준은 latency 회복뿐 아니라 원인 후보 지표의 정상화까지 포함해야 한다.
가져갈 한 문장
병목 분석은 가장 눈에 띄는 지표를 고르는 일이 아니라 시간축으로 원인 후보를 줄이는 일이다.
Case 11. CPU 급등 대응 중 바로 scale out해도 되는지 판단할 때
상황
이벤트 직후 CPU가 급등했다. scale out은 가능하지만, CPU가 실제 업무 계산에 쓰이는지, GC나 재시도 폭주 때문인지, 특정 thread만 hot한지 확인하지 않았다.
관찰할 증거 또는 확인할 단서
- CPU user/system 비율과 load average가 급등한다.
- top에서 Java process가 높지만 thread별 원인은 모른다.
- 최근 배포나 traffic 변화가 같은 시각에 있다.
- scale out 후 downstream 부하가 증가할 가능성이 있다.
먼저 생각해보기
- CPU 급등을 만든 workload는 정상 요청인가, 비정상 루프인가, retry인가?
- thread dump와 top -H가 같은 thread를 가리키는가?
- scale out이 안전한 완화인지, 더 큰 downstream 압박인지 어떻게 확인할 것인가?
찾아볼 문서
- 01. CPU 사용률 급등 대응 상세
- 01. CPU Core와 실행 단위 상세
- 03. Scheduling과 Context Switch 비용 상세
- 01. CPU Bound와 Load Average 상세
- 01. ps top htop으로 Process 보기 상세
정리
CPU 급등 대응은 scale out 가능 여부를 묻기 전에 무엇이 CPU를 쓰는지 확인한다. 정상 traffic 증가라면 scale out이 완화가 될 수 있다. 하지만 tight loop, retry storm, GC, context switch라면 scale out보다 원인 제거가 먼저다.
가져갈 한 문장
CPU 급등에서 scale out은 원인을 모른 채 누르는 버튼이 아니라 workload 성격을 확인한 뒤 쓰는 완화책이다.
Case 12. OOM 직전 무엇을 남기고 무엇을 늦춰야 할 때
상황
heap 사용량이 95%를 넘고 OOM이 임박했다. 사용자는 이미 지연을 겪고 있다. 운영자는 재시작을 원하지만, dump 없이 재시작하면 원인 분석이 어려워진다.
관찰할 증거 또는 확인할 단서
- GC 후 heap이 줄지 않는다.
- old generation 또는 metaspace가 계속 증가한다.
- swap 사용이 늘어 전체 서버가 느려진다.
- heap dump를 뜨면 디스크와 pause 비용이 생긴다.
먼저 생각해보기
- 지금은 사용자 보호가 우선인가, 원인 증거 확보가 우선인가?
- heap dump를 안전하게 남길 여유가 있는가?
- 재시작 후에도 보존해야 할 GC log, JFR, metric 구간은 무엇인가?
찾아볼 문서
- 02. Memory Leak OOM 대응 상세
- 03. Memory Leak과 OOM 원인 상세
- 05. Heap Dump와 메모리 장애 대응 상세
- 04. JVM Heap GC와 메모리 관찰 상세
- 02. JVM Memory 영역과 Metaspace 상세
- 03. GC 기본과 Pause 이해 상세
- 02. Memory Pressure와 Swap 상세
정리
OOM 직전에는 모든 증거를 완벽히 남기려다 서비스를 더 오래 죽일 수 있다. 영향 범위와 남은 여유를 보고 dump 확보, traffic 차단, instance 교체, 재시작을 선택한다. 재시작은 완화이고, leak 원인 분석은 dump와 시계열 지표가 있어야 가능하다.
가져갈 한 문장
OOM 대응은 사용자 보호와 증거 보존 사이의 시간을 관리하는 일이다.
Case 13. Thread 고갈과 lock 대기를 함께 의심해야 할 때
상황
응답 지연이 커지고 thread pool active가 가득 찼다. thread dump에는 외부 I/O 대기와 lock BLOCKED가 같이 보인다. connection pool pending도 올라간다.
관찰할 증거 또는 확인할 단서
- thread dump에서 WAITING과 BLOCKED가 모두 많다.
- Tomcat thread가 DB connection을 기다리는 stack을 가진다.
- deadlock은 아니지만 특정 lock에서 오래 대기한다.
- executor queue가 쌓이고 rejection이 간헐적으로 발생한다.
먼저 생각해보기
- thread가 외부 I/O를 기다리는가, lock을 기다리는가, connection을 기다리는가?
- pool을 늘리면 어떤 downstream 한계에 먼저 닿는가?
- deadlock, starvation, pool 고갈을 어떻게 구분할 것인가?
찾아볼 문서
- 03. Thread 고갈과 응답 지연 대응 상세
- 04. Java Thread와 ExecutorService 상세
- 05. Thread Dump로 병목 읽기 상세
- 04. Thread Pool Starvation 상세
- 05. Blocking Non-blocking 실전 판단 상세
- 03. Deadlock Starvation Livelock 상세
- 05. Thread-safe 설계 판단 상세
- 04. Thread Pool Connection Pool 병목 연결 상세
- 04. Tomcat Connector와 OS Socket 연결 상세
정리
Thread 고갈은 단일 원인으로 보기 어렵다. 외부 I/O 대기, lock 대기, connection pool 대기, executor queue를 분리한다. thread 수 증가는 일시 완화일 수 있지만 downstream 대기를 늘릴 수 있으므로, 먼저 dump와 pool 지표로 기다림의 위치를 좁힌다.
가져갈 한 문장
Thread가 부족하다는 말은 실제로는 어떤 자원을 기다리는 thread가 많은지 확인하라는 뜻이다.
Case 14. File Descriptor와 Socket 고갈을 구분해야 할 때
상황
서버에서 too many open files가 발생하고 신규 연결 실패가 늘어난다. lsof를 보면 socket과 file이 모두 많다. TIME_WAIT도 증가했고 CLOSE_WAIT도 일부 보인다.
관찰할 증거 또는 확인할 단서
- process의 open FD가 limit에 가깝다.
- socket state 중 CLOSE_WAIT가 계속 쌓인다.
- log file, temp file, HTTP client connection이 닫히지 않는 정황이 있다.
- accept queue 지표와 Tomcat connector 지표가 같이 흔들린다.
먼저 생각해보기
- FD를 소모하는 주체는 file인가 socket인가 둘 다인가?
- CLOSE_WAIT는 어느 쪽 close 누락을 의심하게 하는가?
- limit을 올리는 조치와 leak을 고치는 조치는 어떻게 다르게 검증할 것인가?
찾아볼 문서
- 04. File Descriptor Socket 고갈 대응 상세
- 01. File Descriptor와 열린 파일 상세
- 04. Java File IO와 Resource 관리 상세
- 01. Socket Port File Descriptor 상세
- 02. Listen Backlog와 Accept Queue 상세
- 03. Connection State와 TIME_WAIT 상세
- 04. Tomcat Connector와 OS Socket 연결 상세
- 05. ss lsof netstat로 네트워크 관찰 상세
정리
FD 고갈은 limit만 올리면 끝나는 문제가 아니다. 열린 file과 socket을 구분하고, CLOSE_WAIT나 누적 파일 핸들이 leak을 가리키는지 본다. limit 증가는 완화이고, close 누락이나 connection 관리 문제는 근본 수정이다.
가져갈 한 문장
FD 고갈에서 limit 증가는 숨 쉴 공간을 주는 조치이고, 무엇이 FD를 붙잡는지 찾는 것이 원인 분석이다.
Case 15. 장애 보고서를 써야 하는데 증거가 흩어져 있을 때
상황
장애는 끝났지만 보고서를 쓰려니 access log, journalctl, thread dump, GC log, metric screenshot, 배포 기록이 흩어져 있다. 결론은 대략 알지만 어떤 증거로 판단했는지 재현하기 어렵다.
관찰할 증거 또는 확인할 단서
- 타임라인 없이 결론부터 적혀 있다.
- dump와 metric의 수집 시각이 없다.
- 완화 조치와 근본 수정이 섞여 있다.
- 배제한 원인 후보와 남은 리스크가 없다.
먼저 생각해보기
- 사용자 영향, 시작/종료 시각, 현재 상태를 5줄로 요약할 수 있는가?
- Application, JVM, OS, dependency 증거를 같은 시간축에 놓을 수 있는가?
- 재발 방지 action에는 owner와 due date가 있는가?
찾아볼 문서
- 00. Computer System 실전 플레이북 압축 정리
- 05. JVM OS 장애 보고서 작성 상세
- 03. journalctl log 파일 관찰 상세
- 05. 운영 중 안전한 명령 사용 상세
- 04. JFR JMX Actuator 관찰 상세
- 05. JVM Option과 운영 판단 상세
- 05. 성능 장애 원인 격리 루틴 상세
- 02. OS JVM Application 경계 상세
- 03. CPU Memory IO Network 관찰 지점 상세
정리
장애 보고서는 잘 쓴 이야기보다 판단 근거의 압축본이어야 한다. 영향, 타임라인, 증거, 판단, 완화, 근본 수정, 남은 리스크를 분리한다. 다음 장애 때 바로 쓸 수 있도록 runbook과 alert 개선으로 이어져야 한다.
가져갈 한 문장
좋은 장애 보고서는 결론을 설득하는 문서가 아니라 다음 대응을 빠르게 만드는 증거 묶음이다.
Case 16. Computer System 학습을 포트폴리오와 운영 감각으로 연결할 때
상황
CPU, memory, socket, GC 문서를 각각 읽었지만 면접이나 포트폴리오에서는 “리눅스 명령어를 써봤다” 수준으로만 설명된다. 실제 운영 감각으로 보이려면 증상, 지표, 판단, 조치, 재발 방지를 연결해야 한다.
관찰할 증거 또는 확인할 단서
- 학습 노트가 명령어 목록으로 끝난다.
- 개인 프로젝트에서는 장애 증거를 남기는 구조가 없다.
- 운영 수준에서 필요한 SLO, alert, runbook, 변경 통제 관점이 빠져 있다.
- 장애 보고서나 회고가 재발 방지로 이어지지 않는다.
먼저 생각해보기
- 내가 설명할 수 있는 장애 시나리오는 어떤 자원 병목과 연결되는가?
- 개인 프로젝트 수준에서 당장 추가할 관찰 지표는 무엇인가?
- 기업 운영 수준으로 확장하려면 어떤 runbook과 alert가 필요한가?
찾아볼 문서
정리
Computer System 학습은 명령어 암기가 아니라 상황별 판단 훈련이다. 작은 프로젝트에서도 latency, CPU, memory, thread, GC, FD, socket을 관찰할 수 있게 만들고, 장애 시나리오 하나를 보고서 형태로 남기면 학습이 실력으로 바뀐다.
가져갈 한 문장
실행 환경 지식은 명령어를 아는 순간이 아니라 증상과 지표를 연결해 안전한 조치를 설명할 때 드러난다.
보충 Case
이 섹션은 본문 Case에서 한 번만 호출된 문서를 다시 꺼내 보게 하기 위한 보강 영역이다. 기존 16개 Case가 주 경로라면, 보충 Case는 비슷한 장애가 다른 모습으로 나타나는 변형 상황이다.
보충 Case A. 배포 직후 첫 요청만 느리고 CPU가 튈 때
상황
새 버전을 배포한 직후 첫 5분 동안만 API p95가 높고 CPU도 튄다. 시간이 지나면 안정되지만, 이벤트 시간대 배포에서는 이 짧은 흔들림도 사용자에게 보인다.
관찰할 증거 또는 확인할 단서
- 배포 직후 class loading, JIT warm-up, cache warm-up 시점과 latency가 겹친다.
- CPU는 높지만 특정 요청이 반복적으로 느린 것은 아니다.
- process 재시작 직후에만 증상이 나타난다.
- scale out한 instance들이 동시에 warm-up을 겪는다.
먼저 생각해보기
- 이 CPU 사용은 장애인가, runtime warm-up 비용인가?
- 요청 흐름 중 어떤 계층이 첫 요청 비용을 만든 것인가?
- 배포 전략이나 readiness 기준으로 완화할 수 있는가?
찾아볼 문서
- 00. Computer System 전체 지도 압축 정리
- 01. 백엔드 요청과 실행 환경 전체 흐름 상세
- 00. CPU Process Thread Scheduling 압축 정리
- 02. Process와 Thread 차이 상세
- 00. JVM Runtime GC Thread 압축 정리
- 01. Class Loading JIT Runtime 상세
- 01. CPU 사용률 급등 대응 상세
정리
배포 직후 흔들림은 단순 CPU 장애와 다르게 봐야 한다. JVM warm-up, class loading, JIT, connection 초기화, cache warm-up이 같은 시간대에 몰릴 수 있다. 증상이 시간이 지나며 안정된다면 runtime 초기화 비용과 배포 전략을 함께 본다.
가져갈 한 문장
배포 직후 CPU 급등은 장애일 수도 있지만, 먼저 runtime 초기화 비용과 실제 사용자 영향 시간을 분리해야 한다.
보충 Case B. Blocking 코드를 event loop로 옮기려는 리뷰를 할 때
상황
팀이 응답 지연을 줄이기 위해 일부 API를 non-blocking 방식으로 바꾸려 한다. 그런데 내부에는 파일 읽기, JDBC 호출, 외부 HTTP 호출처럼 여전히 blocking 작업이 남아 있다.
관찰할 증거 또는 확인할 단서
- event loop thread에서 blocking 호출이 실행될 가능성이 있다.
- 기존 Tomcat thread pool 고갈 원인이 정확히 분리되지 않았다.
- Java IO, NIO, async 처리 모델의 차이를 구현 방식 이름으로만 설명한다.
- “non-blocking이면 빠르다”는 주장만 있고 실패 시 관측 지표가 없다.
먼저 생각해보기
- 지금 줄이려는 것은 thread 점유인가, I/O latency인가?
- blocking 호출을 event loop에 올리면 어떤 병목이 새로 생기는가?
- thread pool 분리와 timeout 조정으로 충분한 상황은 아닌가?
찾아볼 문서
- 00. Context Switching Blocking Non-blocking 압축 정리
- 01. Blocking이 Thread를 점유하는 방식 상세
- 02. Non-blocking Async Event Loop 기본 상세
- 03. Java IO NIO와 서버 처리 모델 상세
- 04. Thread Pool Starvation 상세
- 05. Blocking Non-blocking 실전 판단 상세
정리
Non-blocking 전환은 API 이름을 바꾸는 일이 아니다. blocking 작업이 어디에 남는지, event loop를 막지 않는지, 별도 scheduler나 worker pool이 필요한지 확인해야 한다. 기존 병목이 thread 점유인지 downstream latency인지 먼저 분리하지 않으면 구조만 복잡해질 수 있다.
가져갈 한 문장
Non-blocking은 기다림을 없애는 기술이 아니라 기다리는 동안 어떤 thread를 점유하지 않을지 정하는 설계다.
보충 Case C. 로그 디버그 배포 후 disk와 socket이 같이 흔들릴 때
상황
장애 분석을 위해 debug log를 켰더니 disk write가 늘고, 동시에 신규 연결 실패도 증가한다. 처음에는 network 문제처럼 보였지만 file descriptor와 disk I/O가 같이 흔들린다.
관찰할 증거 또는 확인할 단서
- iowait, disk busy, log file write가 동시에 상승한다.
- socket도 file도 같은 FD limit 안에서 증가한다.
too many open files가 간헐적으로 보인다.- 로그를 줄이면 일부 latency가 회복된다.
먼저 생각해보기
- 지금 병목은 network인가, disk인가, FD limit인가?
- 로그 증설이 관찰을 돕는 동시에 장애를 키우고 있지는 않은가?
- file과 socket 사용량을 같은 표에서 볼 수 있는가?
찾아볼 문서
- 00. File System과 IO 압축 정리
- 02. Disk IO와 Buffered IO 상세
- 03. 로그 파일 쓰기와 성능 상세
- 05. IO 병목 관찰과 대응 상세
- 00. Socket과 OS Network 압축 정리
- 01. Socket Port File Descriptor 상세
- 05. ss lsof netstat로 네트워크 관찰 상세
정리
로그와 socket은 전혀 다른 문제처럼 보이지만 OS에서는 file descriptor와 I/O 자원을 공유한다. 장애 중 debug log를 켤 때는 로그 write 비용, disk 여유, FD limit, socket state를 함께 본다. 관찰을 늘리는 조치가 서비스 자원을 더 압박할 수 있다.
가져갈 한 문장
운영 중 관찰을 늘릴수록 file, disk, socket 자원을 더 쓰므로 관찰 자체도 부하로 계산해야 한다.
보충 Case D. 재시작했는데 service가 포트와 권한 문제로 반복 실패할 때
상황
서비스를 재시작했지만 계속 기동에 실패한다. application log에는 명확한 원인이 적지 않고, systemd status에는 exit code만 보인다. 포트 바인딩 실패인지, 실행 사용자 권한 문제인지, limit 문제인지 구분해야 한다.
관찰할 증거 또는 확인할 단서
journalctl에는 permission denied, bind failed, working directory 오류가 간헐적으로 보인다.- 배포 후 실행 user나 파일 owner가 바뀌었다.
- 같은 port를 이전 process가 잡고 있을 가능성이 있다.
- systemd unit의 limit과 환경 변수가 기대와 다르다.
먼저 생각해보기
- 재시작 실패는 application bug인가, service 실행 환경 문제인가?
- 포트, 권한, working directory, limit 중 무엇을 먼저 배제할 것인가?
- 재시작을 반복하기 전에 어떤 증거를 남겨야 하는가?
찾아볼 문서
- 00. Linux Process Service Log 관찰 압축 정리
- 02. systemd와 Service 관리 상세
- 03. journalctl log 파일 관찰 상세
- 04. Permission User Group 기본 상세
- 00. Socket과 OS Network 압축 정리
- 04. File Descriptor Socket 고갈 대응 상세
- 00. Computer System 실전 플레이북 압축 정리
정리
재시작 실패는 코드 문제가 아닐 수 있다. systemd unit, 실행 user, 파일 권한, working directory, port 점유, FD limit을 확인한다. 같은 실패를 반복 재시작하면 증거가 밀리고 장애 시간이 길어진다.
가져갈 한 문장
서비스가 뜨지 않을 때는 애플리케이션 로그만 보지 말고 process를 실행시키는 Linux 경계부터 확인해야 한다.
보충 Case E. 공유 캐시와 카운터 최적화 PR을 리뷰할 때
상황
성능 개선 PR에서 공유 Map과 카운터를 추가했다. 개발자는 volatile을 붙였으니 안전하다고 말하지만, 여러 thread가 동시에 읽고 쓰는 복합 연산이 있다.
관찰할 증거 또는 확인할 단서
- read-modify-write가 한 줄처럼 보이지만 실제로는 여러 단계다.
volatile은 보이지만 atomicity 보장은 없다.- synchronized와 concurrent collection을 어떤 기준으로 골랐는지 설명이 없다.
- 장애가 나면 값이 틀릴 수 있지만 재현 테스트가 부족하다.
먼저 생각해보기
- 이 코드는 visibility 문제를 푸는가, atomicity 문제를 푸는가?
- critical section은 어디이고 얼마나 작게 잡을 수 있는가?
- lock, atomic class, concurrent collection 중 어떤 선택이 불변식을 지키는가?
찾아볼 문서
- 00. Concurrency Lock Race Condition 압축 정리
- 01. Race Condition과 Critical Section 상세
- 02. synchronized Lock ReentrantLock 상세
- 04. volatile Atomic Concurrent Collection 상세
- 05. Thread-safe 설계 판단 상세
정리
동시성 PR은 “빠르다”보다 “어떤 불변식을 어떤 도구로 지키는가”를 먼저 본다. volatile은 visibility에 도움을 주지만 복합 연산을 자동으로 안전하게 만들지 않는다. 공유 상태가 있다면 critical section, atomicity, lock 범위, 자료구조 선택을 문서화해야 한다.
가져갈 한 문장
Thread-safe 리뷰의 핵심은 키워드가 아니라 공유 상태의 불변식을 끝까지 지키는지 확인하는 것이다.
보충 Case F. 컨테이너 메모리 제한과 OOM이 섞여 보일 때
상황
Kubernetes나 container 환경에서 Java process가 OOM으로 죽었다. heap은 제한보다 작게 잡혀 있지만 RSS는 계속 늘고, node에는 memory pressure와 swap 징후도 있다.
관찰할 증거 또는 확인할 단서
- heap max와 container memory limit 사이에 여유가 작다.
- native memory, thread stack, direct buffer, metaspace가 빠져 있다.
- OS memory pressure와 JVM heap 지표가 서로 다른 이야기를 한다.
- OOM 대응 runbook이 heap dump만 가정한다.
먼저 생각해보기
- OOM은 Java heap OOM인가, container OOM kill인가?
- heap 밖 메모리는 어떤 항목으로 늘 수 있는가?
- dump 확보와 사용자 보호 중 어떤 순서가 안전한가?
찾아볼 문서
- 00. Memory Stack Heap Virtual Memory 압축 정리
- 01. Stack Heap Native Memory 상세
- 02. Virtual Memory Paging Swap 상세
- 02. JVM Memory 영역과 Metaspace 상세
- 00. OS 관점의 성능 병목 분석 압축 정리
- 02. Memory Pressure와 Swap 상세
- 02. Memory Leak OOM 대응 상세
정리
컨테이너 환경의 OOM은 heap 하나로 설명되지 않는다. heap, metaspace, direct buffer, thread stack, native memory, RSS, cgroup limit을 분리해야 한다. heap dump가 유용한 경우와 container OOM kill이라 다른 증거가 필요한 경우를 구분한다.
가져갈 한 문장
Java 메모리 장애에서 heap은 중요한 일부일 뿐이고, 운영에서는 process RSS와 컨테이너 limit까지 함께 봐야 한다.
보충 Case G. 어떤 플레이북을 먼저 열어야 할지 모르는 장애 회고
상황
장애가 끝났지만 회고에서 CPU, OOM, thread 고갈, FD 고갈이 모두 후보로 언급된다. 실제로는 하나가 주원인이었고 나머지는 결과였을 수 있다. 다음 장애를 위해 어떤 플레이북과 학습 문서를 먼저 보강할지 정해야 한다.
관찰할 증거 또는 확인할 단서
- 회고가 “서버가 느렸다” 같은 넓은 표현으로 끝난다.
- CPU, memory, thread, FD 지표가 같은 시간축에 놓이지 않았다.
- 개인 프로젝트 수준의 대응과 기업 운영 수준의 runbook이 구분되지 않는다.
- 재발 방지 항목이 학습 로드맵이나 운영 문서 개선으로 이어지지 않는다.
먼저 생각해보기
- 장애의 첫 사용자 영향은 무엇이었고, 어떤 자원 지표가 먼저 흔들렸는가?
- 이번 회고에서 업데이트해야 할 플레이북은 CPU, OOM, thread, FD 중 무엇인가?
- 다음 학습 순서에 어떤 문서를 다시 넣어야 하는가?
찾아볼 문서
- 04. 개인 프로젝트와 기업 운영 수준 비교 상세
- 05. Computer System 학습 로드맵 상세
- 00. Computer System 실전 플레이북 압축 정리
- 01. CPU 사용률 급등 대응 상세
- 02. Memory Leak OOM 대응 상세
- 03. Thread 고갈과 응답 지연 대응 상세
- 04. File Descriptor Socket 고갈 대응 상세
- 05. JVM OS 장애 보고서 작성 상세
정리
회고는 원인을 하나로 선언하는 자리가 아니라 다음 대응 속도를 높이는 자리다. 이번 장애가 어느 플레이북을 업데이트해야 하는지, 어떤 지표를 alert로 올려야 하는지, 학습 로드맵에서 어떤 계층을 다시 봐야 하는지 정한다.
가져갈 한 문장
장애 회고의 가치는 맞힌 원인보다 다음 장애 때 더 빨리 좁힐 수 있는 문서와 지표를 남기는 데 있다.
반복해서 다시 볼 문서
| 문서 | 반복해서 볼 상황 |
|---|---|
| 00. Computer System 전체 지도 압축 정리 | 느린 API를 어느 계층부터 볼지 막힐 때 전체 흐름을 다시 잡는다. |
| 02. OS JVM Application 경계 상세 | 증거가 흩어져 있을 때 계층별로 분류한다. |
| 03. CPU Memory IO Network 관찰 지점 상세 | CPU, memory, I/O, network 중 어느 축을 볼지 정한다. |
| 05. Thread Dump로 병목 읽기 상세 | thread pool, lock, blocking 대기를 구분해야 할 때 다시 연다. |
| 04. Thread Pool Starvation 상세 | CPU는 낮은데 응답이 느린 상황에서 다시 본다. |
| 05. Heap Dump와 메모리 장애 대응 상세 | OOM 전에 어떤 증거를 남길지 판단한다. |
| 05. IO 병목 관찰과 대응 상세 | 로그나 disk write가 API latency에 영향을 줄 때 다시 본다. |
| 05. ss lsof netstat로 네트워크 관찰 상세 | socket state와 FD 사용량을 확인해야 할 때 다시 연다. |
| 05. 운영 중 안전한 명령 사용 상세 | 장애 중 명령 실행이 시스템에 부담을 줄 수 있을 때 확인한다. |
| 05. Thread-safe 설계 판단 상세 | 동시성 문제가 코드 리뷰와 장애 분석으로 이어질 때 다시 본다. |
| 04. JFR JMX Actuator 관찰 상세 | JVM 내부 지표를 낮은 비용으로 수집해야 할 때 다시 연다. |
| 05. 성능 장애 원인 격리 루틴 상세 | 여러 지표가 동시에 흔들려 원인 후보를 좁혀야 할 때 쓴다. |
| 05. JVM OS 장애 보고서 작성 상세 | 장애가 끝난 뒤 판단 근거와 재발 방지를 남길 때 다시 본다. |
마무리 체크 질문
- 느린 API를 Application, JVM, OS, dependency 계층으로 나누었는가?
- CPU 사용률과 load average를 core 수, runnable thread, context switch와 함께 보았는가?
- CPU가 낮은 응답 지연에서 thread, lock, I/O, pool 대기를 분리했는가?
- OOM이나 memory pressure 상황에서 heap, native, metaspace, swap을 구분했는가?
- 로그와 file I/O가 관찰 수단이면서 병목이 될 수 있음을 확인했는가?
- 애플리케이션 로그에 없는 요청을 socket queue와 backlog 관점에서도 보았는가?
- service 재시작 전에 systemd, journal, 권한, limit, 최근 변경을 확인했는가?
- 동시성 문제에서 visibility, atomicity, lock order, thread-safe 책임을 나누었는가?
- JVM option 변경 전에 JFR, JMX, Actuator, GC log로 가설을 세웠는가?
- 장애 보고서에 영향 범위, 타임라인, 증거, 판단, 완화, 근본 수정, 남은 리스크가 있는가?