본문 바로가기
프로젝트&&스터디/KANS2기

[KANS-9주차] AWS의 로드밸런서 모드 정리

by james_janghun 2024. 10. 30.

해당 블로그 글을 참조해서 작성한다.

 

AWS LoadBalancer Controller의 필요성과 기능

AWS 로드밸런서 컨트롤러를 통해서 사용자는 좀 더 쉽게 EKS에서 service 타입으로 외부에 서비스를 노출할 수 있다.  특히 자동으로 대상 그룹에 등록할 수 있는 TargetGroupBinding이라는 CRD 항목도 제공해서 자동으로 Pod의 IP를 대상그룹으로 등록해주고 심지어는 IP가 바뀔때마다 자동으로 배포해주고 health check도 진행한다는 점에서 매우 간편하고 운영 효율성이 높아지게 된다.

뿐만아니라 AWS의 WAF 등 다양한 서비스와 결합이 가능하기 때문에 매우 좋다.

로드밸런서 컨트롤러는 타겟 유형에 따라서 다음과 같이 2가지 모드를 지원하고 있다.

 

Instance 모드

Instance 모드는 타겟이 노드그룹(워커노드)가 대상이 된다. 그렇기 때문에 노드 IP주소와 NodePort를 사용해 트래픽을 라우팅하고 내부적으로 pod로 전달되는 구조이다.

 

IP 모드

IP모드는 다이렉트로 pod로 요청이 전달된다. AWS  따라서 latency를 줄여주고 확장을 좀 더 쉽게 진행한다. 다만 pod의 IP를 직접적으로 알고 있어야 하고, 네트워크가 전달되어야 하는 만큼 LoadBalancer Controller의 도움이 필요하다.

 

 

 

AWS Load Balancer Controller 설치

https://docs.aws.amazon.com/ko_kr/eks/latest/userguide/lbc-helm.html 에서 제공하는 가이드를 그대로 따라가면 된다.

헬름으로 간편하게 설치할 수 있다. 먼저 IRSA를 만들어서 aws-loadbalancer controller 가 사용할 정책을 만들어줘야하고 이를 service account에 연결해주는 작업이 필요하다.

curl -O https://raw.githubusercontent.com/kubernetes-sigs/aws-load-balancer-controller/v2.7.2/docs/install/iam_policy.json

위에서 다운로드 한 정책 json 파일을 아래 코드를 통해서 AWS 계정에 배포한다.

aws iam create-policy \
    --policy-name AWSLoadBalancerControllerIAMPolicy \
    --policy-document file://iam_policy.json

 

eksctl을 통해서 service account를 만들고 여기에 attach policy를 적용한다.

eksctl create iamserviceaccount \
  --cluster=my-cluster \
  --namespace=kube-system \
  --name=aws-load-balancer-controller \
  --role-name AmazonEKSLoadBalancerControllerRole \
  --attach-policy-arn=arn:aws:iam::111122223333:policy/AWSLoadBalancerControllerIAMPolicy \
  --approve

 

AWS LoadBalancer 헬름을 배포한다.

helm repo add eks https://aws.github.io/eks-charts
helm repo update eks

helm install aws-load-balancer-controller eks/aws-load-balancer-controller \
  -n kube-system \
  --set clusterName=my-cluster \
  --set serviceAccount.create=false \
  --set serviceAccount.name=aws-load-balancer-controller

 

잘 배포되었는지 확인한다.

kubectl get deployment -n kube-system aws-load-balancer-controller

 

 

AWS Load Balancer Controller의 Blue/Green 트래픽 분산

AWS의 가이드 중 해당 내용을 발췌했다. 

또한 Blue/Green 배포에 대한 가이드는 해당 워크샵에서 확인할 수 있다.

 

AWS Load Balancer Controller의 기능 중 Blue/Green 트래픽 분산이 있다.  ALB는 총 2가지 기능을 지원한다.

- 가중치 대상 그룹(Weighted Target Group) : 같은 리스너에 연결된 대상그룹에서 대상그룹 별로 가중치를 줄 수 있다.

- 고급요청 라우팅 (Advanced request routing) : 개발자들이 요청 HTTP 헤더나 메서드, 소스 IP 등을 작성할 수 있도록 한다. 

 

분산 방법

일단 AWS Controller의 대부분은 annotation 설정이 중요하다. ingress 설정 역시 annotation를 사용해서 작업한다. 리스너에 사용자 설정을 할 수 있다.

alb.ingress.kubernetes.io/actions.${action-name}

 

참고로 annotation의 action-name은 ingress 규칙의 서비스 이름과 일치해야한다. 그리고 servicePort 항목에는 반드시 use-annotation을 작성한다. 컨트롤러 입장에서 use-annotation을 확인하고 annotation을 읽기 때문이다. 예를 들어 annotation에 아래 코드와 같이 있다면 서비스에서도 blue-green이 있어야 한다.

alb.ingress.kubernetes.io/actions.blue-green

 

 

실제 테스트 배포

먼저 테스트를 위한 deployment와 이를 연결한 service를 배포한다.

hello-kubernetes라는 데모이며 헬름으로 배포가 가능하다.

git clone https://github.com/paulbouwer/hello-kubernetes.git
tree hello-kubernetes/

# Install sample application version 1
helm install --create-namespace --namespace hello-kubernetes v1 \
  ./hello-kubernetes/deploy/helm/hello-kubernetes \
  --set message="You are reaching hello-kubernetes version 1" \
  --set ingress.configured=true \
  --set service.type="ClusterIP"

# Install sample application version 2
helm install --create-namespace --namespace hello-kubernetes v2 \
  ./hello-kubernetes/deploy/helm/hello-kubernetes \
  --set message="You are reaching hello-kubernetes version 2" \
  --set ingress.configured=true \
  --set service.type="ClusterIP"

 

이후 해당 service에 트래픽을 조절할 ingress를 배포한다.

일단 deploy-1을 100%로 deploy-2는 0%로 올려둔 상태로 배포한다.

 kubernetes.io/ingress.class 는 없어져서 spec.ingressClassName으로 변경되었다.
cat <<EOF | kubectl apply -f -
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: "hello-kubernetes"
  namespace: "hello-kubernetes"
  annotations:
    alb.ingress.kubernetes.io/scheme: internet-facing
    alb.ingress.kubernetes.io/target-type: ip
    alb.ingress.kubernetes.io/actions.blue-green: |
      {
        "type":"forward",
        "forwardConfig":{
          "targetGroups":[
            {
              "serviceName":"hello-kubernetes-v1",
              "servicePort":"80",
              "weight":100
            },
            {
              "serviceName":"hello-kubernetes-v2",
              "servicePort":"80",
              "weight":0
            }
          ]
        }
      }
  labels:
    app: hello-kubernetes
spec:
  ingressClassName: alb
  rules:
    - http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: blue-green
                port:
                  name: use-annotation
EOF

 

 Ingress 정보확인

describe를 통해서 ingress 정보를 확인할 수 있다.

kubectl describe ingress -n hello-kubernetes

 

 

실제로 반복접속을 해보면 version1만 접속 되는 것을 확인할 수 있다.

ELB_URL=$(kubectl get ingress -n hello-kubernetes -o=jsonpath='{.items[0].status.loadBalancer.ingress[0].hostname}')
while true; do curl -s $ELB_URL | grep version; sleep 1; done

 

이는 실제로 AWS 콘솔 상에서도 확인할 수 있다.

 

이제 v2로 완전히 전환한다고 생각하고 v2로 모두 전환해 보겠다.

ingress에서 v2 부분을 가중치 100으로 변경한다.

alb.ingress.kubernetes.io/actions.blue-green: |
       {
         "type":"forward",
         "forwardConfig":{
           "targetGroups":[
             {
               "serviceName":"hello-kubernetes-v1",
               "servicePort":"80",
               "weight":0
             },
             {
               "serviceName":"hello-kubernetes-v2",
               "servicePort":"80",
               "weight":100
             }
           ]
         }
       }

 

이제는 v2만 발생하는 것을 확인할 수 있다.

 

콘솔에서도 이번에는 ca0로 끝나는 대상그룹으로 트래픽이 100 변경된 것을 확인할 수 있다.

 

LB Controller에서도 해당 로그를 전부 확인할 수 있다. listener rule이 modify 되었다거나 weight 가 반영된 모습 등 모든 로그가 남게 된다.

 

이번에는 만약 v1은 사용자가 접속하도록 두고, v2는 일단 테스트를 위해서 띄어 둔다고 생각해보자.

이럴 경우 ingress의 강력한 기능인 http-header나 http-request-method 등 다양한 방식으로 트래픽을 전송하는 방법을 확인해 보자.

alb.ingress.kubernetes.io/conditions.${conditions-name} 해당 annotation에 conditions-name을 설정해주면된다.

 

 

다음 예시를 보면 기본적인 모든 트래픽은 v1으로 넘어가고, HeaderName: HeaderValue1에 해당하는 것들만 v2로 트래픽이 전송되도록 설정하였다.

cat <<EOF | kubectl apply -f -
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: "hello-kubernetes"
  namespace: "hello-kubernetes"
  annotations:
    alb.ingress.kubernetes.io/scheme: internet-facing
    alb.ingress.kubernetes.io/target-type: ip
    alb.ingress.kubernetes.io/conditions.ab-testing: >
      [{"field":"http-header","httpHeaderConfig":{"httpHeaderName": "HeaderName", "values":["kans-study-end"]}}]
    alb.ingress.kubernetes.io/actions.ab-testing: >
      {"type":"forward","forwardConfig":{"targetGroups":[{"serviceName":"hello-kubernetes-v2","servicePort":80}]}}
  labels:
    app: hello-kubernetes
spec:
  ingressClassName: alb
  rules:
    - http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: ab-testing
                port:
                  name: use-annotation
          - path: /
            pathType: Prefix
            backend:
              service:
                name: hello-kubernetes-v1
                port:
                  name: http
EOF

 

다음과 같이 100번 호출해보면 일반적으로는 version1으로 트래픽이 전송되고, kans-study-end라는 헤더 값을 가진 요청은 전부 v2로 전송되는 것을 확인할 수 있다.

for i in {1..100};  do curl -s $ELB_URL | grep version ; done | sort | uniq -c | sort -nr
for i in {1..100};  do curl -s -H "HeaderName: kans-study-end" $ELB_URL | grep version ; done | sort | uniq -c | sort -nr

 

이상으로 AWS 로드밸런서 컨트롤러에 대한 포스팅을 마친다.