CDK를 사용하여 EKS에서 NFS 볼륨 자동 프로비저닝

35017 단어 kubernetesekscdkaws

목차


  • Introduction
  • Setting up CDK
  • Setting up an EKS cluster
  • Binding a PVC to EBS
  • Setting up EFS autoprovision
  • Binding a PVC to EFS
  • Cleaning up
  • Summary

  • 소개

    I recently set up an EKS cluster for a client. From the cluster we wanted to autoprovision EFS volumes based on persistent volume claims. We wanted to do all of this using CDK.

    I assume you're already somewhat familiar with CDK , Kubernetes , EKS 그리고 ~/.aws/credentials configured 를 가지고 있습니다.

    CDK 설정

    Let's get started!

    Create an empty working directory:

    mkdir eks-efs && cd eks-efs

    Then create a basic CDK TypeScript app:

    npx cdk@^2.32 init app --language typescript

    If this is the first time you use CDK on this AWS account, you need to bootstrap it (= creating some S3 buckets etc) by running:

    npx cdk bootstrap

    Next let's add a variable to keep the naming prefix used with all our resources. (If you already have similarly named resources, change it to something else.) Add this to the top of the file, after the imports:

    // lib/eks-efs-stack.ts
    ...
    const prefix = "my";
    ...
    

    EKS 클러스터 설정

    Add this (and all code snippets following this one) to the end of the constructor:

    // lib/eks-efs-stack.ts
    ...
        const vpc = new aws_ec2.Vpc(this, `${prefix}-vpc`, {
          vpcName: `${prefix}-vpc`,
        });
    
        const cluster = new aws_eks.Cluster(this, `${prefix}-eks`, {
          clusterName: `${prefix}-eks`,
          version: aws_eks.KubernetesVersion.V1_21,
          vpc,
        });
    ...
    

    The explicit VPC is strictly not needed, but we will use it later.

    Now deploy the cluster by running:

    npx cdk@^2.32 deploy

    Creating all the required resources usually takes around 20-25 minutes.

    The result should look something like this (I have removed sensitive information, such as my AWS account number):

     ✅  my-stack
    
    ✨  Deployment time: 1373.08s
    
    Outputs:
    my-stack.myeksConfigCommand76382EC1 = aws eks update-kubeconfig --name my-eks --region eu-west-1 --role-arn arn:aws:iam::...:role/my-stack-myeksMastersRole...-...
    my-stack.myeksGetTokenCommand0DD2F5A8 = aws eks get-token --cluster-name my-eks --region eu-west-1 --role-arn arn:aws:iam::...:role/my-stack-myeksMastersRole...-...
    Stack ARN:
    arn:aws:cloudformation:eu-west-1:...:stack/my-stack/bbb03590-05cf-11ed-808a-02bd3ce8aca3
    

    kubectl 구성



    이제 클러스터를 가동하고 실행했으므로 kubectl을 사용하여 일반적인 방식으로 클러스터를 관리할 수 있습니다. 먼저 이전 단계에서 ConfigCommand 출력 값을 복사하여 클러스터를 kubeconfig에 추가해야 합니다. 또한 컨텍스트 이름이 그다지 기억에 남지 않는 클러스터 ARN이 되므로 --alias 플래그를 추가하는 것이 좋습니다.

    제 경우에는 다음을 실행합니다.
    aws eks update-kubeconfig --name my-eks --region eu-west-1 --role-arn arn:aws:iam::...:role/my-stack-myeksMastersRoleD2A59038-47WE3AI9OHS3 --alias my-eks
    다음과 같은 몇 가지 간단한 확인이 뒤따릅니다.
    kubectl get pod -A
    그리고 얻다:

    NAMESPACE     NAME                       READY   STATUS    RESTARTS   AGE
    kube-system   aws-node-nmwzs             1/1     Running   0          36m
    kube-system   aws-node-r2hjk             1/1     Running   0          36m
    kube-system   coredns-7cc879f8db-8ntfk   1/1     Running   0          42m
    kube-system   coredns-7cc879f8db-dsrs9   1/1     Running   0          42m
    kube-system   kube-proxy-247kc           1/1     Running   0          36m
    kube-system   kube-proxy-6g45p           1/1     Running   0          36m
    


    지원되는 Kubernetes 버전



    여기에서 꽤 오래된 Kubernetes 1.21을 사용하기로 선택한 것을 눈치채셨을 것입니다. 작성 당시에는 불행하게도 CDK와 함께 사용할 수 있는 Kubernetes의 최신 버전입니다. (콘솔을 사용하는 경우 1.22까지 올라갈 수 있습니다.) 1.21은 Kubernetes 프로젝트에서 더 이상 지원되지 않지만I recently learned AWS 자체에서 여전히 지원하므로 보안 수정 사항을 백포트합니다.

    PVC를 EBS에 바인딩

    EBS autoprovisioning support is already built into EKS, via a storage class called gp2.

    If we create a persistent volume claim (PVC):

    cat <<EOF | kubectl apply -f -
    apiVersion: v1
    kind: PersistentVolumeClaim
    metadata:
      name: ebs-rwo
    spec:
      accessModes:
        - ReadWriteOnce
      storageClassName: gp2
      resources:
        requests:
          storage: 1Gi
    EOF
    

    And a pod that uses the PVC:

    cat <<EOF | kubectl apply -f -
    apiVersion: v1
    kind: Pod
    metadata:
      name: ebs-rwo
    spec:
      containers:
        - name: nginx
          image: nginx
      volumes:
        - name: persistent-storage
          persistentVolumeClaim:
            claimName: ebs-rwo
    EOF
    

    Wait a few seconds, then check the status of the new PVC:

    kubectl get pvc

    NAME      STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS
    ebs-rwo   Bound    pvc-71606839-b2d9-4f36-8e1c-942b8d7e38f1   1Gi        RWO            gp2
    

    And the associated newly created persistent volume (PV):

    kubectl get pv

    NAME                                       CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM             STORAGECLASS
    pvc-71606839-b2d9-4f36-8e1c-942b8d7e38f1   1Gi        RWO            Delete           Bound    default/ebs-rwo   gp2
    

    However, there are some reasons why we won't want to use EBS for persistent storage. One of them is that EBS doesn't support access mode ReadWriteMany. EFS to the rescue!

    EFS 자동 프로비저닝 설정

    However, EFS is not supported out-of-the-box as EBS is, and needs some setting up to work as smoothly. The are complete instructions AWS 웹 사이트에 있지만 최신 IaC(Infrastructure-as-Code) 접근 방식과 잘 맞지 않는 명령형 CLI 명령을 사용합니다.

    EFS 파일 시스템 추가



    먼저 네트워크 보안 그룹을 만들어야 합니다.

    // lib/eks-efs-stack.ts
    ...
        const efsInboundSecurityGroup = new aws_ec2.SecurityGroup(
          this,
          `${prefix}-efs-inbound-security-group`,
          {
            securityGroupName: `${prefix}-efs-inbound`,
            vpc,
          },
        );
    ...
    


    수신 EFS 트래픽을 허용하는 수신 규칙 사용:

    // lib/eks-efs-stack.ts
    ...
        new aws_ec2.CfnSecurityGroupIngress(
          this,
          `${prefix}-efs-inbound-security-group-ingress`,
          {
            ipProtocol: "tcp",
            cidrIp: vpc.vpcCidrBlock,
            groupId: efsInboundSecurityGroup.securityGroupId,
            description: "Inbound EFS",
            fromPort: 2049,
            toPort: 2049,
          },
        );
    ...
    


    포트 2049는 표준 EFS 포트입니다. (fromPort 및 toPort는 단순히 포트 범위를 2049에서 2049로 정의하며 TCP 세계의 소스 및 대상 포트와 혼동하지 마십시오.)

    그런 다음 EFS 파일 시스템을 만듭니다.

    // lib/eks-efs-stack.ts
    ...
        const efs_fs = new aws_efs.FileSystem(this, `${prefix}-efs`, {
          fileSystemName: `${prefix}`,
          vpc,
          securityGroup: efsInboundSecurityGroup,
          removalPolicy: RemovalPolicy.DESTROY,
        });
    ...
    


    파일 시스템이 나머지 CDK 스택과 함께 삭제되도록 RemovalPolicy.DESTROY를 지정합니다.

    IAM 역할 추가



    먼저 EFS 액세스 포인트를 보고 만드는 데 필요한 권한을 부여하는 정책을 만듭니다.

    // lib/eks-efs-stack.ts
    ...
        const efsAllowPolicyDocument = aws_iam.PolicyDocument.fromJson({
          "Version": "2012-10-17",
          "Statement": [
            {
              "Effect": "Allow",
              "Action": [
                "elasticfilesystem:DescribeAccessPoints",
                "elasticfilesystem:DescribeFileSystems",
              ],
              "Resource": "*",
            },
            {
              "Effect": "Allow",
              "Action": [
                "elasticfilesystem:CreateAccessPoint",
              ],
              "Resource": "*",
              "Condition": {
                "StringLike": {
                  "aws:RequestTag/efs.csi.aws.com/cluster": "true",
                },
              },
            },
            {
              "Effect": "Allow",
              "Action": "elasticfilesystem:DeleteAccessPoint",
              "Resource": "*",
              "Condition": {
                "StringEquals": {
                  "aws:ResourceTag/efs.csi.aws.com/cluster": "true",
                },
              },
            },
          ],
        });
    ...
    


    다음으로 이 역할을 맡을 수 있는 사람을 지정합니다(AWS 콘솔에서 신뢰 관계라고 함).

    // lib/eks-efs-stack.ts
    ...
        const efsAssumeRolePolicyDocument = aws_iam.PolicyDocument.fromJson({
          "Version": "2012-10-17",
          "Statement": [
            {
              "Effect": "Allow",
              "Principal": {
                "Federated": cluster.openIdConnectProvider.openIdConnectProviderArn,
              },
              "Action": "sts:AssumeRoleWithWebIdentity",
              "Condition": {
                "StringEquals": new CfnJson(
                  this,
                  `${prefix}-assume-role-policy-document-string-equals-value`,
                  {
                    value: {
                      [`${cluster.openIdConnectProvider.openIdConnectProviderIssuer}:aud`]:
                        "sts.amazonaws.com",
                      [`${cluster.openIdConnectProvider.openIdConnectProviderIssuer}:sub`]:
                        "system:serviceaccount:kube-system:efs-csi-controller-sa",
                    },
                  },
                ),
              },
            },
          ],
        });
    ...
    


    클러스터와 함께 생성된 IODC 공급자를 참조합니다. 또한 특별한 마법의 CfnJson 구성을 사용하여 JSON에서 동적 키를 사용할 수 있습니다. (이것을 알아내는 데 꽤 오랜 시간이 걸렸습니다.)

    마지막으로 IAM 역할을 생성하여 모두 하나로 묶습니다.

    // lib/eks-efs-stack.ts
    ...
        const efsRole = new aws_iam.CfnRole(
          this,
          `${prefix}-efs-csi-controller-sa-role`,
          {
            roleName: `${prefix}-efs-csi-controller-sa-role`,
            assumeRolePolicyDocument: efsAssumeRolePolicyDocument,
            policies: [
              {
                policyName: `${prefix}-efs-csi-controller-sa-policy`,
                policyDocument: efsAllowPolicyDocument,
              },
            ],
          },
        );
    ...
    


    EFS CSI 드라이버 설치



    이제 EFS CSI 드라이버를 설치해야 합니다. 바인딩되지 않은 PVC를 확인하고 PV 및 EFS 액세스 포인트를 생성하는 Kubernetes 컨트롤러입니다. 가장 쉬운 방법은 Helm 차트를 이용하는 것입니다.

    // lib/eks-efs-stack.ts
    ...
        cluster.addHelmChart(`${prefix}-aws-efs-csi-driver`, {
          repository: "https://kubernetes-sigs.github.io/aws-efs-csi-driver/",
          chart: "aws-efs-csi-driver",
          version: "2.2.7",
          release: "aws-efs-csi-driver",
          namespace: "kube-system",
          values: {
            controller: {
              serviceAccount: {
                annotations: {
                  "eks.amazonaws.com/role-arn": efsRole.attrArn,
                },
              },
            },
          },
        });
    ...
    


    eks.amazonaws.com/role-arn은 해당 서비스 계정을 지정된 IAM 역할과 연결하는 EKS에서 인식하는 특수 주석입니다. 이는 해당 서비스 계정에서 실행되는 모든 포드/컨테이너가 해당 역할로 AWS API를 호출할 수 있음을 의미합니다. (IAM 역할을 EC2 인스턴스와 연결하는 것과 거의 같은 방식입니다.)

    스토리지 클래스 추가



    마지막으로 해야 할 일은 PVC가 사용할 Kubernetes 스토리지 클래스(SC)를 추가하는 것입니다.

    // lib/eks-efs-stack.ts
    ...
        cluster.addManifest(`${prefix}-k8s-efs-storageclass`, {
          apiVersion: "storage.k8s.io/v1",
          kind: "StorageClass",
          metadata: {
            name: "efs",
          },
          provisioner: "efs.csi.aws.com",
          parameters: {
            directoryPerms: "700",
            provisioningMode: "efs-ap",
            fileSystemId: efs_fs.fileSystemId,
            uid: "1001",
            gid: "1001",
          },
        });
    ...
    


    SC가 새로 설정한 EFS 파일 시스템을 참조하고 일부 Linux 관련 매개변수를 지정하는 방법에 주목하십시오.

    EFS에 PVC 바인딩

    Let's add a PVC which uses the SC:

    cat <<EOF | kubectl apply -f -
    apiVersion: v1
    kind: PersistentVolumeClaim
    metadata:
      name: efs-rwx
    spec:
      accessModes:
        - ReadWriteMany
      storageClassName: efs
      resources:
        requests:
          storage: 1Gi
    `EOF`
    

    And a pod that uses the PVC:

    cat <<EOF | kubectl apply -f -
    apiVersion: v1
    kind: Pod
    metadata:
      name: efs-rwx
    spec:
      containers:
        - name: nginx
          image: nginx
      volumes:
        - name: persistent-storage
          persistentVolumeClaim:
            claimName: efs-rwx
    `EOF`
    

    Now check the status of the new PVC:

    kubectl get pvc

    NAME      STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS
    ebs-rwo   Bound    pvc-71606839-b2d9-4f36-8e1c-942b8d7e38f1   1Gi        RWO            gp2
    efs-rwx   Bound    pvc-5743b62a-1e1a-4b0b-b930-921465fd9d9b   1Gi        RWX            efs
    

    And the associated newly created PV:

    kubectl get pv

    NAME                                       CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM             STORAGECLASS
    pvc-71606839-b2d9-4f36-8e1c-942b8d7e38f1   1Gi        RWO            Delete           Bound    default/ebs-rwo   gp2
    pvc-5743b62a-1e1a-4b0b-b930-921465fd9d9b   1Gi        RWX            Delete           Bound    default/efs-rwx   efs
    

    Notice that the new one is bound, uses SC efs and has access mode RWX (ReadWriteMany).

    We can also view the EFS access point that was created:

    aws efs describe-access-points

    AccessPoints:
    - AccessPointArn: arn:aws:elasticfilesystem:eu-west-1:...:access-point/fsap-082fd6323a789739d
      AccessPointId: fsap-082fd6323a789739d
      ClientToken: pvc-5743b62a-1e1a-4b0b-b930-921465fd9d9b
      FileSystemId: fs-0a724fe641cbd9da7
      LifeCycleState: available
      OwnerId: '...'
      PosixUser:
        Gid: 1001
        Uid: 1001
      RootDirectory:
        CreationInfo:
          OwnerGid: 1001
          OwnerUid: 1001
          Permissions: '700'
        Path: /pvc-5743b62a-1e1a-4b0b-b930-921465fd9d9b
      Tags:
      - Key: efs.csi.aws.com/cluster
        Value: 'true'
    

    청소

    In order to avoid further costs, let's clean up the resources we just created. This is as simple as:

    npx cdk destroy

    Again, this will take around 20-25 minutes.

    요약

    That's it! We set up autoprovisioning of NFS volumes in EKS using CDK. Full working code will soon be available on GitHub.

    Any comments or questions are welcome!

    좋은 웹페이지 즐겨찾기