
이전 포스팅에서 PV(퍼시스턴트 볼륨)과 PVC(퍼시스턴트 볼륨 클레임)의 개념에 대해 설명하였다.
이전 포스팅 (PV, PVC 개념 정리)
또한 바로 이전 포스팅에서 nfs 볼륨을 만드는 실습을 진행하였다.
이번 포스팅에서는 관리자가 생성하는 PV(PersistentVolume)와
개발자가 요청하는 PVC(PersistentVolumeClaim)를 직접 만들어보고 연결하는 실습을 진행해본다.
구분 역할 생성 주체 설명 PV (Persistent Volume) 실제 물리 저장소 공간 관리자(Admin) 노드 외부 스토리지(NFS, AWS EBS 등)에 연결된 볼륨 리소스 PVC (Persistent Volume Claim) 사용자가 요청하는 “스토리지 청구서” 개발자(User) “나 1Gi 스토리지 쓸래!” 같은 요청서로 PV에 연결됨
먼저 관리자가 사용할 스토리지 공간(PV)을 정의한다.
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv-nfs
spec:
capacity:
storage: 3Gi
accessModes:
- ReadWriteMany
persistentVolumeReclaimPolicy: Retain
storageClassName: manual
nfs:
path: /Users/<사용자이름>/nfs_shared/pv-data
server: <NFS 서버 IP>
vim pv.yaml 하여 위 명령어를 자신의 환경에 맞게 세팅 후 복사붙여넣기 해준다.
📍 이 코드는 NFS 서버IP의 /nfs_shared/pv-data 디렉토리를
쿠버네티스 클러스터에서 3Gi 크기의 PV로 등록하는 예시이다.
✅ 적용 명령어
kubectl apply -f pv.yaml
kubectl get pv

이제 개발자가 사용할 PVC를 생성한다.
PVC는 스토리지 크기, 접근 모드, 스토리지 클래스 등을 요청한다.
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: pvc-nfs
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 2Gi
storageClassName: manual
이 파일 또한 vim pvc.yaml 하여 복사붙여넣기 해준다.
📍 storageClassName이 PV와 같아야 연결된다.
storage: 2Gi는 PV(3Gi)보다 작거나 같아야 매칭이 가능하다.
✅ 적용 명령어
kubectl apply -f pvc.yaml
kubectl get pvc

STATUS가 Bound로 바뀌었다.
(Bound 상태는 PV와 PVC가 정상적으로 연결되었다는 의미이다.)
PVC를 파드에 마운트하여, 파드 내부에서 스토리지를 사용하는 예시를 만들어본다.
apiVersion: v1
kind: Pod
metadata:
name: pod-nfs
spec:
containers:
- name: web
image: nginx
volumeMounts:
- mountPath: /usr/share/nginx/html
name: web-vol
volumes:
- name: web-vol
persistentVolumeClaim:
claimName: pvc-nfs
vim pod.yaml 하여 실행 테스트할 pod yaml 설정을 해준다.
📍 이 파드는 nginx 컨테이너의 /usr/share/nginx/html 경로를
PVC(pvc-nfs)로 마운트하여 NFS 스토리지 공간을 사용하게 된다.
✅ 적용 명령어
kubectl apply -f pod.yaml
kubectl get pods
kubectl exec -it pod-nfs -- /bin/sh
ls /usr/share/nginx/html
/usr/share/nginx/html 경로가 비어있다면 정상 연결 상태이다.
(이후 NFS 서버에서 직접 파일을 만들어 확인해볼 수 있다.)
예:
NFS 서버에서 아래 명령 실행 후
echo "Hello from NFS!" > /nfs_shared/pv-data/index.html
다시 파드 내부에서 확인하면 —
cat /usr/share/nginx/html/index.html
→ “Hello from NFS!” 가 출력된다면 성공이다.
kubectl exec -it pod-nfs -- /bin/sh 이 명령어를 치면 정상적으로 찾아야 하는데, 못 찾고 에러가 떴다. 이 에러를 검색해보니
쿠버네티스가 pod-nfs안에서 web에서 찾아야 하는데 못찾는다는 뜻이다.
이 에러가 떴을 때 매우 당황스러웠는데, 단계별로 해결하였다.
일단 파드 안에 컨테이너가 실제로 어떻게 되어있는지를 확인한다.
kubectl describe pod pod-nfs | grep -A 2 "Containers:"

이렇게
Containers:
web:
Container ID:
따라서 kubectl exec 명령어에 -c web 옵션을 명시해
정확히 해당 컨테이너에 접속하도록 명령을 수정해야 한다.
kubectl exec -it pod-nfs -c web -- /bin/sh
이렇게 하면 web이라는 이름의 컨테이너로 정상 접속되어야 하는데..
아직도 에러가 떴다.
그래서 확인해보니

이렇게 아직도 만들고 있는 상태였다(즉, running 상태가 아니다.)
이 상태는 거의 100% 확률로 NFS 볼륨 마운트가 실패하여 멈춘 상태인 것이다.
그래서
이벤트 로그를 분석해보기로 했다.
kubectl describe pod pod-nfs

이렇게 상세로그가 떴다.
로그에서
"mount.nfs: access denied by server while mounting 192.168.219.103:/Users/goorm/nfs_shared/pv-data"가 떴다.
이것은 쿠버네티스 노드가 mac의 nfs 서버에 접근하려했는데 접근 권한이 거부되었다는 것이다.
맥이 지금 “NFS 서버 역할”을 하는 중이고,
쿠버네티스(Docker Desktop의 내부 VM)가 “클라이언트”로 접근하는 구조인데,
그런데 /etc/exports 파일에서 설정한 네트워크 대역이
Docker Desktop 내부 VM의 IP 대역과 일치하지 않아서 거절된 것이다.
이것을 차근차근 해결해보기로 했다.
sudo vi /etc/exports
그리고 안에 있는 기존 줄을
/Users/goorm/nfs_shared -alldirs -mapall=0 -network 192.168.0.0 -mask 255.255.0.0
이렇게 바꿔서 이렇게 하면 192.168.0.0~192.168.255.255 전체 대역에서 접근 가능하니까
Docker Desktop 내부의 가상 네트워크(보통 192.168.65.x)도 접근 허용하게 바꿨다.
그런 후, 다음 명령어를 입력하여 NFS를 재시작하고 확인해준다.
sudo nfsd restart
showmount -e

그런데도 아무리 재시작하고 파드를 다시 만들어봐도 running상태가 되지를 않아서 확인해봤더니
방화벽이 막는듯 하여 방화벽에서 nfsd 프로세스를 허용시켰다.
또한 NFS의 기본 설정이 Docker VM 같은 외부 접근을 막고 있어서 풀어주도록 다음 명령어를 실행했다.
sudo vi /etc/nfs.conf

이렇게 뜨면 이제 정상적으로 NFS 서버가 쿠버네티스 클러스터에서 접근 가능한 상태가 된 것이다.
즉, macOS에서 설정한 /Users/goorm/nfs_shared 경로가 외부(Docker Desktop 내부 VM 포함)에서 마운트될 수 있게 허용된 것이다.
이제 쿠버네티스에서 파드를 다시 배포한다.
kubectl delete pod pod-nfs
kubectl apply -f pod.yaml
이를 실행하여 기존에 배포한 파드를 삭제한 후, 다시 배포해준다.
근데 안됐다.
그래서 세 번째 시도를 해보기로 했다.
nfs의 기본 설정이 docker vm같은 외부 접근을 막고 있어서 풀어줘야 한다.
sudo vi /etc/nfs.conf
를 실행하여 다음 한 줄을 넣고 저장해주었다.
nfs.server.mount.require_resv_port = 0
의미: NFS 클라이언트(쿠버네티스 내부 컨테이너)가
비예약 포트(1024 이상)에서 접근해도 허용하겠다는 뜻이다.
저장 후 종로하고 다시 nfs를 시작해주었다.
sudo nfsd restart
이제 설정이 적용되었는지 확인하기 위해 다음 명령어를 치고 잘 나오는지 확인한다.
showmount -e
그런다음 파드를 삭제하고 배포하고 다시 확인해본다.
kubectl delete pod pod-nfs
kubectl apply -f pod.yaml
kubectl get pods -o wide
또 안됐다.
pv, pvc, pod 전부 삭제하고 yaml 파일부터 만들고 다시 배포하기로 했다.
"pod-nfs 0/1 ContainerCreating 0 23s"
해당 상태라
로그를 조회해보았다.

이제는 이번엔 아예 FailedMount가 사라져서
쿠버네티스가 더 이상 NFS 마운트에 실패하고 있지 않았다.
그 문제는 해결이된 듯 싶었고, 다른 문제가 있는 것 같아서 챗지피티한테 물어봤더니 다음 답변을 얻었다.
지금 상태 ContainerCreating에서 이벤트가 “FailedMount” 없이 그냥 멈춰있다면,
이건 거의 100% 확률로 이미 NFS 연결이 성공했는데 컨테이너가 이미지 풀링 중이거나, Nginx가 뜨는 중인 상황이다.
라고.
그런데도 안되어서
sudo vi /etc/exports
명령어를 통해 다시 대역대를 수정한 뒤 파드를 재배포 해줬더니 드디어 Running 상태가 되었다.
✅ 정리하자면:
- macOS가 NFS 서버, 쿠버네티스(Docker Desktop)가 NFS 클라이언트
/etc/exports에서 대역대를 넓혀 접근 허용/etc/nfs.conf에nfs.server.mount.require_resv_port = 0추가sudo nfsd restart && showmount -e로 확인- 이후 PV → PVC → Pod 순으로 재배포
kubectl exec -it pod-nfs -- /bin/sh
ls /usr/share/nginx/html
아무것도 안 떠야 정상이다.
다시 macOS 터미널로 나와서
echo 'Hello from NFS!' > ~/nfs_shared/pv-data/index.html
위 명령어를 통해 Hello from FNS! 를 출력하는 파일을 생성한다.

그럼 다음과 같이 index.html 파일이 생성된 것을 확인할 수 있다.
kubectl exec -it pod-nfs -- /bin/sh
cat /usr/share/nginx/html/index.html
그 다음 파드 안에 들어가서
cat 명령어를 통해 만든 index.html 파일을 출력하면 다음과 같은 결과를 확인할 수 있다.

macOS (NFS 서버)
└── /Users/goorm/nfs_shared/pv-data
↓
PersistentVolume (pv-nfs)
↓ (storageClassName + nfs path/server)
PersistentVolumeClaim (pvc-nfs)
↓ (claimName)
Pod (pod-nfs)
구성요소 YAML 필드 연결 대상 의미 / 설명 NFS 서버 (macOS) /Users/goorm/nfs_shared/pv-dataPV의 nfs.path실제 물리 저장소. PV가 이 경로를 바라봄 PersistentVolume (pv-nfs) spec.nfs.path: /Users/goorm/nfs_shared/pv-dataspec.nfs.server: 192.168.219.103NFS 서버(macOS) NFS 서버의 공유 디렉토리를 쿠버네티스에 “스토리지 자원”으로 등록 〃 spec.storageClassName: manualPVC의 storageClassNamePVC가 같은 이름으로 요청해야 연결 가능 PersistentVolumeClaim (pvc-nfs) spec.storageClassName: manualPV의 storageClassName동일해야 PV와 자동 매칭됨 〃 spec.accessModes: [ReadWriteMany]PV의 accessModes 여러 파드에서 동시에 쓰기 가능하게 설정 〃 spec.resources.requests.storage: 2GiPV의 capacity.storage: 3Gi요청 용량(2Gi)은 PV의 용량(3Gi)보다 작거나 같아야 연결 가능 Pod (pod-nfs) spec.volumes[].persistentVolumeClaim.claimName: pvc-nfsPVC 이름 Pod이 pvc-nfs라는 PVC를 마운트함 〃 spec.containers[].volumeMounts[].mountPath: /usr/share/nginx/htmlPod 내부 디렉토리 NFS 스토리지를 컨테이너 안에서 이 경로로 접근 〃 spec.containers[].image: nginx- nginx가 구동되며 NFS 경로를 웹루트로 사용
단계 주체 리소스 이름 핵심 연결 키 동작 설명 ① macOS /Users/goorm/nfs_shared/pv-data- NFS 서버가 폴더 공유 시작 ② 쿠버네티스 Admin pv-nfsnfs.path/nfs.serverPV가 macOS 폴더를 등록 ③ 쿠버네티스 User pvc-nfsstorageClassName: manualPVC가 PV를 요청해 Bound 상태로 연결 ④ 쿠버네티스 pod-nfsclaimName: pvc-nfsPod가 PVC를 사용해서 NFS 스토리지를 마운트
┌──────────────────────────────┐
│ macOS (NFS Server) │
│ └── /Users/goorm/nfs_shared/pv-data │
└──────────────┬───────────────┘
│ (nfs.path / nfs.server)
┌──────────────▼───────────────┐
│ PersistentVolume (pv-nfs) │
│ storageClassName: manual │
└──────────────┬───────────────┘
│ (storageClassName)
┌──────────────▼───────────────┐
│ PersistentVolumeClaim (pvc-nfs) │
│ claimName: pvc-nfs │
└──────────────┬───────────────┘
│ (claimName)
┌──────────────▼───────────────┐
│ Pod (pod-nfs) │
│ mountPath: /usr/share/nginx/html │
└──────────────────────────────┘
참고자료:
[쿠버네티스 공식 홈페이지 - Persistent Volumes]
https://kubernetes.io/docs/concepts/storage/persistent-volumes/
[쿠버네티스 공식 홈페이지 - Volumes]
https://kubernetes.io/docs/concepts/storage/volumes/#nfs
본 게시물의 트러블슈팅 및 최종 정리 과정은 ChatGPT의 도움을 받아 진행하였으며,
모든 YAML 구성은 쿠버네티스 공식 문서를 기반으로 작성하였습니다.