Anthos Service Mesh でのマルチクラスタ GKE 構成

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

マルチクラスタの Anthos Service Mesh 構成を使用すると、大規模な組織で重要な課題(スケール、ロケーション、分離など)を解決できます。詳細については、マルチクラスタのユースケースをご覧ください。また、サービス メッシュを最大限活用するため、アプリケーションを最適化する必要があります。詳細については、Anthos Service Mesh 用のアプリケーションの準備をご覧ください。

前提条件

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

  • Anthos Service Mesh バージョン 1.6.8 以降がクラスタにインストールされていること。
    • クラスタが同じプロジェクトにある場合、インストールの概要を参照して、必要なバージョンでクラスタをインストールまたはアップグレードします。
    • クラスタが別のプロジェクトにある場合、複数プロジェクトのインストールと移行の説明に従って、クラスタをインストールまたは必要なバージョンにクラスタをアップグレードします。
  • 異なるプロジェクトのクラスタを追加する場合は、asm-gcp-multiproject プロファイルを使用してクラスタをインストールする必要があります。また、共有 VPC 構成でクラスタを同じネットワークに接続する必要があります。1 つのプロジェクトに共有 VPC をホストし、2 つのサービス プロジェクトでクラスタを作成することをおすすめします。詳細については、共有 VPC を使用したクラスタの設定をご覧ください。
  • Citadel CA を使用する場合は、両方のクラスタで同じカスタムルート CA を使用します。
  • Anthos Service Mesh が限定公開クラスタ上に構築されている場合は、同じ VPC に単一のサブネットを作成することをおすすめします。それ以外の場合は、次のことを確認します。
    1. コントロール プレーンが、クラスタのプライベート IP を使用してリモート プライベート クラスタ コントロール プレーンに到達できる。
    2. リモート コントロール クラスタの承認済みネットワークに、呼び出し元のコントロール プレーンの IP 範囲を追加できる。詳細については、限定公開クラスタ間のエンドポイント検出の構成をご覧ください。

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

  1. 便宜上、作業フォルダを設定します。このフォルダは、前提条件の Anthos Service Mesh のインストールの準備で Anthos Service Mesh ファイルをダウンロードして解凍したフォルダです。

    export PROJECT_DIR=YOUR_WORKING_FOLDER
  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}
    gcloud container clusters get-credentials ${CLUSTER_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
    

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

次のコマンドを使用して、クラスタ間の負荷分散にエンドポイント検出を構成します。この手順では、以下のタスクを実行します。

  • istioctl コマンドを実行して、クラスタに Kube API サーバーへのアクセスを許可するシークレットを作成します。
  • kubectl コマンドを実行してシークレットを別のクラスタに適用し、2 番目のクラスタが最初のクラスタからサービス エンドポイントを読み取るようにします。
istioctl x create-remote-secret --context=${CTX_1} --name=${CLUSTER_1} | \
  kubectl apply -f - --context=${CTX_2}
istioctl x create-remote-secret --context=${CTX_2} --name=${CLUSTER_2} | \
  kubectl apply -f - --context=${CTX_1}

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

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

  1. パブリック IP を含むシークレットを一時ファイルに書き込みます。

    istioctl x create-remote-secret --context=${CTX_1} --name=${CLUSTER_1} > ${CTX_1}.secret
    
    istioctl x create-remote-secret --context=${CTX_2} --name=${CLUSTER_2} > ${CTX_2}.secret
    
  2. 限定公開クラスタのプライベート IP を取得し、一時ファイルのシークレットでパブリック IP をそれに置き換えます。

    IFS="_" read -r -a VALS <<< ${CTX_1}
    PROJECT_1=${VALS[1]}
    LOCATION_1=${VALS[2]}
    CLUSTER_1=${VALS[3]}
    PRIV_IP=`gcloud container clusters describe "${CLUSTER_1}" --project "${PROJECT_1}" \
        --zone "${LOCATION_1}" --format "value(privateClusterConfig.privateEndpoint)"`
    sed -i 's/server\:.*/server\: https:\/\/'"${PRIV_IP}"'/' ${CTX_1}.secret
    
    IFS="_" read -r -a VALS <<< ${CTX_2}
    PROJECT_2=${VALS[1]}
    LOCATION_2=${VALS[2]}
    CLUSTER_2=${VALS[3]}
    PRIV_IP=`gcloud container clusters describe "${CLUSTER_2}" --project "${PROJECT_2}" \
        --zone "${LOCATION_2}" --format "value(privateClusterConfig.privateEndpoint)"`
    sed -i 's/server\:.*/server\: https:\/\/'"${PRIV_IP}"'/' ${CTX_2}.secret
    
  3. 新しいシークレットをクラスタに適用します。

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

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

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

Anthos Service Mesh に複数のクラスタをデプロイする場合、各クラスタ内の Istiod は、リモート クラスタの 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)"
    

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

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

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

各クラスタで istiod がリモート クラスタの 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 のステータスが表示されます。

デプロイを確認する

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

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

  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 サービスをインストールする

各クラスタにサンプルの名前空間とサービス定義を作成します。次のコマンドにある 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. は無視しても問題ありません

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

    kubectl create --context=${CTX_1} \
        -f ${PROJECT_DIR}/samples/helloworld/helloworld.yaml \
        -l service=helloworld -n sample
    
    kubectl create --context=${CTX_2} \
        -f ${PROJECT_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 ${PROJECT_DIR}/samples/helloworld/helloworld.yaml \
      -l version=v1 -n sample
    kubectl create --context=${CTX_2} \
      -f ${PROJECT_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 ${PROJECT_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}