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

이 문서에서는 VPC 네트워크 내의 트래픽을 두 개의 서로 다른 GKE 클러스터에서 실행되는 애플리케이션으로 라우팅하는 내부 멀티 클러스터 게이트웨이를 배포하는 실제 예를 안내합니다.

멀티 클러스터 게이트웨이는 여러 GKE 클러스터에 배포된 서비스의 트래픽을 관리하는 강력한 방법을 제공합니다. Google의 글로벌 부하 분산 인프라를 사용하면 애플리케이션의 단일 진입점을 만들어 관리를 간소화하고 안정성을 개선할 수 있습니다.

이 튜토리얼에서는 샘플 store 애플리케이션을 사용하여 온라인 쇼핑 서비스가 개별 팀에 의해 소유 및 운영되고 공유되는 GKE 클러스터의 Fleet 간에 배포되는 실제 시나리오를 시뮬레이션합니다.

이 예에서는 트래픽을 서로 다른 클러스터로 전달하도록 경로 기반 라우팅을 설정하는 방법을 보여줍니다.

시작하기 전에

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

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

  2. 아직 등록하지 않은 경우 클러스터를 Fleet에 등록합니다.

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

마지막으로 환경에서 컨트롤러를 사용하기 전에 GKE Gateway Controller 제한사항 및 알려진 문제를 검토하세요.

리전 간 내부 멀티 클러스터 게이트웨이 배포

여러 리전의 GKE 클러스터에서 내부 레이어 7 부하 분산을 제공하는 멀티 클러스터 게이트웨이를 배포할 수 있습니다. 이러한 게이트웨이는 gke-l7-cross-regional-internal-managed-mc GatewayClass를 사용합니다. 이 GatewayClass는 Google Cloud 에서 관리하고 VPC 네트워크 내 클라이언트가 액세스할 수 있는 내부 VIP를 사용하는 리전 간 내부 애플리케이션 부하 분산기를 프로비저닝합니다. 이러한 게이트웨이는 해당 리전의 주소를 요청하는 게이트웨이를 사용하여 원하는 지역의 프런트엔드에서 노출할 수 있습니다. 내부 VIP는 단일 IP 주소이거나 게이트웨이에 지정된 리전당 하나의 IP 주소가 있는 여러 리전의 IP 주소일 수 있습니다. 트래픽은 요청을 처리할 수 있는 가장 가까운 정상 백엔드 GKE 클러스터로 전달됩니다.

기본 요건

  1. 프로젝트 ID로 gcloud 환경을 구성하여 프로젝트와 셸을 설정합니다.

    export PROJECT_ID="YOUR_PROJECT_ID"
    gcloud config set project ${PROJECT_ID}
    
  2. 서로 다른 리전에 GKE 클러스터를 만듭니다.

    이 예시에서는 두 개의 클러스터, 즉 us-west1gke-west-1us-east1gke-east-1을 사용합니다. 게이트웨이 API가 사용 설정(--gateway-api=standard)되고 클러스터가 Fleet에 등록되어 있는지 확인합니다.

    gcloud container clusters create gke-west-1 \
        --location=us-west1-a \
        --workload-pool=${PROJECT_ID}.svc.id.goog \
        --project=${PROJECT_ID} \
        --enable-fleet \
        --gateway-api=standard
    
    gcloud container clusters create gke-east-1 \
        --location=us-east1-c \
        --workload-pool=${PROJECT_ID}.svc.id.goog \
        --project=${PROJECT_ID} \
        --enable-fleet \
        --gateway-api=standard
    

    쉽게 액세스할 수 있도록 컨텍스트 이름을 바꿉니다.

    gcloud container clusters get-credentials gke-west-1 \
      --location=us-west1-a \
      --project=${PROJECT_ID}
    
    gcloud container clusters get-credentials gke-east-1 \
      --location=us-east1-c \
      --project=${PROJECT_ID}
    kubectl config rename-context gke_${PROJECT_ID}_us-west1-a_gke-west-1 gke-west1
    kubectl config rename-context gke_${PROJECT_ID}_us-east1-c_gke-east-1 gke-east1
    
  3. 멀티 클러스터 서비스(MCS) 및 멀티 클러스터 인그레스(MCI/게이트웨이)를 사용 설정합니다.

    gcloud container fleet multi-cluster-services enable --project=${PROJECT_ID}
    
    # Set the config membership to one of your clusters (e.g., gke-west-1)
    # This cluster will be the source of truth for multi-cluster Gateway and Route resources.
    gcloud container fleet ingress enable \
        --config-membership=projects/${PROJECT_ID}/locations/us-west1/memberships/gke-west-1 \
        --project=${PROJECT_ID}
    
  4. 프록시 전용 서브넷을 구성합니다. GKE 클러스터가 있고 부하 분산기가 작동할 각 리전에 프록시 전용 서브넷이 필요합니다. 리전 간 내부 애플리케이션 부하 분산기를 사용하려면 이 서브넷 용도를 GLOBAL_MANAGED_PROXY로 설정해야 합니다.

    # Proxy-only subnet for us-west1
    gcloud compute networks subnets create us-west1-proxy-only-subnet \
        --purpose=GLOBAL_MANAGED_PROXY \
        --role=ACTIVE \
        --region=us-west1 \
        --network=default \
        --range=10.129.0.0/23 # Choose an appropriate unused CIDR range
    
    # Proxy-only subnet for us-east1
    gcloud compute networks subnets create us-east1-proxy-only-subnet \
        --purpose=GLOBAL_MANAGED_PROXY \
        --role=ACTIVE \
        --region=us-east1 \
        --network=default \
        --range=10.130.0.0/23 # Choose an appropriate unused CIDR range
    

    기본 네트워크를 사용하지 않는 경우 default를 VPC 네트워크 이름으로 바꿉니다. CIDR 범위가 고유하고 겹치지 않는지 확인합니다.

  5. store와 같은 데모 애플리케이션을 두 클러스터에 모두 배포합니다. gke-networking-recipesstore.yaml 파일 예시에서는 store 네임스페이스와 배포를 만듭니다.

    kubectl apply --context gke-west1 -f https://raw.githubusercontent.com/GoogleCloudPlatform/gke-networking-recipes/main/gateway/gke-gateway-controller/multi-cluster-gateway/store.yaml
    kubectl apply --context gke-east1 -f https://raw.githubusercontent.com/GoogleCloudPlatform/gke-networking-recipes/main/gateway/gke-gateway-controller/multi-cluster-gateway/store.yaml
    
  6. 각 클러스터에서 Kubernetes Service 리소스와 ServiceExport 리소스를 만들어 각 클러스터의 서비스를 내보내면 Fleet 전반에서 서비스를 검색할 수 있게 됩니다. 다음 예시에서는 각 클러스터에서 일반 store 서비스와 리전별 서비스(store-west-1, store-east-1)를 모두 store 네임스페이스 내에서 내보냅니다.

    gke-west1에 적용됩니다.

    cat << EOF | kubectl apply --context gke-west1 -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 # Specific to this cluster
      namespace: store
    spec:
      selector:
        app: store
      ports:
      - port: 8080
        targetPort: 8080
    ---
    kind: ServiceExport
    apiVersion: net.gke.io/v1
    metadata:
      name: store-west-1 # Exporting the region-specific service
      namespace: store
    EOF
    

    gke-east1에 적용됩니다.

    cat << EOF | kubectl apply --context gke-east1 -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 # Specific to this cluster
      namespace: store
    spec:
      selector:
        app: store
      ports:
      - port: 8080
        targetPort: 8080
    ---
    kind: ServiceExport
    apiVersion: net.gke.io/v1
    metadata:
      name: store-east-1 # Exporting the region-specific service
      namespace: store
    EOF
    
  7. ServiceImports 확인: ServiceImport 리소스가 store 네임스페이스 내의 각 클러스터에 생성되었는지 확인합니다. 생성하는 데 몇 분 정도 걸릴 수 있습니다. bash kubectl get serviceimports --context gke-west1 -n store kubectl get serviceimports --context gke-east1 -n store store, store-west-1, store-east-1이 나열되어야 합니다(또는 전파에 따라 관련 항목이 표시될 수 있음).

내부 멀티 리전 게이트웨이 구성

gke-l7-cross-regional-internal-managed-mc GatewayClass를 참조하는 Gateway 리소스를 정의합니다. 이 매니페스트를 지정된 구성 클러스터(예: gke-west-1)에 적용합니다.

spec.addresses 필드를 사용하면 특정 리전에서 임시 IP 주소를 요청하거나 사전 할당된 고정 IP 주소를 사용할 수 있습니다.

  1. 임시 IP 주소를 사용하려면 다음 Gateway 매니페스트를 cross-regional-gateway.yaml로 저장합니다.

    # cross-regional-gateway.yaml
    kind: Gateway
    apiVersion: gateway.networking.k8s.io/v1
    metadata:
      name: internal-cross-region-gateway
      namespace: store # Namespace for the Gateway resource
    spec:
      gatewayClassName: gke-l7-cross-regional-internal-managed-mc
      addresses:
      # Addresses across regions. Address value is allowed to be empty or matching
      # the region name.
      - type: networking.gke.io/ephemeral-ipv4-address/us-west1
        value: "us-west1"
      - type: networking.gke.io/ephemeral-ipv4-address/us-east1
        value: "us-east1"
      listeners:
      - name: http
        protocol: HTTP
        port: 80
        allowedRoutes:
          kinds:
          - kind: HTTPRoute # Only allow HTTPRoute to attach
    

    다음 목록에서는 이전 YAML 파일의 일부 필드를 정의합니다.

    • metadata.namespace: 게이트웨이 리소스가 생성된 네임스페이스(예: store)
    • spec.gatewayClassName: GatewayClass의 이름. gke-l7-cross-regional-internal-managed-mc여야 합니다.
    • spec.listeners.allowedRoutes.kinds: 연결할 수 있는 경로 객체의 종류입니다(예: HTTPRoute).
    • spec.addresses:
      • type: networking.gke.io/ephemeral-ipv4-address/REGION: 임시 IP 주소를 요청합니다.
      • value: 주소의 리전을 지정합니다(예: "us-west1" 또는 "us-east1").
  2. 매니페스트를 구성 클러스터(예: gke-west1)에 적용합니다.

    kubectl apply --context gke-west1 -f cross-regional-gateway.yaml
    

게이트웨이에 HTTPRoute 연결

트래픽 라우팅을 관리하는 HTTPRoute 리소스를 정의하고 이를 구성 클러스터에 적용합니다.

  1. 다음 HTTPRoute 매니페스트를 store-route.yaml로 저장합니다.

    # store-route.yaml
    kind: HTTPRoute
    apiVersion: gateway.networking.k8s.io/v1
    metadata:
      name: store-route
      namespace: store
      labels:
        gateway: cross-regional-internal
    spec:
      parentRefs:
      - name: internal-cross-region-gateway
        namespace: store # Namespace where the Gateway is deployed
      hostnames:
      - "store.example.internal" # Hostname clients will use
      rules:
      - matches: # Rule for traffic to /west
        - path:
            type: PathPrefix
            value: /west
        backendRefs:
        - group: net.gke.io # Indicates a multi-cluster ServiceImport
          kind: ServiceImport
          name: store-west-1 # Targets the ServiceImport for the west cluster
          port: 8080
      - matches: # Rule for traffic to /east
        - path:
            type: PathPrefix
            value: /east
        backendRefs:
        - group: net.gke.io
          kind: ServiceImport
          name: store-east-1 # Targets the ServiceImport for the east cluster
          port: 8080
      - backendRefs: # Default rule for other paths (e.g., /)
        - group: net.gke.io
          kind: ServiceImport
          name: store # Targets the generic 'store' ServiceImport (any region)
          port: 8080
    

    다음 목록에서는 이전 YAML 파일의 일부 필드를 정의합니다.

    • spec.parentRefs: 이 경로를 store 네임스페이스의 internal-cross-region-gateway에 연결합니다.
    • spec.hostnames: 클라이언트가 서비스에 액세스하는 데 사용하는 호스트 이름을 나타냅니다.
    • spec.rules: 라우팅 로직을 정의합니다. 이 예시에서는 경로 기반 라우팅을 사용합니다.
      • /west 트래픽은 store-west-1 ServiceImport로 이동합니다.
      • /east 트래픽은 store-east-1 ServiceImport로 이동합니다.
      • /와 같은 다른 모든 트래픽은 일반 store ServiceImport로 이동합니다.
    • backendRefs:
      • group: net.gke.iokind: ServiceImport는 멀티 클러스터 서비스를 타겟팅합니다.
  2. HTTPRoute 매니페스트를 구성 클러스터에 적용합니다.

    kubectl apply --context gke-west1 -f store-route.yaml
    

게이트웨이 및 경로 상태 확인

  1. 게이트웨이 상태를 확인합니다.

    kubectl get gateway internal-cross-region-gateway -n store -o yaml --context gke-west1
    

    type:Programmedandstatus: "True". You should see IP addresses assigned in thestatus.addressesfield, corresponding to the regions you specified (e.g., one forus-west1and one forus-east1`) 조건이 있는지 확인합니다.

  2. HTTPRoute 상태를 확인합니다.

    kubectl get httproute store-route -n store -o yaml --context gke-west1
    

    type: Accepted(또는 ResolvedRefs) 및 status: "True"status.parents[].conditions의 조건을 찾습니다.

트래픽 확인

게이트웨이에 IP 주소를 할당한 후 VPC 네트워크 내에 있고 리전 중 하나에 있거나 게이트웨이 IP 주소에 연결할 수 있는 리전에 있는 클라이언트 VM에서 트래픽을 테스트할 수 있습니다.

  1. 게이트웨이 IP 주소를 가져옵니다.

    다음 명령어는 JSON 출력을 파싱하려고 시도합니다. 정확한 구조에 따라 jsonpath를 조정해야 할 수도 있습니다.

    kubectl get gateway cross-region-gateway -n store --context gke-west1 -o=jsonpath="{.status.addresses[*].value}".
    

    이 명령어의 출력에는 VIP1_WEST 또는 VIP2_EAST와 같은 VIP가 포함되어야 합니다.

  2. 테스트 요청을 보냅니다. VPC의 클라이언트 VM에서 다음을 실행합니다.

    # Assuming VIP_WEST is an IP in us-west1 and VIP_EAST is an IP in us-east1
    # Traffic to /west should ideally be served by gke-west-1
    curl -H "host: store.example.internal" http://VIP_WEST/west
    curl -H "host: store.example.internal" http://VIP_EAST/west # Still targets store-west-1 due to path
    
    # Traffic to /east should ideally be served by gke-east-1
    curl -H "host: store.example.internal" http://VIP_WEST/east # Still targets store-east-1 due to path
    curl -H "host: store.example.internal" http://VIP_EAST/east
    
    # Traffic to / (default) could be served by either cluster
    curl -H "host: store.example.internal" http://VIP_WEST/
    curl -H "host: store.example.internal" http://VIP_EAST/
    

    응답에는 cluster_name 또는 zone과 같은 요청을 처리한 백엔드 포드를 나타내는 store 애플리케이션의 세부정보가 포함되어야 합니다.

고정 IP 주소 사용

임시 IP 주소 대신 사전 할당된 고정 내부 IP 주소를 사용할 수 있습니다.

  1. 사용하려는 리전에 고정 IP 주소를 만듭니다.

    gcloud compute addresses create cross-region-gw-ip-west --region us-west1 --subnet default --project=${PROJECT_ID}
    gcloud compute addresses create cross-region-gw-ip-east --region us-east1 --subnet default --project=${PROJECT_ID}
    

    기본 서브넷을 사용하지 않는 경우 default를 할당하려는 IP 주소가 있는 서브넷의 이름으로 바꿉니다. 이러한 서브넷은 프록시 전용 서브넷이 아닌 일반 서브넷입니다.

  2. cross-regional-gateway.yaml 파일에서 spec.addresses 섹션을 수정하여 게이트웨이 매니페스트를 업데이트합니다.

    # cross-regional-gateway-static-ip.yaml
    kind: Gateway
    apiVersion: gateway.networking.k8s.io/v1
    metadata:
      name: internal-cross-region-gateway # Or a new name if deploying alongside
      namespace: store
    spec:
      gatewayClassName: gke-l7-cross-regional-internal-managed-mc
      addresses:
      - type: networking.gke.io/named-address-with-region # Use for named static IP
        value: "regions/us-west1/addresses/cross-region-gw-ip-west"
      - type: networking.gke.io/named-address-with-region
        value: "regions/us-east1/addresses/cross-region-gw-ip-east"
      listeners:
      - name: http
        protocol: HTTP
        port: 80
        allowedRoutes:
          kinds:
          - kind: HTTPRoute
    
  3. 업데이트된 게이트웨이 매니페스트를 적용합니다.

    kubectl apply --context gke-west1 -f cross-regional-gateway.yaml
    

기본값이 아닌 서브넷에 대한 특별 고려사항

기본값이 아닌 서브넷을 사용할 때는 다음 사항을 고려하세요.

  • 동일한 VPC 네트워크: 고정 IP 주소, 프록시 전용 서브넷, GKE 클러스터와 같은 모든 사용자 생성 리소스가 동일한 VPC 네트워크 내에 있어야 합니다.

  • 주소 서브넷: 게이트웨이의 고정 IP 주소를 만들면 지정된 리전의 일반 서브넷에서 할당됩니다.

  • 클러스터 서브넷 이름 지정: 각 리전에는 MCG 구성 클러스터가 있는 서브넷과 이름이 동일한 서브넷이 있어야 합니다.

    • 예를 들어 gke-west-1 구성 클러스터가 projects/YOUR_PROJECT/regions/us-west1/subnetworks/my-custom-subnet에 있는 경우 주소를 요청하는 리전에도 my-custom-subnet 서브넷이 있어야 합니다. us-east1us-centra1 리전의 주소를 요청하는 경우 이름이 my-custom-subnet이라는 서브넷도 해당 리전에 있어야 합니다.

삭제

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

  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
    

문제 해결

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

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

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)에 따라 상태 점검을 맞춤설정합니다.

다음 단계