반응형

배경

외부에서 Kubernetes Cluster 위에서 돌아가는 서비스에 접근하기 위해서는 Ingress Controller 를 통해서 가능하다. aws 와 같은 클라우드에서는 ELB -> Ingress Controller -> Workload 경로로 접근가능하다.

현재 사용하고 있는 방법은 CrossPlane 으로 AWS ELB (Classic Type) 를 자동으로 생성하여 Ingress Controller 에 연결하고 있다. 그렇기 때문에 Ingress Controller 를 배포할 때 Service Type을 특정 NodePort 를 지정/오픈하여 연결점을 알아야 한다.

예를 들면 아래와 같이 value 값을 override 해야 한다.

controller:
  replicaCount: 2
  service:
    externalTrafficPolicy: Local
    type: NodePort
    nodePorts:
      http: 32080
      https: 32443
      tcp:
        10254: 32081
  hostPort:
    enabled: true
tcp:
  10254: "10254:healthz"

 

CrossPlane 이 AWS ELB 에 대해서 Classic Type 만 지원하기 때문에 CrossPlane 을 제거하고 Network Type 으로 변경하는 것을 고민하였다. 참고로 현재의 방법은 Ingress Controller 를 실수로 삭제해도 ELB 는 그대로 남겨져 있어야 한다는 전제로 고려한 방법이다. DNS 가 ELB 와 연동되어야 하기 때문에 ELB 의 삭제 후 재생성은 DNS 전파의 시간을 필요로 하기 때문이다. 

하지만 AWS 에서 권장하는 NLB 로 넘어가는 것, NodePort 를 지정하지 않아도 된다는 것, 그리고 CrossPlane 으로 ELB를 관리하지 않아도 되기 때문에 운영 측면에서 더 낫다고 판단되어 방법을 찾아보게 되었다.

 

해결책

Kubernetes 프로젝트 아래 Ingress Controller 의 annotation 을 사용하면 NLB 를 자동으로 생성할 수 있다.

$ helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
$ helm repo update

$ helm search repo ingress-nginx/ingress-nginx

$ vi ingress-nginx-values.yaml
---
controller:
  replicaCount: 2
  affinity:
    podAntiAffinity:
      preferredDuringSchedulingIgnoredDuringExecution:
      - weight: 100
        podAffinityTerm:
          labelSelector:
            matchExpressions:
            - key: app.kubernetes.io/name
              operator: In
              values:
              - ingress-nginx
          topologyKey: "kubernetes.io/hostname"
  nodeSelector:
    app.kubernetes.io/name: ingress-nginx
  service:
    annotations:
#      service.beta.kubernetes.io/aws-load-balancer-name: "ahnsk-ingress"
      service.beta.kubernetes.io/aws-load-balancer-type: "nlb"
      service.beta.kubernetes.io/aws-load-balancer-proxy-protocol: "*"
#      service.beta.kubernetes.io/aws-load-balancer-target-group-attributes: "proxy_protocol_v2.enabled=true,preserve_client_ip.enabled=true,deregistration_delay.timeout_seconds=120,deregistration_delay.connection_termination.enabled=true"
    externalTrafficPolicy: Local
    type: LoadBalancer
#    healthCheckNodePort: 32081
  config:
    enable-underscores-in-headers: "true"
    use-proxy-protocol: "true"
    proxy-body-size: "10m"

 

nodeSelector 와 podAntiAffinity 를 사용하여 가능하면 지정노드에 분포해서 Ingress Controller 를 설치할 수 있다. 

Service 의 annotations 을 사용하면 ELB 를 NLB type 으로 생성하여 자동으로 Ingress Controller 에 연결시켜 준다. 한가지 주의할 점은 aws-load-balancer-proxy-protocol 을 사용하면 aws-load-balancer-target-group-attributes 의 proxy_protocol_v2 를 enabled 해야 한다. 그러나 이 annotations 은 동작하지 않는다. 왜냐하면 aws 에서는 Ingress Controller (https://github.com/kubernetes-sigs/aws-load-balancer-controller/tree/main/helm/aws-load-balancer-controller)를 kubernetes-sigs 아래에 따로 만들어서 관리하기 때문이다. 즉 더 이상의 Nginx Ingress Controller 에 대한 기능 추가가 없다고 공표했다.

그럼에도 불구하고 Nginx Ingress Controller 를 선호한다. 이유는 Nginx 의 기능, 즉 config 를 원하는 대로 세팅할 수 있기 때문이다. 

nginx 를 사용할 때 가장 많이 경험하는 에러가 2가지 있다. 하나는 header 에 '_' 값이 포함될 때 에러가 나고 다른 하나는 body-size 로 인한 에러이다. 이 2가지 값은 기본적으로 허용해 주는 것이 좋다. 

마지막으로 ELB, Ingress Controller 를 거치면서 real client ip 를 알고 싶어 하는 경우가 많다. nginx 의 use-proxy-protocol: "true" 로 client ip 를 알아 낼 수 있다.

 

이렇게 세팅한 값으로 Ingress Controller 를 배포한다.

$ kubectl label node ip-10-0-181-43.ap-northeast-2.compute.internal app.kubernetes.io/name=ingress-nginx
$ kubectl label node ip-10-0-214-57.ap-northeast-2.compute.internal app.kubernetes.io/name=ingress-nginx

$ helm upgrade -i ingress-nginx ingress-nginx/ingress-nginx -n ingress-nginx --version 4.0.16 --create-namespace -f ingress-nginx-values.yaml

 

배포가 진행될 때 다음과 같이 NLB 의 Target Group 속성 중에 proxy_protocol_v2 값을 활성화 해줘야 한다. aws console 에서도 가능하지만 aws cli 를 통해서도 가능하다.

$ aws elbv2 describe-target-groups | jq .'TargetGroups[] | select(.VpcId == "vpc id 값") | .TargetGroupArn'

--- output ---
"arn:aws:elasticloadbalancing:ap-northeast-2:..."
"arn:aws:elasticloadbalancing:ap-northeast-2:..."

$ aws elbv2 modify-target-group-attributes --target-group-arn arn:aws:elasticloadbalancing:ap-northeast-2:... --attributes 'Key=proxy_protocol_v2.enabled,Value=true'

$ aws elbv2 modify-target-group-attributes --target-group-arn arn:aws:elasticloadbalancing:ap-northeast-2:... --attributes 'Key=proxy_protocol_v2.enabled,Value=true'

 

테스트

sample application 을 배포하여 실제 호출이 잘 되는지 확인한다. 아래는 배포 후에 http header 를 출력해 본 결과이다.

➜ ~ curl -L http://nginx-ahnsk.taco-cat.xyz
GET / HTTP/1.1
Host: nginx-ahnsk.taco-cat.xyz
X-Request-ID: 1ca37cbd4fe84f1c20e56e7ce014bd4c
X-Real-IP: 218.237.0.56
X-Forwarded-For: 218.237.0.56
X-Forwarded-Host: nginx-ahnsk.taco-cat.xyz
X-Forwarded-Port: 443
X-Forwarded-Proto: https
X-Forwarded-Scheme: https
X-Scheme: https
user-agent: curl/7.64.1
accept: */*

 

X-Real-IP 와 X-Forwared-For 에 실제 client ip 값이 출력되는 것을 확인할 수 있다.

 

마치며

Kubernetes 위에 서비스를 올린 후 사용자 접근을 오픈하기 위해서는 LB 와 Ingress Controller 를 사용해야 한다. 직접 Kubernetes 를 설치 관리하면서 Nginx 의 기능을 사용하고 싶다면 AWS Load Balancer Controller 보다는 Nginx Ingress Controller 를 활용해야 한다. 물론 IAM Account 연동 등을 위해서 혹은 EKS Cluster 를 사용한다면 AWS Load Balancer Controller 를 사용하는 것이 건강에 좋을 것이다.



반응형
Posted by seungkyua@gmail.com

댓글을 달아 주세요

반응형

2018년에 Kubernetes 상에서 돌아가는 ML Platform 인 Kubeflow 를 처음 알게된 후로 관심을 갖고 재미삼아 하다가 2022년에는 업무와 연관되어 일을 해야하기 때문에 중점으로 살펴보고자 한다. (최근에는 ML Platform 보다는 AI Platform 이라고 말하는 사람들이 많은 듯)

 

Kubeflow 를 KubeCon 에서 만나다

Kubeflow 개발자들을 처음 만난 것은 2019년 KubeCon in Europe (Barcelona) 이다. 초기 Kubeflow 는 Goolge 과 Arrito 가 주축이 되어 개발하고 있었는데 마침 초기 버전이 나오면서 Kubeflow 홍보를 시작하던 때 였었다.

Kubeflow 의 주 개발자는 화면에 보이는 구글의 Senior S/W Engineer 인 Jeremy Lewi 다. 당시 저 발표는 간단한 설명후에 데모 형태로 발표가 되었는데 Kubernetes 위에 Kubeflow 를 설치한 상태에서 Jupyter Notebook 을 띄어 Model 을 개발하고, Fairing 으로 Model Training 후 Model Serving 하는 것을 시연으로 보여주었다.

 

영상은 아래를 클릭하면 볼 수 있다.

https://www.youtube.com/watch?v=-GYiatVNemY 

 

저녁에 Arrito 가 주최하는 Kubeflow 개발자 / 사용자와의 저녁 만남이 있었는데 거기서 Jeremy 와 이야기를 해봤을 때는 전형적인 내성적이고 약간은 고지식한 개발자라는 느낌을 받았던 기억이(물론 이건 주관적인 생각). 시간이 좀 지나니 혼자 따로 앉아 있던데. 

 

 

Kubeflow 를 왜 써야 하나?

AI Platform Developer 라면 아래의 그림은 많이 봐왔을 것이다. 정 가운데에 있는 ML Code 는 데이터 사이언티스트들이 모델을 개발하는 코드인데, 이런 개발을 도와주거나 실제 서비스로 구축하기 위해서는 코드 이외에도 많은 Tool / System 들이 필요하다는 의미를 보여준다.  

https://papers.nips.cc/paper/5656-hidden-technical-debt-in-machine-learning-systems.pdf

 

또한 위의 동영상에서도 아래와 같이 설명하고 있다.

 

 

간단하게 살펴보는 Kubeflow Architecture

아키텍처에 대한 설명은 Kubeflow document 사이트를 참고하여 간단히 설명하고자 한다.

https://www.kubeflow.org/docs/started/architecture/

 

 

 

ML 개발 프로세스는 Experimental Phase (실험 단계) 와 Production Phase (운영 단계) 로 나눌 수 있으며 전체 워크플로우는 아래와 같다.

실험 단계

1. 문제를 인식하고 데이터를 수집/분석

2. ML 알고리즘을 선택하여 모델을 코딩

3. 데이터를 가지고 모델 트레이닝

4. 하이퍼 파라미터 튜닝

 

운영 단계

1. 데이터 변환

2. 모델 트레이닝

3. 온라인/배치 예측을 위한 모델 서빙

4. 모델 성능 측정 (결과를 가지고 다시 트레이닝하거나 튜닝)

 

 

단계에 적용되는 Kubeflow 컴포넌트는 아래와 같다. 

 

  • Fairing: 위성발사를 생각해보면 로켓 맨 상단에 중요 내용물인 위성을 보호하기 위해서 Fairing 으로 감싸고 있다. 모델을 안전하게 다른 시스템으로 전달하는 기능으로 python library 를 제공한다.
  • Pipelines: Argo workflow 기반으로 python library 를 제공하여 pipeline 을 구축할 수 있다. 예를 들면 데이터 전처리 등 반복되는 작업을 자동으로 수행할 수 있다. 
  • Katib: 하이퍼 파라미터 튜닝을 자동으로 해주는 기능이다. 아래 그림과 같이 하이퍼 파라미터를 바꾸면서 결과를 그래프와 표로 보여준다.
  • KFServing:  Kubeflow 에서 제공해주는 Model Serving 기능이다.

 

마치며

현재 Kubeflow 의 최신 버전은 v1.4 이다. 2019년 Arrito 개발자들과 만나서 논의했을 때 (당시에 v0.3 인걸로 기억한다) v1.0 으로 올리는 것이 의미있냐고 질문을 받은 적이 있다. 고객이 오픈소스의 안정성을 의심하기 때문에 기능이 조금 미흡하더라도 v1.0 으로 올리는 것이 좋겠다는 의견을 준적이 있었는데 어느덧 버전이 v1.4 까지 나왔다.(물론 그 때 이후 한참 지나서야 v1.0 이 나왔지만)

많은 기업에서 자체 AI Platform 을 만들어서 사용하는 것으로 알고 있는데, 제 개인적인 생각으로는 지금이라도 Kubeflow 로 바꾸는 것이 좋다는 생각이다. 그 이유는 이미 모두 알고 있을 것이다.

반응형
Posted by seungkyua@gmail.com

댓글을 달아 주세요