K8S Persistent Volume (PV) — EKS + AWS EFS 통합 요약
1. PV(Persistent Volume) 개념
- PV란: 포드(Pod)와 독립적으로 존재하는 클러스터 수준의 스토리지 자원
- 비유: 포드 = 소프트웨어, PV = 하드디스크
핵심 특징
| 특징 |
설명 |
| 독립적 생명주기 |
포드 삭제 후에도 데이터 유지 |
| 클러스터 리소스 |
클러스터 전체에서 관리 |
| 추상화 |
실제 저장소 종류와 무관하게 일관된 접근 가능 |
PV vs PVC
| 개념 |
설명 |
비유 |
| PV |
관리자가 생성한 실제 스토리지 자원 |
빌딩의 빈 사무실 (공급) |
| PVC |
사용자가 크기·접근 모드를 요청하는 객체 |
사무실 임대 계약서 (요청) |
주요 설정 파라미터
- Capacity: 저장 공간 크기 (예: 5Gi)
- Access Modes:
ReadWriteOnce (RWO): 단일 노드 읽기/쓰기
ReadOnlyMany (ROX): 여러 노드 읽기 전용
ReadWriteMany (RWX): 여러 노드 읽기/쓰기 (EFS/NFS)
- Reclaim Policy:
Retain: 데이터 보존 (수동 삭제)
Delete: 물리 저장소 함께 삭제
Recycle: 데이터 비우고 재사용
프로비저닝 방식
- 정적(Static): 관리자가 PV를 수동으로 미리 생성
- 동적(Dynamic): PVC 생성 시 StorageClass를 통해 PV 자동 생성 (클라우드 환경 권장)
2. EKS + AWS EFS 통합 작업 순서
1단계: EFS 파일 시스템 및 네트워크 준비
# EFS 파일 시스템 생성 및 ID 추출
export EFS_ID=$(aws efs create-file-system \
--creation-token $(date +%s) \
--performance-mode generalPurpose \
--throughput-mode bursting \
--region ap-northeast-2 \
--query 'FileSystemId' \
--output text)
echo "생성된 EFS ID: $EFS_ID"
- EFS 보안 그룹 인바운드 규칙: NFS 포트(2049) 허용 필요
- 클러스터 노드가 위치한 모든 서브넷에 마운트 타겟 생성
2단계: EFS CSI 드라이버 설치
helm repo add aws-efs-csi-driver https://kubernetes-sigs.github.io/aws-efs-csi-driver/
helm repo update
helm install aws-efs-csi-driver aws-efs-csi-driver/aws-efs-csi-driver \
--namespace kube-system \
--set controller.serviceAccount.create=false \
--set controller.serviceAccount.name=efs-csi-controller-sa
3단계: StorageClass & PV/PVC 정의
kind: StorageClass
apiVersion: storage.k8s.io/v1
metadata:
name: efs-sc
provisioner: efs.csi.aws.com
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: efs-pv
spec:
capacity:
storage: 5Gi
volumeMode: Filesystem
accessModes:
- ReadWriteMany
persistentVolumeReclaimPolicy: Retain
storageClassName: efs-sc
csi:
driver: efs.csi.aws.com
volumeHandle: <EFS_ID>
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: efs-pvc
spec:
accessModes:
- ReadWriteMany
storageClassName: efs-sc
resources:
requests:
storage: 5Gi
kubectl apply -f efs-pvc.yaml
kubectl get pvc efs-pvc # Bound 상태 확인
4단계: 애플리케이션(Deployment) 연결
volumes:
- name: efs-storage
persistentVolumeClaim:
claimName: efs-pvc
volumeMounts:
- name: efs-storage
mountPath: /usr/share/nginx/html
3. 네트워크 통로(마운트 타겟) 생성
# 노드 보안 그룹 및 서브넷 추출
export NODE_SG=$(aws ec2 describe-instances \
--filters "Name=tag:Name,Values=*node*" \
--query "Reservations[0].Instances[0].SecurityGroups[0].GroupId" \
--output text)
export SUBNETS=$(aws ec2 describe-instances \
--filters "Name=tag:Name,Values=*node*" \
--query "Reservations[*].Instances[*].SubnetId" \
--output text | tr '\t' '\n' | sort -u)
# 2049 포트 인바운드 허용
aws ec2 authorize-security-group-ingress \
--group-id $NODE_SG \
--protocol tcp \
--port 2049 \
--source-group $NODE_SG
# 서브넷별 마운트 타겟 생성
for subnet in $SUBNETS; do
aws efs create-mount-target \
--file-system-id $EFS_ID \
--subnet-id $subnet \
--security-groups $NODE_SG
done
# 상태 확인 (available 확인)
aws efs describe-mount-targets --file-system-id $EFS_ID \
--query 'MountTargets[*].LifeCycleState'
4. 연결 확인 및 테스트
# 포드 상태 확인
kubectl get pods -l app=game-2048
# POD1에서 파일 생성
POD1=$(kubectl get pods -l app=game-2048 -o jsonpath='{.items[0].metadata.name}')
kubectl exec $POD1 -- sh -c "mkdir -p /usr/share/nginx/html/data && echo 'EFS Storage Connect Success' > /usr/share/nginx/html/data/hello.txt"
# POD2에서 파일 확인 (RWX 공유 검증)
POD2=$(kubectl get pods -l app=game-2048 -o jsonpath='{.items[1].metadata.name}')
kubectl exec $POD2 -- cat /usr/share/nginx/html/data/hello.txt
5. 전체 작업 순서 정리
| 단계 |
작업 |
명령어/도구 |
| 1 |
노드 IAM 역할 권한 부여 |
aws iam attach-role-policy |
| 2 |
EFS 보안 그룹 2049 포트 오픈 |
aws ec2 authorize-security-group-ingress |
| 3 |
EFS CSI 드라이버 설치 |
eksctl create addon 또는 Helm |
| 4 |
StorageClass / PV / PVC 배포 |
kubectl apply -f efs-pvc.yaml |
| 5 |
Deployment에 볼륨 마운트 연결 |
kubectl apply -f game-2048-efs.yaml |
| 6 |
포드 재시작 및 RWX 테스트 |
kubectl exec 파일 공유 확인 |
트러블슈팅 체크리스트
6. 삭제
# 쿠버네티스 리소스 삭제
kubectl delete -f game-2048-efs.yaml
kubectl delete -f efs-pvc.yaml
# 마운트 타겟 삭제
MOUNT_TARGET_IDS=$(aws efs describe-mount-targets --file-system-id $EFS_ID \
--query 'MountTargets[*].MountTargetId' --output text)
for mt_id in $MOUNT_TARGET_IDS; do
aws efs delete-mount-target --mount-target-id $mt_id
done
# EFS 파일 시스템 삭제
aws efs delete-file-system --file-system-id $EFS_ID
# CSI 드라이버 삭제
helm uninstall aws-efs-csi-driver -n kube-system
# 보안 그룹 규칙 삭제
aws ec2 revoke-security-group-ingress \
--group-id $NODE_SG \
--protocol tcp \
--port 2049 \
--source-group $NODE_SG
# 클러스터 삭제
eksctl delete cluster --name free-vpc-cluster