StatefulSet 의 파드가 Init 또는 ContainerCreating 에서 멈추고 이벤트에 다음이 반복된다.
Warning FailedMount kubelet
Unable to attach or mount volumes: unmounted volumes=[pgdata],
unattached volumes=[...]: timed out waiting for the condition
스케줄링 단계에서 막히면 메시지가 더 직접적이다.
Warning FailedScheduling default-scheduler
0/5 nodes are available: 5 node(s) had volume node affinity conflict.
특정 워크로드만 이렇고 나머지 파드는 멀쩡한 것이 특징이다. 전체가 다 안 되면 다른 원인이다.
EBS 볼륨은 가용 영역에 묶인다. 한 번 ap-northeast-2a 에 만들어진 볼륨은 ap-northeast-2b 의 노드에 붙일 수 없다. 옮기는 것도 불가능하다. 스냅샷을 떠서 다른 영역에 새로 만드는 것이 유일한 방법이다.
PV 에는 이 제약이 nodeAffinity 로 기록돼 있다. 파드가 그 영역이 아닌 노드로 배정되면 스케줄러가 거부하거나, 배정된 뒤 kubelet 이 붙이지 못해 시간 초과가 난다.
전형적인 발생 경로는 둘이다.
volumeBindingMode 가 Immediate 라 파드가 스케줄되기도 전에 볼륨이 특정 영역에 만들어졌다.먼저 노드가 어느 영역에 있는지 본다.
kubectl get nodes -L topology.kubernetes.io/zone
다음으로 문제의 PV 가 어느 영역에 묶여 있는지 본다.
kubectl get pv | grep -i rabbit
kubectl get pv <PV 이름> -o jsonpath='{.spec.nodeAffinity}' | jq .
StorageClass 의 바인딩 방식을 확인한다.
kubectl get sc gp3 -o yaml | grep -E 'provisioner|volumeBindingMode|allowedTopologies' -A3
AWS CLI 로 볼륨 상태를 보려다 다음 오류를 만나는 일이 흔하다.
An error occurred (InvalidParameterValue) when calling the DescribeVolumes operation:
Value (volumes) for parameter pvc-0458e752-... is invalid. Expected: 'vol-...'.
pvc-... 는 Kubernetes 가 만든 PV 이름이고, AWS 가 요구하는 것은 vol-... 형식의 EBS 볼륨 ID 다. PV 에서 꺼낸다.
kubectl get pv <PV 이름> -o jsonpath='{.spec.csi.volumeHandle}'
in-tree 프로비저너로 만들어진 옛 PV 라면 다른 자리에 있다.
kubectl get pv <PV 이름> -o jsonpath='{.spec.awsElasticBlockStore.volumeID}'
이 값은 aws://ap-northeast-2a/vol-002907a1264e906aa 형태라 영역과 볼륨 ID 를 한 번에 알려 준다. 얻은 ID 로 실제 상태를 본다.
aws ec2 describe-volumes --volume-ids vol-002907a1264e906aa \
--query "Volumes[*].{ID:VolumeId,AZ:AvailabilityZone,State:State,Attachments:Attachments[*].State}"
State 가 available 이면 어디에도 붙어 있지 않다는 뜻이다. in-use 인데 다른 인스턴스에 붙어 있으면 먼저 떼어야 한다.
볼륨을 옮길 수 없으니 파드를 볼륨 쪽으로 보낸다. 해당 워크로드에 영역 제약을 준다.
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: topology.kubernetes.io/zone
operator: In
values: ["ap-northeast-2a"]
그 영역에 노드가 없으면 노드 그룹을 그 영역으로 늘린다. 관리형 노드 그룹이면 서브넷 구성에 해당 영역이 포함돼 있어야 한다.
이 방법은 그 영역이 죽으면 워크로드도 함께 죽는다는 약점이 있다. 임시 조치로 쓰고 아래의 재구성을 계획한다.
StorageClass 를 WaitForFirstConsumer 로 둔다. 파드가 어느 노드에 배정되는지 결정된 뒤에 볼륨이 만들어지므로 영역이 어긋날 수 없다.
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: gp3-wffc
provisioner: ebs.csi.aws.com
volumeBindingMode: WaitForFirstConsumer
allowVolumeExpansion: true
parameters:
type: gp3
fsType: xfs
이미 만들어진 PV 의 영역은 바뀌지 않는다. 데이터를 버려도 되는 환경이라면 해당 워크로드를 0 으로 줄이고 PVC 를 지운 뒤 새 StorageClass 로 다시 만든다. 데이터가 필요하면 EBS 스냅샷을 떠서 목표 영역에 볼륨을 만들고 그 볼륨을 가리키는 PV 를 수동으로 만든다.
PVC 가 지워지고 남은 Released PV 는 새 PVC 와 바인딩되지 않은 채 목록만 어지럽힌다. 지우기 전에 반드시 회수 정책을 확인한다.
kubectl get pv | grep Released
kubectl describe pv <PV 이름> | grep -i 'reclaim policy'
Retain 이면 PV 오브젝트만 사라지고 EBS 볼륨은 AWS 에 남는다. Delete 이면 EBS 볼륨까지 함께 삭제된다. 운영 데이터라면 스냅샷을 먼저 뜬다.
2b, 2 는 2a 같은 상태가 되면 노드 구성이 바뀔 때 일부만 못 뜬다.ReadWriteMany)와 EBS(ReadWriteOnce)를 한 워크로드에 섞지 않는다. 성능과 권한 양쪽에서 문제가 생긴다.