GKE でマルチクラスタ メッシュを設定する

このガイドでは、Mesh CA または Istio CA を使用して 2 つのクラスタを単一の Anthos Service Mesh に追加し、クラスタ間でロード バランシングを行う方法について説明します。このプロセスを拡張することで、任意の数のクラスタをメッシュに組み込むことができます。

マルチクラスタの Anthos Service Mesh 構成を使用すると、大規模な組織で重要な課題(スケール、ロケーション、分離など)を解決できます。詳細については、マルチクラスタのユースケースをご覧ください。

前提条件

このガイドでは、次の要件を満たす Google Cloud GKE クラスタが 2 つ以上存在することを前提としています。

プロジェクトとクラスタ変数を設定する

  1. 作業フォルダを、クラスタ間のロード バランシングが正常に機能することを確認するために使用するサンプルに設定します。サンプルは、asmcli install コマンドで指定した --output_dir ディレクトリのサブディレクトリにあります。次のコマンドで、--output_dir で指定したディレクトリに OUTPUT_DIR を変更し、Anthos Service Mesh のバージョンと一致するように ASM_VERSION を変更します(例: 1.11.8-asm.4)。

    export SAMPLES_DIR=OUTPUT_DIR/istio-ASM_VERSION
    
  2. プロジェクト ID、クラスタゾーンまたはリージョン、クラスタ名、コンテキストのために、次の環境変数を作成します。

    export PROJECT_1=PROJECT_ID_1
    export LOCATION_1=CLUSTER_LOCATION_1
    export CLUSTER_1=CLUSTER_NAME_1
    export CTX_1="gke_${PROJECT_1}_${LOCATION_1}_${CLUSTER_1}"
    
    export PROJECT_2=PROJECT_ID_2
    export LOCATION_2=CLUSTER_LOCATION_2
    export CLUSTER_2=CLUSTER_NAME_2
    export CTX_2="gke_${PROJECT_2}_${LOCATION_2}_${CLUSTER_2}"
    
  3. 新しく作成されたクラスタの場合は、次の gcloud コマンドを使用して、各クラスタの認証情報を取得する必要があります。そうしないと、関連する context をこのガイドの次のステップで使用できません。

    これらのコマンドは、クラスタのタイプ(リージョンまたはゾーン)によって異なります。

    リージョン

    gcloud container clusters get-credentials ${CLUSTER_1} --region ${LOCATION_1}
    gcloud container clusters get-credentials ${CLUSTER_2} --region ${LOCATION_2}
    

    ゾーン

    gcloud container clusters get-credentials ${CLUSTER_1} --zone ${LOCATION_1}
    gcloud container clusters get-credentials ${CLUSTER_2} --zone ${LOCATION_2}
    

ファイアウォール ルールを作成する

場合によっては、クラスタ間トラフィックを許可するファイアウォール ルールを作成する必要があります。たとえば、次のような場合にファイアウォール ルールを作成する必要があります。

  • メッシュ内のクラスタに異なるサブネットを使用している。
  • Pod が 443 と 15002 以外のポートを開く。

GKE は、同じサブネット内のトラフィックを許可するファイアウォール ルールを各ノードに自動的に追加します。メッシュに複数のサブネットが含まれている場合は、サブネット間トラフィックを許可するようにファイアウォール ルールを明示的に設定する必要があります。送信元 IP CIDR ブロックを許可し、すべての受信トラフィックのポートをターゲットにできるように、サブネットごとに新しいファイアウォール ルールを追加する必要があります。

次の手順では、プロジェクト内のすべてのクラスタ間、または $CLUSTER_1$CLUSTER_2 間の通信のみを許可します。

  1. クラスタのネットワークに関する情報を収集します。

    すべてのプロジェクト クラスタ

    クラスタが同じプロジェクト内にある場合は、次のコマンドを使用して、プロジェクト内のすべてのクラスタ間の通信を許可できます。公開したくないクラスタがプロジェクトにある場合は、[特定のクラスタ] タブでコマンドを使用します。

    function join_by { local IFS="$1"; shift; echo "$*"; }
    ALL_CLUSTER_CIDRS=$(gcloud container clusters list --project $PROJECT_1 --format='value(clusterIpv4Cidr)' | sort | uniq)
    ALL_CLUSTER_CIDRS=$(join_by , $(echo "${ALL_CLUSTER_CIDRS}"))
    ALL_CLUSTER_NETTAGS=$(gcloud compute instances list --project $PROJECT_1 --format='value(tags.items.[0])' | sort | uniq)
    ALL_CLUSTER_NETTAGS=$(join_by , $(echo "${ALL_CLUSTER_NETTAGS}"))
    

    特定のクラスタ

    次のコマンドを使用すると、$CLUSTER_1$CLUSTER_2 間の通信を許可します。プロジェクト内の他のクラスタは公開されません。

    function join_by { local IFS="$1"; shift; echo "$*"; }
    ALL_CLUSTER_CIDRS=$(for P in $PROJECT_1 $PROJECT_2; do gcloud --project $P container clusters list --filter="name:($CLUSTER_1,$CLUSTER_2)" --format='value(clusterIpv4Cidr)'; done | sort | uniq)
    ALL_CLUSTER_CIDRS=$(join_by , $(echo "${ALL_CLUSTER_CIDRS}"))
    ALL_CLUSTER_NETTAGS=$(for P in $PROJECT_1 $PROJECT_2; do gcloud --project $P compute instances list  --filter="name:($CLUSTER_1,$CLUSTER_2)" --format='value(tags.items.[0])' ; done | sort | uniq)
    ALL_CLUSTER_NETTAGS=$(join_by , $(echo "${ALL_CLUSTER_NETTAGS}"))
    
  2. ファイアウォール ルールを作成します。

    GKE

    gcloud compute firewall-rules create istio-multicluster-pods \
        --allow=tcp,udp,icmp,esp,ah,sctp \
        --direction=INGRESS \
        --priority=900 \
        --source-ranges="${ALL_CLUSTER_CIDRS}" \
        --target-tags="${ALL_CLUSTER_NETTAGS}" --quiet \
        --network=YOUR_NETWORK
    

    Autopilot

    TAGS=""
    for CLUSTER in ${CLUSTER_1} ${CLUSTER_2}
    do
        TAGS+=$(gcloud compute firewall-rules list --filter="Name:$CLUSTER*" --format="value(targetTags)" | uniq) && TAGS+=","
    done
    TAGS=${TAGS::-1}
    echo "Network tags for pod ranges are $TAGS"
    
    gcloud compute firewall-rules create asm-multicluster-pods \
        --allow=tcp,udp,icmp,esp,ah,sctp \
        --network=gke-cluster-vpc \
        --direction=INGRESS \
        --priority=900 --network=VPC_NAME \
        --source-ranges="${ALL_CLUSTER_CIDRS}" \
        --target-tags=$TAGS
    

クラスタ間のエンドポイント検出の構成

GKE クラスタ間でエンドポイント ディスカバリを構成するには、asmcli create-mesh を実行します。このコマンドは以下を行います。

  • すべてのクラスタを同じフリートに登録します。
  • フリート Workload Identity を信頼するようにメッシュを構成します。
  • リモート シークレットを作成します。

各クラスタの URI または kubeconfig ファイルのパスを指定できます。

クラスタ URI

次のコマンドで、FLEET_PROJECT_ID をフリート ホスト プロジェクトのプロジェクト ID に置き換え、クラスタ URI を各クラスタのクラスタ名、ゾーンまたはリージョン、プロジェクト ID に置き換えます。この例では 2 つのクラスタのみが示されていますが、このコマンドを実行して、GKE Hub サービスの制限の対象となる追加のクラスタでエンドポイントの検出を有効にできます。

./asmcli create-mesh \
    FLEET_PROJECT_ID \
    ${PROJECT_1}/${LOCATION_1}/${CLUSTER_1} \
    ${PROJECT_2}/${LOCATION_2}/${CLUSTER_2}

kubeconfig ファイル

次のコマンドで、FLEET_PROJECT_IDフリート ホスト プロジェクトのプロジェクト ID に、PATH_TO_KUBECONFIG を各 kubeconfig へのパスに置き換えます。この例では 2 つのクラスタのみが示されていますが、このコマンドを実行して、フリートに追加できるクラスタの最大数まで、追加のクラスタでエンドポイントの検出を有効にできます。

./asmcli create-mesh \
    FLEET_PROJECT_ID \
    PATH_TO_KUBECONFIG_1 \
    PATH_TO_KUBECONFIG_2

限定公開クラスタ用の承認済みネットワークの構成

次のすべての条件がメッシュで当てはまる場合のみ、このセクションに従います。

複数の限定公開クラスタをデプロイする場合、各クラスタの Anthos Service Mesh コントロール プレーンは、リモート クラスタの GKE コントロール プレーンを呼び出す必要があります。トラフィックを許可するには、呼び出し元のクラスタ内の Pod アドレス範囲をリモート クラスタの承認済みネットワークに追加する必要があります。

  1. 各クラスタの Pod IP CIDR ブロックを取得します。

    POD_IP_CIDR_1=`gcloud container clusters describe ${CLUSTER_1} --project ${PROJECT_1} --zone ${LOCATION_1} \
      --format "value(ipAllocationPolicy.clusterIpv4CidrBlock)"`
    
    POD_IP_CIDR_2=`gcloud container clusters describe ${CLUSTER_2} --project ${PROJECT_2} --zone ${LOCATION_2} \
      --format "value(ipAllocationPolicy.clusterIpv4CidrBlock)"`
    
  2. Kubernetes クラスタ Pod の IP CIDR ブロックをリモート クラスタに追加します。

    EXISTING_CIDR_1=`gcloud container clusters describe ${CLUSTER_1} --project ${PROJECT_1} --zone ${LOCATION_1} \
     --format "value(masterAuthorizedNetworksConfig.cidrBlocks.cidrBlock)"`
    gcloud container clusters update ${CLUSTER_1} --project ${PROJECT_1} --zone ${LOCATION_1} \
    --enable-master-authorized-networks \
    --master-authorized-networks ${POD_IP_CIDR_2},${EXISTING_CIDR_1//;/,}
    
    EXISTING_CIDR_2=`gcloud container clusters describe ${CLUSTER_2} --project ${PROJECT_2} --zone ${LOCATION_2} \
     --format "value(masterAuthorizedNetworksConfig.cidrBlocks.cidrBlock)"`
    gcloud container clusters update ${CLUSTER_2} --project ${PROJECT_2} --zone ${LOCATION_2} \
    --enable-master-authorized-networks \
    --master-authorized-networks ${POD_IP_CIDR_1},${EXISTING_CIDR_2//;/,}
    

    詳細については、承認済みネットワークを使用したクラスタの作成をご覧ください。

  3. 承認済みネットワークが更新されていることを確認します。

    gcloud container clusters describe ${CLUSTER_1} --project ${PROJECT_1} --zone ${LOCATION_1} \
     --format "value(masterAuthorizedNetworksConfig.cidrBlocks.cidrBlock)"
    
    gcloud container clusters describe ${CLUSTER_2} --project ${PROJECT_2} --zone ${LOCATION_2} \
     --format "value(masterAuthorizedNetworksConfig.cidrBlocks.cidrBlock)"
    

コントロール プレーンのグローバル アクセスを有効にする

次のすべての条件がメッシュで当てはまる場合のみ、このセクションに従います。

  • 限定公開クラスタを使用している。
  • メッシュ内のクラスタに異なるリージョンを使用している。

各クラスタで Anthos Service Mesh コントロール プレーンがリモート クラスタの GKE コントロール プレーンを呼び出すことができるように、コントロール プレーンのグローバル アクセスを有効にする必要があります。

  1. コントロール プレーンのグローバル アクセスを有効にします。

    gcloud container clusters update ${CLUSTER_1} --project ${PROJECT_1} --zone ${LOCATION_1} \
     --enable-master-global-access
    
    gcloud container clusters update ${CLUSTER_2} --project ${PROJECT_2} --zone ${LOCATION_2} \
     --enable-master-global-access
    
  2. コントロール プレーンのグローバル アクセスが有効になっていることを確認します。

    gcloud container clusters describe ${CLUSTER_1} --zone ${LOCATION_1}
    
    gcloud container clusters describe ${CLUSTER_2} --zone ${LOCATION_2}
    

    出力の privateClusterConfig セクションに masterGlobalAccessConfig のステータスが表示されます。

限定公開クラスタ間のエンドポイント ディスカバリを構成する

限定公開クラスタを使用する場合、パブリック IP にアクセスできないため、パブリック IP ではなくリモート クラスタのプライベート IP を構成する必要があります。

  1. 限定公開クラスタのプライベート IP を取得し、一時ファイルのシークレットでパブリック IP をそれに置き換えます。

    PRIV_IP=`gcloud container clusters describe "${CLUSTER_1}" --project "${PROJECT_1}" \
     --zone "${LOCATION_1}" --format "value(privateClusterConfig.privateEndpoint)"`
    
    ./istioctl x create-remote-secret --context=${CTX_1} --name=${CLUSTER_1} --server=https://${PRIV_IP} > ${CTX_1}.secret
    
    PRIV_IP=`gcloud container clusters describe "${CLUSTER_2}" --project "${PROJECT_2}" \
     --zone "${LOCATION_2}" --format "value(privateClusterConfig.privateEndpoint)"`
    
    ./istioctl x create-remote-secret --context=${CTX_2} --name=${CLUSTER_2} --server=https://${PRIV_IP} > ${CTX_2}.secret
    
  2. 新しいシークレットをクラスタに適用します。

    kubectl apply -f ${CTX_1}.secret --context=${CTX_2}
    
    kubectl apply -f ${CTX_2}.secret --context=${CTX_1}
    

デプロイを確認する

このセクションでは、サンプルの HelloWorld サービスと Sleep サービスをマルチクラスタ環境にデプロイして、クラスタ間でのロード バランシングが機能することを確認する方法について説明します。

サイドカー インジェクションを有効にする

  1. 次のコマンドを使用して、istiod サービスからリビジョン ラベルの値を探します。この値は、後のステップで使用します。

    kubectl -n istio-system get pods -l app=istiod --show-labels

    出力は次のようになります。

    NAME                                READY   STATUS    RESTARTS   AGE   LABELS
    istiod-asm-173-3-5788d57586-bljj4   1/1     Running   0          23h   app=istiod,istio.io/rev=asm-173-3,istio=istiod,pod-template-hash=5788d57586
    istiod-asm-173-3-5788d57586-vsklm   1/1     Running   1          23h   app=istiod,istio.io/rev=asm-173-3,istio=istiod,pod-template-hash=5788d57586

    出力の LABELS 列で、接頭辞 istio.io/rev= に続く istiod リビジョン ラベルの値をメモします。この例での値は asm-173-3 です。次のセクションの手順でリビジョンの値を使用します。

HelloWorld サービスをインストールする

  1. 各クラスタにサンプルの名前空間とサービス定義を作成します。次のコマンドにある REVISION を、前の手順でメモした istiod リビジョン ラベルに置き換えます。

    for CTX in ${CTX_1} ${CTX_2}
    do
        kubectl create --context=${CTX} namespace sample
        kubectl label --context=${CTX} namespace sample \
            istio-injection- istio.io/rev=REVISION --overwrite
    done
    

    ここで、REVISION は、以前にメモした istiod のリビジョン ラベルです。

    次のように出力されます。

    label "istio-injection" not found.
    namespace/sample labeled
    

    label "istio-injection" not found. は無視しても問題ありません

  2. 両方のクラスタに HelloWorld サービスを作成します。

    kubectl create --context=${CTX_1} \
        -f ${SAMPLES_DIR}/samples/helloworld/helloworld.yaml \
        -l service=helloworld -n sample
    
    kubectl create --context=${CTX_2} \
        -f ${SAMPLES_DIR}/samples/helloworld/helloworld.yaml \
        -l service=helloworld -n sample
    

各クラスタに HelloWorld v1 と v2 をデプロイする

  1. HelloWorld v1CLUSTER_1 にデプロイし、v2CLUSTER_2 にデプロイします。これは、後でクラスタ間の負荷分散を確認する際に役立ちます。

    kubectl create --context=${CTX_1} \
      -f ${SAMPLES_DIR}/samples/helloworld/helloworld.yaml \
      -l version=v1 -n sample
    kubectl create --context=${CTX_2} \
      -f ${SAMPLES_DIR}/samples/helloworld/helloworld.yaml \
      -l version=v2 -n sample
  2. 次のコマンドを使用して、HelloWorld v1v2 が実行されていることを確認します。次のような出力が表示されていることを確認します。

    kubectl get pod --context=${CTX_1} -n sample
    NAME                            READY     STATUS    RESTARTS   AGE
    helloworld-v1-86f77cd7bd-cpxhv  2/2       Running   0          40s
    kubectl get pod --context=${CTX_2} -n sample
    NAME                            READY     STATUS    RESTARTS   AGE
    helloworld-v2-758dd55874-6x4t8  2/2       Running   0          40s

スリープ サービスをデプロイする

  1. 両方のクラスタに Sleep サービスをデプロイします。この Pod は、デモ用の人為的なネットワーク トラフィックを生成します。

    for CTX in ${CTX_1} ${CTX_2}
    do
        kubectl apply --context=${CTX} \
            -f ${SAMPLES_DIR}/samples/sleep/sleep.yaml -n sample
    done
    
  2. 各クラスタで Sleep サービスが起動するまで待ちます。次のような出力が表示されていることを確認します。

    kubectl get pod --context=${CTX_1} -n sample -l app=sleep
    NAME                             READY   STATUS    RESTARTS   AGE
    sleep-754684654f-n6bzf           2/2     Running   0          5s
    kubectl get pod --context=${CTX_2} -n sample -l app=sleep
    NAME                             READY   STATUS    RESTARTS   AGE
    sleep-754684654f-dzl9j           2/2     Running   0          5s

クラスタ間の負荷分散を確認する

HelloWorld サービスを数回呼び出し、出力をチェックして v1 と v2 からの交互の返信を確認します。

  1. HelloWorld サービスを呼び出します。

    kubectl exec --context="${CTX_1}" -n sample -c sleep \
        "$(kubectl get pod --context="${CTX_1}" -n sample -l \
        app=sleep -o jsonpath='{.items[0].metadata.name}')" \
        -- curl -sS helloworld.sample:5000/hello
    

    出力は次のようになります。

    Hello version: v2, instance: helloworld-v2-758dd55874-6x4t8
    Hello version: v1, instance: helloworld-v1-86f77cd7bd-cpxhv
    ...
  2. HelloWorld サービスを再度呼び出します。

    kubectl exec --context="${CTX_2}" -n sample -c sleep \
        "$(kubectl get pod --context="${CTX_2}" -n sample -l \
        app=sleep -o jsonpath='{.items[0].metadata.name}')" \
        -- curl -sS helloworld.sample:5000/hello
    

    出力は次のようになります。

    Hello version: v2, instance: helloworld-v2-758dd55874-6x4t8
    Hello version: v1, instance: helloworld-v1-86f77cd7bd-cpxhv
    ...

負荷分散されたマルチクラスタの Anthos Service Mesh の検証はこれで完了です。

HelloWorld サービスをクリーンアップする

負荷分散の確認が完了したら、クラスタから HelloWorld サービスと Sleep サービスを削除します。

kubectl delete ns sample --context ${CTX_1}
kubectl delete ns sample --context ${CTX_2}