에지에서 멀티 클러스터 메시로: GKE 게이트웨이와 Cloud Service Mesh를 통해 전 세계에 분산된 애플리케이션 배포

Last reviewed 2024-06-30 UTC

이 문서에서는 다음 태스크를 수행하는 방법을 보여줍니다.

이 배포 가이드는 플랫폼 관리자를 대상으로 합니다. 또한 Cloud Service Mesh를 실행하는 고급 실무자를 대상으로 합니다. 이 안내는 Istio on GKE에도 적용됩니다.

아키텍처

다음 다이어그램에서는 서비스 메시의 기본 인그레스 토폴로지인 단일 클러스터에서 인그레스 게이트웨이 프록시를 노출하는 외부 TCP/UDP 부하 분산기를 보여줍니다.

외부 부하 분산기는 외부 클라이언트를 인그레스 게이트웨이 프록시를 통해 메시로 라우팅합니다.

이 배포 가이드에서는 Google Kubernetes Engine(GKE) 게이트웨이 리소스를 사용합니다. 특히 멀티 클러스터 게이트웨이를 사용하여 두 리전에 분산되는 여러 Autopilot 클러스터 앞에 멀티 리전 부하 분산을 구성합니다.

클라이언트, 부하 분산기, 메시의 TLS 암호화

앞의 다이어그램에서는 클라우드 인그레스 및 메시 인그레스 시나리오를 통해 데이터가 흐르는 방식을 보여줍니다. 자세한 내용은 관련 참조 아키텍처 문서의 아키텍처 다이어그램 설명을 참조하세요.

목표

  • Google Cloud에서 GKE Autopilot 클러스터 쌍을 동일한 Fleet에 배포합니다.
  • Istio 기반 Cloud Service Mesh를 같은 Fleet에 배포합니다.
  • GKE 게이트웨이를 사용하여 공개 HTTPS 트래픽을 종료하도록 부하 분산기를 구성합니다.
  • 여러 클러스터와 리전에 배포된 Cloud Service Mesh에서 호스팅하는 애플리케이션으로 공개 HTTPS 트래픽을 전달합니다.
  • 두 Autopilot 클러스터 모두에 whereami 샘플 애플리케이션을 배포합니다.

비용 최적화

이 문서에서는 비용이 청구될 수 있는 다음과 같은 Google Cloud 구성요소를 사용합니다.

프로젝트 사용량을 기준으로 예상 비용을 산출하려면 가격 계산기를 사용하세요. Google Cloud를 처음 사용하는 사용자는 무료 체험판을 사용할 수 있습니다.

이 문서에 설명된 태스크를 완료했으면 만든 리소스를 삭제하여 청구가 계속되는 것을 방지할 수 있습니다. 자세한 내용은 삭제를 참조하세요.

시작하기 전에

  1. In the Google Cloud console, on the project selector page, select or create a Google Cloud project.

    Go to project selector

  2. Google Cloud 프로젝트에 결제가 사용 설정되어 있는지 확인합니다.

  3. Google Cloud 콘솔에서 Cloud Shell을 활성화합니다.

    Cloud Shell 활성화

    이 배포의 모든 터미널 명령어는 Cloud Shell에서 실행됩니다.

  4. 기본 Google Cloud 프로젝트를 설정합니다.

    export PROJECT=YOUR_PROJECT
    export PROJECT_NUMBER=$(gcloud projects describe PROJECT_ID --format="value(projectNumber)")
    gcloud config set project PROJECT_ID
    

    PROJECT_ID를 이 배포에 사용하려는 프로젝트의 ID로 바꿉니다.

  5. 작업 디렉터리를 만듭니다.

    mkdir -p ${HOME}/edge-to-mesh-multi-region
    cd ${HOME}/edge-to-mesh-multi-region
    export WORKDIR=`pwd`
    

GKE 클러스터 만들기

이 섹션에서는 이 배포 가이드의 뒷부분에서 만드는 애플리케이션과 지원 인프라를 호스팅할 GKE 클러스터를 만듭니다.

  1. Cloud Shell에서 새 kubeconfig 파일을 만듭니다. 이 단계를 수행하면 기존의 기본 kubeconfig 파일과 충돌이 발생하지 않습니다.

    touch edge2mesh_mr_kubeconfig
    export KUBECONFIG=${WORKDIR}/edge2mesh_mr_kubeconfig
    
  2. GKE 클러스터와 클러스터 내에 리소스를 만들 때 사용되는 환경 변수를 정의합니다. 목적에 맞게 기본 리전 선택을 수정합니다.

    export CLUSTER_1_NAME=edge-to-mesh-01
    export CLUSTER_2_NAME=edge-to-mesh-02
    export CLUSTER_1_REGION=us-central1
    export CLUSTER_2_REGION=us-east4
    export PUBLIC_ENDPOINT=frontend.endpoints.PROJECT_ID.cloud.goog
    
  3. 이 가이드 전체에서 사용되는 Google Cloud API를 사용 설정합니다.

    gcloud services enable \
      container.googleapis.com \
      mesh.googleapis.com \
      gkehub.googleapis.com \
      multiclusterservicediscovery.googleapis.com \
      multiclusteringress.googleapis.com \
      trafficdirector.googleapis.com \
      certificatemanager.googleapis.com
    
  4. CLUSTER_1_REGION에서 비공개 노드가 있는 GKE Autopilot 클러스터를 만듭니다. 첫 번째 클러스터가 프로비저닝되고 Fleet에 등록될 때까지 기다리는 것을 방지하려면 --async 플래그를 사용합니다.

    gcloud container clusters create-auto --async \
    ${CLUSTER_1_NAME} --region ${CLUSTER_1_REGION} \
    --release-channel rapid --labels mesh_id=proj-${PROJECT_NUMBER} \
    --enable-private-nodes --enable-fleet
    
  5. CLUSTER_2_REGION에서 두 번째 Autopilot 클러스터를 만들고 등록합니다.

    gcloud container clusters create-auto \
    ${CLUSTER_2_NAME} --region ${CLUSTER_2_REGION} \
    --release-channel rapid --labels mesh_id=proj-${PROJECT_NUMBER} \
    --enable-private-nodes --enable-fleet
    
  6. 클러스터가 실행 중인지 확인합니다. 모든 클러스터가 실행될 때까지 최대 20분이 걸릴 수 있습니다.

    gcloud container clusters list
    

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

    NAME             LOCATION     MASTER_VERSION  MASTER_IP       MACHINE_TYPE  NODE_VERSION    NUM_NODES  STATUS
    edge-to-mesh-01  us-central1  1.27.5-gke.200  34.27.171.241   e2-small      1.27.5-gke.200             RUNNING
    edge-to-mesh-02  us-east4     1.27.5-gke.200  35.236.204.156  e2-small      1.27.5-gke.200             RUNNING
    
  7. CLUSTER_1_NAME의 사용자 인증 정보를 수집합니다. 클러스터가 프로비저닝되는 동안 추가 명령어를 실행할 수 있도록 CLUSTER_1_NAME을 비동기식으로 만들었습니다.

    gcloud container clusters get-credentials ${CLUSTER_1_NAME} \
        --region ${CLUSTER_1_REGION}
    
  8. Kubernetes 컨텍스트 이름을 명확하게 이해하도록 이름을 클러스터 이름으로 이름을 바꿉니다.

    kubectl config rename-context gke_PROJECT_ID_${CLUSTER_1_REGION}_${CLUSTER_1_NAME} ${CLUSTER_1_NAME}
    kubectl config rename-context gke_PROJECT_ID_${CLUSTER_2_REGION}_${CLUSTER_2_NAME} ${CLUSTER_2_NAME}
    

서비스 메시 설치

이 섹션에서는 Fleet API로 관리형 Cloud Service Mesh를 구성합니다. Fleet API를 사용하여 Cloud Service Mesh를 사용 설정하면 서비스 메시를 프로비저닝하는 선언적인 방식이 제공됩니다.

  1. Cloud Shell에서 Fleet에 Cloud Service Mesh를 사용 설정합니다.

    gcloud container fleet mesh enable
    
  2. 자동 컨트롤 플레인과 데이터 영역 관리를 사용 설정합니다.

    gcloud container fleet mesh update \
      --management automatic \
      --memberships ${CLUSTER_1_NAME},${CLUSTER_2_NAME}
    
  3. 20분 정도 기다립니다. 그런 다음 컨트롤 플레인 상태가 ACTIVE인지 확인합니다.

    gcloud container fleet mesh describe
    

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

    createTime: '2023-11-30T19:23:21.713028916Z'
    membershipSpecs:
      projects/603904278888/locations/us-central1/memberships/edge-to-mesh-01:
        mesh:
          management: MANAGEMENT_AUTOMATIC
      projects/603904278888/locations/us-east4/memberships/edge-to-mesh-02:
        mesh:
          management: MANAGEMENT_AUTOMATIC
    membershipStates:
      projects/603904278888/locations/us-central1/memberships/edge-to-mesh-01:
        servicemesh:
          controlPlaneManagement:
            details:
            - code: REVISION_READY
              details: 'Ready: asm-managed-rapid'
            implementation: ISTIOD
            state: ACTIVE
          dataPlaneManagement:
            details:
            - code: OK
              details: Service is running.
            state: ACTIVE
        state:
         code: OK
          description: |-
            Revision ready for use: asm-managed-rapid.
            All Canonical Services have been reconciled successfully.
          updateTime: '2024-06-27T09:00:21.333579005Z'
      projects/603904278888/locations/us-east4/memberships/edge-to-mesh-02:
        servicemesh:
          controlPlaneManagement:
            details:
            - code: REVISION_READY
              details: 'Ready: asm-managed-rapid'
            implementation: ISTIOD
            state: ACTIVE
          dataPlaneManagement:
            details:
            - code: OK
              details: Service is running.
            state: ACTIVE
        state:
          code: OK
          description: |-
            Revision ready for use: asm-managed-rapid.
            All Canonical Services have been reconciled successfully.
          updateTime: '2024-06-27T09:00:24.674852751Z'
    name: projects/e2m-private-test-01/locations/global/features/servicemesh
    resourceState:
      state: ACTIVE
    spec: {}
    updateTime: '2024-06-04T17:16:28.730429993Z'
    

외부 애플리케이션 부하 분산기 배포 및 인그레스 게이트웨이 만들기

이 섹션에서는 GKE Gateway Controller를 통해 외부 애플리케이션 부하 분산기를 배포하고 두 클러스터 모두에 인그레스 게이트웨이를 만듭니다. gatewaygatewayClass 리소스는 부하 분산기와 백엔드 상태 점검의 프로비저닝을 자동화합니다. 부하 분산기에서 TLS 종료를 제공하려면 인증서 관리자 리소스를 만들고 부하 분산기에 연결합니다. 또한 Endpoints를 사용하여 애플리케이션의 공개 DNS 이름을 자동으로 프로비저닝합니다.

두 클러스터 모두에 인그레스 게이트웨이 설치

보안 권장사항으로 메서 컨트롤 플레인과 다른 네임스페이스에 인그레스 게이트웨이를 배포하는 것이 좋습니다.

  1. Cloud Shell에서 각 클러스터에 전용 asm-ingress 네임스페이스를 만듭니다.

    kubectl --context=${CLUSTER_1_NAME} create namespace asm-ingress
    kubectl --context=${CLUSTER_2_NAME} create namespace asm-ingress
    
  2. 네임스페이스 라벨을 asm-ingress 네임스페이스에 추가합니다.

    kubectl --context=${CLUSTER_1_NAME} label namespace asm-ingress istio-injection=enabled
    kubectl --context=${CLUSTER_2_NAME} label namespace asm-ingress istio-injection=enabled
    

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

    namespace/asm-ingress labeled
    

    asm-ingress 네임스페이스에 istio-injection=enabled 라벨을 지정하면 포드가 배포될 때 Cloud Service Mesh에서 Envoy 사이드카 프록시를 자동으로 삽입합니다.

  3. 나중에 사용할 수 있도록 자체 서명 인증서를 생성합니다.

    openssl req -new -newkey rsa:4096 -days 365 -nodes -x509 \
     -subj "/CN=frontend.endpoints.PROJECT_ID.cloud.goog/O=Edge2Mesh Inc" \
     -keyout ${WORKDIR}/frontend.endpoints.PROJECT_ID.cloud.goog.key \
     -out ${WORKDIR}/frontend.endpoints.PROJECT_ID.cloud.goog.crt
    

    인증서는 부하 분산기와 서비스 메시 인그레스 게이트웨이 사이에 추가 암호화 레이어를 제공합니다. 또한 gRPC와 같은 HTTP/2 기반 프로토콜을 지원할 수 있습니다. 자체 서명된 인증서를 인그레스 게이트웨이에 연결하는 방법에 대한 안내는 뒷부분의 외부 IP 주소, DNS 레코드, TLS 인증서 리소스 만들기를 참조하세요.

    인그레스 게이트웨이 인증서 요구사항에 대한 자세한 내용은 부하 분산기에서 백엔드로 암호화를 참조하세요.

  4. 각 클러스터에 Kubernetes 보안 비밀을 만들어 자체 서명 인증서를 저장합니다.

    kubectl --context ${CLUSTER_1_NAME} -n asm-ingress create secret tls \
     edge2mesh-credential \
     --key=${WORKDIR}/frontend.endpoints.PROJECT_ID.cloud.goog.key \
     --cert=${WORKDIR}/frontend.endpoints.PROJECT_ID.cloud.goog.crt
    kubectl --context ${CLUSTER_2_NAME} -n asm-ingress create secret tls \
     edge2mesh-credential \
     --key=${WORKDIR}/frontend.endpoints.PROJECT_ID.cloud.goog.key \
     --cert=${WORKDIR}/frontend.endpoints.PROJECT_ID.cloud.goog.crt
    
  5. 외부 애플리케이션 부하 분산기와 통합하려면 kustomize 변형을 만들어 인그레스 게이트웨이 리소스를 구성합니다.

    mkdir -p ${WORKDIR}/asm-ig/base
    
    cat <<EOF > ${WORKDIR}/asm-ig/base/kustomization.yaml
    resources:
      - github.com/GoogleCloudPlatform/anthos-service-mesh-samples/docs/ingress-gateway-asm-manifests/base
    EOF
    
    mkdir ${WORKDIR}/asm-ig/variant
    
    cat <<EOF > ${WORKDIR}/asm-ig/variant/role.yaml
    apiVersion: rbac.authorization.k8s.io/v1
    kind: Role
    metadata:
      name: asm-ingressgateway
      namespace: asm-ingress
    rules:
    - apiGroups: [""]
      resources: ["secrets"]
      verbs: ["get", "watch", "list"]
    EOF
    
    cat <<EOF > ${WORKDIR}/asm-ig/variant/rolebinding.yaml
    apiVersion: rbac.authorization.k8s.io/v1
    kind: RoleBinding
    metadata:
      name: asm-ingressgateway
      namespace: asm-ingress
    roleRef:
      apiGroup: rbac.authorization.k8s.io
      kind: Role
      name: asm-ingressgateway
    subjects:
      - kind: ServiceAccount
        name: asm-ingressgateway
    EOF
    
    cat <<EOF > ${WORKDIR}/asm-ig/variant/service-proto-type.yaml
    apiVersion: v1
    kind: Service
    metadata:
      name: asm-ingressgateway
      namespace: asm-ingress
    spec:
      ports:
      - name: status-port
        port: 15021
        protocol: TCP
        targetPort: 15021
      - name: http
        port: 80
        targetPort: 8080
        appProtocol: HTTP
      - name: https
        port: 443
        targetPort: 8443
        appProtocol: HTTP2
      type: ClusterIP
    EOF
    
    cat <<EOF > ${WORKDIR}/asm-ig/variant/gateway.yaml
    apiVersion: networking.istio.io/v1beta1
    kind: Gateway
    metadata:
      name: asm-ingressgateway
      namespace: asm-ingress
    spec:
     servers:
      - port:
          number: 443
          name: https
          protocol: HTTPS
        hosts:
        - "*" # IMPORTANT: Must use wildcard here when using SSL, as SNI isn't passed from GFE
        tls:
          mode: SIMPLE
          credentialName: edge2mesh-credential
    EOF
    
    cat <<EOF > ${WORKDIR}/asm-ig/variant/kustomization.yaml
    namespace: asm-ingress
    resources:
    - ../base
    - role.yaml
    - rolebinding.yaml
    patches:
    - path: service-proto-type.yaml
      target:
        kind: Service
    - path: gateway.yaml
      target:
        kind: Gateway
    EOF
    
  6. 두 클러스터 모두에 인그레스 게이트웨이 구성을 적용합니다.

    kubectl --context ${CLUSTER_1_NAME} apply -k ${WORKDIR}/asm-ig/variant
    kubectl --context ${CLUSTER_2_NAME} apply -k ${WORKDIR}/asm-ig/variant
    

멀티 클러스터 서비스를 사용하여 부하 분산기에 인그레스 게이트웨이 포드 노출

이 섹션에서는 ServiceExport 커스텀 리소스를 통해 인그레스 게이트웨이 포드를 내보냅니다. 다음과 같은 이유로 ServiceExport 커스텀 리소스를 통해 인그레스 게이트웨이 포드를 내보내야 합니다.

  1. Cloud Shell에서 Fleet에 멀티 클러스터 서비스(MCS)를 사용 설정합니다.

    gcloud container fleet multi-cluster-services enable
    
  2. MCS에 프로젝트 또는 Fleet에 대한 필수 IAM 권한을 부여합니다.

    gcloud projects add-iam-policy-binding PROJECT_ID \
     --member "serviceAccount:PROJECT_ID.svc.id.goog[gke-mcs/gke-mcs-importer]" \
     --role "roles/compute.networkViewer"
    
  3. ServiceExport YAML 파일을 만듭니다.

    cat <<EOF > ${WORKDIR}/svc_export.yaml
    kind: ServiceExport
    apiVersion: net.gke.io/v1
    metadata:
      name: asm-ingressgateway
      namespace: asm-ingress
    EOF
    
  4. ServiceExport YAML 파일을 두 클러스터 모두에 적용합니다.

    kubectl --context=${CLUSTER_1_NAME} apply -f ${WORKDIR}/svc_export.yaml
    kubectl --context=${CLUSTER_2_NAME} apply -f ${WORKDIR}/svc_export.yaml
    

    다음 오류 메시지가 표시되면 MCS 커스텀 리소스 정의(CRD)가 설치될 때까지 잠시 기다립니다. 그런 다음 명령어를 다시 실행하여 두 클러스터 모두에 ServiceExport YAML 파일을 적용합니다.

    error: resource mapping not found for name: "asm-ingressgateway" namespace: "asm-ingress" from "svc_export.yaml": no matches for kind "ServiceExport" in version "net.gke.io/v1"
    ensure CRDs are installed first
    

외부 IP 주소, DNS 레코드, TLS 인증서 리소스 만들기

이 섹션에서는 이 배포의 뒷부분에서 만드는 부하 분산 리소스를 지원하는 네트워킹 리소스를 만듭니다.

  1. Cloud Shell에서 고정 외부 IP 주소를 예약합니다.

    gcloud compute addresses create mcg-ip --global
    

    GKE 게이트웨이 리소스에서 고정 IP 주소를 사용합니다. 이렇게 하면 외부 부하 분산기가 다시 생성되더라도 IP 주소가 동일하게 유지됩니다.

  2. 고정 IP 주소를 가져와 환경 변수로 저장합니다.

    export MCG_IP=$(gcloud compute addresses describe mcg-ip --global --format "value(address)")
    echo ${MCG_IP}
    

    게이트웨이 IP 주소에 대한 사용자 친화적이고 안정적인 매핑을 만들려면 공개 DNS 레코드가 있어야 합니다.

    원하는 DNS 제공업체 및 자동화 스킴을 사용할 수 있습니다. 이 배포에서는 관리형 DNS 영역을 만드는 대신 Endpoints를 사용합니다. Endpoints는 외부 IP 주소에 무료 Google 관리 DNS 레코드를 제공합니다.

  3. 다음 명령어를 실행하여 dns-spec.yaml YAML 파일을 만듭니다.

    cat <<EOF > ${WORKDIR}/dns-spec.yaml
    swagger: "2.0"
    info:
      description: "Cloud Endpoints DNS"
      title: "Cloud Endpoints DNS"
      version: "1.0.0"
    paths: {}
    host: "frontend.endpoints.PROJECT_ID.cloud.goog"
    x-google-endpoints:
    - name: "frontend.endpoints.PROJECT_ID.cloud.goog"
      target: "${MCG_IP}"
    EOF
    

    dns-spec.yaml 파일은 공개 DNS 레코드를 frontend.endpoints.PROJECT_ID.cloud.goog 형식으로 정의합니다. 여기서 PROJECT_ID는 고유한 프로젝트 식별자입니다.

  4. dns-spec.yaml 파일을 배포하여 DNS 항목을 만듭니다. 이 프로세스는 몇 분 정도 걸립니다.

    gcloud endpoints services deploy ${WORKDIR}/dns-spec.yaml
    
  5. 이전 단계에서 만든 DNS 항목 이름에 인증서 관리자를 사용하여 인증서를 만듭니다.

    gcloud certificate-manager certificates create mcg-cert \
        --domains="frontend.endpoints.PROJECT_ID.cloud.goog"
    

    Google 관리 TLS 인증서는 부하 분산기에서 인바운드 클라이언트 요청을 종료하는 데 사용됩니다.

  6. 인증서 맵을 만듭니다.

    gcloud certificate-manager maps create mcg-cert-map
    

    부하 분산기는 다음 단계에서 만드는 인증서 맵 항목을 통해 인증서를 참조합니다.

  7. 이 섹션의 앞부분에서 만든 인증서의 인증서 맵 항목을 만듭니다.

    gcloud certificate-manager maps entries create mcg-cert-map-entry \
        --map="mcg-cert-map" \
        --certificates="mcg-cert" \
        --hostname="frontend.endpoints.PROJECT_ID.cloud.goog"
    

백엔드 서비스 정책 및 부하 분산기 리소스 만들기

이 섹션에서는 다음 태스크를 수행합니다.

  • 규칙을 사용하여 Google Cloud Armor 보안 정책을 만듭니다.
  • 부하 분산기가 앞에서 만든 ServiceExport YAML 파일을 통해 인그레스 게이트웨이 포드의 응답성을 확인할 수 있는 정책을 만듭니다.
  • GKE Gateway API를 사용하여 부하 분산기 리소스를 만듭니다.
  • GatewayClass 커스텀 리소스를 사용하여 특정 부하 분산기 유형을 설정합니다.
  • Fleet에 멀티 클러스터 부하 분산을 사용 설정하고 클러스터 중 하나를 Fleet의 구성 클러스터로 지정합니다.
  1. Cloud Shell에서 Google Cloud Armor 보안 정책을 만듭니다.

    gcloud compute security-policies create edge-fw-policy \
        --description "Block XSS attacks"
    
  2. 보안 정책 규칙을 만듭니다.

    gcloud compute security-policies rules create 1000 \
        --security-policy edge-fw-policy \
        --expression "evaluatePreconfiguredExpr('xss-stable')" \
        --action "deny-403" \
        --description "XSS attack filtering"
    
  3. 보안 정책에 대한 YAML 파일을 만들고 해당 ServiceImport YAML 파일을 통해 ServiceExport YAML 파일을 참조합니다.

    cat <<EOF > ${WORKDIR}/cloud-armor-backendpolicy.yaml
    apiVersion: networking.gke.io/v1
    kind: GCPBackendPolicy
    metadata:
      name: cloud-armor-backendpolicy
      namespace: asm-ingress
    spec:
      default:
        securityPolicy: edge-fw-policy
      targetRef:
        group: net.gke.io
        kind: ServiceImport
        name: asm-ingressgateway
    EOF
    
  4. 두 클러스터 모두에 Google Cloud Armor 정책을 적용합니다.

    kubectl --context=${CLUSTER_1_NAME} apply -f ${WORKDIR}/cloud-armor-backendpolicy.yaml
    kubectl --context=${CLUSTER_2_NAME} apply -f ${WORKDIR}/cloud-armor-backendpolicy.yaml
    
  5. 부하 분산기가 두 클러스터 모두에서 인그레스 게이트웨이 포드의 Envoy 상태 엔드포인트(/healthz/ready 경로에 있는 포트 15021)에 대해 상태 점검을 수행할 수 있게 해주는 커스텀 YAML 파일을 만듭니다.

    cat <<EOF > ${WORKDIR}/ingress-gateway-healthcheck.yaml
    apiVersion: networking.gke.io/v1
    kind: HealthCheckPolicy
    metadata:
      name: ingress-gateway-healthcheck
      namespace: asm-ingress
    spec:
      default:
        config:
          httpHealthCheck:
            port: 15021
            portSpecification: USE_FIXED_PORT
            requestPath: /healthz/ready
          type: HTTP
      targetRef:
        group: net.gke.io
        kind: ServiceImport
        name: asm-ingressgateway
    EOF
    
  6. 이전 단계에서 만든 커스텀 YAML 파일을 두 클러스터 모두에 적용합니다.

    kubectl --context=${CLUSTER_1_NAME} apply -f ${WORKDIR}/ingress-gateway-healthcheck.yaml
    kubectl --context=${CLUSTER_2_NAME} apply -f ${WORKDIR}/ingress-gateway-healthcheck.yaml
    
  7. Fleet에 멀티 클러스터 부하 분산을 사용 설정하고 CLUSTER_1_NAME을 구성 클러스터로 지정합니다.

    gcloud container fleet ingress enable \
      --config-membership=${CLUSTER_1_NAME} \
      --location=${CLUSTER_1_REGION}
    
  8. Fleet에서 게이트웨이 컨트롤러에 대한 IAM 권한을 부여합니다.

    gcloud projects add-iam-policy-binding PROJECT_ID \
        --member "serviceAccount:service-${PROJECT_NUMBER}@gcp-sa-multiclusteringress.iam.gserviceaccount.com" \
        --role "roles/container.admin"
    
  9. gke-l7-global-external-managed-mc gatewayClass 및 앞에서 만든 고정 IP 주소를 참조하는 게이트웨이 커스텀 리소스를 통해 부하 분산기 YAML 파일을 만듭니다.

    cat <<EOF > ${WORKDIR}/frontend-gateway.yaml
    kind: Gateway
    apiVersion: gateway.networking.k8s.io/v1beta1
    metadata:
      name: external-http
      namespace: asm-ingress
      annotations:
        networking.gke.io/certmap: mcg-cert-map
    spec:
      gatewayClassName: gke-l7-global-external-managed-mc
      listeners:
      - name: http # list the port only so we can redirect any incoming http requests to https
        protocol: HTTP
        port: 80
      - name: https
        protocol: HTTPS
        port: 443
        allowedRoutes:
          kinds:
          - kind: HTTPRoute
      addresses:
      - type: NamedAddress
        value: mcg-ip
    EOF
    
  10. 두 클러스터 모두에 frontend-gateway YAML 파일을 적용합니다. 다른 구성 클러스터를 권한 있음으로 지정하지 않는 한 CLUSTER_1_NAME만 권한이 있습니다.

    kubectl --context=${CLUSTER_1_NAME} apply -f ${WORKDIR}/frontend-gateway.yaml
    kubectl --context=${CLUSTER_2_NAME} apply -f ${WORKDIR}/frontend-gateway.yaml
    
  11. 게이트웨이 리소스가 요청을 인그레스 게이트웨이로 보내도록 지시하는 default-httproute.yaml이라는 HTTPRoute YAML 파일을 만듭니다.

    cat << EOF > ${WORKDIR}/default-httproute.yaml
    apiVersion: gateway.networking.k8s.io/v1beta1
    kind: HTTPRoute
    metadata:
      name: default-httproute
      namespace: asm-ingress
    spec:
      parentRefs:
      - name: external-http
        namespace: asm-ingress
        sectionName: https
      rules:
      - backendRefs:
        - group: net.gke.io
          kind: ServiceImport
          name: asm-ingressgateway
          port: 443
    EOF
    
  12. 이전 단계에서 만든 HTTPRoute YAML 파일을 두 클러스터 모두에 적용합니다.

    kubectl --context=${CLUSTER_1_NAME} apply -f ${WORKDIR}/default-httproute.yaml
    kubectl --context=${CLUSTER_2_NAME} apply -f ${WORKDIR}/default-httproute.yaml
    
  13. HTTP에서 HTTP(S)로 리디렉션을 수행하려면 default-httproute-redirect.yaml이라는 추가 HTTPRoute YAML 파일을 만듭니다.

    cat << EOF > ${WORKDIR}/default-httproute-redirect.yaml
    kind: HTTPRoute
    apiVersion: gateway.networking.k8s.io/v1beta1
    metadata:
      name: http-to-https-redirect-httproute
      namespace: asm-ingress
    spec:
      parentRefs:
      - name: external-http
        namespace: asm-ingress
        sectionName: http
      rules:
      - filters:
        - type: RequestRedirect
          requestRedirect:
            scheme: https
            statusCode: 301
    EOF
    
  14. 리디렉션 HTTPRoute YAML 파일을 두 클러스터 모두에 적용합니다.

    kubectl --context=${CLUSTER_1_NAME} apply -f ${WORKDIR}/default-httproute-redirect.yaml
    kubectl --context=${CLUSTER_2_NAME} apply -f ${WORKDIR}/default-httproute-redirect.yaml
    
  15. 게이트웨이 리소스를 검사하여 부하 분산기 배포 진행 상황을 확인합니다.

    kubectl --context=${CLUSTER_1_NAME} describe gateway external-http -n asm-ingress
    

    이 섹션에 입력한 정보가 출력에 표시됩니다.

whereami 샘플 애플리케이션 배포

이 가이드에서는 whereami를 샘플 애플리케이션으로 사용하여 요청에 응답하는 클러스터에 대한 직접 피드백을 제공합니다. 다음 섹션에서는 두 클러스터 간에 별도의 두 가지 whereami 배포인 frontend 배포와 backend 배포를 설정합니다.

frontend 배포는 요청을 수신하는 첫 번째 워크로드입니다. 그런 다음 backend 배포를 호출합니다.

이 모델은 멀티 서비스 애플리케이션 아키텍처를 보여주는 데 사용됩니다. frontendbackend 서비스 모두 두 클러스터 모두에 배포됩니다.

  1. Cloud Shell에서 두 클러스터 모두에서 whereami frontend 및 whereami backend의 네임스페이스를 만들고 네임스페이스 삽입을 사용 설정합니다.

    kubectl --context=${CLUSTER_1_NAME} create ns frontend
    kubectl --context=${CLUSTER_1_NAME} label namespace frontend istio-injection=enabled
    kubectl --context=${CLUSTER_1_NAME} create ns backend
    kubectl --context=${CLUSTER_1_NAME} label namespace backend istio-injection=enabled
    kubectl --context=${CLUSTER_2_NAME} create ns frontend
    kubectl --context=${CLUSTER_2_NAME} label namespace frontend istio-injection=enabled
    kubectl --context=${CLUSTER_2_NAME} create ns backend
    kubectl --context=${CLUSTER_2_NAME} label namespace backend istio-injection=enabled
    
  2. whereami backend의 kustomize 변형을 만듭니다.

    mkdir -p ${WORKDIR}/whereami-backend/base
    
    cat <<EOF > ${WORKDIR}/whereami-backend/base/kustomization.yaml
    resources:
      - github.com/GoogleCloudPlatform/kubernetes-engine-samples/quickstarts/whereami/k8s
    EOF
    
    mkdir ${WORKDIR}/whereami-backend/variant
    
    cat <<EOF > ${WORKDIR}/whereami-backend/variant/cm-flag.yaml
    apiVersion: v1
    kind: ConfigMap
    metadata:
      name: whereami
    data:
      BACKEND_ENABLED: "False" # assuming you don't want a chain of backend calls
      METADATA:        "backend"
    EOF
    
    cat <<EOF > ${WORKDIR}/whereami-backend/variant/service-type.yaml
    apiVersion: "v1"
    kind: "Service"
    metadata:
      name: "whereami"
    spec:
      type: ClusterIP
    EOF
    
    cat <<EOF > ${WORKDIR}/whereami-backend/variant/kustomization.yaml
    nameSuffix: "-backend"
    namespace: backend
    commonLabels:
      app: whereami-backend
    resources:
    - ../base
    patches:
    - path: cm-flag.yaml
      target:
        kind: ConfigMap
    - path: service-type.yaml
      target:
        kind: Service
    EOF
    
  3. whereami backend 변형을 두 클러스터 모두에 적용합니다.

    kubectl --context=${CLUSTER_1_NAME} apply -k ${WORKDIR}/whereami-backend/variant
    kubectl --context=${CLUSTER_2_NAME} apply -k ${WORKDIR}/whereami-backend/variant
    
  4. whereami frontend의 kustomize 변형을 만듭니다.

    mkdir -p ${WORKDIR}/whereami-frontend/base
    
    cat <<EOF > ${WORKDIR}/whereami-frontend/base/kustomization.yaml
    resources:
      - github.com/GoogleCloudPlatform/kubernetes-engine-samples/quickstarts/whereami/k8s
    EOF
    
    mkdir whereami-frontend/variant
    
    cat <<EOF > ${WORKDIR}/whereami-frontend/variant/cm-flag.yaml
    apiVersion: v1
    kind: ConfigMap
    metadata:
      name: whereami
    data:
      BACKEND_ENABLED: "True"
      BACKEND_SERVICE: "http://whereami-backend.backend.svc.cluster.local"
    EOF
    
    cat <<EOF > ${WORKDIR}/whereami-frontend/variant/service-type.yaml
    apiVersion: "v1"
    kind: "Service"
    metadata:
      name: "whereami"
    spec:
      type: ClusterIP
    EOF
    
    cat <<EOF > ${WORKDIR}/whereami-frontend/variant/kustomization.yaml
    nameSuffix: "-frontend"
    namespace: frontend
    commonLabels:
      app: whereami-frontend
    resources:
    - ../base
    patches:
     path: cm-flag.yaml
      target:
        kind: ConfigMap
    - path: service-type.yaml
      target:
        kind: Service
    EOF
    
  5. whereami frontend 변형을 두 클러스터 모두에 적용합니다.

    kubectl --context=${CLUSTER_1_NAME} apply -k ${WORKDIR}/whereami-frontend/variant
    kubectl --context=${CLUSTER_2_NAME} apply -k ${WORKDIR}/whereami-frontend/variant
    
  6. VirtualService YAML 파일을 만들어 요청을 whereami frontend로 라우팅합니다.

    cat << EOF > ${WORKDIR}/frontend-vs.yaml
    apiVersion: networking.istio.io/v1beta1
    kind: VirtualService
    metadata:
      name: whereami-vs
      namespace: frontend
    spec:
      gateways:
      - asm-ingress/asm-ingressgateway
      hosts:
      - 'frontend.endpoints.PROJECT_ID.cloud.goog'
      http:
      - route:
        - destination:
            host: whereami-frontend
            port:
              number: 80
    EOF
    
  7. frontend-vs YAML 파일을 두 클러스터 모두에 적용합니다.

    kubectl --context=${CLUSTER_1_NAME} apply -f ${WORKDIR}/frontend-vs.yaml
    kubectl --context=${CLUSTER_2_NAME} apply -f ${WORKDIR}/frontend-vs.yaml
    
  8. 이제 frontend-vs.yaml을 두 클러스터 모두에 배포했으므로 클러스터의 공개 엔드포인트를 호출합니다.

    curl -s https://frontend.endpoints.PROJECT_ID.cloud.goog | jq
    

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

    {
      "backend_result": {
        "cluster_name": "edge-to-mesh-02",
        "gce_instance_id": "8396338201253702608",
        "gce_service_account": "e2m-mcg-01.svc.id.goog",
        "host_header": "whereami-backend.backend.svc.cluster.local",
        "metadata": "backend",
        "node_name": "gk3-edge-to-mesh-02-pool-2-675f6abf-645h",
        "pod_ip": "10.124.0.199",
        "pod_name": "whereami-backend-7cbdfd788-8mmnq",
        "pod_name_emoji": "📸",
        "pod_namespace": "backend",
        "pod_service_account": "whereami-backend",
        "project_id": "e2m-mcg-01",
        "timestamp": "2023-12-01T03:46:24",
        "zone": "us-east4-b"
      },
      "cluster_name": "edge-to-mesh-01",
      "gce_instance_id": "1047264075324910451",
      "gce_service_account": "e2m-mcg-01.svc.id.goog",
      "host_header": "frontend.endpoints.e2m-mcg-01.cloud.goog",
      "metadata": "frontend",
      "node_name": "gk3-edge-to-mesh-01-pool-2-d687e3c0-5kf2",
      "pod_ip": "10.54.1.71",
      "pod_name": "whereami-frontend-69c4c867cb-dgg8t",
      "pod_name_emoji": "🪴",
      "pod_namespace": "frontend",
      "pod_service_account": "whereami-frontend",
      "project_id": "e2m-mcg-01",
      "timestamp": "2023-12-01T03:46:24",
      "zone": "us-central1-c"
    }
    

curl 명령어를 몇 차례 실행하면 frontendbackend 모두의 응답이 다른 리전에서 오는 것을 확인할 수 있습니다. 이에 대응하여 부하 분산기는 지역 라우팅을 제공합니다. 즉, 부하 분산기는 클라이언트의 요청을 가장 가까운 활성 클러스터로 라우팅하지만 요청은 여전히 무작위로 랜딩됩니다. 요청이 가끔 한 리전에서 다른 리전으로 이동하면 지연 시간과 비용이 증가합니다.

다음 섹션에서는 서비스 메시에서 지역 부하 분산을 구현하여 요청을 로컬로 유지합니다.

whereami의 지역 부하 분산 사용 설정 및 테스트

이 섹션에서는 서비스 메시에서 지역 부하 분산을 구현하여 요청을 로컬로 유지합니다. 또한 몇 가지 테스트를 수행하여 whereami에서 다양한 장애 시나리오를 처리하는 방법을 확인합니다.

요청을 whereami frontend 서비스에 보내면 부하 분산기에서 요청을 클라이언트에 비해 지연 시간이 가장 짧은 클러스터로 전송합니다. 즉, 메시 내 인그레스 게이트웨이 포드가 두 클러스터에서 요청을 whereami frontend 포드로 부하 분산합니다. 이 섹션에서는 메시 내에서 지역 부하 분산을 사용 설정하여 이 문제를 해결합니다.

  1. Cloud Shell에서 frontend 서비스에 대한 지역 부하 분산 리전 장애 조치를 사용 설정하는 DestinationRule YAML 파일을 만듭니다.

    cat << EOF > ${WORKDIR}/frontend-dr.yaml
    apiVersion: networking.istio.io/v1beta1
    kind: DestinationRule
    metadata:
      name: frontend
      namespace: frontend
    spec:
      host: whereami-frontend.frontend.svc.cluster.local
      trafficPolicy:
        connectionPool:
          http:
            maxRequestsPerConnection: 0
        loadBalancer:
          simple: LEAST_REQUEST
          localityLbSetting:
            enabled: true
        outlierDetection:
          consecutive5xxErrors: 1
          interval: 1s
          baseEjectionTime: 1m
    EOF
    

    앞의 코드 샘플은 frontend 서비스에 로컬 라우팅만 사용 설정합니다. 또한 백엔드를 처리하는 추가 구성이 필요합니다.

  2. frontend-dr YAML 파일을 두 클러스터 모두에 적용합니다.

    kubectl --context=${CLUSTER_1_NAME} apply -f ${WORKDIR}/frontend-dr.yaml
    kubectl --context=${CLUSTER_2_NAME} apply -f ${WORKDIR}/frontend-dr.yaml
    
  3. backend 서비스에 대한 지역 부하 분산 리전 장애 조치를 사용 설정하는 DestinationRule YAML 파일을 만듭니다.

    cat << EOF > ${WORKDIR}/backend-dr.yaml
    apiVersion: networking.istio.io/v1beta1
    kind: DestinationRule
    metadata:
    n    ame: backend
      namespace: backend
    spec:
      host: whereami-backend.backend.svc.cluster.local
      trafficPolicy:
        connectionPool:
          http:
            maxRequestsPerConnection: 0
        loadBalancer:
          simple: LEAST_REQUEST
          localityLbSetting:
            enabled: true
        outlierDetection:
          consecutive5xxErrors: 1
          interval: 1s
          baseEjectionTime: 1m
    EOF
    
  4. backend-dr YAML 파일을 두 클러스터 모두에 적용합니다.

    kubectl --context=${CLUSTER_1_NAME} apply -f ${WORKDIR}/backend-dr.yaml
    kubectl --context=${CLUSTER_2_NAME} apply -f ${WORKDIR}/backend-dr.yaml
    

    DestinationRule YAML 파일 집합 모두가 두 클러스터 모두에 적용되면 요청은 요청이 라우팅되는 클러스터에 대해 로컬로 유지됩니다.

    frontend 서비스 장애 조치를 테스트하려면 기본 클러스터에서 인그레스 게이트웨이 복제본 수를 줄입니다.

    멀티 리전 부하 분산기의 관점에서 이 작업은 클러스터 오류를 시뮬레이션합니다. 이로 인해 클러스터에서 부하 분산기 상태 점검을 실패합니다. 이 예시에서는 CLUSTER_1_REGION의 클러스터를 사용합니다. CLUSTER_2_REGION에 클러스터의 응답만 표시됩니다.

  5. 기본 클러스터에서 인그레스 게이트웨이 복제본 수를 0으로 줄이고 공개 엔드포인트를 호출하여 요청이 다른 클러스터로 장애 조치되었는지 확인합니다.

    kubectl --context=${CLUSTER_1_NAME} -n asm-ingress scale --replicas=0 deployment/asm-ingressgateway
    

    다음과 유사한 결과가 출력됩니다.

    $ curl -s https://frontend.endpoints.PROJECT_ID.cloud.goog | jq
    {
      "backend_result": {
        "cluster_name": "edge-to-mesh-02",
        "gce_instance_id": "2717459599837162415",
        "gce_service_account": "e2m-mcg-01.svc.id.goog",
        "host_header": "whereami-backend.backend.svc.cluster.local",
        "metadata": "backend",
        "node_name": "gk3-edge-to-mesh-02-pool-2-675f6abf-dxs2",
        "pod_ip": "10.124.1.7",
        "pod_name": "whereami-backend-7cbdfd788-mp8zv",
        "pod_name_emoji": "🏌🏽‍♀",
        "pod_namespace": "backend",
        "pod_service_account": "whereami-backend",
        "project_id": "e2m-mcg-01",
        "timestamp": "2023-12-01T05:41:18",
        "zone": "us-east4-b"
      },
      "cluster_name": "edge-to-mesh-02",
      "gce_instance_id": "6983018919754001204",
      "gce_service_account": "e2m-mcg-01.svc.id.goog",
      "host_header": "frontend.endpoints.e2m-mcg-01.cloud.goog",
      "metadata": "frontend",
      "node_name": "gk3-edge-to-mesh-02-pool-3-d42ddfbf-qmkn",
      "pod_ip": "10.124.1.142",
      "pod_name": "whereami-frontend-69c4c867cb-xf8db",
      "pod_name_emoji": "🏴",
      "pod_namespace": "frontend",
      "pod_service_account": "whereami-frontend",
      "project_id": "e2m-mcg-01",
      "timestamp": "2023-12-01T05:41:18",
      "zone": "us-east4-b"
    }
    
  6. 일반적인 트래픽 라우팅을 재개하려면 인그레스 게이트웨이 복제본을 클러스터의 원래 값으로 복원합니다.

    kubectl --context=${CLUSTER_1_NAME} -n asm-ingress scale --replicas=3 deployment/asm-ingressgateway
    
  7. 기본 리전의 복제본 수를 0으로 줄여 backend 서비스 오류를 시뮬레이션합니다.

    kubectl --context=${CLUSTER_1_NAME} -n backend scale --replicas=0 deployment/whereami-backend
    

    frontend 서비스의 응답은 부하 분산기를 통해 us-central1 기본 리전에서 오고 backend 서비스의 응답은 us-east4 보조 리전에서 오는지 확인합니다.

    출력에는 예상대로 기본 리전(us-central1)의 frontend 서비스에 대한 응답과 보조 리전(us-east4)의 backend 서비스에 대한 응답도 포함되어야 합니다.

  8. 일반적인 트래픽 라우팅을 재개하려면 백엔드 서비스 복제본을 원래 값으로 복원합니다.

    kubectl --context=${CLUSTER_1_NAME} -n backend scale --replicas=3 deployment/whereami-backend
    

이제 서비스 메시에서 호스팅되는 멀티 리전 애플리케이션의 프런트엔드 역할을 하는 전역 HTTP(S) 부하 분산기가 준비되었습니다.

삭제

이 배포에서 사용된 리소스 비용이 Google Cloud 계정에 청구되지 않도록 하려면 리소스가 포함된 프로젝트를 삭제하거나 프로젝트는 유지하되 개별 리소스를 삭제하세요.

프로젝트 삭제

  1. Google Cloud 콘솔에서 리소스 관리 페이지로 이동합니다.

    리소스 관리로 이동

  2. 프로젝트 목록에서 삭제할 프로젝트를 선택하고 삭제를 클릭합니다.
  3. 대화상자에서 프로젝트 ID를 입력한 후 종료를 클릭하여 프로젝트를 삭제합니다.

개별 리소스 삭제

이 배포에서 사용된 Google Cloud 프로젝트를 유지하려면 개별 리소스를 삭제합니다.

  1. Cloud Shell에서 HTTPRoute 리소스를 삭제합니다.

    kubectl --context=${CLUSTER_1_NAME} delete -f ${WORKDIR}/default-httproute-redirect.yaml
    kubectl --context=${CLUSTER_2_NAME} delete -f ${WORKDIR}/default-httproute-redirect.yaml
    kubectl --context=${CLUSTER_1_NAME} delete -f ${WORKDIR}/default-httproute.yaml
    kubectl --context=${CLUSTER_2_NAME} delete -f ${WORKDIR}/default-httproute.yaml
    
  2. GKE 게이트웨이 리소스를 삭제합니다.

    kubectl --context=${CLUSTER_1_NAME} delete -f ${WORKDIR}/frontend-gateway.yaml
    kubectl --context=${CLUSTER_2_NAME} delete -f ${WORKDIR}/frontend-gateway.yaml
    
  3. 정책을 삭제합니다.

    kubectl --context=${CLUSTER_1_NAME} delete -f ${WORKDIR}/ingress-gateway-healthcheck.yaml
    kubectl --context=${CLUSTER_2_NAME} delete -f ${WORKDIR}/ingress-gateway-healthcheck.yaml
    kubectl --context=${CLUSTER_1_NAME} delete -f ${WORKDIR}/cloud-armor-backendpolicy.yaml
    kubectl --context=${CLUSTER_2_NAME} delete -f ${WORKDIR}/cloud-armor-backendpolicy.yaml
    
  4. 서비스 내보내기를 삭제합니다.

    kubectl --context=${CLUSTER_1_NAME} delete -f ${WORKDIR}/svc_export.yaml
    kubectl --context=${CLUSTER_2_NAME} delete -f ${WORKDIR}/svc_export.yaml
    
  5. Google Cloud Armor 리소스를 삭제합니다.

    gcloud --project=PROJECT_ID compute security-policies rules delete 1000 --security-policy edge-fw-policy --quiet
    gcloud --project=PROJECT_ID compute security-policies delete edge-fw-policy --quiet
    
  6. 인증서 관리자 리소스를 삭제합니다.

    gcloud --project=PROJECT_ID certificate-manager maps entries delete mcg-cert-map-entry --map="mcg-cert-map" --quiet
    gcloud --project=PROJECT_ID certificate-manager maps delete mcg-cert-map --quiet
    gcloud --project=PROJECT_ID certificate-manager certificates delete mcg-cert --quiet
    
  7. Endpoints DNS 항목을 삭제합니다.

    gcloud --project=PROJECT_ID endpoints services delete "frontend.endpoints.PROJECT_ID.cloud.goog" --quiet
    
  8. 고정 IP 주소를 삭제합니다.

    gcloud --project=PROJECT_ID compute addresses delete mcg-ip --global --quiet
    
  9. GKE Autopilot 클러스터를 삭제합니다. 이 단계는 몇 분 정도 걸립니다.

    gcloud --project=PROJECT_ID container clusters delete ${CLUSTER_1_NAME} --region ${CLUSTER_1_REGION} --quiet
    gcloud --project=PROJECT_ID container clusters delete ${CLUSTER_2_NAME} --region ${CLUSTER_2_REGION} --quiet
    

다음 단계

참여자

저자:

기타 참여자: