본문으로 건너뛰기
안동민 개발노트 아이콘

안동민 개발노트

본문 시작
14장 : 실무에서 만나는 OS 개념

리눅스 필수 명령어

프로세스·메모리·디스크·텍스트·네트워크·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프로세스 소유자root, www-data
%CPUCPU 사용률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은 프로세스에게 정리할 기회를 주지 않으므로 파일이 손상되거나 임시 자원이 남을 수 있습니다.

시그널번호기본 동작잡을 수 있나용도
SIGHUP1종료O데몬 설정 리로드
SIGINT2종료OCtrl+C
SIGKILL9강제 종료X최후의 수단
SIGTERM15종료O정상 종료 요청
SIGSTOP19일시 정지X디버깅
SIGCONT18재개O정지된 프로세스 재개

백그라운드 작업

job_control.sh
# 백그라운드 실행
./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 worker

nohup은 SIGHUP을 무시하여 터미널이 닫혀도 프로세스가 살아남습니다.

하지만 실무에서는 tmuxscreen을 사용하는 것이 더 편리합니다.


메모리 확인

메모리 상태
# 시스템 메모리 요약
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 -m

free -h에서 available 열이 실제로 프로세스가 사용할 수 있는 메모리입니다.

buff/cache는 OS가 I/O 성능을 위해 사용 중이지만, 필요하면 반환되는 메모리입니다.

used가 높아도 available이 충분하면 메모리 부족이 아닙니다.

memory_check.py
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 모니터링
# 프로세스별 I/O 사용량
iotop

# 디스크별 I/O 통계 (2초 간격)
iostat -xz 2

# 출력 핵심 열:
# %util: 디스크 사용률 (100%에 가까우면 병목)
# await: 평균 I/O 대기 시간 (ms)
# r/s, w/s: 초당 읽기/쓰기 요청 수

텍스트 처리

서버 로그 분석에 필수인 텍스트 처리 명령어입니다.

text_processing.sh
# 패턴 검색
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.com

ss -s로 TIME_WAIT 소켓 수를 확인합니다.

TIME_WAIT이 수만 개라면 짧은 연결이 과도하게 생성되고 있다는 뜻이며, Connection Pool이나 Keep-Alive를 검토해야 합니다.

방화벽과 포트 관리

firewall.sh
# 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 --reload

systemd 서비스 관리

systemd.sh
# 서비스 상태 확인
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
/etc/systemd/system/myapp.service
[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.target

Restart=on-failure로 프로세스가 비정상 종료되면 5초 후 자동 재시작합니다.

LimitNOFILE로 파일 디스크립터 제한을 설정합니다.

다음 절에서는 시스템 콜 추적과 로그 분석 등 심층 디버깅 기법을 다루겠습니다.

리눅스 필수 명령어에서 먼저 이해할 개념, 따라가야 할 순서, 마지막 검증 질문을 한 화면에 묶었습니다.

다음 학습으로 넘어가기 전, 현재 실행 중인 프로세스 목록에서 남은 개념 경계와 실습 확인 포인트를 점검합니다.