infra
Platform

모듈 맵

[Linux] 리눅스 파일시스템과 디렉토리 구조

0 / 20 완료

펼치기

Linux-master · 01 / 20

[Linux] 리눅스 파일시스템과 디렉토리 구조

FHS 표준 디렉토리 역할, inode 구조, 하드링크/심볼릭링크를 이해하고 디스크 사용량을 분석합니다

리눅스 파일시스템과 디렉토리 구조

🚨INCIDENT ALERT
HIGH

배포 파이프라인이 "No space left on device"로 멈췄습니다. 그런데 df -h 를 보면 디스크는 60%밖에 안 찼습니다. 용량은 남는데 왜 파일을 못 만들까요?

df -i 를 쳐보니 inode 사용률이 100%였습니다. 작은 캐시 파일 수백만 개가 inode를 모두 소진한 것이었습니다. 용량(블록)과 inode는 별개의 자원이라는 걸 모르면 이 장애 앞에서 한참을 헤맵니다.

파일시스템은 "용량이 얼마 남았나"보다 훨씬 깊은 구조를 가집니다. FHS 디렉토리 배치, inode와 링크, /etc/fstab 마운트까지 — 장애가 났을 때 어디를 봐야 하는지를 이 모듈에서 손으로 확인합니다.

리눅스 시스템에서 "모든 것은 파일이다(Everything is a file)"라는 철학은 단순한 슬로건이 아닙니다. 디바이스 드라이버, 프로세스 정보, 네트워크 소켓까지 파일 인터페이스로 접근합니다. 파일시스템 구조를 이해하면 장애 대응 속도가 획기적으로 빨라집니다.


이번 챕터에서 배울 것

리눅스마스터 1급 시험의 첫 번째 핵심 도메인입니다. FHS 디렉토리 구조, inode 개념, 링크 차이는 매회 출제됩니다. 이론을 이해하고 직접 실습하여 디스크 공간 문제를 스스로 진단하는 능력을 기릅니다.

  • 1FHS(Filesystem Hierarchy Standard) — /bin, /sbin, /usr, /etc, /var, /proc, /sys 등 주요 디렉토리 역할
  • 2inode 구조 — 파일 메타데이터 저장 방식, 파일명이 inode에 없는 이유, 링크 카운트
  • 3하드링크와 심볼릭링크 — ln / ln -s 명령어, 동작 차이, 원본 삭제 후 접근 가능 여부
  • 4df / du / stat / find 명령어 — 디스크 사용량 분석과 대용량 파일 탐색
  • 5/etc/fstab 6개 필드 구조 — dump/pass 필드 의미, UUID 사용 이유
실습 환경 준비

이 챕터는 별도 소프트웨어 설치 없이 기본 리눅스 명령어만으로 진행합니다. /tmp 공간이 최소 100MB 이상 남아 있는지 확인하세요. 사용자 계정으로 진행하며 일부 명령어는 sudo가 필요합니다.

CentOS Stream 9 또는 Rocky Linux 9 환경 권장

Ubuntu 22.04 LTS도 동일하게 실습 가능

현재 마운트된 파일시스템과 여유 공간 확인
df -hT
루트 디렉토리 구조 확인
ls -la /
실습 디렉토리 생성
mkdir -p /tmp/fs-lab && echo '준비 완료'
💡개념

FHS 표준 디렉토리 구조

Filesystem Hierarchy Standard란?

FHS(Filesystem Hierarchy Standard)는 리눅스/유닉스 시스템에서 디렉토리와 파일의 위치를 표준화한 규격입니다. 배포판마다 경로가 달라지면 스크립트와 소프트웨어의 이식성이 깨집니다. FHS 덕분에 CentOS에서 작성한 관리 스크립트가 Ubuntu에서도 동일하게 동작합니다.

/
├── bin        → 모든 사용자용 필수 명령어 바이너리 (ls, cp, mv, cat, bash...)
├── sbin       → 시스템 관리자용 필수 바이너리 (fdisk, fsck, ip, ifconfig...)
├── usr/
│   ├── bin    → 추가 사용자 명령어 (gcc, git, vim, python3...)
│   ├── sbin   → 추가 시스템 관리 명령어 (useradd, groupadd...)
│   ├── lib    → /usr/bin, /usr/sbin 공유 라이브러리
│   └── local/ → 관리자가 직접 빌드한 소프트웨어 (make install 기본 경로)
│       ├── bin/
│       └── lib/
├── etc        → 시스템 전역 설정 파일 (/etc/passwd, /etc/nginx/nginx.conf...)
├── var/
│   ├── log    → 로그 파일 (/var/log/messages, /var/log/secure...)
│   ├── spool  → 메일, 프린터 큐 (/var/spool/mail...)
│   ├── lib    → 서비스 상태 데이터 (/var/lib/mysql...)
│   └── run    → 런타임 PID 파일 (/var/run/nginx.pid...)
├── tmp        → 임시 파일 (재부팅 시 삭제, 누구나 쓰기 가능, sticky bit 설정)
├── home       → 일반 사용자 홈 디렉토리 (/home/alice, /home/bob)
├── root       → root 사용자 홈 디렉토리 (루트 파티션 / 가 아님!)
├── opt        → 벤더 제공 독립 패키지 (/opt/oracle, /opt/splunk, /opt/jdk-21)
├── mnt        → 수동 임시 마운트 포인트 (관리자가 직접 마운트)
├── media      → 자동 마운트 이동식 미디어 (USB, CD-ROM)
├── dev        → 디바이스 파일 (/dev/sda, /dev/null, /dev/random, /dev/tty...)
├── proc       → 프로세스/커널 정보 가상 파일시스템 (메모리에만 존재, 디스크 0)
├── sys        → 커널 서브시스템/하드웨어 정보 가상 파일시스템 (sysfs)
├── boot       → 부트로더, 커널 이미지 (vmlinuz-x.y.z, initramfs...)
├── lib        → /bin, /sbin 실행에 필요한 공유 라이브러리 (.so 파일)
└── srv        → 서비스 데이터 (FTP 루트, HTTP 루트 등)

핵심 디렉토리 역할 비교

디렉토리특성대표 파일/용도
/bin싱글유저 모드에서도 필요한 필수 명령어ls, cp, bash, cat
/sbin시스템 부팅/복구에 필요한 관리 명령어fdisk, mkfs, fsck
/usr/bin멀티유저 환경 일반 명령어gcc, vim, git, python3
/usr/local/bin관리자가 직접 컴파일/설치한 소프트웨어커스텀 빌드 프로그램
/opt독립 패키지 전체 (자체 bin/lib 포함)/opt/jdk-21/bin/java
/etc정적 설정 파일 (실행 파일 없음)passwd, fstab, sshd_config
/var가변 데이터 (로그, 캐시, 데이터베이스)/var/log/messages
/tmp임시 파일, 재부팅 시 삭제 보장빌드 임시 파일, 업로드 버퍼
/proc가상 파일시스템, 디스크 미사용/proc/cpuinfo, /proc/[PID]/
/sys가상 파일시스템, 커널 오브젝트 노출/sys/class/net/eth0/

/proc와 /sys 가상 파일시스템

로컬 터미널
# /proc는 디스크 공간을 전혀 사용하지 않음
df -h /proc
# Filesystem      Size  Used Avail Use% Mounted on
# proc               0     0     0    - /proc

# 커널 정보 실시간 조회
cat /proc/cpuinfo | grep "model name" | head -1
# model name : Intel(R) Xeon(R) CPU E5-2676 v3 @ 2.40GHz

cat /proc/meminfo | head -3
# MemTotal:       8169748 kB
# MemFree:        2034000 kB
# MemAvailable:   5832000 kB

cat /proc/version
# Linux version 5.14.0 (gcc 11.4.1) #1 SMP PREEMPT

# /proc/sys는 커널 파라미터 읽기/쓰기 가능
cat /proc/sys/net/ipv4/ip_forward
# 0
echo 1 | sudo tee /proc/sys/net/ipv4/ip_forward  # 런타임 변경
# 또는: sudo sysctl -w net.ipv4.ip_forward=1

# /proc/[PID] - 개별 프로세스 정보
ls /proc/1/
# cmdline  cwd  environ  exe  fd  maps  mem  mounts  net  status
cat /proc/1/cmdline | tr '\0' ' '
# /usr/lib/systemd/systemd --switched-root --system

/bin과 /usr/bin: 현대 리눅스의 변화

로컬 터미널
# 전통적 FHS: /bin은 싱글유저 모드 전용, /usr/bin은 멀티유저
# 현대 배포판(CentOS 7+, Ubuntu 20.04+): usrmerge로 통합
ls -la /bin
# lrwxrwxrwx 1 root root 7 /bin -> usr/bin   ← 심볼릭링크!

# 시험에서는 전통적 FHS 기준으로 답안 선택
# /bin: 부팅 필수 명령어
# /usr/bin: 일반 사용자 명령어
# /usr/local/bin: 직접 설치한 소프트웨어

💡개념

inode 구조와 파일 저장 메커니즘

inode란 무엇인가?

파일시스템은 크게 두 영역으로 구성됩니다: inode 영역(메타데이터)과 데이터 블록 영역(실제 내용)입니다.

파일시스템 전체 구조
┌──────────────────────────────────────────────────────┐
│  슈퍼블록: 파일시스템 전체 메타데이터 (블록 크기, 총 inode 수...) │
├──────────────────────────────────────────────────────┤
│  inode 비트맵: 어떤 inode 번호가 사용 중인지 추적         │
├──────────────────────────────────────────────────────┤
│  데이터 비트맵: 어떤 데이터 블록이 사용 중인지 추적         │
├──────────────────────────────────────────────────────┤
│  inode 테이블: inode 구조체 배열                        │
│  [inode#1234][inode#1235][inode#1236]...             │
├──────────────────────────────────────────────────────┤
│  데이터 블록: 실제 파일 내용                             │
└──────────────────────────────────────────────────────┘

inode 구조 상세

inode #1234
┌──────────────────────────────────┐
│ 파일 타입 + 접근 권한 (mode)       │  -rw-r--r-- = 0644
│ 링크 카운트 (link count)          │  하드링크 수
│ 소유자 UID                        │
│ 소유 그룹 GID                     │
│ 파일 크기 (bytes)                 │
│ atime: 마지막 접근(read) 시간      │
│ mtime: 마지막 내용 수정 시간       │
│ ctime: 마지막 메타데이터 변경 시간  │  chmod, chown, rename 포함
│ 직접 블록 포인터 × 12             │
│ 단일 간접 블록 포인터              │
│ 이중 간접 블록 포인터              │
│ 삼중 간접 블록 포인터              │
└──────────────────────────────────┘
※ 파일명은 inode에 없음 → 디렉토리 엔트리에 저장

inode에 저장되는 정보 vs 저장되지 않는 정보

inode에 저장됨inode에 저장되지 않음
파일 타입 (일반/디렉토리/링크/소켓...)파일명
접근 권한 (chmod 값)파일 내용 (데이터 블록에 저장)
소유자 UID / 그룹 GID파일의 경로 (전체 경로)
파일 크기 (bytes)
하드링크 카운트
atime / mtime / ctime
데이터 블록 포인터

핵심 포인트: 파일명은 디렉토리 파일(엔트리)에 저장됩니다. 디렉토리는 내부적으로 "파일명 → inode 번호" 매핑 테이블입니다.

디렉토리 내부 구조

/tmp/fs-lab/ 디렉토리 파일의 실제 내용
┌─────────────────┬───────────┐
│   파일명         │ inode 번호 │
├─────────────────┼───────────┤
│  .  (현재)       │  262145   │
│  .. (부모)       │  131073   │
│  original.txt   │  262146   │
│  hardlink.txt   │  262146   │  ← 동일한 inode! (하드링크)
│  symlink.txt    │  262147   │  ← 별도 inode (심볼릭링크)
└─────────────────┴───────────┘

inode 번호와 상세 정보 확인

로컬 터미널
# -i 옵션으로 inode 번호 표시
ls -li /etc/passwd
# 131073 -rw-r--r-- 1 root root 2847 Mar 15 09:00 /etc/passwd
# └─inode번호  └링크수

# stat 명령으로 inode 전체 정보 확인
stat /etc/passwd
# File: /etc/passwd
# Size: 2847       Blocks: 8          IO Block: 4096   regular file
# Device: fd00h    Inode: 131073      Links: 1
# Access: (0644/-rw-r--r--)  Uid: (0/root)  Gid: (0/root)
# Access: 2026-03-28 09:00:00.000000000 +0900   ← atime
# Modify: 2026-03-15 09:00:00.000000000 +0900   ← mtime (내용 변경)
# Change: 2026-03-15 09:00:00.000000000 +0900   ← ctime (메타데이터 변경)

# file 명령으로 실제 파일 타입 확인 (확장자와 무관)
file /bin/ls /etc/passwd /dev/sda /usr/lib/libc.so.6
# /bin/ls:              ELF 64-bit LSB pie executable, x86-64
# /etc/passwd:          ASCII text
# /dev/sda:             block special (8/0)
# /usr/lib/libc.so.6:   ELF 64-bit LSB shared object

하드링크(Hard Link)

하드링크는 기존 inode를 가리키는 새로운 디렉토리 엔트리입니다. 원본과 동일한 inode 번호를 공유합니다.

로컬 터미널
# 하드링크 생성 (ln 명령, -s 옵션 없음)
echo "중요한 데이터" > original.txt
ln original.txt hardlink.txt

# inode 번호가 동일한지 확인
ls -li original.txt hardlink.txt
# 268451 -rw-r--r-- 2 user user 15 Mar 28 10:00 hardlink.txt
# 268451 -rw-r--r-- 2 user user 15 Mar 28 10:00 original.txt
# └─동일한 inode!         └─링크 카운트 = 2로 증가

# 원본 삭제 → 링크 카운트 1 감소, 데이터 블록은 유지
rm original.txt
cat hardlink.txt
# 중요한 데이터  ← 여전히 읽기 가능!

ls -li hardlink.txt
# 268451 -rw-r--r-- 1 user user 15 Mar 28 10:00 hardlink.txt
#                   └─ 링크 카운트 = 1로 감소 (0이 되면 실제 삭제)

하드링크 제약:

  • 다른 파일시스템(파티션) 간에는 생성 불가
  • 디렉토리에는 생성 불가 (순환 참조 방지)

심볼릭링크(Symbolic Link / Soft Link)

심볼릭링크는 대상 경로 문자열을 저장하는 독립적인 파일입니다. 별도의 inode를 가집니다.

로컬 터미널
# 심볼릭링크 생성 (-s 옵션 필수)
echo "대상 파일" > target.txt
ln -s target.txt symlink.txt              # 상대경로 심볼릭링크
ln -s /etc/hostname /tmp/hostname_link    # 절대경로 심볼릭링크

# inode 번호가 다름을 확인
ls -li target.txt symlink.txt
# 268451 -rw-r--r-- 1 user user 10 Mar 28 10:00 target.txt
# 268452 lrwxrwxrwx 1 user user 10 Mar 28 10:00 symlink.txt -> target.txt
# └─다른 inode! └─ 'l' = 링크 타입

# 원본 삭제 → 심볼릭링크가 깨짐 (Dangling Link)
rm target.txt
cat symlink.txt
# cat: symlink.txt: No such file or directory  ← 접근 불가!

# 깨진 심볼릭링크 찾기
find /tmp -type l ! -follow 2>/dev/null
# /tmp/symlink.txt  ← 빨간색으로 표시됨

하드링크 vs 심볼릭링크 핵심 비교

항목하드링크심볼릭링크
inode 공유원본과 동일한 inode별도 inode 할당
원본 삭제 후데이터 유지 (링크 카운트 기반)깨진 링크 (Dangling Link)
파티션 간 생성불가 (동일 파일시스템만)가능
디렉토리 대상불가가능
ls -l 표시일반 파일과 동일 (-)l 타입, -> 경로 표시
링크 카운트 변화증가 (원본 삭제 시 감소)변화 없음
실무 사용 예로그 백업, 공간 절약/usr/bin/python -> python3.11, nginx sites-enabled

💡개념

/etc/fstab 구조와 마운트 관리

/etc/fstab 파일 형식

/etc/fstab은 시스템 부팅 시 자동으로 마운트할 파일시스템을 정의합니다. 6개 필드를 공백(탭/스페이스)으로 구분합니다.

# /etc/fstab 예시
# <장치>                    <마운트포인트> <타입>  <옵션>              <dump> <pass>
UUID=a1b2c3d4-e5f6-...     /             xfs     defaults             0      1
UUID=c9d0e1f2-a3b4-...     /boot         xfs     defaults             0      2
UUID=e5f6a7b8-c9d0-...     /home         ext4    defaults,noexec      1      2
UUID=b3c4d5e6-a7b8-...     swap          swap    defaults             0      0
tmpfs                      /tmp          tmpfs   defaults,nosuid,size=1g  0  0
//192.168.1.100/share       /mnt/nas      cifs    credentials=/etc/samba/creds,uid=1000  0  0

6개 필드 상세 설명

필드 번호이름설명
1번째장치UUID=..., LABEL=..., /dev/sda1, NFS/CIFS 주소
2번째마운트 포인트마운트될 디렉토리 경로 (swap은 예외로 "swap" 사용)
3번째파일시스템 타입ext4, xfs, btrfs, tmpfs, nfs, cifs, swap, vfat 등
4번째마운트 옵션defaults, ro, noexec, nosuid, nodev, user, bind 등
5번째dump0: dump 백업 비대상, 1: dump 백업 대상
6번째pass0: fsck 안 함, 1: 루트 파일시스템(최우선), 2: 기타

시험 필수 암기: 5번째(dump) = 백업 여부, 6번째(pass) = fsck 점검 순서

주요 마운트 옵션

로컬 터미널
# defaults = rw,suid,dev,exec,auto,nouser,async 의 조합

# 보안 강화 옵션 (조합 사용 권장)
noexec    # 실행 파일 실행 금지 (/tmp, /home 적용 권장)
nosuid    # setuid/setgid 비트 무시 (권한 상승 방지)
nodev     # 디바이스 파일 인식 금지

# 마운트 동작
ro        # 읽기 전용 (read-only)
rw        # 읽기/쓰기 (기본값)
auto      # 부팅 시 자동 마운트 (기본 포함)
noauto    # 수동 마운트만 허용 (mount -a에서 제외)
user      # 일반 사용자도 마운트 가능

# fstab 변경 후 적용
sudo mount -a           # fstab에 정의된 미마운트 항목 전체 마운트
sudo mount -o remount / # 루트 파일시스템 옵션 변경 적용

UUID 사용 이유

로컬 터미널
# 장치명(/dev/sdb)은 디스크 추가/제거 시 변경될 수 있음
# UUID는 포맷 시 부여된 고유 값으로 영구적

# UUID 확인 방법
blkid
# /dev/sda1: UUID="a1b2c3d4-e5f6-7890-abcd-ef1234567890" TYPE="xfs"
# /dev/sda2: UUID="c9d0e1f2-a3b4-5678-cdef-012345678901" TYPE="xfs"

blkid /dev/sda1           # 특정 장치만 확인
lsblk -o NAME,UUID,TYPE   # 트리 형태로 확인

ls -lai로 inode 번호 확인하고 링크 실습하기

목표

inode 번호, 링크 카운트, 파일 타입을 직접 확인하여 하드링크와 심볼릭링크의 동작을 체감합니다.

실습 환경 준비

로컬 터미널
mkdir -p /tmp/linux-master/part1/filesystem && cd /tmp/linux-master/part1/filesystem
# 실습용 디렉토리 구조 생성
mkdir -p {etc,var/log,var/lib,home/testuser,opt/myapp,tmp}
cat > etc/app.conf << 'EOF'
[server]
host = 0.0.0.0
port = 8080
log_dir = /var/log/myapp

[database]
host = localhost
port = 5432
name = mydb
EOF
for i in 1 2 3; do
  echo "$(date): Log entry $i" >> var/log/app.log
done
echo "export APP_HOME=/opt/myapp" >> etc/environment
ls -la
로컬 터미널
mkdir -p /tmp/fs-lab && cd /tmp/fs-lab
echo "리눅스 inode 실습 파일" > original.txt

1단계: ls -lai로 inode 정보 확인

로컬 터미널
ls -lai /tmp/fs-lab/
# total 12
# 262145 drwxr-xr-x  2 user user 4096 Mar 28 10:00 .       ← 현재 디렉토리 inode
# 131073 drwxrwxrwt 18 root root 4096 Mar 28 10:00 ..      ← 부모(/tmp) inode
# 262146 -rw-r--r--  1 user user   22 Mar 28 10:00 original.txt
#   ↑inode번호    ↑링크수

출력 필드: inode번호 | 권한 | 링크수 | 소유자 | 그룹 | 크기 | 날짜 | 파일명

2단계: 하드링크 생성 후 inode 변화 관찰

로컬 터미널
ln original.txt hardlink.txt

ls -lai
# 262146 -rw-r--r-- 2 user user 22 Mar 28 10:00 hardlink.txt
# 262146 -rw-r--r-- 2 user user 22 Mar 28 10:00 original.txt
# └─ 동일한 inode 번호!  └─ 링크 카운트 2로 증가

# stat으로 링크 카운트 명시적 확인
stat original.txt | grep Links
# Links: 2

3단계: 심볼릭링크 생성 후 inode 비교

로컬 터미널
ln -s original.txt symlink.txt

ls -lai
# 262146 -rw-r--r-- 2 user user 22 Mar 28 10:00 hardlink.txt   ← inode 262146
# 262146 -rw-r--r-- 2 user user 22 Mar 28 10:00 original.txt   ← inode 262146 (동일)
# 262147 lrwxrwxrwx 1 user user 12 Mar 28 10:00 symlink.txt -> original.txt ← 다른 inode!
# ↑다른 번호  ↑l=link타입

4단계: 원본 삭제 후 각 링크 동작 확인

로컬 터미널
rm original.txt

# 하드링크는 여전히 읽기 가능
cat hardlink.txt
# 리눅스 inode 실습 파일  ← 정상 출력

ls -li hardlink.txt
# 262146 -rw-r--r-- 1 user user 22 Mar 28 10:00 hardlink.txt
#                   └─ 링크 카운트가 1로 감소 (0이 되면 데이터 블록 해제)

# 심볼릭링크는 깨짐 (Dangling Link)
cat symlink.txt
# cat: symlink.txt: No such file or directory

ls -la symlink.txt
# lrwxrwxrwx 1 user user 12 Mar 28 10:00 symlink.txt -> original.txt
# (터미널에서 빨간색으로 표시됨)

5단계: find로 깨진 심볼릭링크 탐색

로컬 터미널
find /tmp/fs-lab -type l ! -follow
# /tmp/fs-lab/symlink.txt  ← 깨진 심볼릭링크 목록 출력

df / du로 디스크 사용량 분석하기

목표

df와 du 명령어를 활용하여 파일시스템 사용량을 분석하고 대용량 파일 또는 디렉토리를 찾습니다.

1단계: df로 파일시스템 전체 현황 파악

로컬 터미널
# -h: human-readable 단위, -T: 파일시스템 타입 표시
df -hT
# Filesystem              Type      Size  Used Avail Use% Mounted on
# /dev/mapper/rl-root     xfs        47G   12G   35G  26% /
# /dev/sda1               xfs      1014M  261M  754M  26% /boot
# /dev/mapper/rl-home     xfs        24G  200M   24G   1% /home
# tmpfs                   tmpfs     1.9G   12K  1.9G   1% /run
# proc                    proc         0     0     0    - /proc  ← 0bytes!

# inode 사용량 확인 (이것도 반드시 체크!)
df -i
# Filesystem       Inodes  IUsed   IFree IUse% Mounted on
# /dev/mapper/rl  6291456  45231 6246225    1% /
# (IUse%가 100%면 inode 고갈 → "No space left on device" 오류 발생)

2단계: du로 디렉토리별 용량 분석

로컬 터미널
# 최상위 디렉토리별 용량 (1레벨 요약)
sudo du -sh /*
# 0       /dev     ← 디바이스 파일, 실제 크기 없음
# 0       /proc    ← 가상 파일시스템
# 5.3M    /etc
# 89M     /var
# 227M    /usr

# 특정 디렉토리 하위 분석 (정렬 포함)
du -sh /var/* | sort -rh | head -10
# 50M     /var/log
# 12M     /var/cache
# 1.1M    /var/lib

# /var/log 내부 드릴다운
du -sh /var/log/* | sort -rh | head -5
# 28M     /var/log/journal
# 15M     /var/log/audit
# 4.5M    /var/log/messages

3단계: find로 대용량 파일 검색

로컬 터미널
# 100MB 이상 파일 검색
find / -xdev -type f -size +100M -exec ls -lh {} \; 2>/dev/null
# -rwxr-xr-x 1 root root 150M /usr/lib/jvm/java-17/lib/server/libjvm.so
# -rw-r--r-- 1 root root 2.1G /var/lib/mysql/ibdata1

# 오늘 새로 생긴 대용량 파일
find /var -newer /var/log/wtmp -size +50M -type f 2>/dev/null

# 특정 사용자의 파일 총 용량
find /home/alice -type f -exec du -ch {} + | tail -1
# 2.3G    total

4단계: stat으로 파일 상세 메타데이터 확인

로컬 터미널
stat /etc/hostname
# File: /etc/hostname
# Size: 10          Blocks: 8          IO Block: 4096   regular file
# Device: fd00h     Inode: 262148      Links: 1
# Access: (0644/-rw-r--r--)  Uid: (0/root)  Gid: (0/root)
# Access: 2026-03-28 09:00:00   ← 마지막 읽기
# Modify: 2026-03-20 14:23:11   ← 마지막 내용 변경
# Change: 2026-03-20 14:23:11   ← 마지막 메타데이터 변경 (chmod 포함)

/etc/fstab 분석과 mount 실습

목표

/etc/fstab 각 필드를 정확히 해석하고, mount/umount 명령으로 수동 마운트를 실습합니다.

1단계: 현재 fstab 내용 분석

로컬 터미널
cat /etc/fstab
# UUID=a1b2c3d4...  /       xfs  defaults          0 1
# UUID=c9d0e1f2...  /boot   xfs  defaults          0 2
# UUID=e5f6a7b8...  swap    swap defaults           0 0

각 줄을 직접 해석해 보세요:

  • 1번째 필드: 장치 (UUID로 지정, blkid로 확인 가능)
  • 2번째 필드: 마운트 포인트 (swap은 "swap")
  • 3번째 필드: 파일시스템 타입
  • 4번째 필드: 옵션 (defaults의 구성 요소는?)
  • 5번째 필드: dump 백업 (0=비대상, 1=대상)
  • 6번째 필드: fsck 순서 (0=안함, 1=루트, 2=기타)

2단계: tmpfs 임시 마운트 실습

로컬 터미널
# /mnt/tmptest에 100MB 제한 tmpfs 마운트
sudo mkdir -p /mnt/tmptest
sudo mount -t tmpfs -o size=100m tmpfs /mnt/tmptest

# 마운트 확인
df -hT /mnt/tmptest
# Filesystem  Type   Size  Used Avail Use% Mounted on
# tmpfs       tmpfs  100M     0  100M   0% /mnt/tmptest

mount | grep tmptest
# tmpfs on /mnt/tmptest type tmpfs (rw,relatime,size=102400k)

# 파일 생성 테스트
echo "tmpfs 테스트" > /mnt/tmptest/test.txt
cat /mnt/tmptest/test.txt
# tmpfs 테스트

# 언마운트 (재부팅하지 않고 해제)
sudo umount /mnt/tmptest
ls /mnt/tmptest
# (비어 있음 - tmpfs 내용 사라짐)

3단계: 다양한 방법으로 마운트 정보 확인

로컬 터미널
# 방법 1: mount 명령 (현재 마운트 목록)
mount | grep -v "cgroup\|proc\|sys\|dev" | column -t

# 방법 2: /proc/mounts (커널이 직접 관리하는 마운트 정보)
cat /proc/mounts | grep -v "^none" | head -10

# 방법 3: findmnt (트리 형태로 가장 보기 좋음)
findmnt
# TARGET      SOURCE           FSTYPE  OPTIONS
# /           /dev/mapper/rl   xfs     rw,relatime
# ├─/sys      sysfs            sysfs   rw,nosuid,nodev
# ├─/proc     proc             proc    rw,nosuid,nodev
# └─/home     /dev/mapper/rl-h xfs     rw,relatime

# UUID 조회 (fstab 작성 시 필요)
sudo blkid | grep -v swap
# /dev/sda1: UUID="a1b2c3d4-..." TYPE="xfs"
🔍실행 후 확인할 것
  • ls -lai 결과에서 하드링크로 만든 두 파일의 inode 번호가 동일한가?
  • df -h 와 df -i 의 결과가 다를 수 있음을 확인했는가? (용량과 inode는 별개 자원)
  • du -sh /* 로 가장 큰 디렉토리를 한눈에 찾을 수 있는가?
  • fstab에 잘못된 UUID를 넣으면 부팅이 막힐 수 있으니, mount -a 로 검증 후 재부팅하는 습관이 있는가?

문제 상황

웹 서버 로그 기록 도중 갑자기 쓰기 오류가 발생했습니다.

로컬 터미널
$ echo "test" > /var/log/app/new.log
bash: /var/log/app/new.log: No space left on device

$ df -h /var/log
Filesystem               Size  Used Avail Use% Mounted on
/dev/mapper/rl-root       47G   23G   24G  49% /
# 24GB가 남아 있는데 왜 오류가?!

원인 진단: inode 고갈

디스크 블록 공간은 남았지만 inode가 모두 소진된 상황입니다. 소용량 파일이 수백만 개 생성될 때 발생합니다.

로컬 터미널
# inode 사용량 확인 — 핵심 진단 명령어!
df -i /
# Filesystem       Inodes   IUsed   IFree IUse% Mounted on
# /dev/mapper/rl  6291456 6291456       0  100% /
#                                  └──── 0개 남음! 이것이 원인

# 어디에 파일이 집중됐는지 찾기
find / -xdev -type d | while read d; do
  count=$(find "$d" -maxdepth 1 -type f 2>/dev/null | wc -l)
  echo "$count $d"
done | sort -rn | head -10
# 891234 /var/spool/clientmqueue
# 423156 /tmp/php_sessions

원인 파악

로컬 터미널
ls /var/spool/clientmqueue | wc -l
# 891234   ← sendmail 오류 메일 큐 누적

find /tmp -name "sess_*" | wc -l
# 423156   ← PHP 세션 파일 미정리

해결 방법

로컬 터미널
# 1. 불필요한 소용량 파일 대량 삭제
find /var/spool/clientmqueue -type f -delete
find /tmp -name "sess_*" -mtime +1 -delete

# 2. 정리 후 inode 사용률 확인
df -i /
# Filesystem      Inodes   IUsed   IFree IUse% Mounted on
# /dev/mapper/rl 6291456  145231 6146225    3% /

예방 방법

로컬 터미널
# PHP 세션 자동 정리 (cron)
# /etc/cron.d/php-session-cleanup
# 0 * * * * root find /var/lib/php/session -type f -mtime +1 -delete

# sendmail 큐 자동 정리 (systemd timer 또는 logrotate)
# 파일시스템 생성 시 inode 수 조정 (소용량 파일 많은 파티션)
mkfs.ext4 -i 4096 /dev/sdb1  # 기본(16384) 대비 4배 많은 inode

문제 상황

logrotate가 실행되어 /var/log/app.log가 rotate됐는데, df 결과가 전혀 변하지 않습니다.

로컬 터미널
$ ls -lh /var/log/app.log*
-rw-r--r-- 1 app app    0 Mar 28 03:00 /var/log/app.log      ← 새 빈 파일
-rw-r--r-- 1 app app 8.2G Mar 27 23:59 /var/log/app.log.1   ← rotate된 파일

$ df -h /
Filesystem  Size  Used Avail Use%
/dev/sda1    50G   48G  2.0G  96%  ← 8.2GB가 회복되지 않았다!

원인: 프로세스가 삭제된 파일의 fd(file descriptor)를 계속 보유

파일이 rename/delete되어도 프로세스가 파일을 열어두고 있으면 inode 참조 카운트가 0이 되지 않아 실제 디스크 블록이 해제되지 않습니다.

로컬 터미널
# 삭제됐지만 프로세스가 열어두고 있는 파일 찾기
lsof | grep deleted
# java  12345  app  3w  REG  253,0  8825052160  131074 /var/log/app.log (deleted)
# └─ PID 12345 프로세스가 삭제(renamed)된 파일을 fd 3으로 계속 쓰고 있음

# 특정 파일시스템 기준으로 확인
lsof +L1 /var/log

해결 방법

로컬 터미널
# 방법 1: 애플리케이션에 SIGHUP 전송 → 로그 파일 재오픈 (권장)
kill -HUP $(cat /var/run/myapp.pid)

# systemd 서비스라면 reload
systemctl reload myapp

# 방법 2: 프로세스 재시작
systemctl restart myapp

# 방법 3: 실행 중인 채로 파일 내용만 비우기 (임시 조치, 데이터 유실)
> /proc/12345/fd/3   # 해당 fd를 직접 truncate

logrotate 설정에 postrotate 추가 (근본 해결)

로컬 터미널
# /etc/logrotate.d/myapp
/var/log/myapp/*.log {
    daily
    rotate 14
    compress
    missingok
    notifempty
    postrotate
        kill -HUP $(cat /var/run/myapp.pid 2>/dev/null) 2>/dev/null || true
    endscript
}
# 또는 copytruncate 옵션: 복사 후 원본을 0바이트로 잘라내므로 재오픈 불필요
# (단, 복사~truncate 사이 짧은 순간 데이터 유실 가능)

💼
실무 맥락
현업 패턴

실제 업무 시나리오

모니터링 알람: "운영 서버 / 파티션 사용률 85% 초과". 어떤 순서로 분석하고 조치할까요?

표준 진단 절차 (5분 이내)

로컬 터미널
# 1단계: 전체 파일시스템 현황 파악 (30초)
df -hT          # 블록 사용량 확인
df -i           # inode 사용량도 반드시 확인

# 2단계: 주요 디렉토리별 용량 비교 (1분)
sudo du -sh /var/log /var/lib /home /opt /tmp 2>/dev/null
# 예시 출력:
# 32G     /var/log     ← 로그가 주범
# 8.5G    /var/lib
# 2.1G    /home

# 3단계: 주범 디렉토리 내부 드릴다운 (2분)
sudo du -sh /var/log/* | sort -rh | head -10
# 28G     /var/log/nginx/access.log   ← nginx 접근 로그 이상 증가

# 4단계: 해당 파일 상세 확인
ls -lh /var/log/nginx/access.log
stat /var/log/nginx/access.log

# 5단계: 프로세스가 잡고 있는지 확인
lsof /var/log/nginx/access.log

# 6단계: 조치
logrotate -f /etc/logrotate.d/nginx   # 강제 rotate + SIGHUP

현업에서 자주 쓰는 one-liner

로컬 터미널
# 시스템 전체에서 가장 큰 파일 Top 20
find / -xdev -type f -exec du -Sh {} + 2>/dev/null | sort -rh | head -20

# 삭제됐지만 열린 fd로 낭비되는 공간 총합
lsof +L1 | awk 'NR>1 {sum += $8} END {printf "%.1f GB\n", sum/1024/1024/1024}'

# 30일 이상 접근 안 된 로그 파일 목록
find /var/log -type f -atime +30 -name "*.log" -exec ls -lh {} \;

증상별 진단 명령어 요약표

증상진단 명령어예상 원인
df 꽉 참, du 합계 불일치lsof | grep deleted열린 fd로 인한 미해제 블록
df 여유 있음, 쓰기 오류df -iinode 고갈
대용량 파일 위치 모름du -sh /* | sort -rh디렉토리별 드릴다운
파일 수정 시각 확인stat <파일>atime/mtime/ctime 확인
디스크 블록 낭비 의심lsof +L1프로세스가 삭제 파일 점유

지식 확인

퀴즈 — 5문제

Q1

/proc 디렉토리에 대한 설명으로 올바른 것은?

Q2

원본 파일을 삭제한 후에도 파일 내용에 정상적으로 접근할 수 있는 링크 방식은?

Q3

inode에 저장되지 않는 정보는 무엇인가요?

Q4

df -h와 du -sh /home 명령어의 차이를 가장 정확하게 설명한 것은?

Q5

/etc/fstab의 5번째 필드(dump) 값이 '1'인 경우의 의미는?

0 / 5 답변

이것도 배워보세요

linux-master중급 · 80
[Linux] Shell 스크립팅과 자동화
Linux-master 트랙 계속