Troubleshooting 2026.08.10 6 min read

Kubernetes PV가 Bound인데도 안심하면 안 되는 이유

PV의 Bound 표시와 실제 디스크 상태를 나눠 점검했다. Local PV의 마운트, LVM, I/O 로그와 앱 응답을 확인하는 예방 점검 기록이다.

환경: RKE2, Local PersistentVolume, LVM, Ghost, MySQL

“edge 노드의 PV가 죽었는지 어떻게 확인하지?”라는 질문에서 점검을 시작했다. PV는 Bound로 보였지만 이 표시만으로 실제 디스크와 파일시스템 상태까지 알 수는 없었다.

실제 디스크 장애를 확정하고 복구한 사례는 아니다. Local PV를 점검하면서 제어면 상태, 노드 마운트, 커널 I/O, 애플리케이션 읽기·쓰기를 나눠 확인한 예방 점검 기록이다.

Local PV의 구조

Ghost/MySQL Pod
  → PVC
  → Local PV (nodeAffinity: on-prem worker)
  → /srv/.../volume
  → Linux mount point
  → LVM logical volume
  → virtual/physical disk

Local PV의 경로는 특정 노드에 묶인다. 다른 노드에 같은 이름의 디렉터리를 만들어도 데이터가 따라오지는 않는다. PV의 nodeAffinity와 실제 Pod 배치를 함께 확인한 이유다.

또한 이 구조는 고가용성 공유 스토리지가 아니다. 노드나 디스크 장애에 대비하려면 별도의 원격 백업과 복구 절차가 필요하다.

Bound가 보장하는 것과 보장하지 않는 것

Bound는 PVC와 PV 객체가 Kubernetes 제어면에서 연결됐다는 뜻이다. 다음을 직접 보장하지는 않는다.

  • 실제 블록 장치가 정상인지
  • LVM LV가 활성화되어 있는지
  • 파일시스템이 올바른 경로에 마운트됐는지
  • 파일시스템이 read-only로 전환되지 않았는지
  • 커널에 I/O error가 없는지
  • 애플리케이션이 데이터를 정상적으로 읽고 쓰는지

마운트가 풀린 뒤 mount point 디렉터리만 남는 경우도 확인해야 했다. kubelet이나 애플리케이션이 그 디렉터리를 사용하면 데이터 디스크 대신 root filesystem에 새 파일을 쓸 수 있다.

1단계: Kubernetes 제어면

kubectl get node -o wide
kubectl get pv
kubectl -A get pvc
kubectl -A get pod -o wide
kubectl get events -A --sort-by=.lastTimestamp | tail -n 100

제어면에서는 아래 상태를 확인했다.

  • 저장소 노드가 Ready인가?
  • PVC/PV가 Bound인가?
  • Pod가 기대한 노드에 배치됐는가?
  • FailedMount, FailedAttachVolume, I/O error 이벤트가 있는가?
  • Pod가 CrashLoopBackOff 또는 장시간 Terminating 상태인가?

PV의 nodeAffinity와 실제 경로도 확인한다.

kubectl get pv <pv-name> -o yaml
kubectl describe pv <pv-name>
kubectl describe pod -n <namespace> <pod-name>

2단계: 노드의 실제 마운트

저장소 노드에 접속한 뒤 ls로 파일을 보기 전에 findmnt로 실제 마운트 대상을 확인했다.

findmnt -T /srv/kubernetes-pv/ghost-content/volume
findmnt -T /srv/kubernetes-pv/ghost-db/volume
mountpoint /srv/kubernetes-pv/ghost-content
mountpoint /srv/kubernetes-pv/ghost-db
df -hT /srv/kubernetes-pv/ghost-content /srv/kubernetes-pv/ghost-db

findmnt -T는 해당 경로를 실제로 어느 filesystem이 담당하는지 보여 준다. 예상한 LV가 아니라 /가 나온다면 마운트가 빠진 상태일 수 있다.

다음으로 블록 장치와 LVM 연결을 본다.

lsblk -o NAME,TYPE,FSTYPE,SIZE,MOUNTPOINTS,UUID
sudo pvs
sudo vgs
sudo lvs -a -o +devices

df의 사용률 100%, LV 비활성, 사라진 device, 예상과 다른 UUID를 확인한다.

3단계: read-only와 I/O 오류

findmnt -no SOURCE,FSTYPE,OPTIONS \
  /srv/kubernetes-pv/ghost-content
sudo dmesg -T | grep -Ei \
  'I/O error|EXT4-fs error|XFS.*error|Buffer I/O|read-only|nvme|blk_update'
sudo journalctl -k --since '24 hours ago' | grep -Ei \
  'I/O error|filesystem|read-only|nvme|scsi|blk'

mount option에 ro가 보이거나 커널에 filesystem error가 반복되면 애플리케이션 쓰기를 중단하고 증거를 보존해야 한다.

가상 머신에 물리 디스크의 SMART 정보가 전달되지 않을 수 있다. 이때 smartctl이 정상이라고 나오지 않는다고 해서 디스크가 고장났다고 단정할 수 없고, 하이퍼바이저 호스트에서 물리 장치를 검사해야 한다.

4단계: 애플리케이션 관점

마운트를 확인한 뒤에는 Ghost와 MySQL에서도 데이터를 읽을 수 있는지 점검했다.

# Ghost content PVC가 예상 파일을 보이는지
kubectl -n <namespace> exec <ghost-pod> -- \
  sh -c 'test -d /var/lib/ghost/content && find /var/lib/ghost/content -maxdepth 1 -type d'

# MySQL 컨테이너의 자체 health 명령 예시
kubectl -n <namespace> exec <mysql-pod> -- \
  mysqladmin ping -h 127.0.0.1

쓰기 시험이 필요하면 애플리케이션이 허용하는 health check나 별도 검증 경로를 사용하도록 했다. 운영 데이터 디렉터리에 임의 파일을 만드는 방식은 피하고, DB는 논리 쿼리와 오류 로그를 함께 확인했다.

kubectl -n <namespace> logs <mysql-pod> --tail=200
kubectl -n <namespace> logs <ghost-pod> --tail=200

절대 급하게 하지 말아야 할 것

  • 마운트된 운영 파일시스템에 fsck 실행
  • 원인 확인 전에 Pod와 노드를 반복 재부팅
  • mount point 디렉터리를 삭제하거나 새로 생성
  • PV를 삭제했다가 다시 만들기
  • 장애 디스크에 대량 쓰기 시험
  • 복구가 검증되지 않은 백업만 믿고 포맷

fsck는 일반적으로 파일시스템을 unmount한 뒤 maintenance window에서 수행해야 한다. 데이터베이스 Pod 중지, 백업 확보, 장치 경로 재확인 없이 실행하면 피해를 키울 수 있다.

백업으로 마무리하는 검증

Local PV는 노드 종속성이 있으므로 원격 restic 저장소를 별도로 운영했다. 검증은 세 단계로 했다.

restic snapshots
restic check
restic restore <snapshot-id> --target <isolated-restore-dir>

복구 위치에서도 다음 항목을 확인해야 한다.

  • 데이터베이스 dump 파일이 존재하고 읽을 수 있는가?
  • 콘텐츠 파일 수와 주요 디렉터리가 예상과 같은가?
  • 실제 애플리케이션이 복구 데이터를 사용할 수 있는가?
  • 원본 노드에 접근하지 않아도 복구 가능한가?

빠른 판단표

관찰 해석
PV Bound, findmnt가 root filesystem 표시 제어면 연결은 됐지만 실제 데이터 mount 유실 가능
Pod Pending, nodeAffinity 불일치 저장소 노드 선택 문제
FailedMount 이벤트 kubelet/경로/권한/mount 문제
filesystem option ro 오류로 read-only 전환됐을 가능성
커널 I/O 오류 반복 하위 디스크·가상 디스크·filesystem 조사 필요
마운트 정상, MySQL ping 실패 애플리케이션 또는 DB 계층 문제

재발 방지

  • mount point마다 findmnt 기반 모니터링을 둔다.
  • root filesystem과 데이터 filesystem 사용률을 따로 알린다.
  • Kubernetes event와 커널 I/O 로그를 함께 수집한다.
  • Local PV의 nodeAffinity와 노드 label 변경을 관리한다.
  • 원격 백업과 정기 restore drill을 수행한다.
  • 장애 절차에 “쓰기 중지 → 증거 수집 → 백업 확인 → 복구” 순서를 명시한다.

참고 자료

SEARCH JOURNAL

기록 검색

글 제목, 요약, 태그에서 찾습니다.

검색을 준비하고 있어요.

검색어를 입력해 주세요.

    Ctrl/⌘ K 검색 Esc 닫기 ↑ ↓ 결과 이동