infra
Platform

모듈 맵

[Linux] 운영 서버 프로세스 이상 진단: 좀비, 메모리 누수, CPU 과점유 실전 대응

0 / 37 완료

펼치기
0 / 37 완료0%

리눅스 서버 운영 · 10 / 37

[Linux] 운영 서버 프로세스 이상 진단: 좀비, 메모리 누수, CPU 과점유 실전 대응

서버가 갑자기 느려졌다는 문의에서 시작해 CPU 과점유 프로세스, 좀비 프로세스, 파일 디스크립터 누수를 재시작 없이 찾고 조치하는 실전 흐름을 익힙니다

🚨INCIDENT ALERT
HIGH

오전 10시. 슬랙에 긴급 메시지가 왔다. "API 서버가 갑자기 엄청 느려졌어요. CPU가 97%인데 뭐가 먹는지 모르겠어요. 재시작하면 될까요?"

재시작은 마지막 수단입니다. 원인을 모른 채 재시작하면 30분 후에 또 같은 일이 생깁니다. SSH로 접속해서 무엇이 CPU를 먹는지, 좀비가 쌓여 있는지, 파일 디스크립터가 새고 있는지 — 재시작 없이 찾는 방법을 배웁니다.

이번 챕터에서 배울 것
  • 1ps aux와 top으로 CPU 과점유 프로세스를 30초 안에 특정할 수 있다
  • 2ps aux의 STAT 컬럼(R/S/D/Z)을 읽고 프로세스 이상 징후를 판단할 수 있다
  • 3좀비 프로세스의 원인과 해결 방법을 설명하고 직접 정리할 수 있다
  • 4lsof로 파일 디스크립터 누수를 진단하고 한도 설정을 확인할 수 있다
  • 5SIGTERM과 SIGKILL의 차이를 알고 프로세스를 안전하게 종료하는 순서를 따를 수 있다

💡개념

프로세스 상태 코드 — STAT 컬럼 읽기

ps aux의 STAT 컬럼은 프로세스의 현재 상태를 한 글자로 나타냅니다. 이 코드를 읽으면 "왜 느린지"에 대한 첫 번째 단서를 얻을 수 있습니다.

상태코드의미장애 신호
RCPU에서 실행 중 또는 실행 대기하나가 100% 점유하면 문제
SI/O 대기 (인터럽트 가능, 정상)대부분의 프로세스가 이 상태
DI/O 대기 (인터럽트 불가)NFS 행, 디스크 불량 의심
Z좀비 — 종료됐지만 부모가 회수 안 함수백 개 쌓이면 PID 테이블 고갈
T정지됨 (Ctrl+Z 또는 SIGSTOP)잊힌 백그라운드 프로세스 확인

STAT 뒤에 붙는 수식어도 중요합니다: s = 세션 리더, + = 포어그라운드, l = 멀티스레드, < = 높은 우선순위, N = nice 적용(낮은 우선순위)

프로세스 상태 머신과 시그널 — ps aux STAT 상태 전환(R 실행·S 슬립·D 디스크슬립·T 정지·Z 좀비)과 SIGTERM(정상 종료, 정리 루틴 실행) vs SIGKILL(강제 종료, 정리 건너뜀) 비교확대


💡개념

프로세스가 태어나는 법 — fork·exec 4단계

앞의 STAT 표에서 좀비(Z)는 "종료됐지만 부모가 회수 안 함", 고아는 "부모가 먼저 죽음"이라고 했습니다. 왜 이런 상태가 생기는지는 프로세스가 어떻게 태어나고 죽는지를 알아야 이해됩니다. 리눅스에서 새 프로세스는 무에서 생기지 않고 항상 부모를 복제(fork)한 뒤 다른 프로그램으로 갈아끼우는(exec) 방식으로 만들어집니다. bash에서 ls를 치는 것도, 컨테이너에서 앱이 뜨는 것도 모두 이 4단계 흐름의 결과입니다.

TEXT
[부모]  bash (PID 2000)
   │
   │  ① fork()  ── 부모를 통째로 복제. 메모리는 Copy-on-Write로
   │              일단 공유하다 어느 쪽이 쓰면 그 페이지만 복사
   ▼
   ├──────────────────────────────┐
 부모(PID 2000)                 자식(새 PID 2001)
   │                              │
   │                              │  ② exec()  ── 자기 프로세스 이미지를
   │                              │              /bin/ls 로 통째 교체
   │                              │              (PID는 그대로, 내용만 바뀜)
   │                              │
   │  ③ wait() 로 자식 종료를 대기   │  ④ exit(code) ── 일 끝나면 종료코드 반환
   │            ◄──────────────────┘
   │  종료코드 수거 → 자식을 프로세스 테이블에서 제거(완전 소멸)
   ▼
[부모 계속 실행]

각 단계가 하는 일과, 어긋나면 어떤 증상인가:

단계하는 일여기서 어긋나면
fork()부모가 자신을 복제해 자식을 만든다. 주소공간은 Copy-on-Write로 일단 공유하다 어느 쪽이 쓰는 순간 그 페이지만 복사 — 그래서 복제가 싸고 빠르다fork 폭주(fork bomb)로 PID·메모리 고갈 → Resource temporarily unavailable
exec()자식이 execve()로 자기 프로세스 이미지를 실제 실행할 프로그램(/bin/ls 등)으로 덮어쓴다. PID는 유지되고 코드·데이터만 교체실행 파일 없음·권한 없음 → command not found, Permission denied
wait()부모가 wait()/waitpid()로 자식의 종료 코드를 수거하고 프로세스 테이블에서 항목을 제거부모가 수거를 안 함 → 자식이 좀비(Z, <defunct>)로 잔존
exit()자식이 종료 코드를 커널에 반환하고 자원을 반납. 부모가 거둘 때까지 "종료 상태"만 테이블에 남는다부모가 자식보다 먼저 죽음 → 자식은 고아가 되어 PID 1(systemd)이 입양

좀비와 고아는 이 흐름의 두 가지 어긋남입니다. 좀비(Z)는 ③이 빠진 경우 — 자식은 이미 exit했는데(④) 부모가 wait로 거두지 않아, 자원은 거의 없이 종료 코드만 프로세스 테이블에 남습니다. 이미 죽었으니 kill이 통하지 않고, 부모가 wait하거나 부모가 사라져야만 정리됩니다. 고아는 ③보다 먼저 부모가 죽은 경우 — 이때 자식은 PID 1(systemd)에게 입양되고, PID 1은 주기적으로 wait하는 특별한 역할을 하므로 입양된 자식이 종료되면 곧바로 거둬 좀비가 영구화되지 않게 합니다. 그래서 "좀비가 안 사라진다"의 해결책은 좀비를 kill하는 게 아니라 부모를 재시작해 PID 1이 입양·회수하게 만드는 것입니다.

프로세스는 이 사이에서 STAT 상태를 오갑니다 — fork/exec 직후 실행 가능해지면 R, I/O를 기다리며 잠들면 S(깨울 수 있음)·D(디스크 등 인터럽트 불가), 정지 신호를 받으면 T, exit 후 미수거 상태가 Z입니다. ps -ef --forest로 부모-자식(PPID) 관계를 트리로 보면, 어느 부모가 자식을 안 거두고 있는지(좀비의 원인)를 바로 짚을 수 있습니다.


1CPU 과점유 프로세스 파악

장애 접수 후 첫 번째로 실행하는 명령입니다. CPU를 가장 많이 소비하는 프로세스 10개를 내림차순으로 정렬합니다.

--sort=-%cpu-는 내림차순입니다. 메모리 기준으로 보려면 --sort=-%mem을 씁니다.

ps aux --sort=-%cpu | head -10
🔍실행 후 확인할 것
  • %CPU 컬럼 — 상위 프로세스가 50% 이상을 점유하고 있는가
  • STAT 컬럼 — R 상태(실행 중)인 프로세스가 여러 개인가, 한 개인가
  • PID와 COMMAND — 어떤 프로세스인지, 언제부터 실행됐는지 (TIME 컬럼으로 누적 CPU 시간 확인)
  • USER 컬럼 — 서비스 계정(www-data, node 등)인지 예상치 못한 계정인지 확인
OUTPUT
USER       PID %CPU %MEM    VSZ   RSS TTY  STAT START   TIME COMMAND
www-data  7823 94.3  2.1  512340 43012 ?  R    09:47  12:31 node /app/server.js
root       892  1.2  0.3   65432  6108 ?  Ss   03:21   0:12 sshd: /usr/sbin/sshd
postgres  1204  0.8  1.4  412340 28890 ?  Ss   03:21   1:04 postgres: checkpointer
www-data  7824  0.3  0.2   85340  4120 ?  S    09:47   0:02 node /app/worker.js

2좀비 프로세스 확인

$8은 STAT 컬럼(8번째 필드)입니다. Z를 포함한 행만 출력합니다.

결과가 비어 있으면 좀비 없음 — 정상입니다. 출력이 있다면 개수와 어떤 프로세스에서 발생했는지 확인합니다.

ps aux | awk '$8 ~ /Z/'
🔍실행 후 확인할 것
  • 결과가 없으면 좀비 프로세스 없음 — 이 원인은 제외
  • 결과가 있으면 PPID(부모 PID)를 확인한다 — 좀비 자체가 아니라 부모 프로세스가 문제의 원인
  • COMMAND에 <defunct> 표시가 있으면 확정 좀비이다
  • 좀비 10개 이하는 일반적으로 즉각 위험하지 않지만 수백 개라면 PID 테이블 고갈 위험
OUTPUT
USER       PID %CPU %MEM  VSZ  RSS TTY STAT START   TIME COMMAND
www-data  8012  0.0  0.0    0    0 ?   Z    09:52   0:00 [node] <defunct>
www-data  8013  0.0  0.0    0    0 ?   Z    09:52   0:00 [node] <defunct>

3파일 디스크립터 누수 확인

lsof -p <PID>로 해당 프로세스가 현재 열어둔 파일, 소켓, 파이프 전체를 조회하고 개수를 셉니다. 정상 프로세스는 수십~수백 개 수준입니다. 수천 개 이상이면 FD 누수를 의심합니다.

lsof -p 7823 | wc -l
🔍실행 후 확인할 것
  • 100개 이하: 정상
  • 수백 개: 활성 연결이 많은 경우로 트래픽에 비례하면 정상일 수 있음
  • 수천 개 이상: FD 누수 강력 의심 — ulimit -n 한도 확인 필요
  • lsof -p <PID> | grep socket | wc -l — 소켓만 따로 세어 연결 폭발 여부 확인
로컬 터미널
# 현재 한도 확인
ulimit -n
cat /proc/7823/limits | grep "open files"

# 어떤 타입의 FD가 많은지 분류
lsof -p 7823 | awk '{print $5}' | sort | uniq -c | sort -rn | head -10

4원인 파악 후 프로세스 조치

원인을 충분히 파악했을 때 먼저 SIGTERM으로 정상 종료를 요청합니다. 프로세스가 핸들러를 등록했다면 DB 연결 정리, 캐시 플러시 등을 실행하고 종료합니다.

10초 이상 기다려도 살아있으면 그때 SIGKILL을 사용합니다.

kill -SIGTERM 7823
🔍실행 후 확인할 것
  • kill -SIGTERM 후 5~10초 대기 — ps aux | grep 7823으로 종료 여부 확인
  • 종료됐다면 서비스 재시작 후 CPU 사용률이 정상화됐는지 top으로 확인
  • 여전히 살아있다면 kill -0 7823 2>/dev/null && kill -SIGKILL 7823 으로 강제 종료
위험 명령어데이터 유실, 락 파일 미정리, DB 연결 비정상 종료

SIGKILL (강제 종료) — 마지막 수단

안전한 실행 조건: SIGTERM 10초 이상 대기 후에도 프로세스가 살아있을 때만

실행 전 반드시 확인

  • SIGTERM으로 먼저 정상 종료를 시도했는가
  • 10초 이상 기다렸는가
  • 데이터 손실 가능성을 인지했는가 (DB 트랜잭션, 캐시, 파일 쓰기 등)
  • 서비스 복구 방법(재시작 방법)을 확인했는가
kill -9 7823

위 항목을 모두 확인한 후 복사할 수 있습니다


판단 문제이 서버에서 무엇을 먼저 조치해야 하는가?

실제 출력

USER       PID  %CPU %MEM  STAT  COMMAND
www-data  7823  93.1  2.1  R     node /app/server.js
www-data  8012   0.0  0.0  Z     [node] <defunct>
www-data  8013   0.0  0.0  Z     [node] <defunct>
www-data  8014   0.0  0.0  Z     [node] <defunct>

$ lsof -p 7823 | wc -l
4821

$ ulimit -n
1024

CPU 93%인 node 프로세스(7823)와 좀비 3개가 동시에 발견됐습니다. 무엇을 먼저 조치해야 하고, 그 이유는 무엇인가요?


좀비 프로세스에 kill -9를 해도 사라지지 않습니다. 정상입니다 — 좀비에게 어떤 시그널도 보낼 수 없습니다.

이유: 좀비는 이미 종료된 프로세스입니다. 커널이 부모 프로세스가 wait()를 호출할 때 전달할 종료 코드만 프로세스 테이블에 남겨둔 것입니다. "살아있는" 프로세스가 아니므로 시그널을 받을 수 없습니다.

해결 방법:

로컬 터미널
# 1. 좀비의 부모 PID 확인
ps -o ppid= -p <ZOMBIE_PID>
# 또는
ps -el | grep Z | awk '{print $4, $5}'   # PPID PID

# 2. 부모에게 SIGCHLD 전송 (wait() 유도)
kill -SIGCHLD <PARENT_PID>

# 3. 그래도 안 되면 부모 프로세스 재시작
# 부모가 죽으면 좀비는 PID 1(systemd)에 입양되고 즉시 회수됨
systemctl restart <서비스명>

# 4. 개수 확인
ps aux | awk '$8 ~ /Z/' | wc -l

예방: 애플리케이션 코드에서 자식 프로세스를 생성하는 경우 SIGCHLD 핸들러를 등록하거나 waitpid(-1, WNOHANG)를 주기적으로 호출해야 합니다.

ps aux에서 %CPU가 낮은데 서버 응답이 느립니다. STAT 컬럼에 D가 많이 보입니다.

D 상태(Uninterruptible Sleep)의 특징:

  • 인터럽트 불가능한 I/O 대기 — 디스크, NFS, 블록 디바이스를 기다리는 중
  • SIGKILL로도 종료되지 않음 (커널이 다음 스케줄링 때 처리하는데, D 상태에서는 스케줄링 기회 자체가 없음)
  • 다수 발생 시 시스템 전체가 I/O에 막힌 상태
로컬 터미널
# D 상태 프로세스 확인
ps aux | awk '$8 == "D"'

# 어디서 I/O를 기다리는지 확인
cat /proc/<PID>/wchan     # 커널에서 어떤 wait 함수에 있는지

# NFS 마운트가 원인인지 확인
mount | grep nfs
# NFS가 있다면: NFS 서버 네트워크 연결 상태 확인

# 디스크 I/O 상태
iostat -x 1 5
dmesg | tail -30 | grep -i "error\|failed\|timeout"

해결: NFS면 umount -l -f /mnt/share로 강제 해제. 디스크 하드웨어 오류면 dmesg에 disk error 로그가 남습니다. I/O 자체 문제라면 서버 재시작이 유일한 해결책인 경우도 있습니다.


💡개념

컨테이너 안의 프로세스 — PID 네임스페이스와 kill 1의 함정

같은 프로세스라도 호스트와 컨테이너에서 보이는 PID가 다릅니다. 이걸 모르면 엉뚱한 PID를 죽여 다른 컨테이너나 호스트를 통째로 내릴 수 있기 때문에, 컨테이너 환경에서 프로세스를 다루기 전에 반드시 짚어야 합니다.

컨테이너는 PID 네임스페이스로 격리되어, 컨테이너 안에서 ps aux를 하면 그 컨테이너의 메인 프로세스가 PID 1로 보입니다. 하지만 호스트에서 보면 같은 프로세스가 847 같은 다른 번호를 가집니다. 그래서 컨테이너 내부에서 kill 1은 그 컨테이너만 종료하지만, 호스트에서 kill 1은 systemd를 죽여 시스템 전체가 다운됩니다. 호스트에서 컨테이너 프로세스를 확인하려면 cat /proc/<호스트PID>/cgroup으로 어느 컨테이너 소속인지, docker inspect <컨테이너> | grep Pid로 매핑을 먼저 확인합니다.

컨테이너 PID 네임스페이스 — 호스트 뷰에서는 nginx master가 PID 847이지만 컨테이너 뷰에서는 PID 1로 보이며, 같은 프로세스가 네임스페이스마다 다른 번호를 가짐. 잘못된 PID로 kill하면 다른 컨테이너·호스트 프로세스를 종료할 위험확대

💡개념

프로세스 소유권 — 왜 root로 실행하면 안 되는가

ps aux의 USER 컬럼이 root인 서비스 프로세스를 보면 경계해야 합니다. 그 프로세스에 취약점이 하나라도 있으면 공격자가 곧바로 서버 전체 권한을 얻기 때문입니다.

서비스를 전용 계정(예: myapp)으로 실행하면 취약점이 뚫려도 피해가 그 계정이 접근 가능한 범위로 제한됩니다. systemd 서비스에 User=/Group=으로 전용 계정을 지정하고, NoNewPrivileges=yes(SUID 권한 상승 차단)·ProtectSystem=full(/usr·/etc 읽기 전용)·PrivateTmp=yes(/tmp 격리)를 더하면 최소 권한 원칙이 적용됩니다. 핵심은 단일 서비스의 취약점이 서버 전체 장악으로 번지지 않게 막는 것입니다.

프로세스 소유권과 서비스 계정 — root로 실행하면 취약점 하나로 서버 전체(파일·계정·DB·커널) 장악 가능, 전용 서비스 계정은 systemd User=/Group=/NoNewPrivileges/ProtectSystem/PrivateTmp로 피해를 해당 서비스 데이터로 한정확대


심화 — CPU가 한가한데 느릴 때, 원인은 프로세스 밖에 있다

💡개념

심화: steal time과 cgroup CPU 스로틀링 — %CPU가 낮아도 느린 이유

지금까지 '느린 서버'의 범인을 프로세스 안에서(과점유·좀비·D 상태) 찾았습니다. 그런데 top의 프로세스 %CPU는 낮고 load도 높지 않은데 응답만 느린 경우가 있습니다. 이때는 범인이 프로세스 밖 — 가상화 계층이나 cgroup 제한 — 에 있습니다.

  • top의 CPU 줄을 끝까지 읽어야 합니다: us(유저)·sy(커널) 말고 st(steal)와 wa(iowait)가 힌트입니다. **st(steal time)**는 '내 VM이 CPU를 쓰려 했는데 하이퍼바이저가 다른 VM에 줘서 뺏긴 시간'입니다. st가 높으면 내 프로세스는 죄가 없고, 물리 호스트가 과매각(oversubscribed)됐거나 시끄러운 이웃(noisy neighbor)이 있는 것입니다.
  • 컨테이너는 CPU에도 상한이 있습니다: 메모리에 limit이 있듯 CPU에도 cgroup 쿼터(CFS quota)가 걸립니다. 예를 들어 '0.5 코어' 제한은 100ms 주기마다 50ms만 쓰게 하고, 그 안에서 다 쓰면 남은 주기 동안 강제로 멈춥니다(throttle). 이때 컨테이너 안에서 본 %CPU는 낮은데 실제로는 계속 정지당해 지연이 튑니다.
  • 스로틀링은 평균 사용률에 숨습니다: 1초 평균 CPU가 40%여도, 100ms 창 단위로는 앞 50ms 폭주 후 뒤 50ms 정지가 반복될 수 있습니다. 그래서 '사용률은 낮은데 p99 지연이 나쁜' 전형적 패턴이 됩니다. 증거는 사용률이 아니라 cpu.statnr_throttled·throttled_time입니다.
  • 진단 지점이 다릅니다: steal은 top·vmstatst, mpstat%steal로 봅니다. 스로틀링은 컨테이너 cgroup의 cpu.stat을 봐야 하고, 호스트에서 %CPU만 보면 절대 안 잡힙니다.

핵심은 '느림'의 책임이 항상 내 프로세스에 있지는 않다는 것입니다. 프로세스 안(과점유·I/O)을 봤는데 깨끗하다면, 그 위의 **가상화(steal)와 cgroup 제한(throttle)**으로 시선을 올려야 합니다.

상황: 특정 컨테이너 서비스의 p99 지연이 들쭉날쭉합니다. 컨테이너 안에서 top·ps를 봐도 프로세스 CPU는 30%대로 여유롭고, 호스트 전체 부하도 낮습니다. '자원은 남는데 느린' 모순이라 원인을 못 찾습니다.

원인: 이 컨테이너에 CPU 제한(예: 0.5코어)이 걸려 있었고, 트래픽이 순간적으로 몰릴 때 100ms 주기의 쿼터를 앞부분에서 소진해 남은 주기 동안 CFS 스로틀링으로 강제 정지당했습니다. 컨테이너 안에서 본 30%는 1초 평균이라 낮게 보였을 뿐, 짧은 창에서는 정지가 반복되고 있었습니다. 호스트가 한가한 것과는 무관합니다 — 제한은 호스트 여유가 아니라 이 cgroup에만 적용되기 때문입니다.

진단: 사용률이 아니라 cgroup의 스로틀 카운터를 봅니다.

로컬 터미널
# cgroup v2: 스로틀 발생 횟수와 누적 정지 시간
cat /sys/fs/cgroup/cpu.stat
# nr_periods 12000
# nr_throttled 3400        ← 주기 중 3400번 스로틀됨
# throttled_usec 91000000  ← 누적 강제 정지 시간

# 컨테이너에 걸린 CPU 상한 확인
cat /sys/fs/cgroup/cpu.max
# 50000 100000   ← 100ms당 50ms = 0.5코어

# docker 레벨에서 스로틀 지표 감 잡기
docker stats --no-stream

nr_throttledthrottled_usec가 부하 구간에서 계속 증가하면 스로틀링이 지연의 원인입니다.

해결: 워크로드가 순간 버스트가 필요한데 상한이 낮으면 CPU 제한을 올립니다(cpu.max 또는 컨테이너 --cpus·쿠버네티스 CPU limit 상향). 상한을 유지해야 한다면 요청당 CPU 사용을 줄이거나(무거운 동기 작업 분산) 워커 수를 조정해 버스트 폭을 낮춥니다. steal이 원인인 경우(가상 머신에서 st가 높음)는 제한이 아니라 호스트 과매각 문제이므로, 더 여유로운 호스트·인스턴스 타입으로 옮기거나 배치를 분산합니다. 재발 방지로 사용률뿐 아니라 nr_throttled(컨테이너)와 %steal(VM)을 함께 모니터링합니다(OOM Killer 방지를 위한 Swap 메모리 파티션 튜닝의 cgroup 메모리 제한과 같은 층위의 제한입니다).


💼
실무 맥락
현업 패턴

실무 문의 시나리오: "API 서버 CPU 97%, 재시작할까요?"

금요일 오후 6시, 슬랙 DM이 왔습니다. 신입 개발자가 서버가 갑자기 느려졌다며 재시작 여부를 묻습니다.

로컬 터미널
# T+0:00 — SSH 접속 후 즉시 현황 파악
top -bn1 | head -20
ps aux --sort=-%cpu | head -10

# T+0:01 — PID 특정 (예: 7823)
# 언제부터? 배포 시각과 일치하는가?
ps -p 7823 -o pid,lstart,etimes,command

# T+0:02 — 좀비, FD 누수 동시 확인
ps aux | awk '$8 ~ /Z/' | wc -l
lsof -p 7823 | wc -l

# T+0:03 — 무엇을 하고 있는지 10초 확인 (프로덕션에서 짧게만)
timeout 10 strace -c -p 7823
# futex 호출이 압도적이면 스레드 락 경합
# write 압도적이면 로그/파일 I/O 폭주

# T+0:05 — Java/Node라면 스레드 덤프로 증거 수집
# Java
kill -SIGUSR1 7823    # JVM 스레드 덤프 → stdout/stderr
# Node.js
kill -SIGUSR1 7823    # V8 inspector 열림

# T+0:10 — 원인 파악 후 SIGTERM으로 정상 종료
kill -SIGTERM 7823
# 10초 대기 후
kill -0 7823 2>/dev/null && kill -SIGKILL 7823

# T+0:12 — 서비스 재시작 및 정상화 확인
systemctl restart myapp
top -bn1 | head -5

슬랙 응답 예시:

"확인해보니 7823 프로세스가 CPU 94% 점유 중이고 FD가 4821개(한도 1024 초과)입니다. 10:47에 배포 후 시작됐고 파일 디스크립터 누수로 보입니다. SIGTERM으로 프로세스 재시작했고 CPU 정상화됐습니다. 배포된 코드에서 파일/소켓 close() 누락 부분 확인이 필요합니다."

원인 없이 "재시작했더니 됐어요"는 같은 장애가 반복됩니다. 30초라도 증거를 수집하면 재발 방지가 됩니다.



명령어·단축키 빠른 참조

이 모듈에서 다룬 프로세스 관리 명령을 실전 옵션과 함께 모았습니다. "예" 열의 조합을 그대로 써도 됩니다.

명령어/단축키용도자주 쓰는 예
ps aux전체 프로세스 스냅샷ps aux --sort=-%cpu | head (CPU 상위)
ps -ef --forest부모-자식(PPID) 트리로 보기좀비의 부모 추적 시 유용
top / htop실시간 자원 모니터top -o %MEM (메모리순 정렬)
pgrep / pkill이름으로 찾기·종료pgrep -a nginx / pkill -f myapp
kill시그널 전송(기본 TERM)kill -15 PID → 안 죽으면 kill -9 PID
nice / renice실행 우선순위 조절renice -n 10 -p PID (낮은 우선순위로)
jobs / fg / bg셸 작업 제어Ctrl+Z(정지) → bg(백그라운드) → jobs
nohup … &로그아웃해도 유지되는 백그라운드 실행nohup ./run.sh > out.log 2>&1 &
cat /sys/fs/cgroup/…/cpu.statcgroup CPU 스로틀 확인nr_throttled가 늘면 CFS 쿼터 제한

관련 모듈로 더 깊이:

다음 모듈에서는 시그널(Signal) 처리를 더 깊이 다룹니다 — 애플리케이션에서 SIGTERM 핸들러를 작성하는 방법, graceful shutdown 패턴, 컨테이너 환경에서 PID 1의 신호 전달 문제를 배웁니다.

지식 확인

퀴즈 — 10문제

Q1

ps aux 출력의 STAT 컬럼에서 'Z' 상태가 의미하는 것은?

Q2

CPU 97%인 서버에서 kill -9로 원인 프로세스를 즉시 종료하는 것이 권장되지 않는 이유는?

Q3

lsof -p <PID> | wc -l 결과가 수천 개라면 무엇을 의심해야 하는가?

Q4

top 명령어에서 load average: 4.20, 2.80, 1.50이 출력됐고 서버는 2코어 CPU다. 이 값의 의미는?

Q5

ps에서 좀비(Z) 프로세스가 보이는데 kill -9로도 사라지지 않는다. 올바른 조치는?

Q6

2코어 서버에서 load average의 1분값이 2.0이다. 이 상태를 가장 잘 설명한 것은?

Q7

[심화] top의 CPU 요약에서 프로세스 %CPU는 낮은데 st(steal)가 높게 나온다. 이것이 뜻하는 것은?

Q8

[심화] 컨테이너 안 top의 CPU가 30%로 낮은데 p99 지연이 튄다. CPU 스로틀링(CFS quota) 여부를 확인하는 지표는?

Q9

[1급] nice 값의 범위와 변경 권한에 대한 설명으로 옳은 것은?

Q10

[1급] ps로 본 STAT(프로세스 상태) 코드에 대한 설명으로 옳은 것은?

0 / 10 답변

🧪 실습으로 확인하기

새 서버 인수인계 — 처음 30분

초급

낯선 Linux 서버를 인수받았을 때 OS, 서비스, 로그를 빠르게 파악하는 루틴을 직접 수행한다.

30📋 3단계💻 직접 환경
실습 시작하기 →

이것도 배워보세요