티스토리 뷰

AEWS study

11주차 - ML Infra(GPU) on EKS

haru224 2025. 4. 15. 21:34

CloudNet@ 가시다님이 진행하는 AWS EKS Hands-on Study 내용 참고.

 

0. 실습 환경 구성


 

실습을 위해서는 G type Quota 확보 필요

 

  • 로컬에 아래 도구 설치되어 있는지 확인!
$ eksdemo create cluster gpusharing-demo -i t3.large -N 2 --region us-west-2 --dry-run

Eksctl Resource Manager Dry Run:
eksctl create cluster -f -
---
apiVersion: eksctl.io/v1alpha5
kind: ClusterConfig

metadata:
  name: gpusharing-demo
  region: us-west-2
  version: "1.32"
  tags:
    eksdemo.io/version: 0.18.2

addons:
- name: vpc-cni
  version: latest
  configurationValues: |-
    enableNetworkPolicy: "true"
    env:
      ENABLE_PREFIX_DELEGATION: "false"

cloudWatch:
  clusterLogging:
    enableTypes: ["*"]

iam:
  withOIDC: true
  serviceAccounts:
  - metadata:
      name: aws-load-balancer-controller
      namespace: awslb
    roleName: eksdemo.us-west-2.gpusharing-demo.awslb.aws-load-balanc-e4dab3bd
    roleOnly: true
    attachPolicy:
      Version: '2012-10-17'
      Statement:
      - Effect: Allow
        Action:
        - iam:CreateServiceLinkedRole
        Resource: "*"
        Condition:
          StringEquals:
            iam:AWSServiceName: elasticloadbalancing.amazonaws.com
      - Effect: Allow
        Action:
        - ec2:DescribeAccountAttributes
        - ec2:DescribeAddresses
        - ec2:DescribeAvailabilityZones
        - ec2:DescribeInternetGateways
        - ec2:DescribeVpcs
        - ec2:DescribeVpcPeeringConnections
        - ec2:DescribeSubnets
        - ec2:DescribeSecurityGroups
        - ec2:DescribeInstances
        - ec2:DescribeNetworkInterfaces
        - ec2:DescribeTags
        - ec2:GetCoipPoolUsage
        - ec2:DescribeCoipPools
        - elasticloadbalancing:DescribeLoadBalancers
        - elasticloadbalancing:DescribeLoadBalancerAttributes
        - elasticloadbalancing:DescribeListeners
        - elasticloadbalancing:DescribeListenerCertificates
        - elasticloadbalancing:DescribeSSLPolicies
        - elasticloadbalancing:DescribeRules
        - elasticloadbalancing:DescribeTargetGroups
        - elasticloadbalancing:DescribeTargetGroupAttributes
        - elasticloadbalancing:DescribeTargetHealth
        - elasticloadbalancing:DescribeTags
        - elasticloadbalancing:DescribeTrustStores
        - elasticloadbalancing:DescribeListenerAttributes
        Resource: "*"
      - Effect: Allow
        Action:
        - cognito-idp:DescribeUserPoolClient
        - acm:ListCertificates
        - acm:DescribeCertificate
        - iam:ListServerCertificates
        - iam:GetServerCertificate
        - waf-regional:GetWebACL
        - waf-regional:GetWebACLForResource
        - waf-regional:AssociateWebACL
        - waf-regional:DisassociateWebACL
        - wafv2:GetWebACL
        - wafv2:GetWebACLForResource
        - wafv2:AssociateWebACL
        - wafv2:DisassociateWebACL
        - shield:GetSubscriptionState
        - shield:DescribeProtection
        - shield:CreateProtection
        - shield:DeleteProtection
        Resource: "*"
      - Effect: Allow
        Action:
        - ec2:AuthorizeSecurityGroupIngress
        - ec2:RevokeSecurityGroupIngress
        Resource: "*"
      - Effect: Allow
        Action:
        - ec2:CreateSecurityGroup
        Resource: "*"
      - Effect: Allow
        Action:
        - ec2:CreateTags
        Resource: arn:aws:ec2:*:*:security-group/*
        Condition:
          StringEquals:
            ec2:CreateAction: CreateSecurityGroup
          'Null':
            aws:RequestTag/elbv2.k8s.aws/cluster: 'false'
      - Effect: Allow
        Action:
        - ec2:CreateTags
        - ec2:DeleteTags
        Resource: arn:aws:ec2:*:*:security-group/*
        Condition:
          'Null':
            aws:RequestTag/elbv2.k8s.aws/cluster: 'true'
            aws:ResourceTag/elbv2.k8s.aws/cluster: 'false'
      - Effect: Allow
        Action:
        - ec2:AuthorizeSecurityGroupIngress
        - ec2:RevokeSecurityGroupIngress
        - ec2:DeleteSecurityGroup
        Resource: "*"
        Condition:
          'Null':
            aws:ResourceTag/elbv2.k8s.aws/cluster: 'false'
      - Effect: Allow
        Action:
        - elasticloadbalancing:CreateLoadBalancer
        - elasticloadbalancing:CreateTargetGroup
        Resource: "*"
        Condition:
          'Null':
            aws:RequestTag/elbv2.k8s.aws/cluster: 'false'
      - Effect: Allow
        Action:
        - elasticloadbalancing:CreateListener
        - elasticloadbalancing:DeleteListener
        - elasticloadbalancing:CreateRule
        - elasticloadbalancing:DeleteRule
        Resource: "*"
      - Effect: Allow
        Action:
        - elasticloadbalancing:AddTags
        - elasticloadbalancing:RemoveTags
        Resource:
        - arn:aws:elasticloadbalancing:*:*:targetgroup/*/*
        - arn:aws:elasticloadbalancing:*:*:loadbalancer/net/*/*
        - arn:aws:elasticloadbalancing:*:*:loadbalancer/app/*/*
        Condition:
          'Null':
            aws:RequestTag/elbv2.k8s.aws/cluster: 'true'
            aws:ResourceTag/elbv2.k8s.aws/cluster: 'false'
      - Effect: Allow
        Action:
        - elasticloadbalancing:AddTags
        - elasticloadbalancing:RemoveTags
        Resource:
        - arn:aws:elasticloadbalancing:*:*:listener/net/*/*/*
        - arn:aws:elasticloadbalancing:*:*:listener/app/*/*/*
        - arn:aws:elasticloadbalancing:*:*:listener-rule/net/*/*/*
        - arn:aws:elasticloadbalancing:*:*:listener-rule/app/*/*/*
      - Effect: Allow
        Action:
        - elasticloadbalancing:ModifyLoadBalancerAttributes
        - elasticloadbalancing:SetIpAddressType
        - elasticloadbalancing:SetSecurityGroups
        - elasticloadbalancing:SetSubnets
        - elasticloadbalancing:DeleteLoadBalancer
        - elasticloadbalancing:ModifyTargetGroup
        - elasticloadbalancing:ModifyTargetGroupAttributes
        - elasticloadbalancing:DeleteTargetGroup
        - elasticloadbalancing:ModifyListenerAttributes
        Resource: "*"
        Condition:
          'Null':
            aws:ResourceTag/elbv2.k8s.aws/cluster: 'false'
      - Effect: Allow
        Action:
        - elasticloadbalancing:AddTags
        Resource:
        - arn:aws:elasticloadbalancing:*:*:targetgroup/*/*
        - arn:aws:elasticloadbalancing:*:*:loadbalancer/net/*/*
        - arn:aws:elasticloadbalancing:*:*:loadbalancer/app/*/*
        Condition:
          StringEquals:
            elasticloadbalancing:CreateAction:
            - CreateTargetGroup
            - CreateLoadBalancer
          'Null':
            aws:RequestTag/elbv2.k8s.aws/cluster: 'false'
      - Effect: Allow
        Action:
        - elasticloadbalancing:RegisterTargets
        - elasticloadbalancing:DeregisterTargets
        Resource: arn:aws:elasticloadbalancing:*:*:targetgroup/*/*
      - Effect: Allow
        Action:
        - elasticloadbalancing:SetWebAcl
        - elasticloadbalancing:ModifyListener
        - elasticloadbalancing:AddListenerCertificates
        - elasticloadbalancing:RemoveListenerCertificates
        - elasticloadbalancing:ModifyRule
        Resource: "*"

  - metadata:
      name: ebs-csi-controller-sa
      namespace: kube-system
    roleName: eksdemo.us-west-2.gpusharing-demo.kube-system.ebs-csi-c-937ae3a3
    roleOnly: true
    attachPolicyARNs:
    - arn:aws:iam::aws:policy/service-role/AmazonEBSCSIDriverPolicy
  - metadata:
      name: external-dns
      namespace: external-dns
    roleName: eksdemo.us-west-2.gpusharing-demo.external-dns.external-dns
    roleOnly: true
    attachPolicy:
      Version: '2012-10-17'
      Statement:
      - Effect: Allow
        Action:
        - route53:ChangeResourceRecordSets
        Resource:
        - arn:aws:route53:::hostedzone/*
      - Effect: Allow
        Action:
        - route53:ListHostedZones
        - route53:ListResourceRecordSets
        - route53:ListTagsForResource
        Resource:
        - "*"

  - metadata:
      name: karpenter
      namespace: karpenter
    roleName: eksdemo.us-west-2.gpusharing-demo.karpenter.karpenter
    roleOnly: true
    attachPolicy:
      Version: "2012-10-17"
      Statement:
      - Sid: AllowScopedEC2InstanceAccessActions
        Effect: Allow
        Resource:
        - arn:aws:ec2:us-west-2::image/*
        - arn:aws:ec2:us-west-2::snapshot/*
        - arn:aws:ec2:us-west-2:*:security-group/*
        - arn:aws:ec2:us-west-2:*:subnet/*
        Action:
        - ec2:RunInstances
        - ec2:CreateFleet
      - Sid: AllowScopedEC2LaunchTemplateAccessActions
        Effect: Allow
        Resource: arn:aws:ec2:us-west-2:*:launch-template/*
        Action:
        - ec2:RunInstances
        - ec2:CreateFleet
        Condition:
          StringEquals:
            aws:ResourceTag/kubernetes.io/cluster/gpusharing-demo: owned
          StringLike:
            aws:ResourceTag/karpenter.sh/nodepool: "*"
      - Sid: AllowScopedEC2InstanceActionsWithTags
        Effect: Allow
        Resource:
        - arn:aws:ec2:us-west-2:*:fleet/*
        - arn:aws:ec2:us-west-2:*:instance/*
        - arn:aws:ec2:us-west-2:*:volume/*
        - arn:aws:ec2:us-west-2:*:network-interface/*
        - arn:aws:ec2:us-west-2:*:launch-template/*
        - arn:aws:ec2:us-west-2:*:spot-instances-request/*
        Action:
        - ec2:RunInstances
        - ec2:CreateFleet
        - ec2:CreateLaunchTemplate
        Condition:
          StringEquals:
            aws:RequestTag/kubernetes.io/cluster/gpusharing-demo: owned
            aws:RequestTag/eks:eks-cluster-name: gpusharing-demo
          StringLike:
            aws:RequestTag/karpenter.sh/nodepool: "*"
      - Sid: AllowScopedResourceCreationTagging
        Effect: Allow
        Resource:
        - arn:aws:ec2:us-west-2:*:fleet/*
        - arn:aws:ec2:us-west-2:*:instance/*
        - arn:aws:ec2:us-west-2:*:volume/*
        - arn:aws:ec2:us-west-2:*:network-interface/*
        - arn:aws:ec2:us-west-2:*:launch-template/*
        - arn:aws:ec2:us-west-2:*:spot-instances-request/*
        Action: ec2:CreateTags
        Condition:
          StringEquals:
            aws:RequestTag/kubernetes.io/cluster/gpusharing-demo: owned
            aws:RequestTag/eks:eks-cluster-name: gpusharing-demo
            ec2:CreateAction:
            - RunInstances
            - CreateFleet
            - CreateLaunchTemplate
          StringLike:
            aws:RequestTag/karpenter.sh/nodepool: "*"
      - Sid: AllowScopedResourceTagging
        Effect: Allow
        Resource: arn:aws:ec2:us-west-2:*:instance/*
        Action: ec2:CreateTags
        Condition:
          StringEquals:
            aws:ResourceTag/kubernetes.io/cluster/gpusharing-demo: owned
          StringLike:
            aws:ResourceTag/karpenter.sh/nodepool: "*"
          StringEqualsIfExists:
            aws:RequestTag/eks:eks-cluster-name: gpusharing-demo
          ForAllValues:StringEquals:
            aws:TagKeys:
            - eks:eks-cluster-name
            - karpenter.sh/nodeclaim
            - Name
      - Sid: AllowScopedDeletion
        Effect: Allow
        Resource:
        - arn:aws:ec2:us-west-2:*:instance/*
        - arn:aws:ec2:us-west-2:*:launch-template/*
        Action:
        - ec2:TerminateInstances
        - ec2:DeleteLaunchTemplate
        Condition:
          StringEquals:
            aws:ResourceTag/kubernetes.io/cluster/gpusharing-demo: owned
          StringLike:
            aws:ResourceTag/karpenter.sh/nodepool: "*"
      - Sid: AllowRegionalReadActions
        Effect: Allow
        Resource: "*"
        Action:
        - ec2:DescribeImages
        - ec2:DescribeInstances
        - ec2:DescribeInstanceTypeOfferings
        - ec2:DescribeInstanceTypes
        - ec2:DescribeLaunchTemplates
        - ec2:DescribeSecurityGroups
        - ec2:DescribeSpotPriceHistory
        - ec2:DescribeSubnets
        Condition:
          StringEquals:
            aws:RequestedRegion: "us-west-2"
      - Sid: AllowSSMReadActions
        Effect: Allow
        Resource: arn:aws:ssm:us-west-2::parameter/aws/service/*
        Action:
        - ssm:GetParameter
      - Sid: AllowPricingReadActions
        Effect: Allow
        Resource: "*"
        Action:
        - pricing:GetProducts
      - Sid: AllowInterruptionQueueActions
        Effect: Allow
        Resource: arn:aws:sqs:us-west-2:767397897074:karpenter-gpusharing-demo
        Action:
        - sqs:DeleteMessage
        - sqs:GetQueueUrl
        - sqs:ReceiveMessage
      - Sid: AllowPassingInstanceRole
        Effect: Allow
        Resource: arn:aws:iam::767397897074:role/KarpenterNodeRole-gpusharing-demo
        Action: iam:PassRole
        Condition:
          StringEquals:
            iam:PassedToService:
            - ec2.amazonaws.com
            - ec2.amazonaws.com.cn
      - Sid: AllowScopedInstanceProfileCreationActions
        Effect: Allow
        Resource: arn:aws:iam::767397897074:instance-profile/*
        Action:
        - iam:CreateInstanceProfile
        Condition:
          StringEquals:
            aws:RequestTag/kubernetes.io/cluster/gpusharing-demo: owned
            aws:RequestTag/eks:eks-cluster-name: gpusharing-demo
            aws:RequestTag/topology.kubernetes.io/region: "us-west-2"
          StringLike:
            aws:RequestTag/karpenter.k8s.aws/ec2nodeclass: "*"
      - Sid: AllowScopedInstanceProfileTagActions
        Effect: Allow
        Resource: arn:aws:iam::767397897074:instance-profile/*
        Action:
        - iam:TagInstanceProfile
        Condition:
          StringEquals:
            aws:ResourceTag/kubernetes.io/cluster/gpusharing-demo: owned
            aws:ResourceTag/topology.kubernetes.io/region: "us-west-2"
            aws:RequestTag/kubernetes.io/cluster/gpusharing-demo: owned
            aws:RequestTag/eks:eks-cluster-name: gpusharing-demo
            aws:RequestTag/topology.kubernetes.io/region: "us-west-2"
          StringLike:
            aws:ResourceTag/karpenter.k8s.aws/ec2nodeclass: "*"
            aws:RequestTag/karpenter.k8s.aws/ec2nodeclass: "*"
      - Sid: AllowScopedInstanceProfileActions
        Effect: Allow
        Resource: arn:aws:iam::767397897074:instance-profile/*
        Action:
        - iam:AddRoleToInstanceProfile
        - iam:RemoveRoleFromInstanceProfile
        - iam:DeleteInstanceProfile
        Condition:
          StringEquals:
            aws:ResourceTag/kubernetes.io/cluster/gpusharing-demo: owned
            aws:ResourceTag/topology.kubernetes.io/region: "us-west-2"
          StringLike:
            aws:ResourceTag/karpenter.k8s.aws/ec2nodeclass: "*"
      - Sid: AllowInstanceProfileReadActions
        Effect: Allow
        Resource: arn:aws:iam::767397897074:instance-profile/*
        Action: iam:GetInstanceProfile
      - Sid: AllowAPIServerEndpointDiscovery
        Effect: Allow
        Resource: arn:aws:eks:us-west-2:767397897074:cluster/gpusharing-demo
        Action: eks:DescribeCluster


vpc:
  cidr: 192.168.0.0/16
  hostnameType: resource-name

managedNodeGroups:
- name: main
  ami: ami-092590b6039cd49ed
  amiFamily: AmazonLinux2
  desiredCapacity: 2
  iam:
    attachPolicyARNs:
    - arn:aws:iam::aws:policy/AmazonEKSWorkerNodePolicy
    - arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryReadOnly
    - arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore
  instanceType: t3.large
  minSize: 0
  maxSize: 10
  volumeSize: 80
  volumeType: gp3
  overrideBootstrapCommand: |
    #!/bin/bash
    /etc/eks/bootstrap.sh gpusharing-demo
  privateNetworking: true
  spot: false

 

  • 실행 명령어: eksdemo create cluster gpusharing-demo -i t3.large -N 2 --region us-west-2

결과 예시

$ eksdemo create cluster gpusharing-demo -i t3.large -N 2 --region us-west-2
2025-04-12 23:50:21 [ℹ]  eksctl version 0.207.0
2025-04-12 23:50:21 [ℹ]  using region us-west-2
2025-04-12 23:50:22 [ℹ]  setting availability zones to [us-west-2a us-west-2b us-west-2d]
2025-04-12 23:50:22 [ℹ]  subnets for us-west-2a - public:192.168.0.0/19 private:192.168.96.0/19
2025-04-12 23:50:22 [ℹ]  subnets for us-west-2b - public:192.168.32.0/19 private:192.168.128.0/19
2025-04-12 23:50:22 [ℹ]  subnets for us-west-2d - public:192.168.64.0/19 private:192.168.160.0/19
2025-04-12 23:50:22 [ℹ]  nodegroup "main" will use "ami-092590b6039cd49ed" [AmazonLinux2/1.32]
2025-04-12 23:50:22 [ℹ]  using Kubernetes version 1.32
2025-04-12 23:50:22 [ℹ]  creating EKS cluster "gpusharing-demo" in "us-west-2" region with managed nodes
2025-04-12 23:50:22 [ℹ]  1 nodegroup (main) was included (based on the include/exclude rules)
2025-04-12 23:50:22 [ℹ]  will create a CloudFormation stack for cluster itself and 1 managed nodegroup stack(s)
2025-04-12 23:50:22 [ℹ]  if you encounter any issues, check CloudFormation console or try 'eksctl utils describe-stacks --region=us-west-2 --cluster=gpusharing-demo'
2025-04-12 23:50:22 [ℹ]  Kubernetes API endpoint access will use default of {publicAccess=true, privateAccess=false} for cluster "gpusharing-demo" in "us-west-2"
2025-04-12 23:50:22 [ℹ]  configuring CloudWatch logging for cluster "gpusharing-demo" in "us-west-2" (enabled types: api, audit, authenticator, controllerManager, scheduler & no types disabled)
2025-04-12 23:50:22 [ℹ]  default addons coredns, metrics-server, kube-proxy were not specified, will install them as EKS addons
2025-04-12 23:50:22 [ℹ]
2 sequential tasks: { create cluster control plane "gpusharing-demo",
    2 sequential sub-tasks: {
        5 sequential sub-tasks: {
            1 task: { create addons },
            wait for control plane to become ready,
            associate IAM OIDC provider,
            4 parallel sub-tasks: {
                create IAM role for serviceaccount "awslb/aws-load-balancer-controller",
                create IAM role for serviceaccount "kube-system/ebs-csi-controller-sa",
                create IAM role for serviceaccount "external-dns/external-dns",
                create IAM role for serviceaccount "karpenter/karpenter",
            },
            update VPC CNI to use IRSA if required,
        },
        create managed nodegroup "main",
    }
}
2025-04-12 23:50:22 [ℹ]  building cluster stack "eksctl-gpusharing-demo-cluster"
2025-04-12 23:50:23 [ℹ]  deploying stack "eksctl-gpusharing-demo-cluster"
2025-04-12 23:50:54 [ℹ]  waiting for CloudFormation stack "eksctl-gpusharing-demo-cluster"
2025-04-12 23:51:25 [ℹ]  waiting for CloudFormation stack "eksctl-gpusharing-demo-cluster"
2025-04-12 23:52:27 [ℹ]  waiting for CloudFormation stack "eksctl-gpusharing-demo-cluster"
2025-04-12 23:53:28 [ℹ]  waiting for CloudFormation stack "eksctl-gpusharing-demo-cluster"
2025-04-12 23:54:30 [ℹ]  waiting for CloudFormation stack "eksctl-gpusharing-demo-cluster"
2025-04-12 23:55:32 [ℹ]  waiting for CloudFormation stack "eksctl-gpusharing-demo-cluster"
2025-04-12 23:56:34 [ℹ]  waiting for CloudFormation stack "eksctl-gpusharing-demo-cluster"
2025-04-12 23:57:36 [ℹ]  waiting for CloudFormation stack "eksctl-gpusharing-demo-cluster"
2025-04-12 23:58:38 [ℹ]  waiting for CloudFormation stack "eksctl-gpusharing-demo-cluster"
2025-04-12 23:58:42 [!]  recommended policies were found for "vpc-cni" addon, but since OIDC is disabled on the cluster, eksctl cannot configure the requested permissions; the recommended way to provide IAM permissions for "vpc-cni" addon is via pod identity associations; after addon creation is completed, add all recommended policies to the config file, under `addon.PodIdentityAssociations`, and run `eksctl update addon`
2025-04-12 23:58:42 [ℹ]  creating addon: vpc-cni
2025-04-12 23:58:43 [ℹ]  successfully created addon: vpc-cni
2025-04-12 23:58:43 [ℹ]  creating addon: coredns
2025-04-12 23:58:44 [ℹ]  successfully created addon: coredns
2025-04-12 23:58:44 [ℹ]  creating addon: metrics-server
2025-04-12 23:58:45 [ℹ]  successfully created addon: metrics-server
2025-04-12 23:58:45 [ℹ]  creating addon: kube-proxy
2025-04-12 23:58:46 [ℹ]  successfully created addon: kube-proxy
2025-04-13 00:00:52 [ℹ]  building iamserviceaccount stack "eksctl-gpusharing-demo-addon-iamserviceaccount-karpenter-karpenter"
2025-04-13 00:00:52 [ℹ]  building iamserviceaccount stack "eksctl-gpusharing-demo-addon-iamserviceaccount-awslb-aws-load-balancer-controller"
2025-04-13 00:00:52 [ℹ]  building iamserviceaccount stack "eksctl-gpusharing-demo-addon-iamserviceaccount-kube-system-ebs-csi-controller-sa"
2025-04-13 00:00:52 [ℹ]  building iamserviceaccount stack "eksctl-gpusharing-demo-addon-iamserviceaccount-external-dns-external-dns"
2025-04-13 00:00:53 [ℹ]  deploying stack "eksctl-gpusharing-demo-addon-iamserviceaccount-awslb-aws-load-balancer-controller"
2025-04-13 00:00:53 [ℹ]  deploying stack "eksctl-gpusharing-demo-addon-iamserviceaccount-karpenter-karpenter"
2025-04-13 00:00:53 [ℹ]  deploying stack "eksctl-gpusharing-demo-addon-iamserviceaccount-kube-system-ebs-csi-controller-sa"
2025-04-13 00:00:53 [ℹ]  deploying stack "eksctl-gpusharing-demo-addon-iamserviceaccount-external-dns-external-dns"
2025-04-13 00:00:53 [ℹ]  waiting for CloudFormation stack "eksctl-gpusharing-demo-addon-iamserviceaccount-awslb-aws-load-balancer-controller"
2025-04-13 00:00:53 [ℹ]  waiting for CloudFormation stack "eksctl-gpusharing-demo-addon-iamserviceaccount-karpenter-karpenter"
2025-04-13 00:00:53 [ℹ]  waiting for CloudFormation stack "eksctl-gpusharing-demo-addon-iamserviceaccount-kube-system-ebs-csi-controller-sa"
2025-04-13 00:00:53 [ℹ]  waiting for CloudFormation stack "eksctl-gpusharing-demo-addon-iamserviceaccount-external-dns-external-dns"
2025-04-13 00:01:24 [ℹ]  waiting for CloudFormation stack "eksctl-gpusharing-demo-addon-iamserviceaccount-awslb-aws-load-balancer-controller"
2025-04-13 00:01:24 [ℹ]  waiting for CloudFormation stack "eksctl-gpusharing-demo-addon-iamserviceaccount-kube-system-ebs-csi-controller-sa"
2025-04-13 00:01:24 [ℹ]  waiting for CloudFormation stack "eksctl-gpusharing-demo-addon-iamserviceaccount-karpenter-karpenter"
2025-04-13 00:01:24 [ℹ]  waiting for CloudFormation stack "eksctl-gpusharing-demo-addon-iamserviceaccount-external-dns-external-dns"
2025-04-13 00:01:58 [ℹ]  waiting for CloudFormation stack "eksctl-gpusharing-demo-addon-iamserviceaccount-external-dns-external-dns"
2025-04-13 00:02:02 [ℹ]  waiting for CloudFormation stack "eksctl-gpusharing-demo-addon-iamserviceaccount-karpenter-karpenter"
2025-04-13 00:02:14 [ℹ]  waiting for CloudFormation stack "eksctl-gpusharing-demo-addon-iamserviceaccount-awslb-aws-load-balancer-controller"
2025-04-13 00:02:15 [ℹ]  addon "vpc-cni" active
2025-04-13 00:02:17 [ℹ]  deploying stack "eksctl-gpusharing-demo-addon-vpc-cni"
2025-04-13 00:02:18 [ℹ]  waiting for CloudFormation stack "eksctl-gpusharing-demo-addon-vpc-cni"
2025-04-13 00:02:48 [ℹ]  waiting for CloudFormation stack "eksctl-gpusharing-demo-addon-vpc-cni"
2025-04-13 00:02:49 [ℹ]  updating addon
2025-04-13 00:03:00 [ℹ]  addon "vpc-cni" active
2025-04-13 00:03:02 [ℹ]  building managed nodegroup stack "eksctl-gpusharing-demo-nodegroup-main"
2025-04-13 00:03:03 [ℹ]  deploying stack "eksctl-gpusharing-demo-nodegroup-main"
2025-04-13 00:03:03 [ℹ]  waiting for CloudFormation stack "eksctl-gpusharing-demo-nodegroup-main"
2025-04-13 00:03:34 [ℹ]  waiting for CloudFormation stack "eksctl-gpusharing-demo-nodegroup-main"
2025-04-13 00:04:25 [ℹ]  waiting for CloudFormation stack "eksctl-gpusharing-demo-nodegroup-main"
2025-04-13 00:06:05 [ℹ]  waiting for CloudFormation stack "eksctl-gpusharing-demo-nodegroup-main"
2025-04-13 00:06:05 [ℹ]  waiting for the control plane to become ready
2025-04-13 00:06:05 [✔]  saved kubeconfig as "/home/ian/.kube/config"
2025-04-13 00:06:05 [ℹ]  no tasks
2025-04-13 00:06:05 [✔]  all EKS cluster resources for "gpusharing-demo" have been created
2025-04-13 00:06:05 [✔]  created 1 managed nodegroup(s) in cluster "gpusharing-demo"
2025-04-13 00:06:07 [ℹ]  kubectl command should work with "/home/ian/.kube/config", try 'kubectl get nodes'
2025-04-13 00:06:07 [✔]  EKS cluster "gpusharing-demo" in "us-west-2" region is ready

 

  • cluster 생성 후 gpu 서버가 생성되지 않았고, 특정 az 에는 gpu type 서버가 생성되지 않는 것으로 확인되어 cluster 생성 시 az 를 지정하여 cluster 재생성 진행
# gpu 서버가 가능한 az 확인
aws ec2 describe-instance-type-offerings \
  --location-type availability-zone \
  --filters Name=instance-type,Values=g5.2xlarge \
  --region us-west-2 \
  --query "InstanceTypeOfferings[].Location" \
  --output text
  
  us-west-2b      us-west-2c      us-west-2a
  
  
# a, b, c zone 을 지정하여 cluster 생성되도록 지정
eksdemo create cluster gpusharing-demo -i t3.large -N 2 --region us-west-2 --zones us-west-1a,us-west-1b,us-west-1c --dry-run
eksdemo create cluster gpusharing-demo -i t3.large -N 2 --region us-west-2 --zones us-west-1a,us-west-1b,us-west-1c

 

 

1. AI 워크로드에 대한 컨테이너 사용


 

머신러닝과 딥러닝이 기업의 핵심 경쟁력으로 부상하면서, 데이터 과학자들은 점점 더 복잡하고 계산 집약적인 모델을 개발하고 있다. 이러한 모델을 효율적으로 학습하고 배포하기 위해서는 강력한 컴퓨팅 인프라가 필수적입니다. 특히 GPU를 사용하는 것은 병렬 처리 능력으로 딥러닝 워크로드를 가속화하는 데 있어 중요한 역할을 한다.

전통적으로 ML 엔지니어들은 베어메탈 서버에 직접 GPU 드라이버와 라이브러리를 설치하여 작업하였다. 이 접근 방식은 몇 가지 심각한 문제점을 가지고 있었다.

 

  • 환경 구성의 복잡성: CUDA, cuDNN 등 복잡한 드라이버 스택 설치 및 관리가 필요. 특히 버전 호환성 문제로 인해 특정 프레임워크(TensorFlow, PyTorch)가 특정 CUDA/cuDNN 버전만 지원하는 경우가 많았음.
  • 재현성 부족: 동일한 실험 환경을 다른 시스템에서 재현하기 어려움. '내 컴퓨터에서는 잘 작동하는데요?.'와 같은 이야기를 많이 들을 수 있었음.
  • 리소스 비효율성: 고가의 GPU 리소스가 특정 사용자나 프로젝트에 고정되어 활용도가 저하. (베어메탈 GPU 서버의 활용률은 30% 미만인 경우가 많았음)
  • 확장성 제한: 대규모 분산 학습을 위한 인프라 확장이 어려웠음. 새로운 GPU 서버를 추가할 때마다 동일한 환경 구성 과정을 반복 필요.

컨테이너 기술은 주로 두 가지 핵심 Linux 커널 기능에 의존한다:

  • Cgroups (Control Groups): 프로세스 그룹의 리소스 사용(CPU, 메모리, 디스크 I/O, 네트워크 등)을 제한하고 격리하는 메커니즘
  • Namespaces: 프로세스가 시스템의 특정 리소스만 볼 수 있도록 격리하는 기능 Linux는 여러 유형의 네임스페이스(PID, 네트워크, 마운트, UTS, IPC, 사용자 등)를 제공

이러한 기술들은 CPU와 메모리 같은 리소스를 효과적으로 관리할 수 있게 해주지만, GPU와 같은 특수 하드웨어 장치에 대해서는 몇 가지 근본적인 제약이 있었음:

  • 물리적으로 분할하기 어려운 GPU: CPU 코어나 메모리와 달리, 초기 GPU는 물리적으로 분할하여 여러 컨테이너에 할당하기 어려웠음 (최근에는 MIG 기술로 개선됨 - Nvidia GPU A100에서 새롭게 생긴 기능, 2020년 등장).

☞ 참고: https://brunch.co.kr/@f38b64b143b343c/19 / https://everenew.tistory.com/491

더보기

MIG(Multi-Instance GPU)

MIG는 GPU를 독립적인 여러 인스턴스로 분할하여 사용하는 가상화 기술이다.

NVIDIA의 A100은 Ampere 아키텍처로 설계되어, MIG 가상화를 지원한다.

MIG로 최대 7배 많은 GPU 인스턴스를  제공하기 때문에 자원 활용률이 중요한 CSP에서 특히 활용하기 좋다.

각 인스턴스는 메모리, 캐시, 컴퓨팅 코어가 하드웨어 수준에서 완전 격리/보호된다.

따라서 각 인스턴스 별로 다른 유형의 워크로드를 수행할 수 있다.

또한 각 인스턴스는 필요에 따라 프로비저닝 까지 가능하다.

예를 들어 NVIDIA A100 40GB는 20GB 메모리의 인스턴스 2개를 생성하거나 10GB인 인스턴스 2개와 5GB 인스턴스 4개를 생성할 수 있다.

특히 동적으로 GPU 리스소스를 전환할 수 있으므로 다양한 수요에 대처할 수 있다.

출처: https://blogs.nvidia.co.kr/blog/multi-instance-gpus/

 

vGPU(Virtual GPU)

vGPU는 방식은 CPU 가상화와 유사하다.

하이퍼바이저들은 VM 별로 CPU 사용 시간을 나누어 제공하는 time-shared 방식으로 vCPU를 제공한다.

이때 GPU 메모리를 작업 전환마다 다시 적제한다면 오버헤드가 굉장히 많이 발생할 것이다.

따라서  GPU 메모리는 각 vGPU마다 전용 구간을 나누어 가지고, 컴퓨팅 리소스는 time-shared 방식으로 시간을 나누어 실행하게 된다.

vGPU 소프트웨어는 하이퍼바이저와 함께 가상화 계층에 설치되어 vCPU처럼 vGPU를 제공한다.

출처: https://www.nvidia.com/ko-kr/data-center/virtualization/it-management/

MIG는 하이퍼바이저 뿐만 아니라 베어메탈 서버에서도 GPU를 나누어 쓸 수 있다.

그러므로 활용성 자체는 MIG가 좋지만, NVIDIA GPU에서만 지원되므로 의존성이 높다.

 

  • GPU 드라이버 복잡성: GPU 접근은 복잡한 사용자 공간 라이브러리와 커널 드라이버를 통해 이루어짐
  • 장치 파일 접근 제어: /dev/nvidia* 과 같은 장치 파일에 대한 접근을 안전하게 관리 필요

 

☞ 컨테이너 환경에서의 GPU 리소스 사용 진화 (단일 GPU)

  • 초기 단계 (2016-2018)
    • 초기에는 GPU 장치 파일을 컨테이너에 직접 마운트하고 필요한 라이브러리를 볼륨으로 공유해야 했음.
    • 아래는 Tensorflow에서 CUDA를 통해 NVIDIA GPU 장치에 액세스하도록 Docker 명령어를 수동으로 실행하는 예시임
docker run --device=/dev/nvidia0:/dev/nvidia0 \
           --device=/dev/nvidiactl:/dev/nvidiactl \
           -v /usr/local/cuda:/usr/local/cuda \
           tensorflow/tensorflow:latest-gpu

 

  • 이 방식 문제점
    • 모든 장치 파일을 수동으로 지정해야 함
    • 호스트와 컨테이너 간 라이브러리 버전 충돌 가능성
    • 여러 컨테이너 간 GPU 공유 메커니즘 부재
    • 오케스트레이션 환경에서 자동화하기 어려움

 

  • NVIDIA Container Runtime 활용 (2018-2020)
    • NVIDIA는 이러한 문제를 해결하기 위해 NVIDIA Container Runtime을 개발하였음. NVIDIA Container Runtime: Docker, CRI-O 등 컨테이너 기술에서 사용하는 Open Containers Initiative (OCI) 스펙과 호환되는 GPU 인식 컨테이너 런타임임.
    • 이 런타임은 다음과 같은 기능을 자동화함:
      • GPU 장치 파일 마운트
      • NVIDIA 드라이버 라이브러리 주입
      • CUDA 호환성 검사
      • GPU 기능 감지 및 노출
# Docker 19.03 이전 버전 사용
docker run --runtime=nvidia nvidia/cuda:11.0-base nvidia-smi

# Docker 19.03 이후부터는 더 간단하게 --gpus 플래그를 사용
docker run --gpus '"device=0,1"' nvidia/cuda:11.0-base nvidia-smi

 

  • 이 방식을 통해 다음과 같은 개선점을 얻음
    • 자동화된 GPU 검출 및 설정
    • 호스트-컨테이너 간 드라이버 호환성 자동 관리
    • 컨테이너 이미지 이식성 향상
apiVersion: v1
kind: Pod
metadata:
name: gpu-pod
spec:
containers:
  - name: gpu-container
    image: nvidia/cuda:11.0-base
    command: ["nvidia-smi"]
    resources:
    limits:
      nvidia.com/gpu: 2 # 2개의 GPU 요청
  • 이 방식을 통해 다음과 같은 장점 지원:
    • 선언적 리소스 관리
    • 클러스터 수준의 GPU 리소스 스케줄링
    • 자동화된 GPU 할당 및 격리
    • 다중 테넌트 환경에서의 리소스 공정성

☞ 참고1 - GPU 동시성

출처: https://aws.amazon.com/blogs/containers/gpu-sharing-on-amazon-eks-with-nvidia-time-slicing-and-accelerated-ec2-instances/

 

CUDA의 단일 프로세스

  • 이는 GPU 활용의 가장 기본적인 형태로, 단일 프로세스가 계산적 필요 사항을 충족하기 위해 CUDA (Compute Unified Device Architecture)를 사용하여 GPU에 액세스합니다.
  • 독립형 애플리케이션이나 GPU의 전체 성능이 필요한 작업에 이상적입니다.
  • GPU가 특정 고성능 컴퓨팅 작업에만 독점적으로 할당되어 공유할 필요가 없는 경우입니다.

CUDA 다중 프로세스 서비스(MPS, Multi-Process Service)를 사용한 다중 프로세스

  • CUDA MPS는 여러 프로세스가 단일 GPU 컨텍스트를 공유할 수 있도록 하는 CUDA의 기능입니다. 즉, 여러 작업이 상당한 컨텍스트 전환 오버헤드 없이 동시에 GPU에 액세스할 수 있습니다.
  • 여러 애플리케이션이나 작업이 GPU에 동시에 액세스해야 하는 경우.
  • 작업의 GPU 요구 사항이 다양하고 큰 오버헤드 없이 GPU 활용도를 극대화하려는 시나리오에 이상적입니다.

시간 분할

  • 타임 슬라이싱은 GPU 접근을 작은 시간 간격으로 나누어 여러 작업이 미리 정의된 시간 간격으로 GPU를 사용할 수 있도록 하는 것입니다. 이는 CPU가 여러 프로세스 간에 타임 슬라이싱을 하는 방식과 유사합니다.
  • 간헐적으로 GPU에 접근해야 하는 여러 작업이 있는 환경에 적합합니다.
  • 작업에 예측할 수 없는 GPU 요구가 있는 경우 모든 작업에 대해 GPU에 공평하게 액세스할 수 있도록 보장하는 것이 좋습니다.

멀티 인스턴스 GPU (MIG, Multi-Instance GPU)

  • NVIDIA A100 Tensor Core GPU에 특화된 MIG는 단일 GPU를 각각 별도의 메모리, 캐시 및 컴퓨팅 코어를 가진 여러 인스턴스로 분할할 수 있도록 합니다. 이를 통해 각 인스턴스의 성능이 보장됩니다.
  • 특정 작업에 대해 특정 수준의 성과를 보장하고자 할 때.
  • 엄격한 격리를 보장하는 것이 목표인 다중 테넌트 환경에서.
  • 클라우드 환경이나 데이터 센터에서 GPU 활용도를 극대화합니다.

가상 GPU(vGPU) 를 통한 가상화

  • NVIDIA vGPU 기술은 여러 가상 머신(VM)이 하나의 물리적 GPU 성능을 공유할 수 있도록 합니다. GPU 리소스를 가상화하여 각 VM이 전용 GPU 슬라이스를 가질 수 있도록 합니다.
  • 가상화된 환경에서 목표는 GPU 기능을 여러 가상 머신으로 확장하는 것입니다.
  • 클라우드 서비스 제공업체나 기업의 목표는 클라이언트에게 서비스로 GPU 기능을 제공하는 것입니다.
  • 서로 다른 작업이나 사용자 간에 데이터를 분리하는 것이 목표인 경우.

☞ 참고2 - 2020년 10월 발표한 AWS 블로그 내용

 

Virtual GPU device plugin for inference workloads in Kubernetes | Amazon Web Services

Machine learning (ML) has become a centerpiece for enterprise transformation. AWS provides a broad and deep set of ML capabilities for builders with all levels of expertise. Developers with no prior ML experience can seamlessly build sophisticated AI-drive

aws.amazon.com

 

[실습 준비 2] GPU Time-slicing on Amazon EKS - GPU 노드 그룹 추가 (https://aws.amazon.com/blogs/containers/gpu-sharing-on-amazon-eks-with-nvidia-time-slicing-and-accelerated-ec2-instances/)

  • GPU 노드 추가: g5.8xlarge 로 실습 예정

☞ Dry-run 예시

$ eksdemo create nodegroup gpu -i g5.8xlarge -N 1 -c gpusharing-demo --dry-run

Eksctl Resource Manager Dry Run:
eksctl create nodegroup -f - --install-nvidia-plugin=false --install-neuron-plugin=false
---
apiVersion: eksctl.io/v1alpha5
kind: ClusterConfig

metadata:
  name: gpusharing-demo
  region: us-west-2
  version: "1.32"
  tags:
    eksdemo.io/version: 0.18.2

managedNodeGroups:
- name: gpu
  ami: ami-0111ed894dc3ec059
  amiFamily: AmazonLinux2
  desiredCapacity: 1
  iam:
    attachPolicyARNs:
    - arn:aws:iam::aws:policy/AmazonEKSWorkerNodePolicy
    - arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryReadOnly
    - arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore
  instanceType: g5.8xlarge
  minSize: 0
  maxSize: 10
  volumeSize: 80
  volumeType: gp3
  overrideBootstrapCommand: |
    #!/bin/bash
    /etc/eks/bootstrap.sh gpusharing-demo
  privateNetworking: true
  spot: false
  taints:
  - key: nvidia.com/gpu
    value: ""
    effect: NoSchedule

 

 

☞ GPU 노드 그룹 생성

$ eksdemo create nodegroup gpu -i g5.8xlarge -N 1 -c gpusharing-demo
2025-04-13 00:41:45 [ℹ]  nodegroup "gpu" will use "ami-0111ed894dc3ec059" [AmazonLinux2/1.32]
2025-04-13 00:41:48 [ℹ]  1 existing nodegroup(s) (main) will be excluded
2025-04-13 00:41:48 [ℹ]  1 nodegroup (gpu) was included (based on the include/exclude rules)
2025-04-13 00:41:48 [ℹ]  will create a CloudFormation stack for each of 1 managed nodegroups in cluster "gpusharing-demo"
2025-04-13 00:41:49 [ℹ]
2 sequential tasks: { fix cluster compatibility, 1 task: { 1 task: { create managed nodegroup "gpu" } }
}
2025-04-13 00:41:49 [ℹ]  checking cluster stack for missing resources
2025-04-13 00:41:51 [ℹ]  cluster stack has all required resources
2025-04-13 00:41:52 [ℹ]  building managed nodegroup stack "eksctl-gpusharing-demo-nodegroup-gpu"
2025-04-13 00:41:52 [ℹ]  skipping us-west-2d from selection because it doesn't support the following instance type(s): g5.8xlarge
2025-04-13 00:41:53 [ℹ]  deploying stack "eksctl-gpusharing-demo-nodegroup-gpu"
2025-04-13 00:41:53 [ℹ]  waiting for CloudFormation stack "eksctl-gpusharing-demo-nodegroup-gpu"
2025-04-13 00:42:24 [ℹ]  waiting for CloudFormation stack "eksctl-gpusharing-demo-nodegroup-gpu"
2025-04-13 00:43:10 [ℹ]  waiting for CloudFormation stack "eksctl-gpusharing-demo-nodegroup-gpu"
2025-04-13 00:43:46 [ℹ]  waiting for CloudFormation stack "eksctl-gpusharing-demo-nodegroup-gpu"
2025-04-13 00:44:55 [ℹ]  waiting for CloudFormation stack "eksctl-gpusharing-demo-nodegroup-gpu"
2025-04-13 00:44:55 [ℹ]  as you are using a GPU optimized instance type you will need to install NVIDIA Kubernetes device plugin.
2025-04-13 00:44:55 [ℹ]          see the following page for instructions: https://github.com/NVIDIA/k8s-device-plugin
2025-04-13 00:44:55 [ℹ]  no tasks
2025-04-13 00:44:55 [✔]  created 0 nodegroup(s) in cluster "gpusharing-demo"
2025-04-13 00:44:55 [✔]  created 1 managed nodegroup(s) in cluster "gpusharing-demo"
2025-04-13 00:44:57 [ℹ]  checking security group configuration for all nodegroups
2025-04-13 00:44:57 [ℹ]  all nodegroups have up-to-date cloudformation templates

 

실습 중 확인 내용

  • vCPU 8개만 증설되어 해당 인스턴스 타입으로 nodegroup 생성
eksdemo create nodegroup gpu -i g5.xlarge -N 1 -c gpusharing-demo --dry-run
eksdemo create nodegroup gpu -i g5.xlarge -N 1 -c gpusharing-demo

 

 

☞ 참고: AWS에서 지원하는 GPU 인스턴스 타입

  • AWS에서는 GPU 인스턴스 타입을 포함하여 “가속 컴퓨팅 (Accelerated Computing, XC)”으로 “하드웨어 액셀러레이터 또는 코프로세서를 사용하여 부동 소수점 수 계산이나 그래픽 처리, 데이터 패턴 일치 등의 기능을 CPU에서 실행되는 소프트웨어보다 훨씬 효율적으로 수행”하는 인스턴스 유형으로 구분함

 

☞ [실습] GPU Time-slicing on Amazon EKS - Time Slicing 안했을 때

  • 데모에 사용하는 컨테이너 이미지 (AWS Public ECR: r5m2h0c9/cifar10_cnn - 4.58GB) - Link
$ kubectl get node --label-columns=node.kubernetes.io/instance-type,eks.amazonaws.com/capacityType,topology.kubernetes.io/zone
NAME                                             STATUS   ROLES    AGE     VERSION               INSTANCE-TYPE   CAPACITYTYPE   ZONE
i-04110cf42adf9e073.us-west-2.compute.internal   Ready    <none>   8m42s   v1.32.1-eks-5d632ec   g5.xlarge       ON_DEMAND      us-west-2a
i-087921739acc2a1a3.us-west-2.compute.internal   Ready    <none>   33m     v1.32.1-eks-5d632ec   t3.large        ON_DEMAND      us-west-2a
i-0e0b4574ef0ee22cf.us-west-2.compute.internal   Ready    <none>   33m     v1.32.1-eks-5d632ec   t3.large        ON_DEMAND      us-west-2c


# g5 - GPU 노드 에 label을 지정: "i-04110cf42adf9e073.us-west-2.compute.internal" 를 바꿔주세요!
$ kubectl label node i-04110cf42adf9e073.us-west-2.compute.internal eks-node=gpu

# 참고: 노드 그룹 스케일링 (할당량 한도 내에서 2 등으로 변경 가능)
$ eksctl scale nodegroup --name gpu --cluster gpusharing-demo --nodes 2

# nvdp-values.yaml 파일 다운로드
$ curl -O https://raw.githubusercontent.com/sanjeevrg89/eks-gpu-sharing-demo/refs/heads/main/nvdp-values.yaml

# nvidia-device-plugin을 helm을 사용하여 설치
$ helm repo add nvdp https://nvidia.github.io/k8s-device-plugin
$ helm repo update
$ helm upgrade -i nvdp nvdp/nvidia-device-plugin \
  --namespace kube-system \
  -f nvdp-values.yaml \
  --version 0.14.0
Release "nvdp" does not exist. Installing it now.
NAME: nvdp
LAST DEPLOYED: Thu Apr 17 15:15:09 2025
NAMESPACE: kube-system
STATUS: deployed
REVISION: 1
TEST SUITE: None

$ kubectl get daemonset -n kube-system | grep nvidia
nvdp-nvidia-device-plugin   1         1         1       1            1           eks-node=gpu    23s

# time-slicing을 활성화하지 않았을 때: GPU가 1개임
$ kubectl get nodes -o json | jq -r '.items[] | select(.status.capacity."nvidia.com/gpu" != null) | {name: .metadata.name, capacity: .status.capacity}'
{
  "name": "i-04110cf42adf9e073.us-west-2.compute.internal",
  "capacity": {
    "cpu": "4",
    "ephemeral-storage": "83873772Ki",
    "hugepages-1Gi": "0",
    "hugepages-2Mi": "0",
    "memory": "16181656Ki",
    "nvidia.com/gpu": "1",
    "pods": "58"
  }
}

# GPU 모델 배포
$ kubectl create namespace gpu-demo
$ cat << EOF > cifar10-train-deploy.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: tensorflow-cifar10-deployment
  namespace: gpu-demo
  labels:
    app: tensorflow-cifar10
spec:
  replicas: 5
  selector:
    matchLabels:
      app: tensorflow-cifar10
  template:
    metadata:
      labels:
        app: tensorflow-cifar10
    spec:
      containers:
      - name: tensorflow-cifar10
        image: public.ecr.aws/r5m2h0c9/cifar10_cnn:v2
        resources:
          limits:
            nvidia.com/gpu: 1
EOF
$ kubectl apply -f cifar10-train-deploy.yaml

# 시간이 지나면 ContainerCreating -> Running으로 변경
$ watch -d 'kubectl get pods -n gpu-demo'

# [Optional] 상태 살펴보기: 모델 다운로드 중
$ kubectl describe pod tensorflow-cifar10-deployment-7c6f89c8d6-5xmfd -n gpu-demo
Name:             tensorflow-cifar10-deployment-7c6f89c8d6-5xmfd
Namespace:        gpu-demo
Priority:         0
Service Account:  default
Node:             i-04110cf42adf9e073.us-west-2.compute.internal/192.168.100.121
Start Time:       Thu, 17 Apr 2025 15:17:53 +0000
Labels:           app=tensorflow-cifar10
                  pod-template-hash=7c6f89c8d6
Annotations:      <none>
Status:           Pending
IP:               
IPs:              <none>
Controlled By:    ReplicaSet/tensorflow-cifar10-deployment-7c6f89c8d6
Containers:
  tensorflow-cifar10:
    Container ID:   
    Image:          public.ecr.aws/r5m2h0c9/cifar10_cnn:v2
    Image ID:       
    Port:           <none>
    Host Port:      <none>
    State:          Waiting
      Reason:       ContainerCreating
    Ready:          False
    Restart Count:  0
    Limits:
      nvidia.com/gpu:  1
    Requests:
      nvidia.com/gpu:  1
    Environment:       <none>
    Mounts:
      /var/run/secrets/kubernetes.io/serviceaccount from kube-api-access-6x2jm (ro)
Conditions:
  Type                        Status
  PodReadyToStartContainers   False 
  Initialized                 True 
  Ready                       False 
  ContainersReady             False 
  PodScheduled                True 
Volumes:
  kube-api-access-6x2jm:
    Type:                    Projected (a volume that contains injected data from multiple sources)
    TokenExpirationSeconds:  3607
    ConfigMapName:           kube-root-ca.crt
    ConfigMapOptional:       <nil>
    DownwardAPI:             true
QoS Class:                   BestEffort
Node-Selectors:              <none>
Tolerations:                 node.kubernetes.io/not-ready:NoExecute op=Exists for 300s
                             node.kubernetes.io/unreachable:NoExecute op=Exists for 300s
                             nvidia.com/gpu:NoSchedule op=Exists
Events:
  Type    Reason     Age   From               Message
  ----    ------     ----  ----               -------
  Normal  Scheduled  109s  default-scheduler  Successfully assigned gpu-demo/tensorflow-cifar10-deployment-7c6f89c8d6-5xmfd to i-04110cf42adf9e073.us-west-2.compute.internal
  Normal  Pulling    108s  kubelet            Pulling image "public.ecr.aws/r5m2h0c9/cifar10_cnn:v2"
  
  
# 이제 Running!
$ kubectl get pods -n gpu-demo
NAME                                             READY   STATUS              RESTARTS   AGE
tensorflow-cifar10-deployment-7c6f89c8d6-5xmfd   0/1     ContainerCreating   0          2m44s
tensorflow-cifar10-deployment-7c6f89c8d6-72j2r   0/1     Pending             0          2m44s
tensorflow-cifar10-deployment-7c6f89c8d6-7hgdf   0/1     Pending             0          2m44s
tensorflow-cifar10-deployment-7c6f89c8d6-hzzqp   0/1     Pending             0          2m44s
tensorflow-cifar10-deployment-7c6f89c8d6-xmb4g   0/1     Pending             0          2m44s

# 다른 pod를 조회해보면 GPU 리소스 부족으로 생성 불가능한 상황
$ kubectl describe pod tensorflow-cifar10-deployment-7c6f89c8d6-72j2r -n gpu-demo
Name:             tensorflow-cifar10-deployment-7c6f89c8d6-5xmfd
Namespace:        gpu-demo
Priority:         0
Service Account:  default
Node:             i-04110cf42adf9e073.us-west-2.compute.internal/192.168.100.121
Start Time:       Thu, 17 Apr 2025 15:17:53 +0000
Labels:           app=tensorflow-cifar10
                  pod-template-hash=7c6f89c8d6
Annotations:      <none>
Status:           Running
IP:               192.168.109.22
IPs:
  IP:           192.168.109.22
Controlled By:  ReplicaSet/tensorflow-cifar10-deployment-7c6f89c8d6
Containers:
  tensorflow-cifar10:
~ $ kubectl describe pod tensorflow-cifar10-deployment-7c6f89c8d6-72j2r -n gpu-demo
Name:             tensorflow-cifar10-deployment-7c6f89c8d6-72j2r
Namespace:        gpu-demo
Priority:         0
Service Account:  default
Node:             <none>
Labels:           app=tensorflow-cifar10
                  pod-template-hash=7c6f89c8d6
Annotations:      <none>
Status:           Pending
IP:               
IPs:              <none>
Controlled By:    ReplicaSet/tensorflow-cifar10-deployment-7c6f89c8d6
Containers:
  tensorflow-cifar10:
    Image:      public.ecr.aws/r5m2h0c9/cifar10_cnn:v2
    Port:       <none>
    Host Port:  <none>
    Limits:
      nvidia.com/gpu:  1
    Requests:
      nvidia.com/gpu:  1
    Environment:       <none>
    Mounts:
      /var/run/secrets/kubernetes.io/serviceaccount from kube-api-access-hdz6t (ro)
Conditions:
  Type           Status
  PodScheduled   False 
Volumes:
  kube-api-access-hdz6t:
    Type:                    Projected (a volume that contains injected data from multiple sources)
    TokenExpirationSeconds:  3607
    ConfigMapName:           kube-root-ca.crt
    ConfigMapOptional:       <nil>
    DownwardAPI:             true
QoS Class:                   BestEffort
Node-Selectors:              <none>
Tolerations:                 node.kubernetes.io/not-ready:NoExecute op=Exists for 300s
                             node.kubernetes.io/unreachable:NoExecute op=Exists for 300s
                             nvidia.com/gpu:NoSchedule op=Exists
Events:
  Type     Reason            Age    From               Message
  ----     ------            ----   ----               -------
  Warning  FailedScheduling  4m57s  default-scheduler  0/4 nodes are available: 4 Insufficient nvidia.com/gpu. preemption: 0/4 nodes are available: 4 No preemption victims found for incoming pod.

 

☞ [실습] GPU Time-slicing on Amazon EKS - Time Slicing 했을 때

# Time-slicing 적용하기
$ cat << EOF > nvidia-device-plugin.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: nvidia-device-plugin
  namespace: kube-system
data:
  any: |-
    version: v1
    flags:
      migStrategy: none
    sharing:
      timeSlicing:
        resources:
        - name: nvidia.com/gpu
          replicas: 10
EOF
$ kubectl apply -f nvidia-device-plugin.yaml

# 새로운 ConfigMap 기반으로 반영하기
$ helm upgrade -i nvdp nvdp/nvidia-device-plugin \
  --namespace kube-system \
  -f nvdp-values.yaml \
  --version 0.14.0 \
  --set config.name=nvidia-device-plugin \
  --force
Release "nvdp" has been upgraded. Happy Helming!
NAME: nvdp
LAST DEPLOYED: Thu Apr 17 15:25:35 2025
NAMESPACE: kube-system
STATUS: deployed
REVISION: 2
TEST SUITE: None

# 조금 지나고 조회해보면 GPU값이 늘어남! 
$ kubectl get nodes -o json | jq -r '.items[] | select(.status.capacity."nvidia.com/gpu" != null) | {name: .metadata.name, capacity: .status.capacity}'
{
  "name": "i-04110cf42adf9e073.us-west-2.compute.internal",
  "capacity": {
    "cpu": "4",
    "ephemeral-storage": "83873772Ki",
    "hugepages-1Gi": "0",
    "hugepages-2Mi": "0",
    "memory": "16181656Ki",
    "nvidia.com/gpu": "10",
    "pods": "58"
  }
}

# 모두 실행된 상황
$ kubectl get pods -n gpu-demo
NAME                                             READY   STATUS    RESTARTS      AGE
tensorflow-cifar10-deployment-7c6f89c8d6-5xmfd   1/1     Running   0             9m57s
tensorflow-cifar10-deployment-7c6f89c8d6-72j2r   1/1     Running   2 (53s ago)   9m57s
tensorflow-cifar10-deployment-7c6f89c8d6-7hgdf   1/1     Running   2 (53s ago)   9m57s
tensorflow-cifar10-deployment-7c6f89c8d6-hzzqp   1/1     Running   2 (46s ago)   9m57s
tensorflow-cifar10-deployment-7c6f89c8d6-xmb4g   1/1     Running   0             9m57s

# 그런데 계속 재시작이 되고 있음
$ kubectl get pods -n gpu-demo
NAME                                             READY   STATUS    RESTARTS        AGE
tensorflow-cifar10-deployment-7c6f89c8d6-5xmfd   1/1     Running            0             12m
tensorflow-cifar10-deployment-7c6f89c8d6-72j2r   0/1     CrashLoopBackOff   4 (53s ago)   12m
tensorflow-cifar10-deployment-7c6f89c8d6-7hgdf   0/1     CrashLoopBackOff   4 (75s ago)   12m
tensorflow-cifar10-deployment-7c6f89c8d6-hzzqp   0/1     CrashLoopBackOff   4 (63s ago)   12m
tensorflow-cifar10-deployment-7c6f89c8d6-xmb4g   1/1     Running            0             12m

# 메모리 부족 이슈
$ kubectl logs tensorflow-cifar10-deployment-7c6f89c8d6-72j2r -n gpu-demo | grep memory
~ $ kubectl logs tensorflow-cifar10-deployment-7c6f89c8d6-72j2r -n gpu-demo | grep memory
2025-04-17 15:28:27.641403: F tensorflow/tsl/platform/statusor.cc:33] Attempting to fetch value instead of handling error INTERNAL: failed initializing StreamExecutor for CUDA device ordinal 0: INTERNAL: failed call to cuDevicePrimaryCtxRetain: CUDA_ERROR_OUT_OF_MEMORY: out of memory; total memory reported: 23819976704
~ $ kubectl logs -f tensorflow-cifar10-deployment-7c6f89c8d6-72j2r -n gpu-demo | grep memory 
2025-04-17 15:28:27.641403: F tensorflow/tsl/platform/statusor.cc:33] Attempting to fetch value instead of handling error INTERNAL: failed initializing StreamExecutor for CUDA device ordinal 0: INTERNAL: failed call to cuDevicePrimaryCtxRetain: CUDA_ERROR_OUT_OF_MEMORY: out of memory; total memory reported: 23819976704
~ $ kubectl logs  tensorflow-cifar10-deployment-7c6f89c8d6-72j2r -n gpu-demo | grep memory 
2025-04-17 15:29:55.527313: F tensorflow/tsl/platform/statusor.cc:33] Attempting to fetch value instead of handling error INTERNAL: failed initializing StreamExecutor for CUDA device ordinal 0: INTERNAL: failed call to cuDevicePrimaryCtxRetain: CUDA_ERROR_OUT_OF_MEMORY: out of memory; total memory reported: 23819976704

 

  • 리소스 삭제
$ eksdemo delete cluster gpusharing-demo

 

 

 

2. 멀티 GPU 활용을 위한 AI/ML 인프라 with EKS


 

☞ 컨테이너 환경에서의 GPU 리소스 사용 진화 (멀티 GPU)

  • 분산 학습에서 발생하는 네트워크 병목 현상
    • 대규모 모델이 등장하면서, 이제 단일 GPU로는 학습이 불가능해짐.
      • GPT-3: 1,750억 파라미터
      • Megatron-Turing NLG: 5,300억 파라미터
      • PaLM: 5,400억 파라미터
    • AWS에서도 이러한 추세에 맞춰 대규모 모델 개발과 학습을 위한 인프라를 지속적으로 발전시키고 있음.
      • Amazon Bedrock: 100개 이상의 LLM을 지원하며
      • Trainium/Inferentia 칩을 통한 모델 성능 향상과 같은 AI 인프라 혁신에 투자
    • 2025년 현재 AWS는 이러한 대규모 모델의 학습과 추론을 위해 분산 컴퓨팅 기술을 적극적으로 활용하고 있으며, 단일 GPU로는 처리할 수 없는 규모의 모델 개발을 지원하고 있음.
    • 이러한 대규모 모델은 필연적으로 여러 GPU와 여러 노드에 걸친 분산 학습이 필요
    • 분산 학습에서 이루어지는 주요 통신 패턴 - AllReduce, AllGather, ReduceScatter 등

출처: https://brunch.co.kr/@chris-song/96

 

출처:https://docs.nvidia.com/deeplearning/nccl/user-guide/docs/usage/operations.html

  • 이러한 병목 현상에 따라 발생하는 이슈 예시
    • 8개 GPU 노드 간 AllReduce 작업에서 최대 70%의 시간이 통신에 소비
    • GPU 활용률 저하 (60-70% 수준)
    • 학습 시간 증가 (최대 2-3배)

 

☞ NCCL: GPU 간 통신을 위한 NVIDIA의 해결책

  • 분산 학습에서 GPU 간 통신 성능이 중요해짐에 따라, NVIDIA는 NCCL(NVIDIA Collective Communications Library)을 개발
  • NCCL은 여러 GPU 간의 집합 통신 연산을 최적화하기 위해 설계된 라이브러리임.
  • NCCL 주요 특징
    • 고성능 집합 통신 연산: AllReduce, Broadcast, Reduce, AllGather, ReduceScatter 등 딥러닝에 필수적인 통신 패턴을 최적화
    • 다양한 토폴로지 지원: PCIe, NVLink, NVSwitch 등 GPU 간 연결 토폴로지를 자동으로 감지하고 최적화
    • 멀티 노드 확장성: InfiniBand, RoCE, IP 소켓 등을 통한 노드 간 통신 지원
    • CUDA 통합: CUDA 스트림과 통합되어 계산과 통신의 오버랩 가능
  • NCCL은 딥러닝 프레임워크(PyTorch, TensorFlow 등)에 통합되어 사용자가 명시적으로 통신 코드를 작성하지 않아도 효율적인 분산 학습이 가능하게 함
  • 그러나 NCCL만으로는 노드 간 네트워크 병목 현상을 완전히 해결할 수 없음.

☞ AWS EFA (Elastic Fabric Adapter) 기술

  • GPU 네트워크 병목 문제를 해결하기 위해 설계된 혁신적인 네트워크 인터페이스
  • 기반 핵심 기술:
    • OS 바이패스 기술: EFA는 운영 체제 커널을 우회하여 사용자 공간 애플리케이션이 네트워크 어댑터와 직접 통신 가능
      • 커널 컨텍스트 스위칭 제거
      • 시스템 콜 오버헤드 제거
      • 인터럽트 처리 최소화
    • RDMA (Remote Direct Memory Access): 원격 시스템의 메모리에 CPU 개입 없이 직접 접근할 수 있는 기술
      • 원격 시스템의 CPU를 사용하지 않고 메모리 읽기/쓰기 가능
      • 제로 카피(Zero-copy) 데이터 전송
      • DMA(Direct Memory Access)를 통한 효율적인 데이터 이동
    • NCCL과 EFA와의 통합: NVIDIA Collective Communications Library(NCCL)는 EFA와 최적화된 통합을 제공
      • GPU 직접 통신 최적화
      • 토폴로지 인식 통신 알고리즘
      • 자동 튜닝 메커니즘

  • Amazon EKS에 EFA를 통합함으로써 얻을 수 있는 주요 이점:
    1. 인프라 관리 간소화
      • Kubernetes를 통한 선언적 인프라 관리
      • GPU 및 EFA 리소스의 자동 검출 및 할당
      • 자동 확장 및 축소 기능
    2. 비용 최적화
      • 리소스 활용도 향상 (GPU 활용률 90% 이상)
      • 학습 시간 단축으로 인한 인스턴스 비용 절감
      • Spot 인스턴스와의 통합 가능성
    3. 성능 최적화
      • 노드 간 초저지연 통신
      • 분산 학습 확장성 향상
      • 대규모 모델 학습 가속화
    4. 개발자 경험 개선
      • 일관된 개발 및 배포 환경
      • 재현 가능한 실험 설정
      • CI/CD 파이프라인과의 통합 용이성

 

[실습 준비] EKS Blueprint with Terraform w/ RDMA 미지원 g5.8xlarge - “EKS Cluster w/ NVIDIA GPUs and EFA for Machine Learning

$ git clone https://github.com/aws-ia/terraform-aws-eks-blueprints.git

$ cd terraform-aws-eks-blueprints/patterns/nvidia-gpu-efa

$ sed -i "s/p5.48xlarge/g5.8xlarge/g" eks.tf
$ sed -i "s/us-west-2/us-east-1/g" main.tf

$ terraform init
$ terraform apply -target="module.vpc" -auto-approve
$ terraform apply -target="module.eks" -auto-approve
$ terraform apply -auto-approve

 

 

해당 실습을 시도해 보았지만 node 생성이 안되어 실습은 진행되지 않음 (efa 미지원)

생성이 안됨

 

 

☞ [실습 진행] MPI Operator를 사용한 테스트 w/ RDMA 미지원 g5.8xlarge

$ aws eks --region us-east-1 update-kubeconfig --name nvidia-gpu-efa

# 인스턴스 유형별 노드 나열
$ kubectl get nodes -L node.kubernetes.io/instance-type
NAME                          STATUS   ROLES    AGE   VERSION               INSTANCE-TYPE
ip-10-0-1-129.ec2.internal    Ready    <none>   22m   v1.32.1-eks-5d632ec   g5.8xlarge
ip-10-0-11-12.ec2.internal    Ready    <none>   15m   v1.32.1-eks-5d632ec   g5.8xlarge
ip-10-0-12-158.ec2.internal   Ready    <none>   23m   v1.32.1-eks-5d632ec   m5.large
ip-10-0-26-134.ec2.internal   Ready    <none>   23m   v1.32.1-eks-5d632ec   m5.large

# Kubeflow MPI Operator 배포
$ kubectl apply -f https://raw.githubusercontent.com/kubeflow/mpi-operator/v0.4.0/deploy/v2beta1/mpi-operator.yaml
namespace/mpi-operator created
customresourcedefinition.apiextensions.k8s.io/mpijobs.kubeflow.org created
serviceaccount/mpi-operator created
clusterrole.rbac.authorization.k8s.io/kubeflow-mpijobs-admin created
clusterrole.rbac.authorization.k8s.io/kubeflow-mpijobs-edit created
clusterrole.rbac.authorization.k8s.io/kubeflow-mpijobs-view created
clusterrole.rbac.authorization.k8s.io/mpi-operator created
clusterrolebinding.rbac.authorization.k8s.io/mpi-operator created
deployment.apps/mpi-operator created

# 또한 mpi-operator 클러스터 역할에 패치를 적용하여 mpi-operator 서비스 계정이 apiGroup leases 리소스에 액세스할 수 있도록 설정
$ kubectl apply -f https://raw.githubusercontent.com/aws-samples/aws-do-eks/main/Container-Root/eks/deployment/kubeflow/mpi-operator/clusterrole-mpi-operator.yaml
clusterrole.rbac.authorization.k8s.io/mpi-operator configured

# 실습에 사용하는 g5.8xlarge 인스턴스에는 인스턴스 당 GPU 1개 및 EFA 1개를 포함하고 있으므로 생성 스크립트를 수정하여 실행
$ sed -i "s/GPU_PER_WORKER=8/GPU_PER_WORKER=1/g" generate-efa-info-test.sh
$ sed -i "s/EFA_PER_WORKER=32/EFA_PER_WORKER=1/g" generate-efa-info-test.sh

# efa 테스트 수행
$ ./generate-efa-info-test.sh
$ kubectl apply -f ./efa-info-test.yaml
mpijob.kubeflow.org/efa-info-test created

# 모니터링 (워커 포드가 완전히 실행될 때까지 런처 포드가 몇 번 다시 시작되는 것은 정상임)
$ watch 'kubectl get pods'
매 2.0초:   kubectl get pods                                                                                                                               LOCK-PC: Sun Apr 13 01:27:45 2025
NAME                           READY   STATUS              RESTARTS   AGE
efa-info-test-launcher-mbql8   0/1     ContainerCreating   0          57s
efa-info-test-worker-0         0/1     ContainerCreating   0          57s
efa-info-test-worker-1         0/1     ContainerCreating   0          57s

# (...시간이 지나고...)
매 2.0초:   kubectl get pods                                                                                                                               LOCK-PC: Sun Apr 13 01:29:43 2025
NAME                           READY   STATUS      RESTARTS   AGE
efa-info-test-launcher-mbql8   0/1     Completed   0          2m56s

# 테스트 결과 확인
$ kubectl logs -f $(kubectl get pods | grep launcher | cut -d ' ' -f 1)
Warning: Permanently added 'efa-info-test-worker-1.efa-info-test.default.svc' (ED25519) to the list of known hosts.
Warning: Permanently added 'efa-info-test-worker-0.efa-info-test.default.svc' (ED25519) to the list of known hosts.
[1,1]<stdout>:provider: efa
[1,1]<stdout>:    fabric: efa
[1,1]<stdout>:    domain: rdmap0s29-rdm
[1,1]<stdout>:    version: 122.0
[1,1]<stdout>:    type: FI_EP_RDM
[1,1]<stdout>:    protocol: FI_PROTO_EFA
[1,0]<stdout>:provider: efa
[1,0]<stdout>:    fabric: efa
[1,0]<stdout>:    domain: rdmap0s29-rdm
[1,0]<stdout>:    version: 122.0
[1,0]<stdout>:    type: FI_EP_RDM
[1,0]<stdout>:    protocol: FI_PROTO_EFA

# 작업 제거
$ kubectl delete -f ./efa-info-test.yaml

 

☞ [실습 진행] EFA NCCL 테스트 w/ RDMA 미지원 g5.8xlarge

# 실습에 사용하는 g5.8xlarge 인스턴스에는 인스턴스 당 GPU 1개 및 EFA 1개를 포함하고 있으므로 생성 스크립트를 수정하여 실행
$ sed -i "s/INSTANCE_TYPE=p5e\.48xlarge/INSTANCE_TYPE=g5\.8xlarge/g" generate-efa-nccl-test.sh
$ sed -i "s/GPU_PER_WORKER=8/GPU_PER_WORKER=1/g" generate-efa-nccl-test.sh
$ sed -i "s/EFA_PER_WORKER=32/EFA_PER_WORKER=1/g" generate-efa-nccl-test.sh
$ sed -i "s/FI_EFA_USE_DEVICE_RDMA=1/FI_EFA_USE_DEVICE_RDMA=0/g" generate-efa-nccl-test.sh
$ ./generate-efa-nccl-test.sh

$ kubectl apply -f ./efa-nccl-test.yaml
mpijob.kubeflow.org/efa-nccl-test created

$ watch kubectl get pods
매 2.0초:   kubectl get pods                                                                                                                               LOCK-PC: Sun Apr 13 01:39:18 2025
NAME                           READY   STATUS    RESTARTS      AGE
efa-nccl-test-launcher-mmfxv   1/1     Running   2 (75s ago)   77s
efa-nccl-test-worker-0         1/1     Running   0             77s
efa-nccl-test-worker-1         1/1     Running   0             77s

# (...시간이 지나고...)
매 2.0초:   kubectl get pods                                                                                                                               LOCK-PC: Sun Apr 13 01:48:52 2025
NAME                           READY   STATUS      RESTARTS   AGE
efa-nccl-test-launcher-mmfxv   0/1     Completed   2          10m

$ kubectl logs -f $(kubectl get pods | grep launcher | cut -d ' ' -f 1)
Warning: Permanently added 'efa-nccl-test-worker-0.efa-nccl-test.default.svc' (ED25519) to the list of known hosts.
Warning: Permanently added 'efa-nccl-test-worker-1.efa-nccl-test.default.svc' (ED25519) to the list of known hosts.
[1,0]<stdout>:# nThread 1 nGpus 1 minBytes 8 maxBytes 17179869184 step: 2(factor) warmup iters: 5 iters: 100 agg iters: 1 validation: 1 graph: 0
[1,0]<stdout>:#
[1,0]<stdout>:# Using devices
[1,0]<stdout>:#  Rank  0 Group  0 Pid     33 on efa-nccl-test-worker-0 device  0 [0000:00:1e] NVIDIA A10G
[1,0]<stdout>:#  Rank  1 Group  0 Pid     33 on efa-nccl-test-worker-1 device  0 [0000:00:1e] NVIDIA A10G
[1,0]<stdout>:#
[1,0]<stdout>:# Reducing maxBytes to 7624021333 due to memory limitation
[1,0]<stdout>:NCCL version 2.26.2+cuda12.2
[1,0]<stdout>:#
[1,0]<stdout>:#                                                              out-of-place                       in-place
[1,0]<stdout>:#       size         count      type   redop    root     time   algbw   busbw #wrong     time   algbw   busbw #wrong
[1,0]<stdout>:#        (B)    (elements)                               (us)  (GB/s)  (GB/s)            (us)  (GB/s)  (GB/s)
[1,0]<stdout>:           8             2     float     sum      -1    67.14    0.00    0.00      0    67.69    0.00    0.00      0
[1,0]<stdout>:          16             4     float     sum      -1    69.12    0.00    0.00      0    68.49    0.00    0.00      0
[1,0]<stdout>:          32             8     float     sum      -1    70.05    0.00    0.00      0    69.90    0.00    0.00      0
[1,0]<stdout>:          64            16     float     sum      -1    70.44    0.00    0.00      0    69.89    0.00    0.00      0
[1,0]<stdout>:         128            32     float     sum      -1    70.64    0.00    0.00      0    70.60    0.00    0.00      0
[1,0]<stdout>:         256            64     float     sum      -1    71.02    0.00    0.00      0    70.79    0.00    0.00      0
[1,0]<stdout>:         512           128     float     sum      -1    72.00    0.01    0.01      0    71.11    0.01    0.01      0
[1,0]<stdout>:        1024           256     float     sum      -1    72.34    0.01    0.01      0    72.39    0.01    0.01      0
[1,0]<stdout>:        2048           512     float     sum      -1    74.97    0.03    0.03      0    74.22    0.03    0.03      0
[1,0]<stdout>:        4096          1024     float     sum      -1    80.03    0.05    0.05      0    79.39    0.05    0.05      0
[1,0]<stdout>:        8192          2048     float     sum      -1    89.68    0.09    0.09      0    88.78    0.09    0.09      0
[1,0]<stdout>:       16384          4096     float     sum      -1    95.54    0.17    0.17      0    94.89    0.17    0.17      0
[1,0]<stdout>:       32768          8192     float     sum      -1    109.7    0.30    0.30      0    108.3    0.30    0.30      0
[1,0]<stdout>:       65536         16384     float     sum      -1    112.6    0.58    0.58      0    111.5    0.59    0.59      0
[1,0]<stdout>:      131072         32768     float     sum      -1    142.0    0.92    0.92      0    141.3    0.93    0.93      0
[1,0]<stdout>:      262144         65536     float     sum      -1    281.7    0.93    0.93      0    281.3    0.93    0.93      0
[1,0]<stdout>:      524288        131072     float     sum      -1    382.5    1.37    1.37      0    381.4    1.37    1.37      0
[1,0]<stdout>:     1048576        262144     float     sum      -1    475.7    2.20    2.20      0    477.6    2.20    2.20      0
[1,0]<stdout>:     2097152        524288     float     sum      -1    779.3    2.69    2.69      0    779.7    2.69    2.69      0
[1,0]<stdout>:     4194304       1048576     float     sum      -1   1369.3    3.06    3.06      0   1356.9    3.09    3.09      0
[1,0]<stdout>:     8388608       2097152     float     sum      -1   3039.0    2.76    2.76      0   3016.8    2.78    2.78      0
[1,0]<stdout>:    16777216       4194304     float     sum      -1   6375.0    2.63    2.63      0   6341.5    2.65    2.65      0
[1,0]<stdout>:    33554432       8388608     float     sum      -1    12124    2.77    2.77      0    12060    2.78    2.78      0
[1,0]<stdout>:    67108864      16777216     float     sum      -1    23665    2.84    2.84      0    23565    2.85    2.85      0
[1,0]<stdout>:   134217728      33554432     float     sum      -1    47018    2.85    2.85      0    46881    2.86    2.86      0
[1,0]<stdout>:   268435456      67108864     float     sum      -1    94155    2.85    2.85      0    93550    2.87    2.87      0
[1,0]<stdout>:   536870912     134217728     float     sum      -1   185925    2.89    2.89      0   187473    2.86    2.86      0
[1,0]<stdout>:  1073741824     268435456     float     sum      -1   372377    2.88    2.88      0   371851    2.89    2.89      0
[1,0]<stdout>:  2147483648     536870912     float     sum      -1   745248    2.88    2.88      0   745261    2.88    2.88      0
[1,0]<stdout>:  4294967296    1073741824     float     sum      -1  1491861    2.88    2.88      0  1490178    2.88    2.88      0
[1,0]<stdout>:# Out of bounds values : 0 OK
[1,0]<stdout>:# Avg bus bandwidth    : 1.35737
[1,0]<stdout>:#
[1,0]<stdout>:

 

 

 

3. Trainium/Inferencia와 FSx 스토리지


출처: https://catalog.workshops.aws/genaifsxeks/en-US

  • 참고 솔루션/SDK
    • AWS Neuron SDK: 컴파일러, 런타임 및 프로파일링 도구로 구성된 SDK로, 고성능 및 비용 효율적인 딥러닝 가속화를 제공
    • Amazon FSx for Lustre
      • 속도가 중요한 워크로드(예: 머신 러닝, 분석, 고성능 컴퓨팅)를 위한 고성능 병렬 파일 시스템을 제공하는 완전 관리형 서비스
      • 밀리초 미만의 대기 시간으로 데이터에 액세스하고 TB/s의 처리량과 수백만 개의 IOPS로 확장 가능
      • Amazon S3와도 통합되어 있어 클라우드 데이터의 대량을 쉽게 저장, 액세스 및 처리
        • S3 버킷과 연결되면 FSx for Lustre 파일 시스템은 S3 객체를 파일로 투명하게 제공하며, 파일이 Lustre 파일 시스템에 추가, 수정 또는 삭제되면 연결된 S3 버킷의 내용을 자동으로 업데이트 가능
  • 목차
    • Kubernetes 스토리지 개념 및 FSx for Lustre 통합
    • Amazon EKS에서 vLLM을 사용하여 Mistral-7B-Instruct 배포
    • vLLM 구성 검사 및 생성된 데이터 자산 복제
    • 데이터 레이어 테스트를 위한 자체 환경 생성

 

  • 워크샵 준비

 

☞스토리지 구성 - Amazon FSx for Lustre에 모델 데이터 호스팅

 

  • CSI Driver 배포
ACCOUNT_ID=$(aws sts get-caller-identity --query "Account" --output text)

cat << EOF >  fsx-csi-driver.json
{
    "Version":"2012-10-17",
    "Statement":[
        {
            "Effect":"Allow",
            "Action":[
                "iam:CreateServiceLinkedRole",
                "iam:AttachRolePolicy",
                "iam:PutRolePolicy"
            ],
            "Resource":"arn:aws:iam::*:role/aws-service-role/s3.data-source.lustre.fsx.amazonaws.com/*"
        },
        {
            "Action":"iam:CreateServiceLinkedRole",
            "Effect":"Allow",
            "Resource":"*",
            "Condition":{
                "StringLike":{
                    "iam:AWSServiceName":[
                        "fsx.amazonaws.com"
                    ]
                }
            }
        },
        {
            "Effect":"Allow",
            "Action":[
                "s3:ListBucket",
                "fsx:CreateFileSystem",
                "fsx:DeleteFileSystem",
                "fsx:DescribeFileSystems",
                "fsx:TagResource"
            ],
            "Resource":[
                "*"
            ]
        }
    ]
}
EOF

aws iam create-policy \
        --policy-name Amazon_FSx_Lustre_CSI_Driver \
        --policy-document file://fsx-csi-driver.json
        
eksctl create iamserviceaccount \
    --region $AWS_REGION \
    --name fsx-csi-controller-sa \
    --namespace kube-system \
    --cluster $CLUSTER_NAME \
    --attach-policy-arn arn:aws:iam::$ACCOUNT_ID:policy/Amazon_FSx_Lustre_CSI_Driver \
    --approve
    
    
export ROLE_ARN=$(aws cloudformation describe-stacks --stack-name "eksctl-${CLUSTER_NAME}-addon-iamserviceaccount-kube-system-fsx-csi-controller-sa" --query "Stacks[0].Outputs[0].OutputValue"  --region $AWS_REGION --output text)
echo $ROLE_ARN
kubectl get pods -n kube-system -l app.kubernetes.io/name=aws-fsx-csi-driver
kubectl apply -k "github.com/kubernetes-sigs/aws-fsx-csi-driver/deploy/kubernetes/overlays/stable/?ref=release-1.2"

kubectl annotate serviceaccount -n kube-system fsx-csi-controller-sa \
 eks.amazonaws.com/role-arn=$ROLE_ARN --overwrite=true


kubectl get sa/fsx-csi-controller-sa -n kube-system -o yaml

 

  • EKS Persistent Volume 생성 

 

  • Amazon FSx 콘솔에서 옵션 및 성능 세부 정보 보기 (workshop 에서 이미 생성되어 있음)

 

 

  • 모니터링 및 성능 확인 가능

 

  • 모델 추론을 위해 AWS Inferentia 노드에 vLLM 배포
cd /home/ec2-user/environment/eks/genai
kubectl apply -f inferentia_nodepool.yaml

kubectl get nodepool,ec2nodeclass inferentia

kubectl apply -f https://raw.githubusercontent.com/aws-neuron/aws-neuron-sdk/master/src/k8/k8s-neuron-device-plugin-rbac.yml
kubectl apply -f https://raw.githubusercontent.com/aws-neuron/aws-neuron-sdk/master/src/k8/k8s-neuron-device-plugin.yml

kubectl apply -f https://raw.githubusercontent.com/aws-neuron/aws-neuron-sdk/master/src/k8/k8s-neuron-scheduler-eks.yml
kubectl apply -f https://raw.githubusercontent.com/aws-neuron/aws-neuron-sdk/master/src/k8/my-scheduler.yml


kubectl apply -f mistral-fsxl.yaml

cat mistral-fsxl.yaml

kubectl get pod

 

 

AWS Inferentia inf2.xlarge 컴퓨팅 노드 확인

 

  • 모델과 상호 작용하기 위한 WebUI 채팅 애플리케이션 배포
kubectl apply -f open-webui.yaml

kubectl get ing

 

 

 

☞  Mistral-7B 데이터를 검사하고 생성된 데이터 자산을 공유 및 복제합니다.

 

  • S3 연결 FSx for Lustre 인스턴스에 대한 S3 크로스 리전 복제 구성

 

  • fsx-lustre-xxxx (2ndregion 은 들어가지 않음) bucket 설정

  • Replication rules 생성

Enable Versioning 클릭
Enable Versioning 클릭
저장 후 기존 객체 복제 팝업

 

  • Mistral-7B 데이터를 검사하고 Pod에서 테스트 파일을 생성하여 데이터의 자동 내보내기 및 복제를 활성화합니다.
cd /home/ec2-user/environment/eks/FSxL

kubectl get pods
NAME                                            READY   STATUS    RESTARTS   AGE
kube-ops-view-5d9d967b77-jrctx                  1/1     Running   0          2d1h
open-webui-deployment-5d7ff94bc9-8kkkd          1/1     Running   0          5h15m
vllm-mistral-inf2-deployment-7d886c8cc8-lfnn9   1/1     Running   0          5h24m

kubectl exec -it <YOUR-vLLM-POD-NAME> -- bash
kubectl exec -it vllm-mistral-inf2-deployment-7d886c8cc8-lfnn9 -- bash

df -h
Filesystem                 Size  Used Avail Use% Mounted on
overlay                    100G   23G   78G  23% /
tmpfs                       64M     0   64M   0% /dev
tmpfs                      7.7G     0  7.7G   0% /sys/fs/cgroup
10.0.42.106@tcp:/tiytbb4v  1.2T   28G  1.1T   3% /work-dir
/dev/nvme0n1p1             100G   23G   78G  23% /etc/hosts
shm                         64M     0   64M   0% /dev/shm
tmpfs                       15G   12K   15G   1% /run/secrets/kubernetes.io/serviceaccount
tmpfs                      7.7G     0  7.7G   0% /proc/acpi
tmpfs                      7.7G     0  7.7G   0% /sys/firmware

cd /work-dir/
ls -ll

cd Mistral-7B-Instruct-v0.2/
ls -ll

 

 

  • test 라는 새 폴더 아래에 testfile 이라는 테스트 파일을 만들어 보겠습니다 . 그러면 이 FSx 인스턴스에 연결된 S3 버킷으로 테스트 파일을 내보내고, 이후 S3 버킷 간에 테스트 파일을 대상 S3 버킷(us-east-2 지역)으로 S3 복제합니다.
cd /work-dir
mkdir test
cd test
cp /work-dir/Mistral-7B-Instruct-v0.2/README.md /work-dir/test/testfile
ls -ll /work-dir/test

 

 

 

☞ 데이터 계층 테스트를 위한 자체 환경 생성

 

☞ 동적 프로비저닝을 사용하여 테스트를 위해 새 PV 및 FSx Lustre 인스턴스를 배포합니다.

VPC_ID=$(aws eks describe-cluster --name $CLUSTER_NAME --region $AWS_REGION --query "cluster.resourcesVpcConfig.vpcId" --output text)
SUBNET_ID=$(aws eks describe-cluster --name $CLUSTER_NAME --region $AWS_REGION --query "cluster.resourcesVpcConfig.subnetIds[0]" --output text)
SECURITY_GROUP_ID=$(aws ec2 describe-security-groups --filters Name=vpc-id,Values=${VPC_ID} Name=group-name,Values="FSxLSecurityGroup01"  --query "SecurityGroups[*].GroupId" --output text)  

echo $SUBNET_ID
echo $SECURITY_GROUP_ID

cd /home/ec2-user/environment/eks/FSxL

sed -i'' -e "s/SUBNET_ID/$SUBNET_ID/g" fsxL-storage-class.yaml
sed -i'' -e "s/SECURITY_GROUP_ID/$SECURITY_GROUP_ID/g" fsxL-storage-class.yaml

cat fsxL-storage-class.yaml

  • fsxL-storage-class.yaml 파일의 값 필드 이해
    • subnetId - Amazon FSx for Lustre 파일 시스템을 생성할 서브넷 ID입니다. Amazon FSx for Lustre는 모든 가용 영역에서 지원되지 않습니다. Amazon FSx for Lustre 콘솔을 열고 https://console.aws.amazon.com/fsx/사용하려는 서브넷이 지원되는 가용 영역에 있는지 확인하십시오. 서브넷에는 노드가 포함될 수도 있고, 다른 서브넷 또는 VPC일 수도 있습니다. 지정한 서브넷이 노드가 있는 서브넷과 다른 경우, VPC를 연결해야 하며 보안 그룹에서 필요한 포트가 열려 있는지 확인해야 합니다.
    • securityGroupIds – 노드의 보안 그룹 ID입니다.
    • s3ImportPath – 영구 볼륨으로 데이터를 복사할 Amazon Simple Storage Service 데이터 저장소입니다.
    • s3ExportPath – 새 파일이나 수정된 ​​파일을 내보내려는 Amazon S3 데이터 저장소입니다.
    • 배포 유형 - 파일 시스템 배포 유형입니다. 유효한 값은 SCRATCH_1, SCRATCH_2 및 PERSISTENT_1입니다. 배포 유형에 대한 자세한 내용은 Amazon FSx for Lustre 파일 시스템 생성을 참조하세요.
    • autoImportPolicy - FSx가 따르는 정책으로, 연결된 데이터 저장소의 변경 사항을 파일 시스템에 자동으로 업데이트하는 방식을 결정합니다. 사용 가능한 정책 목록은 FSx for Lustre 공식 문서를 참조하세요. 
    • perUnitStorageThroughput - 배포 유형 PERSISTENT_1의 경우, 고객은 스토리지 처리량을 지정할 수 있습니다. 기본값: "200". 단, 여기서는 "200" 또는 "100"과 같은 문자열로 지정해야 합니다.

 

kubectl apply -f fsxL-storage-class.yaml

kubectl get sc
NAME            PROVISIONER             RECLAIMPOLICY   VOLUMEBINDINGMODE      ALLOWVOLUMEEXPANSION   AGE
fsx-lustre-sc   fsx.csi.aws.com         Delete          Immediate              false                  8m3s
gp2             kubernetes.io/aws-ebs   Delete          WaitForFirstConsumer   false                  2d1h
gp3 (default)   ebs.csi.aws.com         Delete          WaitForFirstConsumer   true                   2d1h

cat fsxL-dynamic-claim.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: fsx-lustre-dynamic-claim
spec:
  accessModes:
    - ReadWriteMany
  storageClassName: fsx-lustre-sc
  resources:
    requests:
      storage: 1200Gi
      
kubectl apply -f fsxL-dynamic-claim.yaml

kubectl describe pvc/fsx-lustre-dynamic-claim

kubectl get pvc

 

 

 

  • 성능 테스트
cd /home/ec2-user/environment/eks/FSxL

aws ec2 describe-subnets --subnet-id $SUBNET_ID --region $AWS_REGION | jq .Subnets[0].AvailabilityZone
"us-west-2b"

vi pod_performance.yaml

....
#  nodeSelector:
#    topology.kubernetes.io/zone: us-east-2c

# 아래와 같이 변경
  nodeSelector:
    topology.kubernetes.io/zone: us-west-2b
    
kubectl apply -f pod_performance.yaml

kubectl get pods
NAME                                            READY   STATUS    RESTARTS   AGE
fsxl-performance                                1/1     Running   0          21s
kube-ops-view-5d9d967b77-jrctx                  1/1     Running   0          2d1h
open-webui-deployment-5d7ff94bc9-8kkkd          1/1     Running   0          5h40m
vllm-mistral-inf2-deployment-7d886c8cc8-lfnn9   1/1     Running   0          5h49m



#컨테이너에 로그인하고 FIO 및 IOping 테스트를 수행합니다.
kubectl exec -it fsxl-performance  -- bash

# FIO 및 IOping 설치
apt-get update
apt-get install fio ioping -y

ioping -c 20 .
4 KiB <<< . (overlay overlay 19.9 GiB): request=1 time=340.5 us (warmup)
4 KiB <<< . (overlay overlay 19.9 GiB): request=2 time=586.6 us
4 KiB <<< . (overlay overlay 19.9 GiB): request=3 time=587.6 us
4 KiB <<< . (overlay overlay 19.9 GiB): request=4 time=626.2 us
4 KiB <<< . (overlay overlay 19.9 GiB): request=5 time=556.4 us
4 KiB <<< . (overlay overlay 19.9 GiB): request=6 time=662.0 us
4 KiB <<< . (overlay overlay 19.9 GiB): request=7 time=600.8 us
4 KiB <<< . (overlay overlay 19.9 GiB): request=8 time=525.3 us
4 KiB <<< . (overlay overlay 19.9 GiB): request=9 time=509.6 us
4 KiB <<< . (overlay overlay 19.9 GiB): request=10 time=579.6 us
4 KiB <<< . (overlay overlay 19.9 GiB): request=11 time=541.8 us
4 KiB <<< . (overlay overlay 19.9 GiB): request=12 time=462.1 us
4 KiB <<< . (overlay overlay 19.9 GiB): request=13 time=554.2 us
4 KiB <<< . (overlay overlay 19.9 GiB): request=14 time=713.5 us
4 KiB <<< . (overlay overlay 19.9 GiB): request=15 time=723.8 us
4 KiB <<< . (overlay overlay 19.9 GiB): request=16 time=598.6 us
4 KiB <<< . (overlay overlay 19.9 GiB): request=17 time=1.27 ms (slow)
4 KiB <<< . (overlay overlay 19.9 GiB): request=18 time=519.8 us
4 KiB <<< . (overlay overlay 19.9 GiB): request=19 time=484.8 us
4 KiB <<< . (overlay overlay 19.9 GiB): request=20 time=473.2 us

--- . (overlay overlay 19.9 GiB) ioping statistics ---
19 requests completed in 11.6 ms, 76 KiB read, 1.64 k iops, 6.41 MiB/s
generated 20 requests in 19.0 s, 80 KiB, 1 iops, 4.21 KiB/s
min/avg/max/mdev = 462.1 us / 609.4 us / 1.27 ms / 171.7 us

mkdir -p /data/performance
cd /data/performance
fio --randrepeat=1 --ioengine=libaio --direct=1 --gtod_reduce=1 --name=fiotest --filename=testfio8gb --bs=1MB --iodepth=64 --size=8G --readwrite=randrw --rwmixread=50 --numjobs=8 --group_reporting --runtime=10
fiotest: (g=0): rw=randrw, bs=(R) 1024KiB-1024KiB, (W) 1024KiB-1024KiB, (T) 1024KiB-1024KiB, ioengine=libaio, iodepth=64
...
fio-3.33
Starting 8 processes
fiotest: Laying out IO file (1 file / 8192MiB)
Jobs: 3 (f=3): [_(4),m(3),_(1)][19.7%][r=157MiB/s,w=158MiB/s][r=157,w=158 IOPS][eta 01m:01s]
fiotest: (groupid=0, jobs=8): err= 0: pid=725: Sat Apr 19 02:50:20 2025
  read: IOPS=99, BW=99.5MiB/s (104MB/s)(1402MiB/14093msec)
   bw (  KiB/s): min=16359, max=380480, per=100.00%, avg=125838.98, stdev=11943.91, samples=149
   iops        : min=   11, max=  367, avg=119.50, stdev=11.70, samples=149
  write: IOPS=99, BW=99.2MiB/s (104MB/s)(1398MiB/14093msec); 0 zone resets
   bw (  KiB/s): min=16359, max=441802, per=100.00%, avg=126976.16, stdev=12902.20, samples=150
   iops        : min=   11, max=  427, avg=120.96, stdev=12.61, samples=150
  cpu          : usr=0.09%, sys=0.70%, ctx=5579, majf=0, minf=52
  IO depths    : 1=0.3%, 2=0.6%, 4=1.1%, 8=2.3%, 16=4.6%, 32=9.1%, >=64=82.0%
     submit    : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.0%, >=64=0.0%
     complete  : 0=0.0%, 4=99.7%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.3%, >=64=0.0%
     issued rwts: total=1402,1398,0,0 short=0,0,0,0 dropped=0,0,0,0
     latency   : target=0, window=0, percentile=100.00%, depth=64

Run status group 0 (all jobs):
   READ: bw=99.5MiB/s (104MB/s), 99.5MiB/s-99.5MiB/s (104MB/s-104MB/s), io=1402MiB (1470MB), run=14093-14093msec
  WRITE: bw=99.2MiB/s (104MB/s), 99.2MiB/s-99.2MiB/s (104MB/s-104MB/s), io=1398MiB (1466MB), run=14093-14093msec

 

 

  • 성과 요약

FSx for Lustre 파일 시스템에서 워크로드가 처리할 수 있는 구체적인 처리량 및 IOPS는 처리량, 파일 시스템의 스토리지 용량 구성, 그리고 워크로드의 특성에 따라 달라집니다. Amazon FSx for Lustre 성능에 대한 자세한 내용은 Amazon FSx for Lustre 성능 차트 링크를 참조하세요. 

'AEWS study' 카테고리의 다른 글

12주차 - Amazon VPC Lattice for Amazon EKS  (0) 2025.04.21
10주차 - K8s 시크릿 관리 Update  (0) 2025.04.09
9주차 - EKS Upgrade  (0) 2025.03.30
8주차 - K8S CI/CD - #2  (0) 2025.03.29
8주차 - K8S CI/CD - #1  (0) 2025.03.28
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
TAG
more
«   2025/05   »
1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30 31
글 보관함