Kubernetes/Istio

Istio VirtualService

김 정출 2024. 10. 13. 22:15

Istio VirtualService

Istio의 Virtual Service는 서비스 메시에 있는 트래픽의 라우팅을 제어하는 중요한 구성 요소입니다. Kubernetes와 같은 환경에서 마이크로서비스 간의 통신을 세밀하게 관리하고, 서비스로의 요청 흐름을 정의하는 데 사용됩니다. Virtual Service는 서비스의 특정 버전으로 트래픽을 보낼 수 있게 하며, 라우팅 규칙, 트래픽 분할, A/B 테스트, Canary 배포, 실패 복구 등을 쉽게 설정할 수 있도록 지원합니다.


1. VirtualService 란?

주요 기능 및 개념

  1. 트래픽 라우팅: Virtual Service는 트래픽을 여러 버전의 서비스로 라우팅할 수 있습니다. 예를 들어, 80%의 트래픽을 서비스의 v1 버전으로 보내고, 나머지 20%는 v2 버전으로 보내는 식으로 트래픽을 분할할 수 있습니다.
  2. 호스트 및 경로 기반 라우팅: 특정 URL 경로나 HTTP 헤더 값에 따라 서로 다른 서비스를 호출하게 설정할 수 있습니다. 예를 들어, /api/v1로 오는 요청은 v1 버전 서비스로, /api/v2로 오는 요청은 v2 버전 서비스로 라우팅하는 식입니다.
  3. A/B 테스트 및 Canary 배포: 새로운 버전의 서비스가 배포될 때, Virtual Service를 통해 트래픽의 일부만 새 버전으로 보내서 안전하게 테스트할 수 있습니다. 이 방식으로 Canary 배포나 A/B 테스트가 가능합니다.
  4. 실패 복구: Virtual Service는 트래픽을 자동으로 리트라이하거나 타임아웃을 설정하는 등의 기능을 제공하여 서비스 장애 시 트래픽을 안전하게 처리할 수 있게 도와줍니다.
  5. 부하 분산 및 페일오버: 여러 서비스 인스턴스 간 부하를 고르게 분산시키거나 특정 인스턴스가 다운되었을 때 다른 인스턴스로 트래픽을 자동으로 전환하는 기능을 지원합니다.

Virtual Service의 구성 요소

  • hosts: Virtual Service가 적용될 대상 서비스의 이름을 정의합니다.
  • http, tcp, tls: 다양한 프로토콜에 따라 라우팅 규칙을 정의합니다.
  • match: 요청을 구분하는 조건을 정의하는 부분으로, URL 경로, HTTP 헤더, 쿠키 등 다양한 기준을 기반으로 설정 가능합니다.
  • route: 특정 매치 조건을 만족하는 트래픽을 어디로 라우팅할지 설정합니다. 보통 특정 서비스와 관련된 Destination을 지정합니다.

Virtual Service의 예시

yaml
Copy code
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: my-service
spec:
  hosts:
  - my-service
  http:
  - match:
    - uri:
        exact: /v1
    route:
    - destination:
        host: my-service
        subset: v1
  - match:
    - uri:
        exact: /v2
    route:
    - destination:
        host: my-service
        subset: v2

위 예시에서 /v1로 요청이 오면 서비스의 v1으로, /v2로 요청이 오면 서비스의 v2로 라우팅됩니다.


2. 라우팅 방식

Istio에서 트래픽을 분배하는 방식은 여러 가지가 있으며, weight 방식 외에도 다양한 라우팅 전략을 사용할 수 있습니다. 기본적으로 Istio는 트래픽을 Round Robin 방식으로 분배합니다. 이를 통해 여러 서비스 인스턴스에 균등하게 트래픽을 분산할 수 있습니다.

1. Round Robin 라우팅

Round Robin은 각 서비스 인스턴스에 순차적으로 트래픽을 전달하는 방식입니다. Istio의 기본 동작이므로 특별한 설정 없이도 라우팅을 할 수 있습니다. Round Robin은 다음과 같이 설정할 수 있습니다.

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: my-virtualservice
spec:
  hosts:
  - my-service.my-namespace.svc.cluster.local
  http:
  - route:
    - destination:
        host: service-a.my-namespace.svc.cluster.local
    - destination:
        host: service-b.my-namespace.svc.cluster.local

위의 설정은 기본적으로 Round Robin 방식으로 service-aservice-b 간에 트래픽을 균등하게 분배합니다.

2. Cookie 기반 세션 스티키니스 (Session Affinity)

Istio에서는 특정 사용자에게 동일한 서비스 인스턴스로 트래픽을 보내기 위해 session affinity를 설정할 수 있습니다. 이를 통해 사용자가 첫 번째 요청을 보낸 서비스에 대해 이후 요청도 같은 서비스로 라우팅할 수 있습니다.

예를 들어, HTTP 쿠키를 기반으로 세션을 유지하는 방법은 다음과 같습니다:

apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
  name: destination-rule-for-my-service
spec:
  host: my-service.my-namespace.svc.cluster.local
  trafficPolicy:
    loadBalancer:
      simple: LEAST_CONN  # 최소 연결 수 기준으로 분배

3. Weighted Routing

Weighted Routing은 앞서 설명한 weight 방식으로, 여러 서비스로 트래픽을 비율에 따라 분산할 수 있는 방법입니다. 이 방법은 특정 서비스에 대한 트래픽 비율을 조절할 수 있어, 새로운 버전의 서비스로의 점진적인 롤아웃에 유용합니다.

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: my-canary-service
spec:
  hosts:
  - my-service.my-namespace.svc.cluster.local
  http:
  - route:
    - destination:
        host: service-v1.my-namespace.svc.cluster.local
      weight: 90
    - destination:
        host: service-v2.my-namespace.svc.cluster.local
      weight: 10

4. A/B 테스트 및 Canary 배포

Istio는 A/B 테스트 또는 Canary 배포를 통해 일부 트래픽만 새로운 서비스 버전으로 보내고 나머지는 기존 버전으로 라우팅할 수 있습니다. 예를 들어, Canary 배포를 설정하면 다음과 같이 특정 비율의 트래픽만 새로운 서비스로 보낼 수 있습니다

5. 기타 라우팅 옵션

  • Path Routing: 특정 경로에 따라 트래픽을 다른 서비스로 라우팅할 수 있습니다.
  • Header Routing: HTTP 요청의 헤더에 따라 라우팅을 조정할 수 있습니다.

요약

  • Round Robin은 기본적으로 지원되는 트래픽 분배 방식으로, 별도의 설정 없이 사용할 수 있습니다.
  • Session Affinity를 통해 사용자 세션을 관리하거나, A/B 테스트Canary 배포와 같은 방법으로 특정 서비스에만 트래픽을 보내는 것도 가능합니다.
  • 다양한 라우팅 방식과 설정을 활용하여 애플리케이션의 요구사항에 맞게 Istio의 트래픽 관리를 조정할 수 있습니다.

3. Service 장애 시 분배 처리

IstioVirtualService에서 weight로 트래픽을 분배하는 경우, 한쪽 서비스가 다운되었을 때 Istio는 기본적으로 트래픽을 정상적으로 동작하는 다른 서비스로 자동으로 라우팅하지는 않습니다. 즉, Istio는 주어진 weight에 따라 트래픽을 보내려 하지만, 다운된 서비스로 트래픽을 보내면 실패할 수 있습니다.

Istio가 기본적으로 제공하는 기능:

  1. 헬스체크 또는 Probe: Kubernetes에서는 각 서비스에 대해 LivenessReadiness Probe를 설정할 수 있습니다. 서비스가 다운되거나 비정상적인 경우, 해당 서비스로 트래픽을 보내지 않도록 Kubernetes가 관리하지만, Istio는 이와는 별개로 동작합니다.
  2. Istio의 기본 리트라이 기능: Istio는 서비스가 일시적으로 응답하지 않을 때 트래픽을 다시 시도할 수 있도록 리트라이(retry) 기능을 제공하지만, 완전히 다운된 경우에 대해서는 이 기능만으로는 충분하지 않습니다.
  3. Failover를 구현하는 방법: Istio에서 한쪽 서비스가 죽었을 때 자동으로 다른 서비스로 트래픽을 라우팅하려면 추가적인 설정이 필요합니다. 예를 들어 DestinationRule을 이용해 outlier detection을 설정하여, 특정 조건에서 서비스가 비정상적일 때 이를 감지하고 해당 서비스로 트래픽을 보내지 않도록 할 수 있습니다.

해결 방법: Outlier Detection (오류 감지 및 제거)

이를 구현하려면 Istio의 DestinationRule에서 Outlier Detection을 설정해줍니다. 이 설정은 일정 횟수 이상 오류가 발생하면 해당 인스턴스를 "격리"하고 트래픽을 보내지 않도록 만듭니다.

Outlier Detection: DestinationRuletrafficPolicy 안에 outlierDetection 설정을 추가합니다.

  • consecutive5xxErrors: 연속해서 5xx 오류가 발생한 횟수입니다. 설정된 횟수에 도달하면 해당 인스턴스가 격리됩니다.
  • interval: 상태 체크 주기입니다. 이 주기마다 서비스 인스턴스의 상태를 검사합니다.
  • baseEjectionTime: 격리된 인스턴스가 다시 사용할 수 있게 되기까지 대기하는 시간입니다.
  • maxEjectionPercent: 서비스의 인스턴스 중 격리할 수 있는 최대 비율입니다.

다음은 예시입니다:

apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
  name: destination-rule-for-my-service
spec:
  host: my-service.my-namespace.svc.cluster.local
  trafficPolicy:
    outlierDetection:
      consecutive5xxErrors: 2    # 5xx 오류가 2회 발생하면 서비스 격리
      interval: 5s               # 5초마다 상태 체크
      baseEjectionTime: 30s      # 30초 동안 격리
      maxEjectionPercent: 100    # 100%까지 인스턴스 제거 가능

이 설정이 적용되면, 연속적인 5xx 에러가 두 번 발생할 경우 해당 인스턴스는 격리되고 일정 시간 동안 트래픽을 받지 않게 됩니다. 이때 살아 있는 다른 서비스로 트래픽이 자동으로 라우팅됩니다. 인스턴스가 격리된 후, baseEjectionTime에 설정된 30초가 지나면, 다시 상태를 확인하여 정상으로 돌아온 경우 트래픽을 다시 수용할 수 있습니다.

전체 설정 예시:

아래는 트래픽을 분산시키면서 장애가 발생한 경우 자동으로 대응하는 설정입니다.

  1. VirtualService: 두 서비스로 트래픽을 분배
  2. DestinationRule: Outlier Detection을 통한 장애 인스턴스 격리
# VirtualService
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: my-virtualservice
spec:
  hosts:
  - my-service.my-namespace.svc.cluster.local
  http:
  - route:
    - destination:
        host: service-a.my-namespace.svc.cluster.local
      weight: 70
    - destination:
        host: service-b.my-namespace.svc.cluster.local
      weight: 30

---
# DestinationRule for service-a
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
  name: destination-rule-service-a
spec:
  host: service-a.my-namespace.svc.cluster.local
  trafficPolicy:
    outlierDetection:
      consecutive5xxErrors: 2
      interval: 5s
      baseEjectionTime: 30s
      maxEjectionPercent: 100

---
# DestinationRule for service-b
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
  name: destination-rule-service-b
spec:
  host: service-b.my-namespace.svc.cluster.local
  trafficPolicy:
    outlierDetection:
      consecutive5xxErrors: 2
      interval: 5s
      baseEjectionTime: 30s
      maxEjectionPercent: 100
  • Weight 방식만 사용하면 서비스가 죽었을 때 트래픽을 자동으로 다른 서비스로 라우팅하지 않으므로 Outlier Detection과 같은 추가적인 설정이 필요합니다.
  • Outlier Detection을 사용하면 비정상적인 서비스는 자동으로 격리되고, 다른 서비스로 트래픽이 전달됩니다.