반응형

배경

외부에서 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
,