리눅스 필수 명령어
프로세스·메모리·디스크·텍스트·네트워크·systemd를 다루는 Linux 명령을 서버 운영 상황별로 익힙니다.
서버 개발자에게 리눅스 명령어는 도구 상자입니다.
서버 장애가 발생했을 때, 성능 문제를 추적할 때, 배포 스크립트를 작성할 때 — 이 명령어들을 자유자재로 사용할 수 있어야 합니다.
이 절에서는 실무에서 가장 자주 사용하는 명령어들을 범주별로 정리합니다.
프로세스 관리
# 현재 실행 중인 프로세스 목록
ps aux
# CPU/메모리 사용량 기준 상위 프로세스
ps aux --sort=-%cpu | head -10
ps aux --sort=-%mem | head -10
# 프로세스 트리 (부모-자식 관계)
ps auxf
pstree -p 1234
# 실시간 모니터링
top
htop # 더 직관적인 인터페이스ps aux의 출력에서 각 열의 의미를 알아두면 좋습니다.
비율의 분모와 메모리 단위를 구별합니다.
| 열 | 읽는 값 | 해석 범위 |
|---|---|---|
| USER | 프로세스 소유 사용자 | 실행 권한의 일부 정보이며 모든 권한을 요약하지는 않음 |
| %CPU | 누적 CPU 시간 ÷ 실행 경과 시간 | ps의 생애 평균 비율. top의 최근 구간 값과 구분 |
| %MEM | RSS ÷ 물리 메모리 | 프로세스의 상주 메모리 비율 |
| VSZ | 가상 메모리 크기 · KiB | 예약·매핑된 주소 공간이며 RAM 사용량과 다름 |
| RSS | 상주 메모리 크기 · KiB | 공유 페이지도 포함하므로 프로세스별 합계는 중복될 수 있음 |
| STAT | 실행·대기 등의 상태와 부가 플래그 | R: 실행 가능, S: interruptible sleep, D: uninterruptible sleep, Z: 좀비 |
| TIME | 누적 CPU 시간 | ps aux의 열 이름. 경과 시간이나 top의 TIME+와 혼동하지 않음 |
- USER
- 읽는 값: 프로세스 소유 사용자해석 범위: 실행 권한의 일부 정보이며 모든 권한을 요약하지는 않음
- %CPU
- 읽는 값: 누적 CPU 시간 ÷ 실행 경과 시간해석 범위: ps의 생애 평균 비율. top의 최근 구간 값과 구분
- %MEM
- 읽는 값: RSS ÷ 물리 메모리해석 범위: 프로세스의 상주 메모리 비율
- VSZ
- 읽는 값: 가상 메모리 크기 · KiB해석 범위: 예약·매핑된 주소 공간이며 RAM 사용량과 다름
- RSS
- 읽는 값: 상주 메모리 크기 · KiB해석 범위: 공유 페이지도 포함하므로 프로세스별 합계는 중복될 수 있음
- STAT
- 읽는 값: 실행·대기 등의 상태와 부가 플래그해석 범위: R: 실행 가능, S: interruptible sleep, D: uninterruptible sleep, Z: 좀비
- TIME
- 읽는 값: 누적 CPU 시간해석 범위: ps aux의 열 이름. 경과 시간이나 top의 TIME+와 혼동하지 않음
PID·PPID도 함께 볼 때는 ps -o pid,ppid,state,pcpu,pmem,comm을 사용할 수 있습니다. 한 번의 목록은 원인을 확정하는 증거가 아니라 대상을 고르는 단서입니다.
# 시그널 전송
kill -15 1234 # SIGTERM: 정상 종료 요청
kill -9 1234 # SIGKILL: 강제 종료 (최후 수단)
kill -1 1234 # SIGHUP: 리로드 처리기를 구현한 데몬에서만 설정 다시 읽기
kill -USR1 1234 # SIGUSR1: 애플리케이션 정의 시그널
# 이름으로 프로세스 종료
pkill -f "python.*worker"
killall nginx
# 프로세스 우선순위 변경
nice -n 10 ./heavy_job # 낮은 우선순위로 실행
renice -n -5 -p 1234 # 실행 중 프로세스 우선순위 변경SIGTERM(15)을 먼저 보내고, 응답이 없을 때만 SIGKILL(9)을 사용합니다.
애플리케이션이 중간까지 기록한 파일이나 임시 자원의 상태도 종료 후 확인해야 합니다.
정리 가능한 요청과 잡을 수 없는 제어를 구별합니다.
| 시그널 | 기본 동작 | 처리기와 용도 |
|---|---|---|
| SIGHUP | 종료 | 처리기 등록 가능. 리로드는 앱이 그렇게 구현한 경우 |
| SIGINT | 종료 | 처리기 등록 가능. 터미널의 Ctrl+C로 흔히 전달 |
| SIGTERM | 종료 | 처리기 등록 가능. 정상 정리 후 종료를 요청할 때 사용 |
| SIGKILL | 종료 | 잡거나 무시할 수 없음. 사용자 정리 코드는 실행되지 않음 |
| SIGSTOP | 정지 | 잡거나 무시할 수 없음 |
| SIGCONT | 정지 상태에서 재개 | 처리기 등록 가능. 재개 동작과 핸들러 처리를 구분 |
- SIGHUP
- 기본 동작: 종료처리기와 용도: 처리기 등록 가능. 리로드는 앱이 그렇게 구현한 경우
- SIGINT
- 기본 동작: 종료처리기와 용도: 처리기 등록 가능. 터미널의 Ctrl+C로 흔히 전달
- SIGTERM
- 기본 동작: 종료처리기와 용도: 처리기 등록 가능. 정상 정리 후 종료를 요청할 때 사용
- SIGKILL
- 기본 동작: 종료처리기와 용도: 잡거나 무시할 수 없음. 사용자 정리 코드는 실행되지 않음
- SIGSTOP
- 기본 동작: 정지처리기와 용도: 잡거나 무시할 수 없음
- SIGCONT
- 기본 동작: 정지 상태에서 재개처리기와 용도: 처리기 등록 가능. 재개 동작과 핸들러 처리를 구분
시그널 번호는 아키텍처에 따라 달라질 수 있으므로 이름을 우선 사용합니다. 전송 성공이 정리 완료나 프로세스 종료 완료를 뜻하지는 않습니다.
백그라운드 작업
# 백그라운드 실행
./long_task.sh &
# 현재 백그라운드 작업 목록
jobs -l
# 포그라운드로 전환
fg %1
# 실행 중인 작업을 백그라운드로
# Ctrl+Z로 일시 정지 후
bg %1
# 터미널 종료 후에도 실행 유지
nohup ./server.sh > output.log 2>&1 &
# 또는 screen/tmux 사용 (권장)
tmux new -s worker
# ... 작업 실행 ...
# Ctrl+B, D 로 detach
tmux attach -t workernohup은 SIGHUP을 무시하도록 실행합니다. 다른 종료 신호나 세션 관리자의 정리 정책까지 막는 것은 아닙니다.
하지만 실무에서는 tmux나 screen을 사용하는 것이 더 편리합니다.
메모리 확인
# 시스템 메모리 요약
free -h
# 예시 값: 현재 procps-ng의 used = total - available
# total used free shared buff/cache available
# Mem: 16Gi 8.7Gi 1.1Gi 256Mi 6.7Gi 7.3Gi
# Swap: 4Gi 0.5Gi 3.5Gi
# 상세 메모리 정보
cat /proc/meminfo | grep -E "MemTotal|MemFree|MemAvailable|Buffers|Cached|SwapTotal|SwapFree"
# 가상 메모리 통계 (2초 간격)
vmstat 2
# 특정 프로세스의 메모리 맵
pmap -x 1234
# 공유 메모리 확인
ipcs -mfree -h의 available은 스왑 없이 새 작업에 쓸 수 있는 메모리의 추정치입니다. buff/cache 전체가 즉시 회수되는 것은 아닙니다. 단일 사용량 대신 available의 변화, 회수·스왑 활동과 애플리케이션 지연을 함께 봅니다.
import subprocess
import re
def check_memory():
"""시스템 메모리 상태를 확인하고 경고"""
result = subprocess.run(["free", "-b"], capture_output=True, text=True)
lines = result.stdout.strip().split("\n")
mem = lines[1].split()
total = int(mem[1])
available = int(mem[6])
usage_pct = (1 - available / total) * 100
print(f"전체: {total // (1024**3)}GiB")
print(f"사용 가능: {available // (1024**3)}GiB")
print(f"사용률: {usage_pct:.1f}%")
if usage_pct > 90:
print("[위험] 메모리 부족!")
# 메모리 많이 쓰는 프로세스 상위 5개
ps = subprocess.run(
["ps", "aux", "--sort=-%mem"],
capture_output=True, text=True
)
for line in ps.stdout.split("\n")[1:6]:
print(f" {line}")
elif usage_pct > 70:
print("[주의] 메모리 사용률이 높습니다.")
check_memory()위 파서는 procps-ng의 해당 열 순서와 명령 성공을 전제합니다. 70%·90% 경고는 이 예제의 기준이며 시스템 전체의 장애 판정 기준은 아닙니다.
디스크와 파일
# 파일 시스템 사용량
df -h
# inode 사용량 (파일 수 제한)
df -i
# 디렉토리 크기 (깊이 1)
du -h --max-depth=1 /var | sort -rh | head -10
# 큰 파일 찾기
find / -type f -size +100M -exec ls -lh {} \; 2>/dev/null
# 최근 24시간 내 수정된 파일
find /var/log -type f -mtime -1
# 파일을 열고 있는 프로세스 확인
lsof /var/log/syslog
# 삭제되었지만 프로세스가 잡고 있는 파일 찾기 (공간 미회복)
lsof +L1디스크가 가득 찬 상황에서 lsof +L1은 매우 유용합니다.
삭제된 파일을 프로세스가 계속 열어 둔 것은 공간이 회복되지 않는 원인 중 하나입니다. lsof -p PID로 대상 프로세스의 열린 파일을 좁혀 보고, 해당 파일을 유지하는 마지막 참조가 해제되는지 확인합니다.
# 프로세스별 I/O 사용량
iotop
# 디스크별 I/O 통계 (2초 간격)
iostat -xz 2
# 출력 핵심 열:
# %util: I/O가 진행된 시간 비율 (병렬 SSD/RAID의 포화 지표와는 다름)
# await: 요청의 큐 대기와 처리 시간을 합한 평균 (ms)
# r/s, w/s: 초당 읽기/쓰기 요청 수텍스트 처리
서버 로그 분석에 필수인 텍스트 처리 명령어입니다.
# 패턴 검색
grep -rn "ERROR" /var/log/app/ # 재귀, 줄 번호 포함
grep -c "404" access.log # 404 문자열이 포함된 줄 수
grep -v "healthcheck" access.log # healthcheck 제외
# 실시간 로그 모니터링
tail -f /var/log/app/error.log
tail -f /var/log/app/error.log | grep --line-buffered "CRITICAL"
# 정렬과 중복 제거
sort access.log | uniq -c | sort -rn | head -20
# 필드 추출 (awk)
# 접속 IP별 요청 수 (Apache/Nginx 로그)
awk '{print $1}' access.log | sort | uniq -c | sort -rn | head -10
# 특정 시간대의 요청 수
awk '/15:3[0-9]:/' access.log | wc -l
# 문자열 치환 (sed)
sed -i 's/old_domain/new_domain/g' config.conf
# JSON 처리 (jq)
cat response.json | jq '.data[] | {id, name, status}'집계 결과에서 눈에 띄는 값이 나오면 원본 로그 줄로 돌아가 필드와 문맥을 확인합니다. 반복하는 분석 파이프라인은 스크립트로 남겨 같은 조건으로 비교합니다.
네트워크
# 열린 포트와 연결 상태
ss -tlnp # TCP 리스닝 포트
ss -tanp # 모든 TCP 연결
ss -s # 연결 상태 요약 (established, time-wait 수)
# 특정 포트에 연결된 프로세스
ss -tlnp | grep :8080
# 네트워크 인터페이스 정보
ip addr show
ip route show # 라우팅 테이블
# 연결 테스트
ping -c 4 10.0.0.1
traceroute 10.0.0.1
mtr 10.0.0.1 # ping + traceroute 합체
# DNS 조회
dig example.com
nslookup example.com
# 특정 포트 연결 테스트
nc -zv 10.0.0.1 3306 # MySQL 포트 열려있는지
# 패킷 캡처 (tcpdump)
tcpdump -i eth0 port 80 -c 100
tcpdump -i any host 10.0.0.1 -w capture.pcap
# HTTP 요청 테스트
curl -v https://api.example.com/health
curl -o /dev/null -s -w "%{http_code} %{time_total}s\n" https://api.example.comss -s로 TIME_WAIT 소켓 수를 확인합니다.
TIME_WAIT 수가 많으면 연결 생성률·포트 사용량·실패율을 함께 확인합니다. 수만으로 과도함을 판정하지 않으며, 짧은 연결이 실제 병목이라면 Connection Pool이나 Keep-Alive를 검토합니다.
방화벽과 포트 관리
# iptables 규칙 확인
iptables -L -n -v
# ufw (Ubuntu 방화벽)
ufw status
ufw allow 22/tcp
ufw allow from 10.0.0.0/24 to any port 3306
# firewalld (CentOS/RHEL)
firewall-cmd --list-all
firewall-cmd --add-port=8080/tcp --permanent
firewall-cmd --reloadsystemd 서비스 관리
# 서비스 상태 확인
systemctl status nginx
# 서비스 시작/중지/재시작
systemctl start nginx
systemctl stop nginx
systemctl restart nginx
systemctl reload nginx # 서비스가 제공하는 설정 리로드 동작 요청
# 부팅 시 자동 시작
systemctl enable nginx
systemctl disable nginx
# 서비스 로그 확인
journalctl -u nginx -f
journalctl -u nginx --since "1 hour ago"
# 실패한 서비스 확인
systemctl --failed[Unit]
Description=My Application
After=network.target
[Service]
Type=simple
User=appuser
WorkingDirectory=/opt/myapp
ExecStart=/opt/myapp/bin/server
Restart=on-failure
RestartSec=5
LimitNOFILE=65535
[Install]
WantedBy=multi-user.targetRestart=on-failure는 해당 실패 조건에서 재시작을 시도하며 RestartSec=5는 그 대기 시간을 정합니다. 명시적인 stop이나 시작 횟수 제한 등은 별도로 적용됩니다. After=network.target은 순서만 정하며 네트워크 연결 완료를 보장하지 않습니다. 예제의 사용자와 실행 경로도 미리 존재해야 합니다.
LimitNOFILE로 파일 디스크립터 제한을 설정합니다.
다음 절에서는 시스템 콜 추적과 로그 분석 등 심층 디버깅 기법을 다루겠습니다.