문제의 시작

Kubernetes에서 서비스를 외부에 열 때마다 Load Balancer가 늘어나면 구조는 단순해 보이지만 비용과 quota가 빠르게 부담이 된다. Gateway를 중심으로 ingress 경로를 모으면 비용은 줄일 수 있지만, routing과 certificate, 장애 범위는 더 신중히 봐야 한다. 이 글은 Istio Gateway로 LB 비용을 줄이는 접근을 정리한 기록이다.

EKS에 떠있는 쿠버네티스의 트래픽을 관리할 때, 본래 서비스로 향하는 트래픽을 라우팅하기 위해 모듈 각각에 ALB를 하나씩 두어 트래픽을 각각 관리한다.

구현하면서 확인한 흐름

ingress:
  enabled: true
  className: "alb"
  annotations:
    alb.ingress.kubernetes.io/scheme: internet-facing
    alb.ingress.kubernetes.io/target-type: ip
    alb.ingress.kubernetes.io/subnets: {서브넷명시}
    alb.ingress.kubernetes.io/certificate-arn: {arn명시}
    alb.ingress.kubernetes.io/ssl-redirect: '443'
    alb.ingress.kubernetes.io/listen-ports: '[{"HTTP": 80}, {"HTTPS":443}]'

가령 이런식이다.

values 파일에 arn(SSL을 위한 certificate), port등을 선언하여 ALB를 AWS에 띄운다.

문제는 모든 load balancer(ALB)에 시간별 요금이 붙기 때문에, module이 늘어날수록 비용이 함께 증가한다는 점이다. 개발 서버나 release 서버처럼 traffic이 작은 module이라도 LCU가 크게 다르지 않다면 load balancer 비용은 동일하게 발생한다.

하여 여기에 Istio Gateway + VirtualService를 사용하는 패턴을 도입할 수 있는데, Gateway 하나만을 선언하여 모든 모듈로 가능 트래픽을 NLB 하나만 두어 중심에서 관리할 수 있다.

우선 Global Gateway를 선언하자. 예시는 helm charts를 사용하였다.

apiVersion: networking.istio.io/v1
kind: Gateway
metadata:
  name: global-gateway
  namespace: istio-system
spec:
  selector:
    istio: ingressgateway
  servers:
    - port:
        number: 80
        name: http
        protocol: HTTP
      hosts:
        - {host1}
        - {host2}
      tls:
        httpsRedirect: true
    - port:
        number: 443
        name: http-443
        protocol: HTTP
      hosts:
        - {host1}
        - {host2}

hosts section에는 사용하는 모듈의 host를 모두 선언한다.

다음으로 각 모듈에 VirtualService가 필요하다.

apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
  name: {module-name}
spec:
  hosts:
    - {host1}
    - {host2}
  gateways:
    - istio-system/global-gateway
  http:
    - match:
        - headers:
            Host:
              exact: {host1}
      route:
        - destination:
            host: {app1}
            port:
              number: 8080
    - match:
        - headers:
            Host:
              exact: {host2}
      route:
        - destination:
            host: {app2}
            port:
              number: 8080

이제 로드밸런서에 연결될 cert들을 선언해야 한다. cert는 host별로 고유하므로 필요한 cert를 하나의 Gateway(NLB)에 함께 선언한다.

{
  "metadata": {
    "annotations": {
      "service.beta.kubernetes.io/aws-load-balancer-scheme": "internet-facing",
      "service.beta.kubernetes.io/aws-load-balancer-ssl-cert": "{arn1}, {arn2} ....",
      "service.beta.kubernetes.io/aws-load-balancer-backend-protocol": "http",
      "service.beta.kubernetes.io/aws-load-balancer-ssl-ports": "443"
    }
  }
}

이걸 다음 명령어로 패치해주면 된다.

kubectl patch svc istio-ingressgateway -n istio-system --patch-file {json명}

Ingress 운영 기준

Gateway 기반 구조는 비용 절감만 보고 선택하면 위험하다. 어떤 서비스가 같은 ingress plane을 공유하는지, certificate와 DNS가 어떻게 관리되는지, quota와 target limit에 걸리지 않는지 함께 확인해야 한다. 인프라 비용 최적화는 항상 운영 복잡도와 교환된다.

조심해야 할 것은 무엇일까?

우선 SPOF가 생긴다는 것이다. NLB에 문제가 생기면 모든 모듈로 향해야 하는 트래픽에 장애가 생기고 다운타임이 생긴다. 이런 부분은 레플리카로 해결해야 할 것이다.

다음으로 Quota에 부딪히는 것을 조심해야 하니, https://docs.aws.amazon.com/elasticloadbalancing/latest/network/load-balancer-limits.html 공식문서를 살펴보고 Target limit, cert limit, connection limit 등을 확인해볼 필요가 있다.