AWS 에서 Kubernetes Volume 때문에 Pod 가 Rescheduling 안되는 문제 해결
EKS 서비스를 이용하기 보다는 Kubernetes 를 AWS 에 직접 설치하여 활용하는 경우가 종종 있는데, 이 경우에는 세부적인 세팅들을 해줘야 할 때가 있다. 그 중에 대표적인 추가 모듈이 Pod 에서 EBS Volume 을 연결하여 사용할 수 있는 StorageClass 이다.
잘못된 Storage Class 를 사용하는 경우
Storage class 의 Provisioner 를 kubernetes.io/aws-ebs 로 사용하는 경우가 있는데, 이는 처음에는 문제가 없으나 Pod eviction (pod 가 다른 노드로 옮겨지는 경우) 에서는 문제가 발생한다.
아래와 같은 storage class 를 적용한다.
---
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: bad-storage
provisioner: kubernetes.io/aws-ebs
parameters:
type: gp2
fsType: ext4
처음 만든 Pod 에서 위의 StorageClass 를 사용하여 volume 을 ReadWriteOnce 로 attach 한 경우에는 문제없이 정상 작동한다. 하지만 pod 가 rescheduling 되면 새로 옮겨가는 노드에서 pod 는 생성되지 않는다. 이유는 volume 이 이전 노드의 Pod에 이미 사용되고 있다는 정보 때문에 새로운 노드의 Pod 에서 volume attach 가 안되기 때문이다.
즉, 해당 문제를 해결하기 위해서는 이전의 attach 정보를 삭제해 줘야 하는데 위의 storage class 로는 이를 해결할 수 없다. 그래서 다음과 같은 StroageClass 를 사용해야 한다.
올바른 Storage Class 사용하기
1. CSI Provisioner 설치
AWS EBS 를 위한 CSI Driver 는 다음과 같이 설치해야 한다.
$ kubectl apply -k "github.com/kubernetes-sigs/aws-ebs-csi-driver/deploy/kubernetes/overlays/stable/?ref=release-1.5"
$ kubectl get pods -n kube-system
...
ebs-csi-controller-6fd55f99c8-9j2hr 6/6 Running 0 13d
ebs-csi-controller-6fd55f99c8-x5dkt 6/6 Running 0 13d
ebs-csi-node-bw6jb 3/3 Running 0 13d
ebs-csi-node-nx7gl 3/3 Running 0 13d
ebs-csi-node-zh242 3/3 Running 0 13d
...
kustomize 로 설치하면 ebs-csi-controller 가 HA 로 설치되고, 각 노드 마다 ebs-csi-node 가 설치된다. 이는 ceph 스토리지 용도의 CSI Driver 와도 비슷한데 volume 을 사용할 Pod 가 실행될 VM 노드에 EBS 링크를 연결하고 이를 Pod 에서 사용하기 때문에 각 노드마다 ebs-csi-node 가 설치되는 이유이다.
2. Storage class 생성
driver 를 설치했으면 이제 스토리지 클래스를 생성할 차례이다. 아래와 같이 생성한다.
$ vi standard-ebs-sc.yaml
---
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
annotations:
storageclass.kubernetes.io/is-default-class: "true"
name: standard
provisioner: ebs.csi.aws.com
volumeBindingMode: WaitForFirstConsumer
parameters:
csi.storage.k8s.io/fstype: xfs
type: io1
iopsPerGB: "50"
encrypted: "false"
reclaimPolicy: Delete
allowVolumeExpansion: true
allowedTopologies:
- matchLabelExpressions:
- key: topology.ebs.csi.aws.com/zone
values:
- ap-northeast-2a
- ap-northeast-2b
- ap-northeast-2c
$ kubectl apply -f standard-ebs-sc.yaml
annotations 의 storageclass.kubernetes.io/is-default-class":"true" 값은 스토리지 클래스를 디폴트로 지정하는 값이다. 스토리지 클래스는 여러가지를 사용할 수 있기 때문에 기본을 지정해 주는 것이고, 또한 ebs 가 생성될 AZ 리스트를 넣어줘야 한다.
3. IAM 설정
앞에서 VM 에 ebs 링크를 걸어준다고 했는데 그렇기 때문에 IAM 설정을 해야 한다. 아래와 같이 Policy 를 만들고 해당 Policy 를 control-plane.cluster-api-provider-aws.sigs.k8s.io 과 nodes.cluster-api-provider-aws.sigs.k8s.io 에 attach 해야 한다.
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"ec2:CreateSnapshot",
"ec2:AttachVolume",
"ec2:DetachVolume",
"ec2:ModifyVolume",
"ec2:DescribeAvailabilityZones",
"ec2:DescribeInstances",
"ec2:DescribeSnapshots",
"ec2:DescribeTags",
"ec2:DescribeVolumes",
"ec2:DescribeVolumesModifications"
],
"Resource": "*"
},
{
"Effect": "Allow",
"Action": [
"ec2:CreateTags"
],
"Resource": [
"arn:aws:ec2:*:*:volume/*",
"arn:aws:ec2:*:*:snapshot/*"
],
"Condition": {
"StringEquals": {
"ec2:CreateAction": [
"CreateVolume",
"CreateSnapshot"
]
}
}
},
{
"Effect": "Allow",
"Action": [
"ec2:DeleteTags"
],
"Resource": [
"arn:aws:ec2:*:*:volume/*",
"arn:aws:ec2:*:*:snapshot/*"
]
},
{
"Effect": "Allow",
"Action": [
"ec2:CreateVolume"
],
"Resource": "*"
},
{
"Effect": "Allow",
"Action": [
"ec2:CreateVolume"
],
"Resource": "*"
},
{
"Effect": "Allow",
"Action": [
"ec2:CreateVolume"
],
"Resource": "*"
},
{
"Effect": "Allow",
"Action": [
"ec2:DeleteVolume"
],
"Resource": "*"
},
{
"Effect": "Allow",
"Action": [
"ec2:DeleteVolume"
],
"Resource": "*"
},
{
"Effect": "Allow",
"Action": [
"ec2:DeleteVolume"
],
"Resource": "*"
},
{
"Effect": "Allow",
"Action": [
"ec2:DeleteSnapshot"
],
"Resource": "*"
},
{
"Effect": "Allow",
"Action": [
"ec2:DeleteSnapshot"
],
"Resource": "*"
}
]
}
4. 테스트
샘플 코드를 만들어서 잘 동작하는지 테스트 해 보자.
$ vi app-ebs-example.yaml
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: app-ebs-claim
spec:
accessModes:
- ReadWriteOnce
storageClassName: standard
resources:
requests:
storage: 4Gi
---
apiVersion: v1
kind: Pod
metadata:
name: app
spec:
containers:
- name: app
image: centos
command: ["/bin/sh"]
args: ["-c", "while true; do echo $(date -u) >> /data/out.txt; sleep 5; done"]
volumeMounts:
- name: persistent-storage
mountPath: /data
volumes:
- name: persistent-storage
persistentVolumeCl
claimName: app-ebs-claim
$ kubectl apply -f app-ebs-example.yaml
위의 코드를 설치하면 pvc 가 아래와 같이 잘 생성되는 것을 알 수 있다.
$ kubectl get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
app-ebs-claim Bound pvc-4ab38fe9-7e76-4d81-85d3-0db68da9c1e0 4Gi RWO standard 6s
$ kubectl get volumeattachments
NAME ATTACHER PV NODE ATTACHED AGE
csi-60f252627e620b1e91d7d32a7d463b406a3850f3a7d7b0c535fe209f699ba3bd ebs.csi.aws.com pvc-4ab38fe9-7e76-4d81-85d3-0db68da9c1e0 ip-10-0-214-57.ap-northeast-2.compute.internal true 2m8s
또한 volumeattachments 를 보면 VM 에 해당 volume 이 attach 되어 링크가 걸려 있는 것을 알 수 있다.