본문 바로가기
카테고리 없음

[istio 스터디] 7주차 - 이스티오 스케일링, 데이터 플레인 확장

by ★용호★ 2025. 5. 25.

이스티오 서비스 메시 스케일링 개요

여러 클러스터를 사용해야 하는 다양한 이유:

  • 보안상 격리 필요: 클러스터 간 격리를 통해 보안 강화
  • 장애 영향도 최소화: 한 클러스터 문제가 다른 클러스터에 영향을 주지 않도록 함
  • 데이터 접근 제한: 민감한 데이터에 접근하는 서비스 분리
  • 가용성 향상: 여러 클러스터를 통한 고가용성 구현

따라서 여러 개의 작은 클러스터를 사용하는 방식이 많은 환경에서 선호됩니다.

다중 클러스터 서비스 메시의 유형

다중 클러스터 서비스 메시는 크게 두 가지 모델로 구분됩니다.

  • 다중 클러스터 서비스 메시(Multi-cluster Service Mesh) : 다중 클러스터 서비스 메시에서는 컨트롤 플레인이 여러 Kubernetes 클러스터를 관리하고, 이 모델에서 하나의 컨트롤 플레인이 여러 클러스터의 서비스 메시 전반을 제어함

  • 메시 페더레이션(Mesh Federation) : 메시 페더레이션에서는 각 클러스터가 자체 이스티오 컨트롤 플레인을 갖고 있으며, 이들이 서로 연결되어 통신함. 이 방식은 컨트롤 플레인을 공유하지 않기 때문에 보안과 장애 격리 측면에서 더 강력함

다중 클러스터 서비스 메시 구현을 위한 핵심 요구사항

다중 클러스터 서비스 메시를 구현하기 위해서는 다음 세 가지 중요한 조건을 만족해야 합니다:

디스커버리(Discovery)

이스티오 컨트롤 플레인이 다른 클러스터의 Kubernetes 워크로드를 발견할 수 있어야 합니다. 이를 위해:

  • 서비스 어카운트와 적절한 RBAC 권한 설정
  • Kubernetes API 서버 간 통신 허용
  • 서비스와 엔드포인트 리소스를 조회하기 위한 권한 부여

이스티오 컨트롤 플레인은 상대방 클러스터의 Kubernetes API를 호출하여 서비스 및 엔드포인트 정보를 획득합니다. 이를 위해 서비스 어카운트 토큰과 적절한 권한이 필요합니다.

연결성(Connectivity)

클러스터 간 워크로드가 서로 통신할 수 있도록 네트워크 연결이 필요합니다. 이를 위해 East-West 게이트웨이를 사용합니다:

  • East-West 게이트웨이: 클러스터 간 트래픽을 처리하는 특수 게이트웨이
  • 로드 밸런서: 퍼블릭 클라우드 환경에서는 관리형 로드 밸런서를 활용
  • 중복 IP 해결: 서로 다른 클러스터에서 동일한 IP 주소 범위를 사용하더라도 문제 없이 통신 가능

East-West 게이트웨이는 인그레스 게이트웨이와는 다른 역할을 합니다. 인그레스 게이트웨이가 외부에서 내부로 들어오는 노스-사우스 트래픽을 처리한다면, East-West 게이트웨이는 서비스 메시 클러스터 간 통신을 담당합니다.

공통 신뢰(Common Trust)

클러스터 간 mTLS(상호 TLS) 통신이 가능하도록 공통된 신뢰 체계가 필요합니다:

플러그인 CA 인증서 :

  • 최상위 루트 CA 아래에 중간 CA를 각 클러스터마다 배치
  • 모든 클러스터가 동일한 루트 CA를 신뢰함으로써 상호 인증 가능
  • 중간 CA가 각 클러스터의 워크로드에 인증서 발급

외부 인증기관 통합 :

  • 외부 CA(예: cert-manager)를 사용하여 인증서 관리
  • CSR(Certificate Signing Request)을 통해 외부 인증기관에 서명 요청
  • 모든 클러스터가 동일한 외부 인증기관을 신뢰

다중 클러스터 배포 모델

기본-기본(Primary-Primary) 모델:

  • 복제된 컨트롤 플레인 배포
  • 모든 클러스터에 이스티오 컨트롤 플레인 설치
  • 각 컨트롤 플레인이 다른 클러스터의 정보 액세스
  • 가용성이 높지만 리소스가 더 많이 필요

기본-원격(Primary-Remote) 모델:

  • 한 클러스터에만 컨트롤 플레인 설치
  • 원격 클러스터는 기본 클러스터의 컨트롤 플레인에 의존
  • 리소스 효율성은 높지만 기본 클러스터에 장애 발생 시 취약

외부 컨트롤 플레인 모델:

  • 컨트롤 플레인이 Kubernetes 클러스터와 분리
  • 별도의 환경에서 컨트롤 플레인 실행
  • 여러 클러스터를 중앙에서 관리

인프라 준비

두 개의 Kubernetes 클러스터를 생성합니다 (설정 과정은 생략):

# 웨스트 클러스터 생성
kind create cluster --name west --config west-config.yaml 

# 이스트 클러스터 생성  
kind create cluster --name east --config east-config.yaml

각 클러스터의 kubeconfig 파일을 관리하기 위한 별칭을 설정합니다:

alias kwest="kubectl --kubeconfig=/path/to/west-kubeconfig"
alias keast="kubectl --kubeconfig=/path/to/east-kubeconfig" 

alias iwest='docker exec -it west-control-plane istioctl'
alias ieast='docker exec -it east-control-plane istioctl'

MetalLB 로드밸런서 설치

클러스터 간 통신을 위한 로드밸런서로 MetalLB를 설치합니다:

# 웨스트 클러스터에 MetalLB 설치
kwest apply -f metallb-config.yaml

# 이스트 클러스터에 MetalLB 설치
keast apply -f metallb-config.yaml

인증서 설정

공통 신뢰를 위한 인증서를 준비합니다. 이미 준비된 인증서 파일을 사용하거나 다음과 같이 생성할 수 있습니다:

  1. smallstep 툴을 설치합니다.
  2. 루트 CA와 중간 CA를 생성합니다.
  3. 인증서 체인을 생성합니다.

생성된 인증서를 Kubernetes 시크릿으로 저장합니다:

# 웨스트 클러스터에 인증서 저장
kwest create namespace istio-system
kwest create secret generic cacert -n istio-system \
  --from-file=west.key \
  --from-file=west.crt \
  --from-file=root-ca.crt \
  --from-file=cert-chain.pem

# 이스트 클러스터에도 동일하게 설정
keast create namespace istio-system
keast create secret generic cacert -n istio-system \
  --from-file=east.key \
  --from-file=east.crt \
  --from-file=root-ca.crt \
  --from-file=cert-chain.pem

이렇게 설정된 인증서는 이스티오 컨트롤 플레인이 설치될 때 자동으로 사용됩니다.

시크릿 보안 고려사항

Kubernetes 시크릿은 기본적으로 etcd에 평문으로 저장됩니다. 이를 보호하기 위해:

  • etcd 암호화 설정 권장
  • 시크릿 볼륨 마운트 시 tmpfs(메모리 파일 시스템)에 저장됨
  • 노드에 영구적으로 데이터가 남지 않도록 함
  • 외부 시크릿 관리 솔루션(Vault 등) 사용 권장

시크릿 데이터는 디스크가 아닌 메모리에 저장되어 보안을 강화합니다. 그러나 더 완전한 보안을 위해서는 HashiCorp Vault와 같은 외부 시크릿 관리 솔루션을 고려해야 합니다.

이스티오 컨트롤 플레인 설치

이제 IstioOperator 리소스를 정의하여 이스티오 컨트롤 플레인을 설치합니다.

웨스트 클러스터 설치 파일 내용:

apiVersion: install.istio.io/v1alpha1
kind: IstioOperator
metadata:
  name: istio-control-plane
spec:
  profile: demo
  components:
    egressGateway:
      enabled: false
  values:
    global:
      meshID: us-mesh  # 두 클러스터가 같은 메시에 속함
      multiCluster:
        clusterName: west  # 클러스터 이름
      network: west-network  # 네트워크 식별자

동일한 방식으로 이스트 클러스터에도 설치하되 clusterNamenetwork 값은 해당 클러스터에 맞게 설정합니다.

# 웨스트 클러스터에 이스티오 설치
iwest install -f istio-operator-west.yaml

# 이스트 클러스터에 이스티오 설치
ieast install -f istio-operator-east.yaml

이스티오 시스템 관련 애드온(Kiali 등)도 각 클러스터에 설치합니다.

인증서 확인

설치 후 시크릿과 인증서가 올바르게 적용되었는지 확인합니다.

# 웨스트 클러스터에서 인증서 확인
iwest proxy-status
iwest proxy-config secret istio-ingressgateway -n istio-system

인증서를 디코드하여 내용을 확인해 보면:

  • 서비스 계정의 identity (예: istio-ingressgateway 서비스 계정)
  • 발급자 정보 (중간 CA)
  • 루트 인증서 정보

이를 통해 플러그인 CA 인증서가 올바르게 적용된 것을 확인할 수 있습니다.

클러스터 간 디스커버리(Discovery) 활성화

원격 클러스터 시크릿 생성

istioctl 명령어를 사용하여 원격 클러스터 접근을 위한 시크릿을 생성합니다.

# 웨스트 클러스터에서 이스트 클러스터 접근을 위한 시크릿 생성
ieast x create-remote-secret \
  --name=east \
  --server=https://east-control-plane:6443 \
  | kubectl apply -f - --context=west

# 이스트 클러스터에서 웨스트 클러스터 접근을 위한 시크릿 생성
iwest x create-remote-secret \
  --name=west \
  --server=https://west-control-plane:6443 \
  | kubectl apply -f - --context=east

디스커버리 확인

시크릿 적용 후 로그 및 엔드포인트를 확인:

# 컨트롤 플레인 로그 확인
kwest logs -n istio-system -l app=istiod

# 웨스트 클러스터에서 엔드포인트 확인
iwest proxy-config endpoints <pod-name> -n istio-injection

클러스터 간 연결(Connectivity) 설정

노스-사우스 vs 이스트-웨스트 트래픽

  • 노스-사우스 트래픽: 외부에서 내부로 또는 내부에서 외부로 나가는 트래픽
  • 이스트-웨스트 트래픽: 클러스터 간 또는 서비스 메시 내부 트래픽

서비스 메시 클러스터 간 통신은 이스트-웨스트 트래픽에 해당하며, 이를 위해 East-West 게이트웨이를 사용합니다.

East-West 게이트웨이 설치

각 클러스터에 East-West 게이트웨이를 설치:

apiVersion: install.istio.io/v1alpha1
kind: IstioOperator
metadata:
  name: istio-eastwestgateway
spec:
  profile: empty
  components:
    ingressGateways:
    - name: istio-eastwestgateway
      namespace: istio-system
      enabled: true
      label:
        topology.istio.io/network: east-network  # 해당 네트워크에 맞게 설정
      k8s:
        env:
        - name: ISTIO_META_ROUTER_MODE
          value: "sni-dnat"  # SNI 기반 라우팅 설정
        - name: ISTIO_META_REQUESTED_NETWORK_VIEW
          value: "east-network,west-network"  # 볼 수 있는 네트워크 목록
# 이스트 클러스터에 East-West 게이트웨이 설치
keast apply -f east-eastwestgateway.yaml

# 웨스트 클러스터에 East-West 게이트웨이 설치
kwest apply -f west-eastwestgateway.yaml

SNI 클러스터와 자동통과 모드

  1. SNI 클러스터(SNI Cluster):
    • 트래픽이 라우팅될 수 있는 워크로드 집합의 속성
    • 포트, FQDN(정규화된 도메인 이름), 서브셋 등 포함
    • TLS Client Hello 메시지의 SNI 필드에 인코딩됨
  2. SNI 자동통과(AUTO_PASSTHROUGH) 모드:
    • VirtualService 없이도 SNI 헤더 기반으로 트래픽을 허용
    • 동적으로 생성되는 다양한 서비스에 유연하게 대응 가능
    • Gateway 리소스에 TLS 모드를 AUTO_PASSTHROUGH로 설정
apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
  name: cross-network-gateway
  namespace: istio-system
spec:
  selector:
    istio: eastwestgateway
  servers:
  - port:
      number: 15443
      name: tls
      protocol: TLS
    tls:
      mode: AUTO_PASSTHROUGH
    hosts:
    - "*.local"  # 모든 .local 도메인 허용

East-West 게이트웨이 설정 확인

게이트웨이 설치 후 구성을 확인합니다:

# East-West 게이트웨이 파드 확인
keast get pod -n istio-system -l app=istio-eastwestgateway

# East-West 게이트웨이 서비스 확인
keast get svc -n istio-system istio-eastwestgateway

East-West 게이트웨이 서비스는 LoadBalancer 타입이며 15443 포트(mTLS용)를 노출합니다.

프록시 구성 확인:

# SNI 클러스터 확인
ieast proxy-config listener istio-eastwestgateway-XXXX -n istio-system

클러스터 간 통신 테스트

워크로드 배포

웨스트 클러스터에 웹앱, 이스트 클러스터에 카탈로그 서비스를 배포합니다.

# 웨스트 클러스터에 웹앱 배포
kwest apply -f webapp.yaml

# 웨스트 클러스터에 카탈로그 서비스(stub) 생성
kwest apply -f catalog-service.yaml

# 이스트 클러스터에 카탈로그 배포
keast apply -f catalog.yaml

웨스트 클러스터에 "stub 서비스"를 생성하는데 실제 파드는 없지만 서비스 리소스만 생성하는 것을 의미합니다. (이스트 클러스터의 카탈로그 서비스로 트래픽을 라우팅하기 위함) 이 후 Kiali로 확인합니다.

  • "웨스트 클러스터"와 "이스트 클러스터"가 구분되어 표시됨
  • 웹앱에서 카탈로그로의 트래픽 흐름 확인 가능
  • TCP 레벨의 연결이 East-West 게이트웨이를 통해 처리되는 것을 확인 가능

로그 및 패킷 분석

# 웨스트 클러스터 인그레스 게이트웨이 로그
kwest logs -n istio-system -l app=istio-ingressgateway

# 웹앱 로그
kwest logs -n istio-injection -l app=webapp

# 이스트 클러스터 East-West 게이트웨이 로그
keast logs -n istio-system -l app=istio-eastwestgateway

# 카탈로그 로그
keast logs -n istio-injection -l app=catalog

특히 패킷 캡처를 통해 웹앱이 카탈로그와 통신할 때 직접 카탈로그 파드(10.20.0.X)로 연결하는 것이 아니라, 이스트 클러스터의 East-West 게이트웨이 로드밸런서 IP로 연결하는 것을 확인할 수 있습니다.

카탈로그 파드에서는 X-Forwarded-For 헤더를 통해 원래 클라이언트의 IP를 확인할 수 있습니다. 이는 법적 규제로 클라이언트 IP를 수집해야 하는 경우 유용합니다.

클러스터 간 지역 인식 로드 밸런싱 구현

현재 East, West에 두 개의 Kubernetes 클러스터가 있는 다중 클러스터 서비스 메시 환경이 구성되어 있고, 이를 활용하여 두 개의 샘플 서비스로 테스트를 진행

샘플 서비스 배포

먼저 웨스트 클러스터에 샘플 백엔드 서비스를 배포합니다:

# 웨스트 클러스터에 배포
kwest apply -f sample-backend-west.yaml

이 배포 파일에는 웨스트 클러스터에서 실행될 백엔드 서비스가 정의되어 있으며, 환경 변수를 통해 "Hello from West"라는 메시지를 반환하도록 설정되어 있습니다.

배포가 완료되면 디플로이먼트와 서비스를 확인합니다:

kwest get deployment,service -n istio-injection

외부에서 접근할 수 있도록 게이트웨이와 버추얼서비스를 생성합니다:

kwest apply -f sample-gateway.yaml

이제 서비스에 접근하여 응답을 확인해봅니다:

curl -H "Host: simple-backend.istio.injection.io" http://<INGRESS_IP>

응답 내용을 확인하면 "Hello from West"라는 메시지가 출력됩니다.

다음으로 이스트 클러스터에도 동일한 서비스를 배포합니다:

keast apply -f sample-backend-east.yaml

이 서비스는 "Hello from East"라는 메시지를 반환하도록 설정되어 있습니다.

로드 밸런싱 동작 확인

이제 두 클러스터에 동일한 서비스가 배포되었으므로, 외부에서 요청이 들어올 때 두 클러스터의 서비스로 분산되는지 확인해봅니다. Kiali 대시보드를 확인해보면 트래픽이 두 클러스터로 분산되는 것을 시각적으로 확인할 수 있습니다.

West 클러스터의 인그레스 게이트웨이를 통해 들어온 요청이 East 클러스터의 서비스로도 전달되는 것을 확인할 수 있습니다.

프록시 설정 확인

이스티오의 프록시 설정을 통해 로드 밸런싱이 어떻게 구성되었는지 확인해봅시다:

iwest proxy-config endpoints <pod-name> -n istio-system

출력 결과를 보면 두 가지 엔드포인트가 있는 것을 확인할 수 있습니다:

  1. 웨스트 클러스터 내부의 서비스 파드 IP
  2. 이스트 클러스터의 East-West 게이트웨이 로드밸런서 IP와 15443 포트(mTLS 포트)

이러한 구성을 통해 웨스트 클러스터에서 이스트 클러스터의 서비스로 트래픽이 전달됩니다.

지역 인식 라우팅 구현

현재는 단순히 로드 밸런싱으로 트래픽을 분산하고 있지만, 사용자가 서부 지역에 있다면 동부 데이터센터로 요청을 보내는 것은 효율적이지 않습니다. 이제 지역 인식 라우팅을 설정하여 같은 지역의 서비스를 우선적으로 사용하도록 구성해보겠습니다.

노드에 지역 정보 설정

먼저 각 클러스터의 노드에 지역 정보를 설정합니다:

# 웨스트 클러스터 노드에 지역 정보 설정
kwest label node west-control-plane topology.kubernetes.io/region=us-west topology.kubernetes.io/zone=us-west-1

# 이스트 클러스터 노드에 지역 정보 설정
keast label node east-control-plane topology.kubernetes.io/region=us-east topology.kubernetes.io/zone=us-east-1

이 레이블을 통해 각 노드가 어떤 지역에 위치하는지 Kubernetes에 알려줍니다.

변경된 설정이 적용되도록 디플로이먼트를 다시 롤아웃합니다:

kwest rollout restart deployment -n istio-injection
keast rollout restart deployment -n istio-injection

이제 엔드포인트에 로컬리티(locality) 정보가 포함되어 있는지 확인합니다:

iwest proxy-config endpoints <pod-name> -n istio-system -o json

출력 결과를 보면 각 엔드포인트에 로컬리티 정보가 추가된 것을 확인할 수 있습니다. 예를 들어 웨스트 클러스터의 엔드포인트는 us-west/us-west-1로, 이스트 클러스터의 엔드포인트는 us-east/us-east-1로 표시됩니다.

아웃라이어 감지 설정

지역 인식 로드 밸런싱을 위해서는 헬스 체크와 아웃라이어 감지(outlier detection)가 필수적입니다. 이를 통해 특정 서비스에 문제가 발생했을 때 다른 지역으로 페일오버할 수 있기 때문입니다.

데스티네이션 룰을 생성하여 아웃라이어 감지를 설정합니다:

kwest apply -f destination-rule-outlier.yaml

이제 웨스트 클러스터의 인그레스 게이트웨이를 통해 요청을 보내면, 웨스트 클러스터의 서비스만 응답하는 것을 확인할 수 있습니다:

for i in {1..20}; do curl -H "Host: simple-backend.istio.injection.io" http://<INGRESS_IP>; echo; done

모든 응답이 "Hello from West"로 나타나는 것을 확인할 수 있습니다. 이는 로컬리티 기반 라우팅이 적용되었기 때문입니다.

프록시 설정을 다시 확인해보면 우선순위가 설정된 것을 볼 수 있습니다:

iwest proxy-config endpoints <pod-name> -n istio-system -o json

출력 결과를 보면, 로컬 엔드포인트(웨스트 클러스터)에는 priority가 0으로 설정되어 있고(값이 없으면 0으로 간주), 원격 엔드포인트(이스트 클러스터)에는 priority가 1로 설정되어 있습니다. 값이 작을수록 우선순위가 높기 때문에 로컬 엔드포인트가 먼저 선택됩니다.

장애 시나리오 테스트

이제 웨스트 클러스터의 서비스에 문제가 발생했을 때, 이스트 클러스터의 서비스로 트래픽이 전환되는지 테스트해보겠습니다.

웨스트 클러스터의 서비스가 오류를 반환하도록 환경 변수를 수정합니다:

kwest set env deployment/simple-backend ERROR=true -n istio-injection

이 설정으로 인해 웨스트 클러스터의 서비스는 계속해서 오류를 반환하게 됩니다. 아웃라이어 감지 기능이 이를 감지하고 해당 엔드포인트를 일시적으로 사용 중지합니다.

잠시 후 Kiali 대시보드를 확인하면 트래픽이 이스트 클러스터로 전환된 것을 확인할 수 있습니다. 그리고 다시 요청을 보내면 "Hello from East" 응답이 나오는 것을 볼 수 있습니다:

curl -H "Host: simple-backend.istio.injection.io" http://<INGRESS_IP>

이는 웨스트 클러스터의 서비스가 문제를 겪고 있기 때문에 이스트 클러스터의 서비스로 트래픽이 자동으로 전환된 것입니다.

다중 클러스터 환경에서의 인가 정책 적용

마지막으로 다중 클러스터 환경에서 인가 정책(Authorization Policy)을 적용하여 보안을 강화해보겠습니다.

테스트를 위해 웨스트 클러스터의 백엔드 서비스를 삭제하여, 웨스트 클러스터의 인그레스 게이트웨이가 직접 이스트 클러스터의 서비스로 트래픽을 전달하게 합니다:

kwest delete deployment/simple-backend -n istio-injection

이제 이스트 클러스터에 인가 정책을 적용합니다. 이 정책은 웨스트 클러스터의 인그레스 게이트웨이에서 오는 트래픽만 허용하도록 설정됩니다:

keast apply -f authorization-policy.yaml

인가 정책의 내용은 다음과 같습니다:

apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
  name: simple-backend
  namespace: istio-injection
spec:
  selector:
    matchLabels:
      app: simple-backend
  rules:
  - from:
    - source:
        principals: ["cluster.local/ns/istio-system/sa/istio-ingressgateway-service-account"]

이 정책이 제대로 적용되는지 테스트하기 위해, 웨스트 클러스터에 테스트 파드를 생성하고 이스트 클러스터의 서비스에 직접 접근을 시도합니다:

kwest run mesh -it --image=curlimages/curl -- sh

테스트 파드 내에서 직접 심플 백엔드 서비스에 접근하면 거부됩니다:

curl simple-backend.istio-injection.svc.cluster.local
# RBAC: access denied

하지만 인그레스 게이트웨이를 통해 접근하면 성공합니다:

curl -H "Host: simple-backend.istio.injection.io" istio-ingressgateway.istio-system.svc.cluster.local
# Hello from East

이는 인가 정책이 제대로 적용되어, 웨스트 클러스터의 인그레스 게이트웨이를 통해 오는 트래픽만 허용하고 있음을 보여줍니다.

Istio의 요청 처리 과정

Istio의 요청 처리 전체 과정을 디테일하게 살펴보면 다음과 같은 흐름으로 이루어집니다:

  1. 리스너(Listener)
  2. 리스너 필터 체인
  3. TLS 소켓
  4. 네트워크 필터
  5. HTTP 커넥션 매니저(HCM)
  6. HTTP 필터 체인
  7. 라우터 필터

특히 HTTP 커넥션 매니저 안에 있는 필터 체인의 마지막은 라우터 필터로, 이 필터를 통해 업스트림으로 요청을 전달합니다. 이런 구조를 이해하는 것이 중요한 이유는, Istio의 요청 처리 기능을 확장할 때 어떤 위치에 확장 기능을 넣어야 하는지 알 수 있기 때문입니다.

Istio API가 제공하지 않는 기능들은 다음 세 가지 방법으로 확장할 수 있습니다:

  1. Envoy 필터
  2. Lua 스크립트
  3. WebAssembly(WASM)

Envoy 필터를 통한 확장

Envoy 필터는 Istio의 요청 처리 과정에 추가적인 기능을 구현할 수 있게 해줍니다. Istio API가 추상화하고 있는 하부 Envoy 설정을 직접 조작하는 방식입니다.

Tap 필터 활용

Tap 필터는 요청과 응답을 디버깅 용도로 미러링할 수 있게 해주는 Envoy의 HTTP 필터입니다.

  1. 먼저 테스트용 웹앱 서비스를 배포합니다:
apiVersion: apps/v1
kind: Deployment
metadata:
  name: webapp
  namespace: istio-injection
spec:
  replicas: 1
  selector:
    matchLabels:
      app: webapp
  template:
    metadata:
      labels:
        app: webapp
    spec:
      containers:
      - name: webapp
        image: kennethreitz/httpbin
        ports:
        - containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
  name: webapp
  namespace: istio-injection
spec:
  selector:
    app: webapp
  ports:
  - port: 80
    name: http
  1. Envoy 필터 리소스를 생성하여 Tap 필터를 적용합니다:
apiVersion: networking.istio.io/v1alpha3
kind: EnvoyFilter
metadata:
  name: tap-filter
  namespace: istio-injection
spec:
  workloadSelector:
    labels:
      app: webapp
  configPatches:
  - applyTo: HTTP_FILTER
    match:
      context: SIDECAR_INBOUND
      listener:
        filterChain:
          filter:
            name: "envoy.filters.network.http_connection_manager"
    patch:
      operation: INSERT_BEFORE
      value:
        name: envoy.filters.http.tap
        typed_config:
          "@type": type.googleapis.com/envoy.extensions.filters.http.tap.v3.Tap
          common_config:
            admin_config:
              config_id: tap-config
  1. 포트 포워딩을 설정하여 Tap 기능을 활성화합니다:
kubectl port-forward deploy/webapp 15000:15000 -n istio-injection
  1. 사이드카 프록시의 관리 인터페이스에 접근하여 Tap 설정을 활성화합니다:
curl -X POST localhost:15000/tap -d '{"config_id": "tap-config"}'
  1. 테스트 요청을 보낼 때 특정 헤더를 포함시키면 Tap 필터가 활성화됩니다:
curl -H "X-Tap: true" -H "Host: webapp.example.com" http://INGRESS_IP/api/catalog

이렇게 설정하면 요청과 응답의 헤더, 바디, 트레일러 등 디버깅에 필요한 정보가 출력됩니다. Envoy의 로그 레벨을 조정하면 더 자세한 정보를 확인할 수 있습니다:

istioctl proxy-config log deploy/webapp -n istio-injection --level http:debug,tap:debug

속도 제한(Rate Limiting) 구현

속도 제한은 API 게이트웨이 등에서 자주 사용되는 기능으로, 특정 사용자나 그룹의 API 호출 횟수를 제한할 수 있습니다. Istio에서는 Envoy의 Rate Limit 필터를 활용합니다.

속도 제한 서버 설정

속도 제한을 구현하기 위해서는 다음 두 가지 컴포넌트가 필요합니다:

  1. Rate Limit Server(RLS): Envoy의 속도 제한 API를 구현한 서버
  2. Redis: 카운터를 저장하는 백엔드 저장소
apiVersion: v1
kind: ConfigMap
metadata:
  name: ratelimit-config
spec:
  domain: catalog-service
  descriptors:
    - key: loyalty
      value: gold
      rate_limit:
        unit: minute
        requests_per_unit: 10
    - key: loyalty
      value: silver
      rate_limit:
        unit: minute
        requests_per_unit: 5
    - key: loyalty
      value: bronze
      rate_limit:
        unit: minute
        requests_per_unit: 3
    - key: loyalty
      rate_limit:
        unit: minute
        requests_per_unit: 1

이 설정은 충성도(loyalty) 등급에 따라 다른 속도 제한을 적용합니다:

  • 골드 등급: 분당 10개 요청
  • 실버 등급: 분당 5개 요청
  • 브론즈 등급: 분당 3개 요청
  • 등급 없음: 분당 1개 요청

Envoy 필터 설정

속도 제한을 적용하기 위한 Envoy 필터를 생성합니다:

apiVersion: networking.istio.io/v1alpha3
kind: EnvoyFilter
metadata:
  name: ratelimit-filter
  namespace: istio-system
spec:
  workloadSelector:
    labels:
      app: istio-ingressgateway
  configPatches:
  - applyTo: HTTP_FILTER
    match:
      context: GATEWAY
      listener:
        filterChain:
          filter:
            name: "envoy.filters.network.http_connection_manager"
            subFilter:
              name: "envoy.filters.http.router"
    patch:
      operation: INSERT_BEFORE
      value:
        name: envoy.filters.http.ratelimit
        typed_config:
          "@type": type.googleapis.com/envoy.extensions.filters.http.ratelimit.v3.RateLimit
          domain: catalog-service
          failure_mode_deny: false
          rate_limit_service:
            grpc_service:
              envoy_grpc:
                cluster_name: rate_limit_service
              timeout: 10s

이 필터는 카탈로그 서비스에 대한 요청에 속도 제한을 적용합니다. 요청이 헤더 매칭 조건에 따라 적절한 속도 제한 규칙이 적용됩니다.

테스트

설정이 완료되면, 다양한 등급의 요청을 보내 속도 제한이 제대로 작동하는지 확인합니다:

# 등급 없는 요청 (분당 1개 제한)
curl http://INGRESS_IP/api/catalog

# 실버 등급 요청 (분당 5개 제한)
curl -H "loyalty: silver" http://INGRESS_IP/api/catalog

처음 몇 개의 요청은 성공하지만, 제한을 초과하면 429 Too Many Requests 오류가 반환됩니다.

Lua를 사용한 확장

Lua는 가볍고 내장 가능한 스크립트 언어로, Istio의 데이터 플레인을 확장하는 데 사용할 수 있습니다. Lua 필터를 통해 요청 및 응답을 커스터마이징할 수 있습니다.

Lua 필터의 특징

  • 컴파일 없이 스크립트로 기능 확장 가능
  • 샌드박스 환경에서 동작하여 안전성 보장
  • 요청과 응답 모두에 대한 처리 가능

주의할 점은 Lua 스크립트가 메모리를 많이 사용하면 성능에 영향을 줄 수 있다는 것입니다. 일반적으로 약 200MB 정도의 메모리가 필요할 수 있습니다.

Lua 필터 실습

HTTP 에코 서비스를 배포하고, 요청 및 응답 헤더를 추가하는 Lua 스크립트를 적용합니다:

# cat ch14/lua-filter.yaml
apiVersion: networking.istio.io/v1alpha3
kind: EnvoyFilter
metadata:
  name: httpbin-lua-extension
  namespace: istioinaction
spec:
  workloadSelector:
    labels:
      app: httpbin
  configPatches:
  - applyTo: HTTP_FILTER
    match:
      context: SIDECAR_INBOUND
      listener:
        portNumber: 80
        filterChain:
          filter:
            name: "envoy.filters.network.http_connection_manager"
            subFilter:
              name: "envoy.filters.http.router"
    patch:
      operation: INSERT_BEFORE
      value: 
       name: envoy.lua
       typed_config:
          "@type": "type.googleapis.com/envoy.extensions.filters.http.lua.v3.Lua"
          inlineCode: |
            function envoy_on_request(request_handle) # 아래 줄에 코드 입력
              local headers, test_bucket = request_handle:httpCall(
              "bucket_tester",
              {
                [":method"] = "GET",
                [":path"] = "/",
                [":scheme"] = "http",
                [":authority"] = "bucket-tester.istioinaction.svc.cluster.local",
                ["accept"] = "*/*"
              }, "", 5000) 
              
              sanitized_bucket = string.gsub(test_bucket, "[\r\n]", "") -- sanitize test_bucket string
              request_handle:headers():add("x-test-cohort", sanitized_bucket)               
            end          
            function envoy_on_response(response_handle) # 아래 줄에 코드 입력
              response_handle:headers():add("istioinaction", "it works!")
            end
  - applyTo: CLUSTER
    match:
      context: SIDECAR_OUTBOUND
    patch:
      operation: ADD
      value: # cluster specification
        name: bucket_tester
        type: STRICT_DNS
        connect_timeout: 0.5s
        lb_policy: ROUND_ROBIN
        load_assignment:
          cluster_name: bucket_tester
          endpoints:
          - lb_endpoints:
            - endpoint:
                address:
                  socket_address:
                    protocol: TCP
                    address: bucket-tester.istioinaction.svc.cluster.local
                    port_value: 80

 

이 스크립트는 다음과 같은 작업을 수행합니다:

  1. envoy_on_request 함수:
    • 요청이 들어올 때마다 실행됩니다
    • bucket_tester 서비스에 HTTP 요청을 보냅니다
    • bucket_tester 서비스로부터 받은 응답을 처리합니다
    • 응답에서 줄바꿈 문자를 제거하고(string.gsub 사용) x-test-cohort 헤더로 원래 요청에 추가합니다
    • 이는 사용자를 특정 테스트 그룹(코호트)에 할당하는데 사용됩니다 (A/B 테스트 시나리오)
  2. envoy_on_response 함수:
    • 응답이 클라이언트에게 반환될 때 실행됩니다
    • 모든 응답 헤더에 istioinaction: it works!를 추가합니다
    • 이는 Lua 필터가 제대로 작동하는지 확인하는 표시로 사용됩니다

WebAssembly(WASM)를 통한 확장

WebAssembly(WASM)는 바이너리 형식으로, 여러 프로그래밍 언어로 작성된 코드를 컴파일하여 가상 머신에서 실행할 수 있게 해줍니다. 원래는 웹 브라우저에서 JavaScript 외의 언어로 개발된 코드를 실행하기 위해 만들어졌지만, Istio에서도 활용됩니다.

WASM의 장점

  1. 여러 프로그래밍 언어로 작성 가능
  2. 안전한 샌드박스 환경에서 실행
  3. 메모리 사용이 효율적
  4. 런타임에 동적으로 로드 가능
  5. Envoy를 직접 수정하지 않고도 기능 확장 가능

댓글