멀티 클러스터 게이트웨이 배포


이 페이지에서는 여러 Google Kubernetes Engine(GKE) 클러스터(또는 Fleet)에 인그레스 트래픽을 부하 분산하기 위해 Kubernetes 게이트웨이 리소스를 배포하는 방법을 설명합니다. 멀티 클러스터 게이트웨이를 배포하려면 먼저 멀티 클러스터 게이트웨이 사용 설정을 참조하여 환경을 준비하세요.

인그레스 트래픽을 단일 GKE 클러스터로 부하 분산하기 위한 게이트웨이를 배포하려면 게이트웨이 배포를 참조하세요.

멀티 클러스터 게이트웨이

멀티 클러스터 게이트웨이는 여러 Kubernetes 클러스터 간에 트래픽을 부하 분산하는 게이트웨이 리소스입니다. GKE에서 gke-l7-global-external-managed-mc, gke-l7-regional-external-managed-mc, gke-l7-rilb-mc, gke-l7-gxlb-mc GatewayClass는 여러 GKE 클러스터, Kubernetes 네임스페이스, 리전 간에 HTTP 라우팅, 트래픽 분할, 트래픽 미러링, 상태 기반 장애 조치 등을 제공하는 멀티 클러스터 게이트웨이를 배포합니다. 멀티 클러스터 게이트웨이는 인프라 관리자를 위해 여러 클러스터 및 팀 간의 애플리케이션 네트워킹 관리를 쉽고, 안전하고, 확장 가능하게 만듭니다.

멀티 클러스터 게이트웨이는 여러 Kubernetes 클러스터 간에 트래픽을 부하 분산하는 게이트웨이 리소스입니다.

이 페이지에서는 GKE Gateway Controller를 사용하여 멀티 클러스터 게이트웨이를 배포하는 방법을 알려주기 위해 세 가지 예시를 소개합니다.

  • 예시 1: 인터넷 트래픽을 위해 2개의 GKE 클러스터 간에 부하 분산을 제공하는 외부 멀티 클러스터 게이트웨이
  • 예시 2: 내부 VPC 트래픽을 위해 2개의 GKE 클러스터 간의 블루-그린 가중치 기반 트래픽 분할 및 트래픽 미러링
  • 예시 3: 최대 용량을 기준으로 여러 백엔드로 요청을 부하 분산하는 용량 기반 게이트웨이

각 예시에서는 동일한 매장사이트 애플리케이션을 사용하여 온라인 쇼핑 서비스와 웹사이트 서비스가 개별 팀에 의해 소유 및 운영되고 공유되는 GKE 클러스터의 Fleet 간에 배포되는 실제 시나리오를 모델링합니다. 각 예시에서는 멀티 클러스터 게이트웨이에서 사용 설정하는 서로 다른 토폴로지와 사용 사례를 강조표시합니다.

멀티 클러스터 게이트웨이는 배포되기 전 몇 가지 환경적인 준비가 요구됩니다. 계속하기 전 멀티 클러스터 게이트웨이 사용 설정의 단계를 다르세요.

  1. GKE 클러스터를 배포합니다.

  2. Fleet에 클러스터를 등록합니다.

  3. 멀티 클러스터 서비스 및 멀티 클러스터 게이트웨이 컨트롤러를 사용 설정합니다.

마지막으로 환경에서 사용하기 전에 GKE Gateway Controller 제한사항 및 알려진 문제를 검토합니다.

멀티 클러스터, 멀티 리전, 외부 게이트웨이

이 튜토리얼에서는 2개의 GKE 클러스터에서 실행되는 애플리케이션 간에 외부 트래픽을 제공하는 외부 멀티 클러스터 게이트웨이를 만듭니다.

GKE 클러스터 2개 간에 배포되고 멀티 클러스터 게이트웨이를 통해 인터넷에 노출되는 store.example.com

다음 단계에서 다음을 수행합니다.

  1. gke-west-1gke-east-1 클러스터에 샘플 store 애플리케이션을 배포합니다.
  2. Fleet으로 내보낼 각 클러스터의 서비스를 구성합니다(멀티 클러스터 서비스).
  3. 구성 클러스터(gke-west-1)에 외부 멀티 클러스터 게이트웨이 및 HTTPRoute를 배포합니다.

애플리케이션 및 게이트웨이 리소스가 배포된 후 경로 기반 라우팅을 사용하여 2개의 GKE 클러스터 간에 트래픽을 제어할 수 있습니다.

  • /west에 대한 요청은 gke-west-1 클러스터의 store 포드에 라우팅됩니다.
  • /east에 대한 요청은 gke-east-1 클러스터의 store 포드에 라우팅됩니다.
  • 다른 경로에 대한 요청은 요청 클라이언트에 대한 해당 상태, 용량, 근접성에 따라 각 클러스터에 라우팅됩니다.

데모 애플리케이션 배포

  1. 멀티 클러스터 게이트웨이 사용 설정에 배포된 클러스터 3개 모두에 store 배포 및 네임스페이스를 만듭니다.

    kubectl apply --context gke-west-1 -f https://raw.githubusercontent.com/GoogleCloudPlatform/gke-networking-recipes/main/gateway/gke-gateway-controller/multi-cluster-gateway/store.yaml
    kubectl apply --context gke-west-2 -f https://raw.githubusercontent.com/GoogleCloudPlatform/gke-networking-recipes/main/gateway/gke-gateway-controller/multi-cluster-gateway/store.yaml
    kubectl apply --context gke-east-1 -f https://raw.githubusercontent.com/GoogleCloudPlatform/gke-networking-recipes/main/gateway/gke-gateway-controller/multi-cluster-gateway/store.yaml
    

    각 클러스터에 다음 리소스를 배포합니다.

    namespace/store created
    deployment.apps/store created
    

    이 페이지의 모든 예시에는 이 단계에서 배포된 앱이 사용됩니다. 남은 단계를 시도하기 전 3개 클러스터 모두에 앱이 배포되었는지 확인합니다. 이 예시에서는 gke-west-1gke-east-1 클러스터만 사용되고 gke-west-2는 다른 예시에서 사용됩니다.

멀티 클러스터 서비스

서비스는 포드가 클라이언트에 노출되는 방법을 제어합니다. GKE Gateway Controller에 컨테이너 기반 부하 분산이 사용되기 때문에 포드 연결을 위해 ClusterIP 또는 Kubernetes 부하 분산을 사용하지 않습니다. 트래픽은 부하 분산기에서 포드 IP 주소로 직접 전송됩니다. 하지만 서비스는 여전히 포드 그룹화에 대한 논리적 식별자로서 중요한 역할을 수행합니다.

멀티 클러스터 서비스(MCS)는 클러스터에 걸쳐진 서비스의 API 표준이기도 하고 GKE 클러스터 간에 서비스 검색을 제공하는 GKE 컨트롤러이기도 합니다. 멀티 클러스터 게이트웨이 컨트롤러는 MCS API 리소스를 사용하여 여러 클러스터 간에 주소 지정되거나 걸쳐 있는 서비스로 포드를 그룹화합니다.

멀티 클러스터 Services API는 다음 커스텀 리소스를 정의합니다.

  • ServiceExports는 Kubernetes 서비스에 매핑되며, 해당 서비스의 엔드포인트를 Fleet에 등록된 모든 클러스터로 내보냅니다. 서비스에 해당 ServiceExport가 있으면 멀티 클러스터 게이트웨이가 서비스에 주소 지정할 수 있음을 의미합니다.
  • ServiceImports는 멀티 클러스터 서비스 컨트롤러에서 자동으로 생성됩니다. ServiceExport 및 ServiceImport는 쌍으로 제공됩니다. ServiceExport가 Fleet에 존재할 경우 ServiceExport에 매핑된 서비스를 클러스터 간에 주소 지정할 수 있도록 해당 ServiceImport가 생성됩니다.

서비스 내보내기는 다음 방법으로 작동합니다. 매장 서비스는 해당 클러스터에서 포드 그룹을 선택하는 gke-west-1에 존재합니다. ServiceExport는 Fleet의 다른 클러스터에서 gke-west-1의 포드에 액세스할 수 있게 해주는 클러스터에 생성됩니다. ServiceExport는 ServiceExport 리소스와 이름 및 네임스페이스가 동일한 서비스로 매핑되며 이를 노출합니다.

apiVersion: v1
kind: Service
metadata:
  name: store
  namespace: store
spec:
  selector:
    app: store
  ports:
  - port: 8080
    targetPort: 8080
---
kind: ServiceExport
apiVersion: net.gke.io/v1
metadata:
  name: store
  namespace: store

다음 다이어그램은 ServiceExport가 배포된 후에 발생하는 결과를 보여줍니다. ServiceExport 및 서비스 쌍이 존재할 경우 멀티 클러스터 서비스 컨트롤러가 해당 ServiceImport를 Fleet에 있는 모든 GKE 클러스터에 배포합니다. ServiceImport는 모든 클러스터에 있는 store 서비스의 로컬 표현입니다. 이를 사용하면 gke-east-1client 포드가 ClusterIP 또는 헤드리스 서비스를 사용하여 gke-west-1store 포드에 연결할 수 있습니다. 이러한 방식으로 사용하면 멀티 클러스터 서비스가 내부 LoadBalancer 서비스 없이도 클러스터 간에 east-west 부하 분산을 제공합니다. 클러스터간 부하 분산을 위해 멀티 클러스터 서비스를 사용하려면 멀티 클러스터 서비스 구성을 참조하세요.

클러스터 간 통신을 허용하는 클러스터 간에 서비스를 내보내는 멀티 클러스터 서비스

멀티 클러스터 게이트웨이는 또한 ServiceImports를 사용하지만 클러스터간 부하 분산을 사용하지 않습니다. 대신 게이트웨이는 다른 클러스터에 있는 서비스 또는 여러 클러스터 간에 분산된 서비스에 대한 논리적 식별자로 ServiceImports를 사용합니다. 다음 HTTPRoute는 서비스 리소스 대신 ServiceImport를 참조합니다. ServiceImport를 참조함으로써 하나 이상의 클러스터에서 실행되는 백엔드 포드 그룹으로 트래픽을 전달합니다.

kind: HTTPRoute
apiVersion: gateway.networking.k8s.io/v1beta1
metadata:
  name: store-route
  namespace: store
  labels:
    gateway: multi-cluster-gateway
spec:
  parentRefs:
  - kind: Gateway
    namespace: store
    name: external-http
  hostnames:
  - "store.example.com"
  rules:
  - backendRefs:
    - group: net.gke.io
      kind: ServiceImport
      name: store
      port: 8080

다음 다이어그램은 HTTPRoute가 store.example.com 트래픽을 gke-west-1gke-east-1store 포드로 라우팅하는 방법을 보여줍니다. 부하 분산기는 이를 백엔드 중 하나의 풀로 취급합니다. 클러스터 중 하나의 포드가 비정상 또는 연결할 수 없는 상태가 되거나 트래픽 용량이 없으면 다른 클러스터의 남은 포드로 트래픽 로드가 분산됩니다. store 서비스 및 ServiceExport로 새 클러스터를 추가하거나 삭제할 수 있습니다. 이렇게 하면 명시적인 라우팅 구성 변경 없이 백엔드 포드를 투명하게 추가하거나 삭제할 수 있습니다.

MCS 리소스

서비스 내보내기

이제 애플리케이션이 두 클러스터 간에 실행됩니다. 이제 서비스 및 ServiceExports를 각 클러스터에 배포하여 애플리케이션을 노출하고 내보냅니다.

  1. gke-west-1 클러스터에 다음 매니페스트를 적용하여 storestore-west-1 Service와 ServiceExport를 만듭니다.

    cat << EOF | kubectl apply --context gke-west-1 -f -
    apiVersion: v1
    kind: Service
    metadata:
      name: store
      namespace: store
    spec:
      selector:
        app: store
      ports:
      - port: 8080
        targetPort: 8080
    ---
    kind: ServiceExport
    apiVersion: net.gke.io/v1
    metadata:
      name: store
      namespace: store
    ---
    apiVersion: v1
    kind: Service
    metadata:
      name: store-west-1
      namespace: store
    spec:
      selector:
        app: store
      ports:
      - port: 8080
        targetPort: 8080
    ---
    kind: ServiceExport
    apiVersion: net.gke.io/v1
    metadata:
      name: store-west-1
      namespace: store
    EOF
    
  2. gke-east-1 클러스터에 다음 매니페스트를 적용하여 storestore-east-1 Service와 ServiceExport를 만듭니다.

    cat << EOF | kubectl apply --context gke-east-1 -f -
    apiVersion: v1
    kind: Service
    metadata:
      name: store
      namespace: store
    spec:
      selector:
        app: store
      ports:
      - port: 8080
        targetPort: 8080
    ---
    kind: ServiceExport
    apiVersion: net.gke.io/v1
    metadata:
      name: store
      namespace: store
    ---
    apiVersion: v1
    kind: Service
    metadata:
      name: store-east-1
      namespace: store
    spec:
      selector:
        app: store
      ports:
      - port: 8080
        targetPort: 8080
    ---
    kind: ServiceExport
    apiVersion: net.gke.io/v1
    metadata:
      name: store-east-1
      namespace: store
    EOF
    
  3. 클러스터에 올바른 ServiceExport가 생성되었는지 확인합니다.

    kubectl get serviceexports --context CLUSTER_NAME --namespace store
    

    CLUSTER_NAMEgke-west-1gke-east-1로 바꿉니다. 출력은 다음과 비슷하게 표시됩니다.

    # gke-west-1
    NAME           AGE
    store          2m40s
    store-west-1   2m40s
    
    # gke-east-1
    NAME           AGE
    store          2m25s
    store-east-1   2m25s
    

    이것은 store 서비스에 두 클러스터 간의 store 포드가 포함된 것을 보여주며 store-west-1store-east-1 서비스는 해당 클러스터의 store 포드만 포함합니다. 이러한 겹쳐진 서비스는 여러 클러스터 간의 포드 또는 단일 클러스터의 포드 하위 집합을 대상으로 지정하기 위해 사용됩니다.

  4. 몇 분 후 함께 제공된 ServiceImports가 Fleet의 모든 클러스터 간에 멀티 클러스터 서비스 컨트롤러를 통해 자동으로 생성되었는지 확인합니다.

    kubectl get serviceimports --context CLUSTER_NAME --namespace store
    

    CLUSTER_NAMEgke-west-1gke-east-1로 바꿉니다. 출력은 다음과 비슷하게 표시됩니다.

    # gke-west-1
    NAME           TYPE           IP                  AGE
    store          ClusterSetIP   ["10.112.31.15"]    6m54s
    store-east-1   ClusterSetIP   ["10.112.26.235"]   5m49s
    store-west-1   ClusterSetIP   ["10.112.16.112"]   6m54s
    
    # gke-east-1
    NAME           TYPE           IP                  AGE
    store          ClusterSetIP   ["10.72.28.226"]    5d10h
    store-east-1   ClusterSetIP   ["10.72.19.177"]    5d10h
    store-west-1   ClusterSetIP   ["10.72.28.68"]     4h32m
    

    이것은 Fleet의 두 클러스터 모두에서 3개의 서비스 모두에 액세스할 수 있음을 보여줍니다. 하지만 Fleet당 활성 구성 클러스터가 하나만 있으므로 gke-west-1에서 ServiceImports를 참조하는 Gateways and HTTPRoutes만 배포할 수 있습니다. 구성 클러스터의 HTTPRoute가 이러한 ServiceImports를 백엔드로 참조할 때 게이트웨이는 내보낸 클러스터가 무엇이든 간에 이러한 서비스로 트래픽을 전달할 수 있습니다.

게이트웨이 및 HTTPRoute 배포

애플리케이션이 배포된 후 gke-l7-global-external-managed-mc GatewayClass를 사용하여 게이트웨이를 구성할 수 있습니다. 이 게이트웨이는 대상 클러스터 간에 트래픽을 분산하도록 구성된 외부 애플리케이션 부하 분산기를 만듭니다.

  1. 다음 Gateway 매니페스트를 구성 클러스터(이 예시에서는 gke-west-1)에 적용합니다.

    cat << EOF | kubectl apply --context gke-west-1 -f -
    kind: Gateway
    apiVersion: gateway.networking.k8s.io/v1beta1
    metadata:
      name: external-http
      namespace: store
    spec:
      gatewayClassName: gke-l7-global-external-managed-mc
      listeners:
      - name: http
        protocol: HTTP
        port: 80
        allowedRoutes:
          kinds:
          - kind: HTTPRoute
    EOF
    

    이 게이트웨이 구성은 gkemcg1-NAMESPACE-GATEWAY_NAME-HASH 이름 지정 규칙을 사용하여 외부 애플리케이션 부하 분산기 리소스를 배포합니다.

    이 구성으로 생성된 기본 리소스는 다음과 같습니다.

    • 부하 분산기 1개: gkemcg1-store-external-http-HASH
    • 공개 IP 주소 1개: gkemcg1-store-external-http-HASH
    • 전달 규칙 1개: gkemcg1-store-external-http-HASH
    • 백엔드 서비스 2개:
      • 기본 404 백엔드 서비스: gkemcg1-store-gw-serve404-HASH
      • 기본 500 백엔드 서비스: gkemcg1-store-gw-serve500-HASH
    • 상태 점검 1개:
      • 기본 404 상태 점검: gkemcg1-store-gw-serve404-HASH
    • 라우팅 규칙 0개(URLmap 비어 있음)

    이 단계에서 GATEWAY_IP:80을 요청하면 fault filter abort 메시지가 기본 페이지에 표시됩니다.

  2. 다음 HTTPRoute 매니페스트를 구성 클러스터(이 예시에서는 gke-west-1)에 적용합니다.

    cat << EOF | kubectl apply --context gke-west-1 -f -
    kind: HTTPRoute
    apiVersion: gateway.networking.k8s.io/v1beta1
    metadata:
      name: public-store-route
      namespace: store
      labels:
        gateway: external-http
    spec:
      hostnames:
      - "store.example.com"
      parentRefs:
      - name: external-http
      rules:
      - matches:
        - path:
            type: PathPrefix
            value: /west
        backendRefs:
        - group: net.gke.io
          kind: ServiceImport
          name: store-west-1
          port: 8080
      - matches:
        - path:
            type: PathPrefix
            value: /east
        backendRefs:
          - group: net.gke.io
            kind: ServiceImport
            name: store-east-1
            port: 8080
      - backendRefs:
        - group: net.gke.io
          kind: ServiceImport
          name: store
          port: 8080
    EOF
    

    이 단계에서 GATEWAY_IP:80을 요청하면 fault filter abort 메시지가 기본 페이지에 표시됩니다.

    배포된 다음에는 이 HTTPRoute가 다음 라우팅 동작을 구성합니다.

    • store-west-1 ServiceExport에서 선택한 포드가 gke-west-1 클러스터에만 존재하므로 /west에 대한 요청이 gke-west-1 클러스터의 store 포드로 라우팅됩니다.
    • store-east-1 ServiceExport에서 선택한 포드가 gke-east-1 클러스터에만 존재하므로 /east에 대한 요청이 gke-east-1 클러스터의 store 포드로 라우팅됩니다.
    • 다른 경로에 대한 요청은 요청 클라이언트에 대한 해당 상태, 용량, 근접성에 따라 각 클러스터의 store 포드로 라우팅됩니다.
    • GATEWAY_IP:80을 요청하면 기본 페이지에 fault filter abort 메시지가 표시됩니다.

    HTTPRoute는 겹치는 서비스를 사용하여 다른 클러스터 하위 집합으로 라우팅을 사용 설정합니다.

    지정된 클러스터의 모든 포드가 정상이 아니면(또는 존재하지 않으면) 실제 store 포드를 갖고 있는 클러스터로만 store 서비스가 전송됩니다. 지정된 클러스터에 ServiceExport 및 서비스가 존재한다고 해서 트래픽이 해당 클러스터로 반드시 전송되지는 않습니다. 포드가 존재하고 부하 분산기 상태 확인에 적절하게 반응해야 합니다. 그렇지 않으면 부하 분산기가 다른 클러스터에 있는 정상 store 포드로 트래픽을 전송합니다.

    다음 구성으로 새 리소스가 생성됩니다.

    • 백엔드 서비스 3개:
      • store 백엔드 서비스: gkemcg1-store-store-8080-HASH
      • store-east-1 백엔드 서비스: gkemcg1-store-store-east-1-8080-HASH
      • store-west-1 백엔드 서비스: gkemcg1-store-store-west-1-8080-HASH
    • 상태 점검 3개:
      • store 상태 점검: gkemcg1-store-store-8080-HASH
      • store-east-1 상태 점검: gkemcg1-store-store-east-1-8080-HASH
      • store-west-1 상태 점검: gkemcg1-store-store-west-1-8080-HASH
    • URLmap의 라우팅 규칙 1개:
      • store.example.com 라우팅 규칙:
      • 호스트 1개: store.example.com
      • 새 백엔드 서비스로 라우팅하는 여러 matchRules

다음 다이어그램은 두 클러스터 간에 배포한 리소스를 보여줍니다. gke-west-1은 게이트웨이 구성 클러스터이기 때문에 게이트웨이, HTTPRoutes, ServiceImports가 게이트웨이 컨트롤러에서 감시되는 클러스터입니다. 각 클러스터에는 store ServiceImport 및 해당 클러스터와 관련된 또 다른 ServiceImport가 포함되어 있습니다. 둘 다 동일한 포드를 가리킵니다. 이렇게 하면 HTTPRoute가 특정 클러스터의 store 포드 또는 모든 클러스터 간의 store 포드와 같은 트래픽의 정확한 전송 위치를 지정할 수 있습니다.

두 클러스터 간의 게이트웨이 및 멀티 클러스터 서비스 리소스 모델입니다.

이는 트래픽 흐름을 보여주는 것이 아닌 논리적인 리소스 모델입니다. 트래픽 경로는 부하 분산기에서 백엔드 포드로 직접 이동하며 구성 클러스터가 무엇이든 직접적인 관련은 없습니다.

배포 검증

이제 멀티 클러스터 게이트웨이에 요청을 실행하고 두 GKE 클러스터 간에 트래픽을 분산할 수 있습니다.

  1. 게이트웨이 상태와 이벤트를 조사하여 게이트웨이 및 HTTPRoute가 성공적으로 배포되었는지 검증합니다.

    kubectl describe gateways.gateway.networking.k8s.io external-http --context gke-west-1 --namespace store
    

    출력은 다음과 비슷하게 표시됩니다.

    Name:         external-http
    Namespace:    store
    Labels:       <none>
    Annotations:  networking.gke.io/addresses: /projects/PROJECT_NUMBER/global/addresses/gkemcg1-store-external-http-laup24msshu4
                  networking.gke.io/backend-services:
                    /projects/PROJECT_NUMBER/global/backendServices/gkemcg1-store-gw-serve404-80-n65xmts4xvw2, /projects/PROJECT_NUMBER/global/backendServices/gke...
                  networking.gke.io/firewalls: /projects/PROJECT_NUMBER/global/firewalls/gkemcg1-l7-default-global
                  networking.gke.io/forwarding-rules: /projects/PROJECT_NUMBER/global/forwardingRules/gkemcg1-store-external-http-a5et3e3itxsv
                  networking.gke.io/health-checks:
                    /projects/PROJECT_NUMBER/global/healthChecks/gkemcg1-store-gw-serve404-80-n65xmts4xvw2, /projects/PROJECT_NUMBER/global/healthChecks/gkemcg1-s...
                  networking.gke.io/last-reconcile-time: 2023-10-12T17:54:24Z
                  networking.gke.io/ssl-certificates: 
                  networking.gke.io/target-http-proxies: /projects/PROJECT_NUMBER/global/targetHttpProxies/gkemcg1-store-external-http-94oqhkftu5yz
                  networking.gke.io/target-https-proxies: 
                  networking.gke.io/url-maps: /projects/PROJECT_NUMBER/global/urlMaps/gkemcg1-store-external-http-94oqhkftu5yz
    API Version:  gateway.networking.k8s.io/v1beta1
    Kind:         Gateway
    Metadata:
      Creation Timestamp:  2023-10-12T06:59:32Z
      Finalizers:
        gateway.finalizer.networking.gke.io
      Generation:        1
      Resource Version:  467057
      UID:               1dcb188e-2917-404f-9945-5f3c2e907b4c
    Spec:
      Gateway Class Name:  gke-l7-global-external-managed-mc
      Listeners:
        Allowed Routes:
          Kinds:
            Group:  gateway.networking.k8s.io
            Kind:   HTTPRoute
          Namespaces:
            From:  Same
        Name:      http
        Port:      80
        Protocol:  HTTP
    Status:
      Addresses:
        Type:   IPAddress
        Value:  34.36.127.249
      Conditions:
        Last Transition Time:  2023-10-12T07:00:41Z
        Message:               The OSS Gateway API has deprecated this condition, do not depend on it.
        Observed Generation:   1
        Reason:                Scheduled
        Status:                True
        Type:                  Scheduled
        Last Transition Time:  2023-10-12T07:00:41Z
        Message:               
        Observed Generation:   1
        Reason:                Accepted
        Status:                True
        Type:                  Accepted
        Last Transition Time:  2023-10-12T07:00:41Z
        Message:               
        Observed Generation:   1
        Reason:                Programmed
        Status:                True
        Type:                  Programmed
        Last Transition Time:  2023-10-12T07:00:41Z
        Message:               The OSS Gateway API has altered the "Ready" condition semantics and reservedit for future use.  GKE Gateway will stop emitting it in a future update, use "Programmed" instead.
        Observed Generation:   1
        Reason:                Ready
        Status:                True
        Type:                  Ready
      Listeners:
        Attached Routes:  1
        Conditions:
          Last Transition Time:  2023-10-12T07:00:41Z
          Message:               
          Observed Generation:   1
          Reason:                Programmed
          Status:                True
          Type:                  Programmed
          Last Transition Time:  2023-10-12T07:00:41Z
          Message:               The OSS Gateway API has altered the "Ready" condition semantics and reservedit for future use.  GKE Gateway will stop emitting it in a future update, use "Programmed" instead.
          Observed Generation:   1
          Reason:                Ready
          Status:                True
          Type:                  Ready
        Name:                    http
        Supported Kinds:
          Group:  gateway.networking.k8s.io
          Kind:   HTTPRoute
    Events:
      Type    Reason  Age                    From                   Message
      ----    ------  ----                   ----                   -------
      Normal  UPDATE  35m (x4 over 10h)      mc-gateway-controller  store/external-http
      Normal  SYNC    4m22s (x216 over 10h)  mc-gateway-controller  SYNC on store/external-http was a success
    
  2. 게이트웨이가 성공적으로 배포되었으면 external-http 게이트웨이에서 외부 IP 주소를 검색합니다.

    kubectl get gateways.gateway.networking.k8s.io external-http -o=jsonpath="{.status.addresses[0].value}" --context gke-west-1 --namespace store
    

    다음 단계에서 VIP를 출력으로 수신되는 IP 주소로 바꿉니다.

  3. 도메인의 루트 경로로 트래픽을 전송합니다. 이렇게 하면 gke-west-1gke-east-1 클러스터 간에 있는 store ServiceImport로 트래픽을 부하 분산합니다. 부하 분산기가 가장 가까운 리전으로 트래픽을 전송하며, 다른 리전의 응답은 표시되지 않을 수 있습니다.

    curl -H "host: store.example.com" http://VIP
    

    이 출력은 요청이 gke-east-1 클러스터의 포드에서 제공되었는지 확인합니다.

    {
      "cluster_name": "gke-east-1",
      "zone": "us-east1-b",
      "host_header": "store.example.com",
      "node_name": "gke-gke-east-1-default-pool-7aa30992-t2lp.c.agmsb-k8s.internal",
      "pod_name": "store-5f5b954888-dg22z",
      "pod_name_emoji": "",
      "project_id": "agmsb-k8s",
      "timestamp": "2021-06-01T17:32:51"
    }
    
  4. 그런 후 /west 경로로 트래픽을 전송합니다. 이것은 gke-west-1 클러스터에서 실행되는 포드만 있는 store-west-1 ServiceImport로 트래픽을 라우팅합니다. store-west-1과 같은 클러스터 특정 ServiceImport는 부하 분산기가 결정을 내리도록 허용하는 대신 애플리케이션 소유자가 특정 클러스터로 트래픽을 명시적으로 전송할 수 있게 해줍니다.

    curl -H "host: store.example.com" http://VIP/west
    

    이 출력은 요청이 gke-west-1 클러스터의 포드에서 제공되었는지 확인합니다.

    {
      "cluster_name": "gke-west-1", 
      "zone": "us-west1-a", 
      "host_header": "store.example.com",
      "node_name": "gke-gke-west-1-default-pool-65059399-2f41.c.agmsb-k8s.internal",
      "pod_name": "store-5f5b954888-d25m5",
      "pod_name_emoji": "🍾",
      "project_id": "agmsb-k8s",
      "timestamp": "2021-06-01T17:39:15",
    }
    
  5. 마지막으로 /east 경로로 트래픽을 전송합니다.

    curl -H "host: store.example.com" http://VIP/east
    

    이 출력은 요청이 gke-east-1 클러스터의 포드에서 제공되었는지 확인합니다.

    {
      "cluster_name": "gke-east-1",
      "zone": "us-east1-b",
      "host_header": "store.example.com",
      "node_name": "gke-gke-east-1-default-pool-7aa30992-7j7z.c.agmsb-k8s.internal",
      "pod_name": "store-5f5b954888-hz6mw",
      "pod_name_emoji": "🧜🏾",
      "project_id": "agmsb-k8s",
      "timestamp": "2021-06-01T17:40:48"
    }
    

게이트웨이를 사용한 블루-그린 멀티 클러스터 라우팅

gke-l7-global-external-managed-*, gke-l7-regional-external-managed-*, gke-l7-rilb-* GatewayClass에는 트래픽 분할, 헤더 일치, 헤더 조작, 트래픽 미러링 등을 포함하여 많은 고급 트래픽 라우팅 기능이 포함되어 있습니다. 이 예시에서는 가중치에 기반한 트래픽 분할을 사용하여 GKE 클러스터 2개 간의 트래픽 비율을 명시적으로 제어하는 방법을 보여줍니다.

이 예시에서는 서비스 소유자가 애플리케이션을 새 GKE 클러스터로 이동하거나 확장할 때 수행하는 몇 가지 실제 단계를 수행합니다. 블루-그린 배포의 목적은 새 클러스터가 올바르게 작동하는지 확인하는 여러 검증 단계를 통해 위험을 줄이기 위한 것입니다. 이 예시는 배포의 4단계를 통과합니다.

  1. 100%-헤더 기반 카나리아: HTTP 헤더 라우팅을 사용하여 테스트 또는 합성 트래픽만 새 클러스터로 전송합니다.
  2. 100%-트래픽 미러링: 카나리아 클러스터로 사용자 트래픽을 미러링합니다. 이렇게 하면 사용자 트래픽을 100% 이 클러스터로 복사하여 카나리아 클러스터의 용량을 테스트합니다.
  3. 90%-10%: 10%의 분할 트래픽을 카나리아로 테스트하여 새 클러스터를 라이브 트래픽에 느리게 노출시킵니다.
  4. 0%-100%: 오류가 관측된 경우 다시 전환할 수 있는 옵션과 함께 새 클러스터로 완전히 컷오버합니다.

두 GKE 클러스터 간의 블루-그린 트래픽 분할

이 예시는 이전 예시와 비슷하지만 대신 내부 멀티 클러스터 게이트웨이를 배포합니다. 이렇게 하면 VPC 내에서 비공개로 액세스할 수 있는 내부 애플리케이션 부하 분산기를 배포합니다. 이전 단계에서 배포한 것과 동일한 애플리케이션 및 클러스터를 사용하지만, 다른 게이트웨이를 통해 이를 배포합니다.

기본 요건

다음 예시는 외부 멀티 클러스터 게이트웨이 배포의 단계 중 일부에 따라 빌드됩니다. 이 예시를 진행하기 전에 다음 단계를 완료했는지 확인하세요.

  1. 멀티 클러스터 게이트웨이 사용 설정

  2. 데모 애플리케이션 배포

    이 예시에서는 이미 설정된 상태의 gke-west-1gke-west-2 클러스터가 사용됩니다. 이러한 클러스터는 gke-l7-rilb-mc GatewayClass가 리전별 클래스이고 동일한 리전의 클러스터 백엔드만 지원하기 때문에 동일한 리전에 있습니다.

  3. 각 클러스터에 필요한 서비스 및 ServiceExports를 배포합니다. 이전 예시에서 Service 및 ServiceExport를 배포한 경우 이미 일부가 배포되어 있습니다.

    kubectl apply --context gke-west-1 -f https://raw.githubusercontent.com/GoogleCloudPlatform/gke-networking-recipes/main/gateway/gke-gateway-controller/multi-cluster-gateway/store-west-1-service.yaml
    kubectl apply --context gke-west-2 -f https://raw.githubusercontent.com/GoogleCloudPlatform/gke-networking-recipes/main/gateway/gke-gateway-controller/multi-cluster-gateway/store-west-2-service.yaml
    

    비슷한 리소스 집합을 각 클러스터에 배포합니다.

    service/store created
    serviceexport.net.gke.io/store created
    service/store-west-2 created
    serviceexport.net.gke.io/store-west-2 created
    

프록시 전용 서브넷 구성

아직 수행하지 않았으면 내부 게이트웨이를 배포하려는 각 리전에서 프록시 전용 서브넷을 구성합니다. 이 서브넷은 부하 분산기 프록시에 내부 IP 주소를 제공하는 데 사용되며 --purposeREGIONAL_MANAGED_PROXY로만 설정된 상태로 구성되어야 합니다.

내부 애플리케이션 부하 분산기를 관리하는 게이트웨이를 만들기 전에 프록시 전용 서브넷을 만들어야 합니다. 내부 애플리케이션 부하 분산기를 사용하는 Virtual Private Cloud(VPC) 네트워크의 각 리전에는 프록시 전용 서브넷이 있어야 합니다.

gcloud compute networks subnets create 명령어는 프록시 전용 서브넷을 만듭니다.

gcloud compute networks subnets create SUBNET_NAME \
    --purpose=REGIONAL_MANAGED_PROXY \
    --role=ACTIVE \
    --region=REGION \
    --network=VPC_NETWORK_NAME \
    --range=CIDR_RANGE

다음을 바꿉니다.

  • SUBNET_NAME: 프록시 전용 서브넷의 이름입니다.
  • REGION: 프록시 전용 서브넷의 리전입니다.
  • VPC_NETWORK_NAME: 서브넷이 포함된 VPC 네트워크의 이름입니다.
  • CIDR_RANGE: 서브넷의 기본 IP 주소 범위입니다. 리전의 프록시에서 64개 이상의 IP 주소를 사용할 수 있도록 /26 이상의 서브넷 마스크를 사용해야 합니다. 권장 서브넷 마스크는 /23입니다.

게이트웨이 배포

다음 게이트웨이는 gke-l7-rilb-mc GatewayClass에서 생성됩니다. 이것은 동일한 리전의 GKE 클러스터만 대상으로 지정할 수 있는 리전별 내부 게이트웨이입니다.

  1. 다음 Gateway 매니페스트를 구성 클러스터(이 예시에서는 gke-west-1)에 적용합니다.

    cat << EOF | kubectl apply --context gke-west-1 -f -
    kind: Gateway
    apiVersion: gateway.networking.k8s.io/v1beta1
    metadata:
      name: internal-http
      namespace: store
    spec:
      gatewayClassName: gke-l7-rilb-mc
      listeners:
      - name: http
        protocol: HTTP
        port: 80
        allowedRoutes:
          kinds:
          - kind: HTTPRoute
    EOF
    
  2. 게이트웨이가 성공적으로 배포되었는지 확인합니다. 다음 명령어를 사용하여 이 게이트웨이에서 이벤트를 필터링할 수 있습니다.

    kubectl get events --field-selector involvedObject.kind=Gateway,involvedObject.name=internal-http --context=gke-west-1 --namespace store
    

    출력이 다음과 유사하면 게이트웨이 배포가 성공한 것입니다.

    LAST SEEN   TYPE     REASON   OBJECT                  MESSAGE
    5m18s       Normal   ADD      gateway/internal-http   store/internal-http
    3m44s       Normal   UPDATE   gateway/internal-http   store/internal-http
    3m9s        Normal   SYNC     gateway/internal-http   SYNC on store/internal-http was a success
    

헤더 기반 카나리아

헤더 기반 카나리아를 통해 서비스 소유자는 실제 사용자로부터 제공되지 않는 합성 테스트 트래픽을 찾을 수 있습니다. 이렇게 하면 사용자를 직접 노출시키지 않고도 애플리케이션의 기본 네트워킹이 작동하는지 쉽게 확인할 수 있습니다.

  1. 다음 HTTPRoute 매니페스트를 구성 클러스터(이 예시에서는 gke-west-1)에 적용합니다.

    cat << EOF | kubectl apply --context gke-west-1 -f -
    kind: HTTPRoute
    apiVersion: gateway.networking.k8s.io/v1beta1
    metadata:
      name: internal-store-route
      namespace: store
      labels:
        gateway: internal-http
    spec:
      parentRefs:
      - kind: Gateway
        namespace: store
        name: internal-http
      hostnames:
      - "store.example.internal"
      rules:
      # Matches for env=canary and sends it to store-west-2 ServiceImport
      - matches:
        - headers:
          - name: env
            value: canary
        backendRefs:
          - group: net.gke.io
            kind: ServiceImport
            name: store-west-2
            port: 8080
      # All other traffic goes to store-west-1 ServiceImport
      - backendRefs:
        - group: net.gke.io
          kind: ServiceImport
          name: store-west-1
          port: 8080
    EOF
    

    배포된 다음에는 이 HTTPRoute가 다음 라우팅 동작을 구성합니다.

    • env: canary HTTP 헤더가 없는 store.example.internal 헤더에 대한 내부 요청은 gke-west-1 클러스터의 store 포드로 라우팅됩니다.
    • env: canary HTTP 헤더가 있는 store.example.internal에 대한 내부 요청은 gke-west-2 클러스터의 store 포드로 라우팅됩니다.

    HTTPRoute는 HTTP 헤더를 기반으로 다른 클러스터로의 라우팅을 지원합니다.

    게이트웨이 IP 주소로 트래픽을 전송하여 HTTPRoute가 올바르게 작동하는지 확인합니다.

  2. internal-http에서 내부 IP 주소를 검색합니다.

    kubectl get gateways.gateway.networking.k8s.io internal-http -o=jsonpath="{.status.addresses[0].value}" --context gke-west-1 --namespace store
    

    다음 단계에서 VIP를 출력으로 수신되는 IP 주소로 바꿉니다.

  3. env: canary HTTP 헤더를 사용하여 요청을 게이트웨이로 전송합니다. 이렇게 하면 트래픽이 gke-west-2로 라우팅되는지 확인합니다. GKE 클러스터와 동일한 VPC에서 비공개 클라이언트를 사용하여 요청이 올바르게 라우팅되는지 확인합니다. 다음 명령어는 게이트웨이 IP 주소에 대해 비공개 액세스 권한이 있는 머신에서 실행되어야 하며, 그렇지 않으면 작동하지 않습니다.

    curl -H "host: store.example.internal" -H "env: canary" http://VIP
    

    이 출력은 요청이 gke-west-2 클러스터의 포드에서 제공되었는지 확인합니다.

    {
        "cluster_name": "gke-west-2", 
        "host_header": "store.example.internal",
        "node_name": "gke-gke-west-2-default-pool-4cde1f72-m82p.c.agmsb-k8s.internal",
        "pod_name": "store-5f5b954888-9kdb5",
        "pod_name_emoji": "😂",
        "project_id": "agmsb-k8s",
        "timestamp": "2021-05-31T01:21:55",
        "zone": "us-west1-a"
    }
    

트래픽 미러링

이 단계에서는 의도된 클러스터로 트래픽을 전송하지만 또한 카나리아 클러스터로 트래픽을 미러링합니다.

미러링 사용은 어떤 방식으로든 클라이언트에 대한 응답에 영향을 주지 않고 트래픽 로드가 애플리케이션 성능에 미치는 영향을 확인하는 데 유용합니다. 모든 종류의 출시에 필요하지는 않을 수 있지만 성능 또는 로드에 영향을 줄 수 있는 대규모 변경사항을 출시할 때 유용할 수 있습니다.

  1. 다음 HTTPRoute 매니페스트를 구성 클러스터(이 예시에서는 gke-west-1)에 적용합니다.

    cat << EOF | kubectl apply --context gke-west-1 -f -
    kind: HTTPRoute
    apiVersion: gateway.networking.k8s.io/v1beta1
    metadata:
      name: internal-store-route
      namespace: store
      labels:
        gateway: internal-http
    spec:
      parentRefs:
      - kind: Gateway
        namespace: store
        name: internal-http
      hostnames:
      - "store.example.internal"
      rules:
      # Sends all traffic to store-west-1 ServiceImport
      - backendRefs:
        - name: store-west-1
          group: net.gke.io
          kind: ServiceImport
          port: 8080
        # Also mirrors all traffic to store-west-2 ServiceImport
        filters:
        - type: RequestMirror
          requestMirror:
            backendRef:
              group: net.gke.io
              kind: ServiceImport
              name: store-west-2
              port: 8080
    EOF
    
  2. 비공개 클라이언트를 사용하여 요청을 internal-http 게이트웨이로 전송합니다. 이후 단계에서 애플리케이션 로그에서 이 요청을 고유하게 식별할 수 있도록 /mirror 경로를 사용합니다.

    curl -H "host: store.example.internal" http://VIP/mirror
    
  3. 출력을 통해 클라이언트가 gke-west-1 클러스터의 포드에서 응답을 수신했음이 확인됩니다.

    {
        "cluster_name": "gke-west-1", 
        "host_header": "store.example.internal",
        "node_name": "gke-gke-west-1-default-pool-65059399-ssfq.c.agmsb-k8s.internal",
        "pod_name": "store-5f5b954888-brg5w",
        "pod_name_emoji": "🎖",
        "project_id": "agmsb-k8s",
        "timestamp": "2021-05-31T01:24:51",
        "zone": "us-west1-a"
    }
    

    즉, 기본 클러스터가 트래픽에 응답하고 있습니다. 마이그레이션 중인 클러스터가 미러링된 트래픽을 수신하는지 확인해야 합니다.

  4. gke-west-2 클러스터에서 store 포드의 애플리케이션 로그를 확인합니다. 이 로그로 포드가 부하 분산기에서 미러링된 트래픽을 수신했는지 확인됩니다.

    kubectl logs deployment/store --context gke-west-2 -n store | grep /mirror
    
  5. 이 출력은 gke-west-2 클러스터의 포드가 동일한 요청을 수신하는지 확인합니다. 하지만 이러한 요청에 대한 응답은 클라이언트로 다시 전송되지 않습니다. 로그에 표시된 IP 주소는 해당 포드와 통신하는 부하 분산기의 내부 IP 주소입니다.

    Found 2 pods, using pod/store-5c65bdf74f-vpqbs
    [2023-10-12 21:05:20,805] INFO in _internal: 192.168.21.3 - - [12/Oct/2023 21:05:20] "GET /mirror HTTP/1.1" 200 -
    [2023-10-12 21:05:27,158] INFO in _internal: 192.168.21.3 - - [12/Oct/2023 21:05:27] "GET /mirror HTTP/1.1" 200 -
    [2023-10-12 21:05:27,805] INFO in _internal: 192.168.21.3 - - [12/Oct/2023 21:05:27] "GET /mirror HTTP/1.1" 200 -
    

트래픽 분할

트래픽 분할은 새 코드를 출시하거나 새 환경에 안전하게 배포하기 위한 가장 일반적인 방법 중 하나입니다. 서비스 소유자는 실제 사용자 요청에 대해 수락되는 위험 수준에 따라 출시 성공을 확인할 수 있도록 일반적으로 전체 트래픽의 극소량에 해당하는 카나리아 백엔드로 전송되는 명시적인 트래픽 백분율을 설정합니다.

트래픽의 소량에 트래픽 분할을 수행하면 서비스 소유자가 애플리케이션 및 응답 상태를 조사할 수 있습니다. 모든 신호가 정상 상태로 보이면 전체 컷오버로 진행할 수 있습니다.

  1. 다음 HTTPRoute 매니페스트를 구성 클러스터(이 예시에서는 gke-west-1)에 적용합니다.

    cat << EOF | kubectl apply --context gke-west-1 -f -
    kind: HTTPRoute
    apiVersion: gateway.networking.k8s.io/v1beta1
    metadata:
      name: internal-store-route
      namespace: store
      labels:
        gateway: internal-http
    spec:
      parentRefs:
      - kind: Gateway
        namespace: store
        name: internal-http
      hostnames:
      - "store.example.internal"
      rules:
      - backendRefs:
        # 90% of traffic to store-west-1 ServiceImport
        - name: store-west-1
          group: net.gke.io
          kind: ServiceImport
          port: 8080
          weight: 90
        # 10% of traffic to store-west-2 ServiceImport
        - name: store-west-2
          group: net.gke.io
          kind: ServiceImport
          port: 8080
          weight: 10
    EOF
    
  2. 비공개 클라이언트를 사용하여 지속적인 curl 요청을 internal- http 게이트웨이로 전송합니다.

    while true; do curl -H "host: store.example.internal" -s VIP | grep "cluster_name"; sleep 1; done
    

    출력은 이와 비슷하며, 90/10 트래픽 분할이 수행됨을 나타냅니다.

    "cluster_name": "gke-west-1",
    "cluster_name": "gke-west-1",
    "cluster_name": "gke-west-1",
    "cluster_name": "gke-west-1",
    "cluster_name": "gke-west-1",
    "cluster_name": "gke-west-1",
    "cluster_name": "gke-west-1",
    "cluster_name": "gke-west-1",
    "cluster_name": "gke-west-2",
    "cluster_name": "gke-west-1",
    "cluster_name": "gke-west-1",
    ...
    

트래픽 컷오버

블루-그린 마이그레이션의 마지막 단계는 새 클러스터로 완전히 컷오버하고 이전 클러스터를 삭제하는 것입니다. 서비스 소유자가 실제로 두 번째 클러스터를 기존 클러스터로 온보딩하고 있었던 경우에는 최종 단계로 트래픽이 두 클러스터 모두로 전송되므로, 이 마지막 단계가 달라집니다. 이 시나리오에서는 gke-west-1gke-west-2 클러스터 모두의 포드가 포함된 단일 store ServiceImport가 권장됩니다. 이렇게 하면 부하 분산기가 근접성, 상태, 용량을 기준으로 활성-활성 애플리케이션에 대해 트래픽이 전송될 위치를 결정할 수 있습니다.

  1. 다음 HTTPRoute 매니페스트를 구성 클러스터(이 예시에서는 gke-west-1)에 적용합니다.

    cat << EOF | kubectl apply --context gke-west-1 -f -
    kind: HTTPRoute
    apiVersion: gateway.networking.k8s.io/v1beta1
    metadata:
      name: internal-store-route
      namespace: store
      labels:
        gateway: internal-http
    spec:
      parentRefs:
      - kind: Gateway
        namespace: store
        name: internal-http
      hostnames:
      - "store.example.internal"
      rules:
        - backendRefs:
          # No traffic to the store-west-1 ServiceImport
          - name: store-west-1
            group: net.gke.io
            kind: ServiceImport
            port: 8080
            weight: 0
          # All traffic to the store-west-2 ServiceImport
          - name: store-west-2
            group: net.gke.io
            kind: ServiceImport
            port: 8080
            weight: 100
    EOF
    
  2. 비공개 클라이언트를 사용하여 지속적인 curl 요청을 internal- http 게이트웨이로 전송합니다.

    while true; do curl -H "host: store.example.internal" -s VIP | grep "cluster_name"; sleep 1; done
    

    출력은 다음과 비슷합니다. 이제 모든 트래픽이 gke-west-2로 전송됩니다.

    "cluster_name": "gke-west-2",
    "cluster_name": "gke-west-2",
    "cluster_name": "gke-west-2",
    "cluster_name": "gke-west-2",
    ...
    

이 마지막 단계에서는 하나의 GKE 클러스터에서 다른 GKE 클러스터로 전체 블루-그린 애플리케이션 마이그레이션을 완료합니다.

용량 기반 부하 분산 배포

이 섹션의 연습에서는 서로 다른 리전에서 2개의 GKE 클러스터 간에 애플리케이션을 배포하여 전역 부하 분산 및 서비스 용량 개념을 설명합니다. 클러스터 및 리전 간에 트래픽이 부하 분산되는 방식을 보여주기 위해 여러 초당 요청 수(RPS) 수준으로 생성된 트래픽이 전송됩니다.

다음 다이어그램은 배포할 토폴로지와 트래픽이 서비스 용량을 초과할 때 클러스터 및 리전 간에 트래픽이 오버플로되는 방식을 보여줍니다.

한 클러스터에서 다른 클러스터로 오버플로되는 트래픽

트래픽 관리에 대해 자세히 알아보려면 GKE 트래픽 관리를 참조하세요.

개발 환경 준비

  1. 멀티 클러스터 게이트웨이 사용 설정에 따라 환경을 준비합니다.

  2. GatewayClass 리소스가 구성 클러스터에 설치되었는지 확인하세요.

    kubectl get gatewayclasses --context=gke-west-1
    

    출력은 다음과 비슷합니다.

    NAME                                  CONTROLLER                  ACCEPTED   AGE
    gke-l7-global-external-managed        networking.gke.io/gateway   True       16h
    gke-l7-global-external-managed-mc     networking.gke.io/gateway   True       14h
    gke-l7-gxlb                           networking.gke.io/gateway   True       16h
    gke-l7-gxlb-mc                        networking.gke.io/gateway   True       14h
    gke-l7-regional-external-managed      networking.gke.io/gateway   True       16h
    gke-l7-regional-external-managed-mc   networking.gke.io/gateway   True       14h
    gke-l7-rilb                           networking.gke.io/gateway   True       16h
    gke-l7-rilb-mc                        networking.gke.io/gateway   True       14h
    

애플리케이션 배포

두 클러스터 모두에 샘플 웹 애플리케이션 서버를 배포합니다.

kubectl apply --context gke-west-1 -f https://raw.githubusercontent.com/GoogleCloudPlatform/gke-networking-recipes/master/gateway/docs/store-traffic-deploy.yaml
kubectl apply --context gke-east-1 -f https://raw.githubusercontent.com/GoogleCloudPlatform/gke-networking-recipes/master/gateway/docs/store-traffic-deploy.yaml

출력은 다음과 비슷합니다.

namespace/store created
deployment.apps/store created

서비스, 게이트웨이, HTTPRoute 배포

  1. 다음 Service 매니페스트를 gke-west-1gke-east-1 클러스터 모두에 적용합니다.

    cat << EOF | kubectl apply --context gke-west-1 -f -
    apiVersion: v1
    kind: Service
    metadata:
      name: store
      namespace: traffic-test
      annotations:
        networking.gke.io/max-rate-per-endpoint: "10"
    spec:
      ports:
      - port: 8080
        targetPort: 8080
        name: http
      selector:
        app: store
      type: ClusterIP
    ---
    kind: ServiceExport
    apiVersion: net.gke.io/v1
    metadata:
      name: store
      namespace: traffic-test
    EOF
    
    cat << EOF | kubectl apply --context gke-east-1 -f -
    apiVersion: v1
    kind: Service
    metadata:
      name: store
      namespace: traffic-test
      annotations:
        networking.gke.io/max-rate-per-endpoint: "10"
    spec:
      ports:
      - port: 8080
        targetPort: 8080
        name: http
      selector:
        app: store
      type: ClusterIP
    ---
    kind: ServiceExport
    apiVersion: net.gke.io/v1
    metadata:
      name: store
      namespace: traffic-test
    EOF
    

    서비스에 초당 요청 10개로 설정된 max-rate-per-endpoint 주석이 추가됩니다. 복제본이 클러스터당 2개 있는 각 서비스의 용량은 클러스터당 20RPS입니다.

    서비스의 서비스 용량 수준을 선택하는 방법은 서비스 용량 결정을 참조하세요.

  2. 다음 Gateway 매니페스트를 구성 클러스터(이 예시에서는 gke-west-1)에 적용합니다.

    cat << EOF | kubectl apply --context gke-west-1 -f -
    kind: Gateway
    apiVersion: gateway.networking.k8s.io/v1beta1
    metadata:
      name: store
      namespace: traffic-test
    spec:
      gatewayClassName: gke-l7-global-external-managed-mc
      listeners:
      - name: http
        protocol: HTTP
        port: 80
        allowedRoutes:
          kinds:
          - kind: HTTPRoute
    EOF
    

    이 매니페스트는 공개적으로 액세스 가능한 IP 주소를 사용해서 외부 애플리케이션 부하 분산기를 배포하는 외부의 전역 멀티 클러스터 게이트웨이를 기술합니다.

  3. 다음 HTTPRoute 매니페스트를 구성 클러스터(이 예시에서는 gke-west-1)에 적용합니다.

    cat << EOF | kubectl apply --context gke-west-1 -f -
    kind: HTTPRoute
    apiVersion: gateway.networking.k8s.io/v1beta1
    metadata:
      name: store
      namespace: traffic-test
      labels:
        gateway: store
    spec:
      parentRefs:
      - kind: Gateway
        namespace: traffic-test
        name: store
      rules:
      - backendRefs:
        - name: store
          group: net.gke.io
          kind: ServiceImport
          port: 8080
    EOF
    

    이 매니페스트는 모든 트래픽을 저장소 ServiceImport로 전달하는 라우팅 규칙을 사용해서 게이트웨이를 구성하는 HTTPRoute를 기술합니다. store ServiceImport는 두 클러스터 간에 store 서비스 포드를 그룹으로 묶고 부하 분산기에서 단일 서비스로 처리되도록 허용합니다.

    몇 분 후 게이트웨이 이벤트를 검사하여 배포가 완료되었는지 확인할 수 있습니다.

    kubectl describe gateway store -n traffic-test --context gke-west-1
    

    출력은 다음과 비슷합니다.

    ...
    Status:
      Addresses:
        Type:   IPAddress
        Value:  34.102.159.147
      Conditions:
        Last Transition Time:  2023-10-12T21:40:59Z
        Message:               The OSS Gateway API has deprecated this condition, do not depend on it.
        Observed Generation:   1
        Reason:                Scheduled
        Status:                True
        Type:                  Scheduled
        Last Transition Time:  2023-10-12T21:40:59Z
        Message:               
        Observed Generation:   1
        Reason:                Accepted
        Status:                True
        Type:                  Accepted
        Last Transition Time:  2023-10-12T21:40:59Z
        Message:               
        Observed Generation:   1
        Reason:                Programmed
        Status:                True
        Type:                  Programmed
        Last Transition Time:  2023-10-12T21:40:59Z
        Message:               The OSS Gateway API has altered the "Ready" condition semantics and reservedit for future use.  GKE Gateway will stop emitting it in a future update, use "Programmed" instead.
        Observed Generation:   1
        Reason:                Ready
        Status:                True
        Type:                  Ready
      Listeners:
        Attached Routes:  1
        Conditions:
          Last Transition Time:  2023-10-12T21:40:59Z
          Message:               
          Observed Generation:   1
          Reason:                Programmed
          Status:                True
          Type:                  Programmed
          Last Transition Time:  2023-10-12T21:40:59Z
          Message:               The OSS Gateway API has altered the "Ready" condition semantics and reservedit for future use.  GKE Gateway will stop emitting it in a future update, use "Programmed" instead.
          Observed Generation:   1
          Reason:                Ready
          Status:                True
          Type:                  Ready
        Name:                    http
        Supported Kinds:
          Group:  gateway.networking.k8s.io
          Kind:   HTTPRoute
    Events:
      Type    Reason  Age                  From                   Message
      ----    ------  ----                 ----                   -------
      Normal  ADD     12m                  mc-gateway-controller  traffic-test/store
      Normal  SYNC    6m43s                mc-gateway-controller  traffic-test/store
      Normal  UPDATE  5m40s (x4 over 12m)  mc-gateway-controller  traffic-test/store
      Normal  SYNC    118s (x6 over 10m)   mc-gateway-controller  SYNC on traffic-test/store was a success
    

    이 출력은 게이트웨이가 성공적으로 배포되었음을 보여줍니다. 게이트웨이가 배포된 후 트래픽 전달이 시작되려면 여전히 몇 분 정도 기다려야 할 수 있습니다. 다음 단계에서 사용되므로, 이 출력에서 IP 주소를 기록해 둡니다.

트래픽 확인

curl 명령어를 사용해서 게이트웨이 IP 주소를 테스트하여 트래픽이 애플리케이션에 전달되는지 확인합니다.

curl GATEWAY_IP_ADDRESS

출력은 다음과 비슷합니다.

{
  "cluster_name": "gke-west-1",
  "host_header": "34.117.182.69",
  "pod_name": "store-54785664b5-mxstv",
  "pod_name_emoji": "👳🏿",
  "project_id": "project",
  "timestamp": "2021-11-01T14:06:38",
  "zone": "us-west1-a"
}

이 출력은 요청이 처리된 리전을 나타내는 포드 메타데이터를 보여줍니다.

부하 테스트를 사용하여 트래픽 확인

부하 분산기가 작동하는지 확인하려면 gke-west-1 클러스터에서 트래픽 생성기를 배포할 수 있습니다. 트래픽 생성기는 부하 분산기의 용량 및 오버플로 성능을 표시하기 위해 여러 부하 수준으로 트래픽을 생성합니다. 다음 단계에서는 세 가지 부하 수준을 보여줍니다.

  • 10 RPS는 gke-west-1에서 저장소 서비스 용량 아래에 있습니다.
  • 30 RPS는 gke-west-1 저장소 서비스 용량을 초과하고 트래픽이 gke-east-1로 오버플로되도록 합니다.
  • 60 RPS는 두 클러스터 모두 서비스의 용량을 초과합니다.

대시보드 구성

  1. 게이트웨이에 대해 기본 URLmap 이름을 가져옵니다.

    kubectl get gateway store -n traffic-test --context=gke-west-1 -o=jsonpath="{.metadata.annotations.networking\.gke\.io/url-maps}"
    

    출력은 다음과 비슷합니다.

    /projects/PROJECT_NUMBER/global/urlMaps/gkemcg1-traffic-test-store-armvfyupay1t
    
  2. Google Cloud 콘솔에서 측정항목 탐색기 페이지로 이동합니다.

    측정항목 탐색기로 이동

  3. 측정항목 선택에서 코드: MQL을 클릭합니다.

  4. 두 클러스터 간에 저장소 서비스의 트래픽 측정항목을 관측하기 위해 다음 쿼리를 입력합니다.

    fetch https_lb_rule
    | metric 'loadbalancing.googleapis.com/https/backend_request_count'
    | filter (resource.url_map_name == 'GATEWAY_URL_MAP')
    | align rate(1m)
    | every 1m
    | group_by [resource.backend_scope],
        [value_backend_request_count_aggregate:
            aggregate(value.backend_request_count)]
    

    GATEWAY_URL_MAP을 이전 단계의 URLmap 이름으로 바꿉니다.

  5. 쿼리 실행을 클릭합니다. 측정항목이 차트에 표시되도록 다음 섹션에서 로드 생성기를 배포한 후 최소 5분 이상 기다립니다.

10 RPS 테스트

  1. 포드를 gke-west-1 클러스터에 배포합니다.

    kubectl run --context gke-west-1 -i --tty --rm loadgen  \
        --image=cyrilbkr/httperf  \
        --restart=Never  \
        -- /bin/sh -c 'httperf  \
        --server=GATEWAY_IP_ADDRESS  \
        --hog --uri="/zone" --port 80  --wsess=100000,1,1 --rate 10'
    

    GATEWAY_IP_ADDRESS를 이전 단계의 게이트웨이 IP 주소로 바꿉니다.

    출력은 다음과 비슷하며, 트래픽 생성기가 트래픽을 전송하고 있음을 나타냅니다.

    If you don't see a command prompt, try pressing enter.
    

    부하 생성기는 게이트웨이로 계속 10 RPS를 전송합니다. 트래픽이 Google Cloud 리전 내부에서 시작되지만 부하 분산기는 이것을 미국 서부 해안에서 시작되는 클라이언트 트래픽으로 취급합니다. 실제와 같은 클라이언트 다양성을 시뮬레이션하기 위해 부하 분산기는 각 HTTP 요청을 새로운 TCP 연결로 전송합니다. 즉, 트래픽이 백엔드 포드 간에 보다 고르게 분산됩니다.

    생성기가 대시보드에 대해 트래픽을 생성하려면 최대 5분까지 걸립니다.

  2. 측정항목 탐색기 대시보드를 확인합니다. 각 클러스터에 대해 트래픽이 부하 분산된 정도를 나타내는 2개 줄이 표시됩니다.

    클러스터에 부하 분산되는 트래픽을 보여주는 그래프

    us-east1-b가 트래픽을 수신하지 않는 동안 us-west1-a에서 약 10 RPS 트래픽을 수신하는 것이 확인됩니다. 트래픽 생성기가 us-west1에서 실행되므로, 모든 트래픽이 gke-west-1 클러스터의 서비스로 전송됩니다.

  3. Ctrl+C를 사용해서 부하 생성기를 중지하고 포드를 삭제합니다.

    kubectl delete pod loadgen --context gke-west-1
    

30 RPS 테스트

  1. 30 RPS를 전송하도록 구성해서 부하 생성기를 다시 배포합니다.

    kubectl run --context gke-west-1 -i --tty --rm loadgen  \
        --image=cyrilbkr/httperf  \
        --restart=Never  \
        -- /bin/sh -c 'httperf  \
        --server=GATEWAY_IP_ADDRESS  \
        --hog --uri="/zone" --port 80  --wsess=100000,1,1 --rate 30'
    

    생성기가 대시보드에 대해 트래픽을 생성하려면 최대 5분까지 걸립니다.

  2. Cloud 작업 대시보드를 확인합니다.

    gke-east-1로 오버플로되는 트래픽을 보여주는 그래프

    약 20 RPS가 us-west1-a로 전송되고 10 RPS가 us-east1-b로 전송되는 것으로 확인됩니다. 이것은 gke-west-1의 서비스가 완전히 사용되고 있고 트래픽의 10 RPS를 gke-east-1의 서비스로 오버플로하고 있음을 나타냅니다.

  3. Ctrl+C를 사용해서 부하 생성기를 중지하고 포드를 삭제합니다.

    kubectl delete pod loadgen --context gke-west-1
    

60 RPS 테스트

  1. 60 RPS를 전송하도록 구성된 부하 생성기를 배포합니다.

    kubectl run --context gke-west-1 -i --tty --rm loadgen  \
        --image=cyrilbkr/httperf  \
        --restart=Never  \
        -- /bin/sh -c 'httperf  \
        --server=GATEWAY_IP_ADDRESS  \
        --hog --uri="/zone" --port 80  --wsess=100000,1,1 --rate 60'
    
  2. 5분 정도 기다리고 Cloud 작업 대시보드를 확인합니다. 이제 두 클러스터 모두 약 30 RPS를 수신하는 것으로 표시됩니다. 모든 서비스가 전반적으로 초과 사용되고 있기 때문에 트래픽 스필오버가 수행되지 않으며, 서비스가 가능한 모든 트래픽을 흡수합니다.

    초과 사용되는 서비스를 보여주는 그래프

  3. Ctrl+C를 사용해서 부하 생성기를 중지하고 포드를 삭제합니다.

    kubectl delete pod loadgen --context gke-west-1
    

삭제

이 페이지의 연습을 완료한 후에는 다음 단계에 따라 자신의 계정에 원치 않는 비용이 발생하지 않도록 리소스를 삭제합니다.

  1. 클러스터를 삭제합니다.

  2. 다른 목적으로 등록할 필요가 없으면 Fleet에서 클러스터를 등록 취소합니다.

  3. multiclusterservicediscovery 기능을 사용 중지합니다.

    gcloud container fleet multi-cluster-services disable
    
  4. 멀티 클러스터 인그레스 사용 중지

    gcloud container fleet ingress disable
    
  5. API를 사용 중지합니다.

    gcloud services disable \
        multiclusterservicediscovery.googleapis.com \
        multiclusteringress.googleapis.com \
        trafficdirector.googleapis.com \
        --project=PROJECT_ID
    

공유 VPC와 함께 멀티 클러스터 게이트웨이 사용

사용 사례에 따라 다른 토폴로지를 사용해서 멀티 클러스터 게이트웨이를 공유 VPC 환경에 배포할 수도 있습니다.

다음 표에서는 공유 VPC 환경 내에서 지원되는 멀티 클러스터 게이트웨이 토폴로지를 설명합니다.

시나리오 Fleet 호스트 프로젝트 구성 클러스터 워크로드 클러스터
1 공유 VPC 호스트 프로젝트 공유 VPC 호스트 프로젝트 공유 VPC 호스트 프로젝트
2 공유 VPC 서비스 프로젝트 공유 VPC 서비스 프로젝트
(Fleet 서비스 프로젝트와 동일)
공유 VPC 서비스 프로젝트
(Fleet 서비스 프로젝트와 동일)

공유 VPC 환경에서 멀티 클러스터 게이트웨이를 만들려면 다음 단계를 수행합니다.

  1. 공유 VPC를 사용하여 멀티 클러스터 서비스를 설정하는 단계를 따릅니다.

  2. 서비스를 만들어 구성 클러스터로 내보냅니다.

  3. 멀티 클러스터 내부 게이트웨이를 사용하려는 경우 프록시 전용 서브넷을 만듭니다.

  4. 멀티 클러스터 외부 또는 내부 게이트웨이 및 HTTPRoute를 만듭니다.

이 단계를 완료하면 토폴로지에 따라 배포를 검증할 수 있습니다.

문제 해결

내부 게이트웨이의 프록시 전용 서브넷이 없음

내부 게이트웨이에 다음 이벤트가 표시되면 프록시 전용 서브넷이 해당 리전에 없습니다. 이 문제를 해결하려면 프록시 전용 서브넷을 배포합니다.

generic::invalid_argument: error ensuring load balancer: Insert: Invalid value for field 'resource.target': 'regions/us-west1/targetHttpProxies/gkegw-x5vt-default-internal-http-2jzr7e3xclhj'. A reserved and active subnetwork is required in the same region and VPC as the forwarding rule.

정상 업스트림 없음

증상:

게이트웨이를 만들 때 백엔드 서비스에 액세스할 수 없는 경우 다음 문제가 발생할 수 있습니다(503 응답 코드).

no healthy upstream

이유:

이 오류 메시지는 상태 점검 프로버가 정상 백엔드 서비스를 찾을 수 없음을 나타냅니다. 백엔드 서비스가 정상일 수 있지만 상태 점검을 맞춤설정해야 할 수도 있습니다.

해결 방법:

이 문제를 해결하려면 HealthCheckPolicy를 사용하여 애플리케이션 요구사항(예: /health)에 따라 상태 점검을 맞춤설정합니다.

다음 단계