오전 9시, 모니터링 알림이 울립니다. "WAS 응답 없음." SSH로 접속해서 무엇부터 확인해야 할까요? 프로세스는 살아있는지, 어느 포트에서 대기 중인지, CPU와 메모리는 어떤지, 디스크는 꽉 차지 않았는지 — 5분 안에 서버 상태를 파악하는 것이 첫 번째 과제입니다.
이 모듈의 명령어들이 장애 대응 첫 5분의 전부입니다.
- 1ps aux와 top으로 프로세스 목록과 CPU/메모리 사용량을 해석할 수 있다
- 2ss -tlnp로 리스닝 포트와 연결된 프로세스를 확인할 수 있다
- 3df -h로 디스크 사용량을 확인하고 df -i로 inode 소진 여부를 파악할 수 있다
- 4free -m으로 메모리/스왑 상태를 해석할 수 있다
- 5journalctl로 서비스 로그를 시간 기준으로 조회할 수 있다
which ps top htop ss lsof df du free journalctlsudo apt-get install -y htop 2>/dev/null || sudo dnf install -y htop 2>/dev/null; echo donewhich lsof || sudo apt-get install -y lsof 2>/dev/null || sudo dnf install -y lsof 2>/dev/null프로세스 점검
ps로 프로세스 확인
서버에서 갑자기 CPU가 치솟거나 메모리가 부족해질 때, 어느 프로세스가 문제인지 가장 먼저 파악해야 합니다. ps 명령어를 모르면 top 출력만 바라보다 시간을 낭비하게 됩니다. 실무에서는 ps aux와 ps -ef를 상황에 맞게 구분해서 쓰는 것이 첫 번째 진단 루틴입니다.

장애 알림을 받고 서버에 접속했는데 "뭘 먼저 확인해야 할지" 몰라서 5분을 허비했다면, 원인은 점검 루틴이 없어서입니다. Tomcat 프로세스가 살아있는지, CPU를 독점하는 프로세스가 있는지 — ps를 제대로 읽지 못하면 가장 기본적인 판단도 내릴 수 없습니다. 프로세스 상태를 30초 안에 파악하는 것이 장애 대응 속도의 출발점입니다.
# 전체 프로세스 목록 (자주 쓰는 형식)
ps aux
# USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
# tomcat 1234 2.5 8.3 3145728 340000 ? Sl 09:00 2:15 java -Xmx2g ...
# nginx 5678 0.0 0.1 12345 4096 ? Ss 09:00 0:00 nginx: master
# ps -ef 형식 (PPID 포함, 프로세스 계층 파악에 유용)
ps -ef | grep tomcat
# 특정 프로세스만 필터링
ps aux | grep nginx | grep -v grep
# 프로세스 이름으로 PID만 추출
pgrep -f tomcat
# 또는
pidof nginx
ps aux 컬럼 해석:
| 컬럼 | 의미 | 주목할 상황 |
|---|---|---|
%CPU | CPU 사용률 | 지속적으로 100%+ → 무한루프, 고부하 |
%MEM | 메모리 사용률 | 지속 증가 → 메모리 누수 의심 |
VSZ | 가상 메모리 (KB) | Java는 VSZ가 크게 나와도 정상 |
RSS | 실제 사용 물리 메모리 (KB) | 실제 소비량은 RSS 기준 |
STAT | 프로세스 상태 | Z=좀비, D=I/O 대기(많으면 디스크 문제) |
TIME | 누적 CPU 시간 | 갑자기 증가하면 비정상 부하 |
# CPU 사용률 TOP 10
ps aux --sort=-%cpu | head -11
# 메모리 사용률 TOP 10
ps aux --sort=-%mem | head -11
top으로 실시간 리소스 모니터링
CPU가 100%를 찍고 있는데 어떤 프로세스가 원인인지 모르면 재시작만 반복하게 됩니다. 재시작하면 잠깐 나아지다가 30분 후 다시 튀는 패턴이 반복됐는데, top으로 확인하니 특정 배치 스크립트가 주기적으로 CPU를 독점하고 있었습니다. load average가 코어 수보다 높다는 것의 의미, wa(I/O 대기)가 높을 때의 판단 기준을 알아야 원인을 좁힐 수 있습니다.
top
# top - 14:30:00 up 2 days, 3:15, 2 users, load average: 0.52, 0.48, 0.45
# Tasks: 185 total, 1 running, 184 sleeping, 0 stopped, 0 zombie
# %Cpu(s): 5.2 us, 1.1 sy, 0.0 ni, 93.5 id, 0.1 wa, 0.0 hi, 0.0 si
# MiB Mem : 7821.4 total, 2341.2 free, 3621.8 used, 1858.4 buff/cache
# MiB Swap: 2048.0 total, 2048.0 free, 0.0 used. 3819.4 avail Mem
load average 해석:
0.52, 0.48, 0.45: 1분, 5분, 15분 평균 실행 큐 길이- CPU 4코어 서버에서 load average 4.0 = 100% 포화
- CPU 코어 수보다 지속적으로 높으면 응답 지연 발생
%Cpu 컬럼 해석:
| 항목 | 의미 | 주목 기준 |
|---|---|---|
us | 사용자 프로세스 CPU | 높으면 앱 레벨 부하 |
sy | 시스템(커널) CPU | 높으면 시스템 콜 과다 |
wa | I/O 대기 | 높으면(5%+) 디스크/네트워크 병목 |
id | 유휴(idle) | 낮으면 전체 CPU 부하 높음 |
top 인터랙티브 키:
q: 종료
k: 프로세스 종료 (PID 입력)
M: 메모리 사용률 기준 정렬
P: CPU 사용률 기준 정렬 (기본)
1: 코어별 CPU 사용률 표시
- ps aux 첫 컬럼은 USER — 서비스 계정(tomcat, nginx 등)으로 떠있으면 정상
- STAT 컬럼에서 Z(좀비) 프로세스가 있으면 부모 프로세스 문제 의심
- nproc 결과가 4인 서버에서 load average가 4.0 이상이면 CPU 포화 상태
- TIME 컬럼 값이 갑자기 큰 프로세스는 최근 CPU를 집중 소비한 것
포트 점검
ss와 lsof로 포트 확인
"서비스가 안 뜬다"는 제보를 받았는데 systemctl status는 정상이라고 합니다.
확인해보니 Tomcat이 8080이 아닌 18080으로 기동됐고, Nginx의 proxy_pass는 여전히 8080을 바라보고 있었습니다. 포트 번호가 맞지 않으면 서비스가 뜨더라도 트래픽이 전달되지 않습니다. 어떤 프로세스가 어느 포트를 점유하는지 1분 안에 파악하는 것이 연결 문제 진단의 핵심입니다.
netstat은 deprecated입니다. 현대 Linux에서는 ss를 사용합니다.
# 리스닝 중인 TCP 포트 전체 + 프로세스 정보
ss -tlnp
# State Recv-Q Send-Q Local Address:Port Peer Address:Port Process
# LISTEN 0 128 0.0.0.0:80 0.0.0.0:* users:(("nginx",pid=1234,fd=6))
# LISTEN 0 511 0.0.0.0:8080 0.0.0.0:* users:(("java",pid=5678,fd=32))
# LISTEN 0 128 127.0.0.1:3306 0.0.0.0:* users:(("mysqld",pid=9012,fd=25))
# UDP 포트 포함
ss -ulnp
# 특정 포트에 무엇이 연결됐는지
ss -tnp | grep ':8080'
# 특정 포트를 점유한 프로세스 확인
sudo lsof -i:8080
# COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
# java 5678 tomcat 123u IPv4 89012 0t0 TCP *:8080 (LISTEN)
# 특정 PID가 열고 있는 파일/소켓
sudo lsof -p 5678 | head -20
일반적인 포트 매핑:
| 포트 | 서비스 | 비고 |
|---|---|---|
| 80 | HTTP (Nginx/Apache) | |
| 443 | HTTPS | |
| 8080 | Tomcat HTTP | |
| 8443 | Tomcat HTTPS | |
| 3306 | MySQL/MariaDB | 127.0.0.1만 LISTEN이 정상 |
| 5432 | PostgreSQL | |
| 6379 | Redis | |
| 22 | SSH |
# 외부에서 DB 포트가 열려있는지 확인 (보안 점검)
ss -tlnp | grep ':3306'
# LISTEN 0 80 127.0.0.1:3306 0.0.0.0:* ← 안전 (127.0.0.1만 바인딩)
# LISTEN 0 80 0.0.0.0:3306 0.0.0.0:* ← 위험! 모든 IP에서 접근 가능
- ss -tlnp 결과에서 Local Address가 0.0.0.0이면 모든 IP에서 접근 가능
- 127.0.0.1로 바인딩된 포트는 로컬에서만 접근 가능 — DB 포트의 올바른 상태
- 예상 포트(8080 등)가 목록에 없으면 서비스가 다른 포트로 떴거나 죽은 것
- 같은 포트가 두 줄 이상 보이면 포트 충돌 — 서비스 기동 실패 원인
리소스 점검
df로 디스크 사용량 확인
자정에 로그 파일이 자동 삭제됐는데 디스크 사용률이 100%에서 내려오지 않았습니다. 삭제된 파일을 열고 있는 프로세스가 파일 핸들을 놓지 않고 있었기 때문입니다. 공간은 있는데 파일이 안 만들어지는 경우는 inode가 소진됐을 때입니다. df -h와 df -i를 항상 세트로 확인해야 이런 함정에서 벗어날 수 있습니다.
# 마운트된 파티션별 디스크 사용량
df -h
# Filesystem Size Used Avail Use% Mounted on
# /dev/sda1 50G 32G 16G 67% /
# /dev/sdb1 200G 45G 145G 24% /data
# tmpfs 3.9G 1.2M 3.9G 1% /dev/shm
# inode 사용량 (공간이 있어도 파일 못 만드는 상황 진단)
df -i
# Filesystem Inodes IUsed IFree IUse% Mounted on
# /dev/sda1 3276800 198432 3078368 7% /
# /var 655360 654990 370 100% /var ← inode 소진!
# 특정 디렉터리가 어느 파티션에 있는지 확인
df -h /opt/tomcat/logs
디스크 가득 찼을 때 원인 찾기:
# 각 디렉터리별 사용량 (1단계 깊이만)
du -sh /var/log/* | sort -rh | head -20
# 특정 디렉터리 하위 상세 확인
du -sh /var/log/nginx/*
# 가장 큰 파일 TOP 10 찾기 (전체 시스템)
sudo find / -xdev -type f -exec du -sh {} + 2>/dev/null | sort -rh | head -10
# /var 디렉터리 하위 큰 파일들
sudo du -ah /var | sort -rh | head -20
inode 소진 시 대응:
# inode 많이 쓰는 디렉터리 찾기
for dir in /var /tmp /opt; do
echo "$dir: $(find $dir -maxdepth 3 -type f 2>/dev/null | wc -l) files"
done
# 오래된 세션 파일, 임시 파일 정리 예시
find /var/lib/php/sessions -type f -mtime +7 -delete
find /tmp -type f -mtime +3 -delete
free로 메모리 상태 확인
Java 서비스가 갑자기 응답이 느려졌는데 CPU는 멀쩡했습니다. Swap 사용량을 보니 1GB가 사용 중이었고, JVM이 메모리 부족으로 디스크 swap을 쓰고 있었습니다. Swap은 디스크 속도로 동작하기 때문에 Java 애플리케이션은 swap이 0이어야 정상입니다. free -m을 보는 법을 모르면 이 상황을 메모리 문제로 진단하지 못합니다.
free -m
# total used free shared buff/cache available
# Mem: 7821 3621 2341 45 1858 3819
# Swap: 2047 0 2047
# 지속적으로 모니터링
watch -n 2 free -m
free 출력 해석 핵심:
-
available: 실제로 사용 가능한 메모리 (가장 중요한 지표)free + buff/cache 중 회수 가능한 것의 합available이 total의 10~15% 미만이면 메모리 부족 경고
-
buff/cache: OS 캐시 (필요 시 회수 가능) → 정상 현상- Linux는 남는 메모리를 캐시로 활용해 성능 향상
used가 높아 보여도buff/cache가 대부분이면 문제없음
-
Swap used> 0: 메모리 부족으로 디스크를 메모리처럼 사용 중- 응답 속도 급감의 원인, Java 애플리케이션은 특히 치명적
# Java 프로세스 실제 메모리 사용량 확인
ps aux --sort=-%mem | awk 'NR==1 || /java/'
# /proc으로 상세 확인
cat /proc/meminfo | grep -E 'MemTotal|MemFree|MemAvailable|SwapTotal|SwapFree|Cached'
journalctl로 서비스 로그 조회
서비스가 재시작됐는데 왜 재시작됐는지 알 수 없었습니다. catalina.out을 봤는데 로그가 너무 많아 어디서 오류가 났는지 찾기 힘들었습니다. journalctl로 시간 범위를 지정하면 장애 발생 시점의 로그만 추출할 수 있어 원인을 빠르게 좁힐 수 있습니다. 로그 파일을 전체 스크롤하는 것과 시간 기반으로 필터링하는 것의 차이는 30분 대 2분입니다.
# 특정 서비스 로그 (최근 100줄)
journalctl -u nginx -n 100 --no-pager
# 시간 범위 지정 조회
journalctl -u tomcat --since "2025-01-15 09:00:00" --until "2025-01-15 10:00:00"
journalctl -u tomcat --since "1 hour ago"
journalctl -u tomcat --since "30 min ago"
# 에러 레벨 이상만 조회
journalctl -u myapp -p err --since "today"
# 실시간 로그 팔로우
journalctl -u nginx -f
# 부팅 이후 전체 로그
journalctl -b
# 커널 메시지 (OOM killer 등 확인)
journalctl -k --since "today" | grep -E 'oom|killed|error' -i
OOM(Out of Memory) 발생 확인:
# OOM Killer가 프로세스를 죽였는지 확인
journalctl -k --since "today" | grep -i "oom\|killed process\|out of memory"
# 또는
dmesg | grep -i "oom\|killed"
- df -h에서 Use%가 85% 이상인 파티션은 즉시 원인 파악 필요
- df -i에서 IUse%가 90% 이상이면 파일 생성 불가 위험 — 작은 파일 대량 생성 서비스 점검
- free -m에서 Swap used가 0 이상이면 메모리 부족 상태 — Java 서비스 성능 저하 원인
- available이 total의 10% 미만이면 메모리 부족 경보
- uptime 명령어에서 load average 3개 값(1/5/15분)이 CPU 코어 수 이하면 정상
- ss -tlnp에서 서비스에 해당하는 포트(예: 8080, 3306)가 LISTEN 상태로 보인다
- free -m의 available 값이 전체 메모리의 20% 이상이면 여유 있는 상태
- df -i 실행 시 IUse%가 90% 미만이면 inode 고갈 위험 없음
- systemctl list-units --state=failed 결과가 비어있으면 전체 서비스 정상
실제 업무에서 이 지식이 쓰이는 상황:
이 모듈의 명령어들은 장애 대응 시 가장 먼저 실행하는 것들입니다.
온콜(On-call) 대응 시 첫 3분:
# 1. 부하 확인
uptime; top -bn1 | head -15
# 2. 문제 서비스 상태
systemctl status tomcat nginx
# 3. 포트 확인 (서비스가 떠있는지)
ss -tlnp | grep ':8080'
# 4. 최근 에러 로그
journalctl -u tomcat --since "5 min ago" --no-pager
# 5. 디스크/메모리
df -h; free -m
이 다섯 단계로 대부분의 원인을 3분 안에 좁힐 수 있습니다. "뭔지 모르겠어요"가 아니라 "CPU 문제입니다" "디스크 꽉 찼습니다" "프로세스가 죽었습니다"로 빠르게 전달할 수 있어야 합니다.
다음 모듈에서는 이런 점검 과정을 자동화하는 Shell 스크립팅을 다룹니다.