Kubernetes

AWS 에서 Kubernetes Volume 때문에 Pod 가 Rescheduling 안되는 문제 해결

seungkyua@gmail.com 2022. 2. 9. 10:11
반응형

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 되어 링크가 걸려 있는 것을 알 수 있다.

 

 

반응형