리눅스 필수 명령어
서버 개발자에게 리눅스 명령어는 도구 상자입니다. 서버 장애가 발생했을 때, 성능 문제를 추적할 때, 배포 스크립트를 작성할 때 — 이 명령어들을 자유자재로 사용할 수 있어야 합니다. 이 절에서는 실무에서 가장 자주 사용하는 명령어들을 범주별로 정리합니다.
프로세스 관리
# 현재 실행 중인 프로세스 목록
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 | 프로세스 소유자 | root, www-data |
| %CPU | CPU 사용률 | 95.3 |
| %MEM | 메모리 사용률 | 12.4 |
| VSZ | 가상 메모리 크기 (KB) | 주소 공간 전체 |
| RSS | 실제 물리 메모리 (KB) | 실제 사용량 |
| STAT | 상태 | S(sleep), R(run), Z(zombie), D(disk wait) |
| TIME+ | 누적 CPU 시간 | 03:42:15 |
# 시그널 전송
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)을 사용합니다. SIGKILL은 프로세스에게 정리할 기회를 주지 않으므로 파일이 손상되거나 임시 자원이 남을 수 있습니다.
| 시그널 | 번호 | 기본 동작 | 잡을 수 있나 | 용도 |
|---|---|---|---|---|
| SIGHUP | 1 | 종료 | O | 데몬 설정 리로드 |
| SIGINT | 2 | 종료 | O | Ctrl+C |
| SIGKILL | 9 | 강제 종료 | X | 최후의 수단 |
| SIGTERM | 15 | 종료 | O | 정상 종료 요청 |
| SIGSTOP | 19 | 일시 정지 | X | 디버깅 |
| SIGCONT | 18 | 재개 | O | 정지된 프로세스 재개 |
백그라운드 작업
# 백그라운드 실행
./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
# total used free shared buff/cache available
# Mem: 16Gi 8.2Gi 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는 OS가 I/O 성능을 위해 사용 중이지만, 필요하면 반환되는 메모리입니다. used가 높아도 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)}GB")
print(f"사용 가능: {available // (1024**3)}GB")
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()디스크와 파일
# 파일 시스템 사용량
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은 매우 유용합니다. 파일을 삭제했는데 공간이 회복되지 않는다면, 프로세스가 여전히 삭제된 파일의 파일 디스크립터를 열고 있는 것입니다. 해당 프로세스를 재시작하면 공간이 회복됩니다.
# 프로세스별 I/O 사용량
iotop
# 디스크별 I/O 통계 (2초 간격)
iostat -xz 2
# 출력 핵심 열:
# %util: 디스크 사용률 (100%에 가까우면 병목)
# await: 평균 I/O 대기 시간 (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로 프로세스가 비정상 종료되면 5초 후 자동 재시작합니다. LimitNOFILE로 파일 디스크립터 제한을 설정합니다.
다음 절에서는 시스템 콜 추적과 로그 분석 등 심층 디버깅 기법을 다루겠습니다.