1
2
AWS EKS Workshop Study (=AEWS)는 EKS Workshop 실습 스터디입니다.
CloudNet@ Gasida(가시다)님이 진행하시며,공개된 AWS EKS Workshop을 기반으로 진행하고 있습니다.

지난 블로그 글에 이어서 kubeflow 를 스터디한 내용을 공유하고자 한다. 오늘 주제는 kubeflow 의 인프라 요소로 왜 쿠버네티스에서 머신러닝을 쓰는 것이 좋은 가와 kubeflow 가 AWS 클라우드(EKS)에 올라갔을 때 어떤 이점이 있는 지 확인하겠다.

ML on kubernetes

먼저, 쿠버네티스는 머신러닝 워크플로우를 지원하는 데 매우 유용한 플랫폼으로 이유는 다음과 같이 확인할 수가 있다. (참고 : ChatGPT)

  1. 확장성: 쿠버네티스는 애플리케이션을 클러스터의 다양한 노드에 자동으로 분산시키는 능력을 가지고 있다. 이는 머신러닝 모델을 학습하거나 예측을 생성할 때 필요한 컴퓨팅 자원을 자동으로 확장하고 축소할 수 있다는 것을 의미한다.
  2. 포터빌리티와 다중 클라우드 지원: 쿠버네티스는 여러 클라우드 제공 업체에 걸쳐 동일한 방식으로 애플리케이션을 배포하고 관리하는 데 도움이 된다. 이는 머신러닝 워크플로우를 어디서든 쉽게 이동하고 배포할 수 있음을 의미한다.
  3. 자동화와 오케스트레이션: 쿠버네티스는 컨테이너화 된 애플리케이션의 배포, 확장 및 관리를 자동화한다. 이는 머신러닝 파이프라인의 복잡한 워크플로우를 쉽게 관리하고 오케스트레이션할 수 있음을 의미한다.
  4. 자원 관리: 쿠버네티스는 CPU, 메모리 등의 자원을 효과적으로 관리하여 각 애플리케이션에 필요한 자원을 제공한다. 이는 머신러닝 워크플로우에서 중요한 역할이다.
  5. 커뮤니티와 에코시스템: 쿠버네티스는 강력한 커뮤니티와 에코시스템을 가지고 있다. 여기에는 머신러닝에 특화된 도구와 프레임워크를 쿠버네티스에 쉽게 통합할 수 있는 Kubeflow와 같은 프로젝트가 포함된다.

여기서 가장 중요하게 볼 점은 4번인 것 같다. 머신러닝의 모델 학습 과정에서 분산 처리로 학습을 진행하면 속도가 선형적으로 빨라지기 때문이다.

<a href="https://www.youtube.com/watch?v=qctwfYZKK8M&amp;t=528s">https://www.youtube.com/watch?v=qctwfYZKK8M&amp;t=528s</a>

https://www.youtube.com/watch?v=qctwfYZKK8M&t=528s

쿠버네티스가 머신러닝 플랫폼에 유용한 플랫폼인 것을 확인했지만, 어떻게 쿠버네티스에서 머신러닝 플랫폼을 머신러닝 과학자나 분석가 분들에게 제공할 것에 대한 의문이 남는다. 여기에 대한 해결점이 지난 시간에 구축한 kubeflow 인데 ML 플랫폼을 한 데 모아 묶어 설치하고 인터넷 링크를 통해 쉽게 제공할 수 있기 때문이다.

kubeflow-addon2.png

https://www.youtube.com/watch?v=6GYuRy84M1o&t=67s

Kubeflow on AWS

kubeflow on AWS 라는 말은 kubeflow를 EKS에서 배포했을 때를 얘기한다. kubeflow on AWS 로 올리면 AWS 서비스와의 통합을 통해 운영 오버헤드를 줄이면서 안전성, 보안, 이식성, 확장성이 우수한 ML 시스템을 구축할 수 있다.

kubeflow-addon1.png

AWS 서비스 통합을 구체적으로 예를 들자면 다음과 같다.

  • 사용하기 쉬운 파이프라인 아티팩트 스토어를 위한 Amazon Simple Storage Service(Amazon S3)
  • 높은 확장성의 파이프라인 및 메타데이터 스토어를 위한 Amazon Relational Database Service(Amazon RDS)
  • 훈련 성능 향상 목적의 간단하고 확장 가능한 서버리스 파일 스토리지 솔루션을 위한 Amazon Elastic File System/Amazon FSx for Lustre
  • 애플리케이션 액세스에 필요한 보안 암호 보호를 위한 AWS Secrets Manager
  • 영구 로그 관리를 위한 AWS CloudWatch
  • 고도로 최적화된 Jupyter 노트북 서버 이미지를 위한 AWS Deep Learning Containers
  • HTTPS를 경유하는 안전한 외부 트래픽 관리를 위한 AWS Application Load Balancer
  • TLS를 통한 사용자 인증을 위한 AWS Cognito

자세한 서비스 통합 방법 관련하여 Kubeflow 공식문서를 참고하도록 하자.

이번 장에서는 지난 시간에 구축한 kubeflow에 AWS 로드밸런서 서비스인 AWS Application Load Balancer 와 파일 스토리지인 EFS를 추가로 구축하는 방법을 공유하겠다.

AWS ALB 및 External DNS 연결

kubeflow에 AWS ALB을 연결하여 클러스터 외부 인터넷 망에서 HTTPS로 접근하도록 설정하겠다. 이에 대한 사전 작업으로 개인 도메인과 인증서가 필요하다. 필자의 경우 AWS Route53에서 도메인을 구매하였고, AWS ACM을 통해 인증서를 발급받았다. 사전 작업에 대한 내용은 kubeflow on AWS 공식문서를 참고하자.

이어서 EKS 클러스터에 ALB controller 설치가 필요하다. 다음의 스크립트를 통해 진행하자.

 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
# ALB controller 정책 설치
curl -o iam_policy.json https://raw.githubusercontent.com/kubernetes-sigs/aws-load-balancer-controller/v2.4.7/docs/install/iam_policy.json
aws iam create-policy --policy-name AWSLoadBalancerControllerIAMPolicy --policy-document file://iam_policy.json

# 정책 arn 생성 확인 
ACCOUNT_ID={AWS 계정 넘버} 
aws iam get-policy --policy-arn arn:aws:iam::$ACCOUNT_ID:policy/AWSLoadBalancerControllerIAMPolicy --query 'Policy.Arn'
---
"arn:aws:iam::955963799952:policy/AWSLoadBalancerControllerIAMPolicy" 

# OIDC 서비스 어카운트 생성
CLUSTER_NAME={EKS 클러스터 이름} 
eksctl create iamserviceaccount --cluster=$CLUSTER_NAME --namespace=kube-system --name=aws-load-balancer-controller \
--attach-policy-arn=arn:aws:iam::$ACCOUNT_ID:policy/AWSLoadBalancerControllerIAMPolicy --override-existing-serviceaccounts --approve

# OIDC 서비스 어카운트 확인 
kubectl edit sa/aws-load-balancer-controller  -n kube-system
---
apiVersion: v1
kind: ServiceAccount
metadata:
  annotations: # annotation 확인 
    eks.amazonaws.com/role-arn: arn:aws:iam::955963799952:role/eksctl-my-eks-kubeflow-addon-iamserviceaccou-Role1-18M1QX0LI36SU
  creationTimestamp: "2023-05-12T13:22:02Z"
  labels:
    app.kubernetes.io/managed-by: eksctl
  name: aws-load-balancer-controller
  namespace: kube-system
  resourceVersion: "42768"
  uid: 00393ee1-e469-4bd3-bd4e-6a10303a3f76
~

서비스 어카운트 연동이 확인이 되었으면 ALB 을 설치하겠다. 설치는 Helm을 통해 진행했다.

 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
# 클러스터 이름 설정 
CLUSTER_NAME=my-eks-kubeflow
printf 'clusterName='$CLUSTER_NAME'' > awsconfigs/common/aws-alb-ingress-controller/base/params.env

# Install Load Balancer Controoler
kustomize build awsconfigs/common/aws-alb-ingress-controller/base | kubectl apply -f -
kubectl wait --for condition=established crd/ingressclassparams.elbv2.k8s.aws
kustomize build awsconfigs/common/aws-alb-ingress-controller/base | kubectl apply -f -

# ALB 파드 확인 
kubectl get pods -A
---
NAMESPACE     NAME                                            READY   STATUS    RESTARTS   AGE
kube-system   aws-load-balancer-controller-7857849d69-qc9nr   1/1     Running   0          24m

# ALB 로그 확인
kubectl logs pods/aws-load-balancer-controller-7857849d69-qc9nr -n kube-system
---
{"level":"info","ts":"2023-05-08T05:59:45Z","msg":"version","GitVersion":"v2.5.1","GitCommit":"06abaed66e17a411ba064f34e6018b889780ac66","BuildDate":"2023-04-17T22:36:53+0000"}
{"level":"info","ts":"2023-05-08T05:59:45Z","logger":"controller-runtime.metrics","msg":"Metrics server is starting to listen","addr":":8080"}
{"level":"info","ts":"2023-05-08T05:59:45Z","logger":"setup","msg":"adding health check for controller"}
{"level":"info","ts":"2023-05-08T05:59:45Z","logger":"controller-runtime.webhook","msg":"Registering webhook","path":"/mutate-v1-pod"}
{"level":"info","ts":"2023-05-08T05:59:45Z","logger":"controller-runtime.webhook","msg":"Registering webhook","path":"/mutate-v1-service"}
{"level":"info","ts":"2023-05-08T05:59:45Z","logger":"controller-runtime.webhook","msg":"Registering webhook","path":"/validate-elbv2-k8s-aws-v1beta1-ingressclassparams"}
{"level":"info","ts":"2023-05-08T05:59:45Z","logger":"controller-runtime.webhook","msg":"Registering webhook","path":"/mutate-elbv2-k8s-aws-v1beta1-targetgroupbinding"}
{"level":"info","ts":"2023-05-08T05:59:45Z","logger":"controller-runtime.webhook","msg":"Registering webhook","path":"/validate-elbv2-k8s-aws-v1beta1-targetgroupbinding"}
{"level":"info","ts":"2023-05-08T05:59:45Z","logger":"controller-runtime.webhook","msg":"Registering webhook","path":"/validate-networking-v1-ingress"}
{"level":"info","ts":"2023-05-08T05:59:45Z","logger":"setup","msg":"starting podInfo repo"}
{"level":"info","ts":"2023-05-08T05:59:47Z","logger":"controller-runtime.webhook.webhooks","msg":"Starting webhook server"}
{"level":"info","ts":"2023-05-08T05:59:47Z","msg":"Starting server","kind":"health probe","addr":"[::]:61779"}
...

이어서 External DNS 를 설치하자. 다음의 스크립트를 기반으로 OIDC 정책 연동부터 진행하자.

 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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
# vi iam_external_policy.json 생성
cat <<EOT > iam_external_policy.json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "route53:ChangeResourceRecordSets"
      ],
      "Resource": [
        "arn:aws:route53:::hostedzone/*"
      ]
    },
    {
      "Effect": "Allow",
      "Action": [
        "route53:ListHostedZones",
        "route53:ListResourceRecordSets"
      ],
      "Resource": [
        "*"
      ]
    }
  ]
}
EOT

aws iam create-policy --policy-name "AllowExternalDNSUpdates" --policy-document file://iam_external_policy.json.json

# 정책 arn 생성 확인 
aws iam get-policy --policy-arn arn:aws:iam::$ACCOUNT_ID:policy/AllowExternalDNSUpdates --query 'Policy.Arn'
---
"arn:aws:iam::955963799952:policy/AllowExternalDNSUpdates"

# OIDC 서비스 어카운트 생성 
eksctl create iamserviceaccount --cluster=$CLUSTER_NAME --namespace=kube-system --name=external-dns \
--attach-policy-arn=arn:aws:iam::$ACCOUNT_ID:policy/AllowExternalDNSUpdates --override-existing-serviceaccounts --approve

# OIDC 서비스 어카운트 확인 
kubectl edit sa/external-dns -n kube-system
---
apiVersion: v1
kind: ServiceAccount
metadata: 
  annotations: # 마찬가지로 연동 확인이 가능하다. 
    eks.amazonaws.com/role-arn: arn:aws:iam::955963799952:role/eksctl-hanhorang-addon-iamserviceaccount-kub-Role1-499RATDKJVJU
    kubectl.kubernetes.io/last-applied-configuration: |
      {"apiVersion":"v1","kind":"ServiceAccount","metadata":{"annotations":{},"labels":{"app.kubernetes.io/name":"external-dns"},"name":"external-dns","namespace":"kube-system"}}
  creationTimestamp: "2023-05-08T06:02:38Z"
  labels:
    app.kubernetes.io/managed-by: eksctl
    app.kubernetes.io/name: external-dns
  name: external-dns
  namespace: kube-system
  resourceVersion: "13937"
  uid: 67ce20cc-6545-486c-b525-450be6311d65

external DNS을 연동하기 위해서는 도메인이 필요하다. 앞서 Route53에서 생성한 도메인을 연동하자.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# MyDomain=<자신의 도메인>
MyDomain=hanhorang.link 

# 자신의 Route 53 도메인 ID 조회 및 변수 지정
MyDnzHostedZoneId=$(aws route53 list-hosted-zones-by-name --dns-name "${MyDomain}." --query "HostedZones[0].Id" --output text)

# ExternalDNS 배포
curl -s -O https://raw.githubusercontent.com/gasida/PKOS/main/aews/externaldns.yaml
cat externaldns.yaml | yh
MyDomain=$MyDomain MyDnzHostedZoneId=$MyDnzHostedZoneId envsubst < externaldns.yaml | kubectl apply -

# 로그 확인 
kubectl get pods -A
---
kube-system   external-dns-6b5bbbf9d-l7ms2                    1/1     Running   0          4

kubectl logs pods/external-dns-6b5bbbf9d-l7ms2 -n kube-system
---
time="2023-05-08T06:02:43Z" level=info msg="config: {APIServerURL: KubeConfig: RequestTimeout:30s DefaultTargets:[] ContourLoadBalancerService:heptio-contour/contour GlooNamespace:gloo-system SkipperRouteGroupVersion:zalando.org/v1 Sources:[service ingress] Namespace: AnnotationFilter: LabelFilter: FQDNTemplate: CombineFQDNAndAnnotation:false IgnoreHostnameAnnotation:false IgnoreIngressTLSSpec:false IgnoreIngressRulesSpec:false GatewayNamespace: GatewayLabelFilter: Compatibility: PublishInternal:false PublishHostIP:false AlwaysPublishNotReadyAddresses:false ConnectorSourceServer:localhost:8080 Provider:aws GoogleProject: GoogleBatchChangeSize:1000 GoogleBatchChangeInterval:1s GoogleZoneVisibility: DomainFilter:[hanhorang.link] ExcludeDomains:[] RegexDomainFilter: RegexDomainExclusion: ZoneNameFilter:[] ZoneIDFilter:[] TargetNetFilter:[] ExcludeTargetNets:[] AlibabaCloudConfigFile:/etc/kubernetes/alibaba-cloud.json AlibabaCloudZoneType: AWSZoneType:public AWSZoneTagFilter:[] AWSAssumeRole: AWSAssumeRoleExternalID: AWSBatchChangeSize:1000 AWSBatchChangeInterval:1s AWSEvaluateTargetHealth:true AWSAPIRetries:3 AWSPreferCNAME:false AWSZoneCacheDuration:0s AWSSDServiceCleanup:false AzureConfigFile:/etc/kubernetes/azure.json AzureResourceGroup: AzureSubscriptionID: AzureUserAssignedIdentityClientID: BluecatDNSConfiguration: BluecatConfigFile:/etc/kubernetes/bluecat.json BluecatDNSView: BluecatGatewayHost: BluecatRootZone: BluecatDNSServerName: BluecatDNSDeployType:no-deploy BluecatSkipTLSVerify:false CloudflareProxied:false CloudflareDNSRecordsPerPage:100 CoreDNSPrefix:/skydns/ RcodezeroTXTEncrypt:false AkamaiServiceConsumerDomain: AkamaiClientToken: AkamaiClientSecret: AkamaiAccessToken: AkamaiEdgercPath: AkamaiEdgercSection: InfobloxGridHost: InfobloxWapiPort:443 InfobloxWapiUsername:admin InfobloxWapiPassword: InfobloxWapiVersion:2.3.1 InfobloxSSLVerify:true InfobloxView: InfobloxMaxResults:0 InfobloxFQDNRegEx: InfobloxNameRegEx: InfobloxCreatePTR:false InfobloxCacheDuration:0 DynCustomerName: DynUsername: DynPassword: DynMinTTLSeconds:0 OCIConfigFile:/etc/kubernetes/oci.yaml InMemoryZones:[] OVHEndpoint:ovh-eu OVHApiRateLimit:20 PDNSServer:http://localhost:8081 PDNSAPIKey: PDNSTLSEnabled:false TLSCA: TLSClientCert: TLSClientCertKey: Policy:sync Registry:txt TXTOwnerID:/hostedzone/Z08463751O7YNWD79KKIX TXTPrefix: TXTSuffix: Interval:1m0s MinEventSyncInterval:5s Once:false DryRun:false UpdateEvents:false LogFormat:text MetricsAddress::7979 LogLevel:info TXTCacheInterval:0s TXTWildcardReplacement: ExoscaleEndpoint:https://api.exoscale.ch/dns ExoscaleAPIKey: ExoscaleAPISecret: CRDSourceAPIVersion:externaldns.k8s.io/v1alpha1 CRDSourceKind:DNSEndpoint ServiceTypeFilter:[] CFAPIEndpoint: CFUsername: CFPassword: RFC2136Host: RFC2136Port:0 RFC2136Zone: RFC2136Insecure:false RFC2136GSSTSIG:false RFC2136KerberosRealm: RFC2136KerberosUsername: RFC2136KerberosPassword: RFC2136TSIGKeyName: RFC2136TSIGSecret: RFC2136TSIGSecretAlg: RFC2136TAXFR:false RFC2136MinTTL:0s RFC2136BatchChangeSize:50 NS1Endpoint: NS1IgnoreSSL:false NS1MinTTLSeconds:0 TransIPAccountName: TransIPPrivateKeyFile: DigitalOceanAPIPageSize:50 ManagedDNSRecordTypes:[A CNAME] GoDaddyAPIKey: GoDaddySecretKey: GoDaddyTTL:0 GoDaddyOTE:false OCPRouterName: IBMCloudProxied:false IBMCloudConfigFile:/etc/kubernetes/ibmcloud.json TencentCloudConfigFile:/etc/kubernetes/tencent-cloud.json TencentCloudZoneType: PiholeServer: PiholePassword: PiholeTLSInsecureSkipVerify:false PluralCluster: PluralProvider:}"
time="2023-05-08T06:02:43Z" level=info msg="Instantiating new Kubernetes client"
time="2023-05-08T06:02:43Z" level=info msg="Using inCluster-config based on serviceaccount-token"
time="2023-05-08T06:02:43Z" level=info msg="Created Kubernetes client https://10.100.0.1:443"
time="2023-05-08T06:02:45Z" level=info msg="Applying provider record filter for domains: [hanhorang.link. .hanhorang.link.]"
time="2023-05-08T06:02:45Z" level=info msg="All records are already up to date"
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
export certArn= ACM 인증서 arn 
# 경로 kubeflow-manifests
printf 'certArn='$certArn'' > awsconfigs/common/istio-ingress/overlays/https/params.env 

# istio certArn 설정 업데이트
kustomize build awsconfigs/common/istio-ingress/overlays/https | kubectl apply -f - 

# 정상 등록 확인
kubectl get ingress -n istio-system istio-ingress
---
NAME            CLASS    HOSTS   ADDRESS                                                                        PORTS   AGE
istio-ingress   <none>   *       k8s-istiosys-istioing-663e16c023-2114118481.ap-northeast-2.elb.amazonaws.com   80      43s 

이어서 도메인 등록이 필요하다. 다음의 경로 tests/e2e/utils/load_balancer/config.yaml 에서 파일을 수정하자.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
cluster:
  name: my-eks-kubeflow
  region: ap-northeast-2
kubeflow:
  alb:
    scheme: internet-facing
route53:
  rootDomain:
    hostedZoneId: Z08463751O7YNWD79KKIX
    name: hanhorang.link
  subDomain:
    name: platform.hanhorang.link

적용 명령어는 다음과 같다.

1
2
3
4
5
6
7
8
# 경로 kubeflow-manifests 
# 설치 스크립트 패키지 설치 
cd tests/e2e
pip3 install -r requirements.txt

# 경로 확인! 
# 반드시 kubeflow-manifests/tests/e2e 에서 진행하자. (파이썬 e2e 모듈 인식)
PYTHONPATH=.. python3 utils/load_balancer/setup_load_balancer.py

업데이트가 진행되었으면tests/e2e/utils/load_balancer/config.yaml 파일을 다시 확인하자. 등록한 도메인과 certARN을 확인할 수 있다.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
cluster:
  name: my-eks-kubeflow
  region: ap-northeast-2
kubeflow:
  alb:
    dns: k8s-istiosys-istioing-663e16c023-2114118481.ap-northeast-2.elb.amazonaws.com
    scheme: internet-facing
    serviceAccount:
      name: aws-load-balancer-controller
      namespace: kube-system
      policyArn: arn:aws:iam::955963799952:policy/alb_ingress_controller_my-eks-kubeflow3tdc061n5e
route53:
  rootDomain:
    certARN: arn:aws:acm:ap-northeast-2:955963799952:certificate/274b725c-c1ad-4528-a593-e0030aaa62f9
    hostedZoneId: Z08463751O7YNWD79KKIX
    name: hanhorang.link
  subDomain:
    certARN: arn:aws:acm:ap-northeast-2:955963799952:certificate/09d02797-709e-4c9b-9b95-8eef6fee7e3f
    hostedZoneId: Z08293761OELZZQTGI41U
    name: platform.hanhorang.link

약 5분~10분이후 다음의 서브도메인 (kubeflow.platform.hanhorang.link) 에 접속하면 정상적으로 연결된 것을 확인할 수 있다.

kubeflow6.png

EFS 연결

EFS(Amazon Elastic File System)는 AWS에서 제공하는 파일 스토리지이다. 파일 스토리지인 만큼 여러 노드에서 접근이 가능하여 머신러닝같은 워크 플로우에 자주 추천하는 서비스이다. kubeflow에서도 EFS를 활용할 수 있는데 활용하여 얻는 이점은 다음과 같다. (참고 ChatGPT)

  1. 분산 학습: EFS를 사용하면 여러 노드가 동일한 파일 시스템에 접근할 수 있다. 모든 노드가 동일한 데이터에 접근하고, 중간 학습 결과를 공유하면서 동시에 작업을 수행할 수 있어 학습 속도가 빨라진다..
  2. 데이터 공유와 재사용: EFS는 클러스터의 모든 노드에서 동시에 접근할 수 있는 중앙화된 저장 공간을 제공한다. 이는 데이터를 쉽게 공유하고 재사용할 수 있게 하므로, 데이터 관리를 단순화하고 머신러닝 워크플로우를 효율적으로 만든다.
  3. 확장성: EFS는 자동으로 확장되고 축소되므로, 데이터 저장량에 대해 걱정할 필요가 없다. 또한, 데이터는 여러 가용 영역에 걸쳐 복제되므로, 내구성과 가용성도 보장된다.
  4. 지속적인 저장소: EFS는 지속적인 스토리지를 제공한다. 즉, 파드가 종료되거나 노드에 문제가 생겨도 데이터는 안전하게 보호된다. 이는 머신러닝 훈련에서 중요한데, 훈련 중인 모델의 체크포인트를 저장하고 필요할 때 언제든지 복구할 수 있기 때문이다.

아래는 EFS 연결시 각 워커 노드에서 작동하는 아키텍처이다. EFS 특성에 맞게 각 노드가 스토리지를 공유하여 사용하는 것을 알 수 있다.

addon-4.png

EFS 연결을 위해서는 EFS 프로비저닝이 필요하다. EFS 프로비저닝 방법은 정적, 동적 두가지로 이번 글에서는 동적으로 생성하겠다.

 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
# IAM 정책 생성
curl -s -O https://raw.githubusercontent.com/kubernetes-sigs/aws-efs-csi-driver/master/docsiam-policy-example.json
aws iam create-policy --policy-name AmazonEKS_EFS_CSI_Driver_Policy --policy-document file://iam-policy-example.json

# ISRA 설정 : 고객관리형 정책 AmazonEKS_EFS_CSI_Driver_Policy 사용
eksctl create iamserviceaccount \
  --name efs-csi-controller-sa \
  --namespace kube-system \
  --cluster ${CLUSTER_NAME} \
  --attach-policy-arn arn:aws:iam::${ACCOUNT_ID}:policy/AmazonEKS_EFS_CSI_Driver_Policy \
  --approve

# 적용 확인, annotation 
kubectl get sa -n kube-system efs-csi-controller-sa -o yaml | head -5
---
apiVersion: v1
kind: ServiceAccount
metadata:
  annotations: # 적용 확인
    eks.amazonaws.com/role-arn: arn:aws:iam::955963799952:role/eksctl-my-eks-kubeflow-addon-iamserviceaccou-Role1-1TAOJ41

# efs csi driver 설치
helm repo add aws-efs-csi-driver https://kubernetes-sigs.github.io/aws-efs-csi-driver/
helm repo update
helm upgrade -i aws-efs-csi-driver aws-efs-csi-driver/aws-efs-csi-driver \
    --namespace kube-system \
    --set image.repository=602401143452.dkr.ecr.ap-northeast-2.amazonaws.com/eks/aws-efs-csi-driver \
    --set controller.serviceAccount.create=false \
    --set controller.serviceAccount.name=efs-csi-controller-sa

EFS 프로비저닝 전 EFS 스토리지 생성이 필요하다. 스토리지 생성 단계 전 EKS CIDR 보안 그룹 추가(NFS 트래픽 허용)가 필요하다. 아래 스크립트를 통해 보안 그룹을 생성하고 EFS 파일 시스템을 생성하겠다.

 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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50

# 보안 그룹 생성을 위한 네트워크 변수 설정
vpc_id=$(aws eks describe-cluster \
    --name my-eks-kubeflow \
    --query "cluster.resourcesVpcConfig.vpcId" \
    --output text)

cidr_range=$(aws ec2 describe-vpcs \
    --vpc-ids $vpc_id \
    --query "Vpcs[].CidrBlock" \
    --output text \
    --region ap-northeast-2)

# 보안 그룹 생성
security_group_id=$(aws ec2 create-security-group \
    --group-name MyEfsSecurityGroup \
    --description "My EFS security group" \
    --vpc-id $vpc_id \
    --output text)

# NFS 트래픽 허용 
aws ec2 authorize-security-group-ingress \
    --group-id $security_group_id \
    --protocol tcp \
    --port 2049 \
    --cidr $cidr_range

# EFS 파일 시스템 생성
file_system_id=$(aws efs create-file-system \
    --region ap-northeast-2 \
    --performance-mode generalPurpose \
    --query 'FileSystemId' \
    --output text)

# EFS 확인 
echo $file_system_id
--
fs-01eea8c4de75fcd80

# 스토리지 클래스 생성 
EfsFsId=$(aws efs describe-file-systems --query "FileSystems[*].FileSystemId" --output text) 
curl -s -O https://raw.githubusercontent.com/kubernetes-sigs/aws-efs-csi-driver/master/examples/kubernetes/dynamic_provisioning/specs/storageclass.yaml
sed -i "s/fs-92107410/$EfsFsId/g" storageclass.yaml
kubectl apply -f storageclass.yaml
kubectl get sc efs-sc
--
NAME               PROVISIONER             RECLAIMPOLICY   VOLUMEBINDINGMODE      ALLOWVOLUMEEXPANSION   AGE
ebs-sc (default)   ebs.csi.aws.com         Delete          WaitForFirstConsumer   false                  6h48m
efs-sc             efs.csi.aws.com         Delete          Immediate              false                  25s
gp2                kubernetes.io/aws-ebs   Delete          WaitForFirstConsumer   false                  7h37m

kubeflow 대시보드에 접속하여 EFS 스토리지를 생성해보자. 기본 아이디와 비밀번호는 user@example.com , 12341234 이다.

kubeflow-login1.png

로그인 이후 왼쪽 메뉴 [Volumes] → [New Volume] 클릭 후 Storage Class 확인시 efs-sc 를 확인할 수 있다.

efs2.png

생성된 볼륨은 콘솔 및 터미널에서 확인이 가능하다.

Efs3.png

efs4.png

생성한 볼륨은 jupyter notebook 생성시 데이터 볼륨 설정해서 볼륨 지정이 가능하다

efs5.png

이후 EFS 볼륨을 사용하는 기계 학습 훈련의 예제는 GitHub에 따라 진행하도록 하자.

마치며

Kubeflow on AWS 관련하여 ALB 와 EFS를 연동하여 테스트를 해보았다. 다음 시간에는 kubeflow를 통해 파이프라인을 통한 모델 생성 및 이를 기반한 API deployment 등등 ML 쪽 예제를 다룰 예정이다. 또한 못 다룬 이야기지만 kubeflow 학습시 노드 확장성으로 (노드 셀렉터, 어피니티)를 통해 노드가 자동 확장된다는데 이 점에 대해서도 확인할 생각이다.