GKE でマルチクラスタ メッシュを設定する
このガイドでは、Mesh CA または Istio CA を使用して 2 つのクラスタを単一の Cloud Service Mesh に追加し、クラスタ間で負荷分散を行う方法について説明します。このプロセスを拡張することで、任意の数のクラスタをメッシュに組み込むことができます。
マルチクラスタの Cloud Service Mesh 構成を使用すると、大規模な組織で重要な課題(スケール、ロケーション、分離など)を解決できます。詳細については、マルチクラスタのユースケースをご覧ください。
前提条件
このガイドでは、次の要件を満たす Google Cloud GKE クラスタが 2 つ以上存在することを前提としています。
asmcli install
を使用してクラスタに Cloud Service Mesh バージョン 1.11 以降がインストールされている。asmcli
、istioctl
ツール、asmcli install
を実行したときに--output_dir
で指定したディレクトリにasmcli
がダウンロードするサンプルが必要です。設定する必要がある場合は、依存ツールのインストールとクラスタの検証の手順に沿って操作します。Cloud Service Mesh を構成する前に、メッシュ内のクラスタがすべての Pod に接続する必要があります。また、異なるプロジェクトのクラスタを追加する場合は、同じフリート ホスト プロジェクトにクラスタを登録する必要があります。また、共有 VPC 構成でクラスタを同じネットワークに接続する必要があります。1 つのプロジェクトに共有 VPC をホストし、2 つのサービス プロジェクトでクラスタを作成することをおすすめします。詳細については、共有 VPC を使用したクラスタの設定をご覧ください。
Istio CA を使用する場合は、両方のクラスタで同じカスタムルート証明書を使用します。
Cloud Service Mesh が限定公開クラスタ上に構築されている場合は、同じ VPC に単一のサブネットを作成することをおすすめします。それ以外の場合は、次のことを確認します。
- コントロール プレーンが、クラスタのプライベート IP を使用してリモート プライベート クラスタ コントロール プレーンに到達できる。
- リモート コントロール クラスタの承認済みネットワークに、呼び出し元のコントロール プレーンの IP 範囲を追加できる。詳細については、限定公開クラスタ間のエンドポイント検出の構成をご覧ください。
API サーバーは、マルチクラスタ メッシュ内の Cloud Service Mesh コントロール プレーンの他のインスタンスから到達可能である必要があります。
- クラスタでグローバル アクセスが有効になっていることを確認します。
- マスター承認済みネットワークで、許可リストによって Cloud Service Mesh コントロール プレーンの IP が適切に許可されていることを確認します。
プロジェクトとクラスタ変数を設定する
プロジェクト 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}"
新しく作成されたクラスタの場合は、次の
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
間の通信のみを許可します。
クラスタのネットワークに関する情報を収集します。
すべてのプロジェクト クラスタ
クラスタが同じプロジェクト内にある場合は、次のコマンドを使用して、プロジェクト内のすべてのクラスタ間の通信を許可できます。公開したくないクラスタがプロジェクトにある場合は、[特定のクラスタ] タブでコマンドを使用します。
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}"))
ファイアウォール ルールを作成します。
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
エンドポイント ディスカバリを構成する
エンドポイント ディスカバリの構成に必要な手順は、宣言型 API をフリート内のクラスタ間で使用するか、一般公開クラスタまたは限定公開クラスタで手動で有効化するかどうかによって異なります。
宣言型 API を使用してクラスタ間のエンドポイント ディスカバリを有効にする(プレビュー版)
asm-options
configmap で構成 "multicluster_mode":"connected"
を適用して、フリート内のクラスタ間でエンドポイント ディスカバリを有効にすることもできます。この構成を同じフリートで有効にした場合、クラスタ間でクラスタ間サービス ディスカバリが自動的に有効になります。
この方法は、すべてのリリース チャンネルのマネージド Cloud Service Mesh のインストールで使用できます。続行する前に、ファイアウォール ルールを作成しておく必要があります。
複数のプロジェクトで、FLEET_PROJECT_ID.svc.id.goog
がまだ trustDomainAliases
に存在しない場合は、リビジョンの meshConfig
に手動で追加する必要があります。
有効にする
クラスタに asm-options
configmap がすでに存在する場合は、クラスタのエンドポイント ディスカバリを有効にします。
kubectl patch configmap/asm-options -n istio-system --type merge -p '{"data":{"multicluster_mode":"connected"}}'
クラスタに asm-options
configmap がまだ存在しない場合は、関連データを使用して作成し、クラスタのエンドポイント ディスカバリを有効にします。
kubectl --context ${CTX_1} create configmap asm-options -n istio-system --from-file <(echo '{"data":{"multicluster_mode":"connected"}}')
無効にする
クラスタのエンドポイント ディスカバリを無効にします。
kubectl patch configmap/asm-options -n istio-system --type merge -p '{"data":{"multicluster_mode":"manual"}}'
エンドポイント ディスカバリを無効にせずにフリートからクラスタの登録を解除した場合、シークレットがクラスタに残る場合があります。残りのシークレットは手動でクリーンアップする必要があります。
クリーンアップが必要なシークレットを確認するには、次のコマンドを実行します。
kubectl get secrets -n istio-system -l istio.io/owned-by=mesh.googleapis.com,istio/multiCluster=true
各シークレットを削除します。
kubectl delete secret SECRET_NAME
残りのシークレットごとに、この手順を繰り返します。
一般公開クラスタ間のエンドポイント ディスカバリを構成する
GKE クラスタ間でエンドポイント ディスカバリを構成するには、asmcli create-mesh
を実行します。このコマンドは以下を行います。
- すべてのクラスタを同じフリートに登録します。
- フリート Workload Identity を信頼するようにメッシュを構成します。
- リモート シークレットを作成します。
各クラスタの URI または kubeconfig ファイルのパスを指定できます。
クラスタ URI
次のコマンドで、FLEET_PROJECT_ID
をフリート ホスト プロジェクトのプロジェクト ID に置き換え、クラスタ URI を各クラスタのクラスタ名、ゾーンまたはリージョン、プロジェクト ID に置き換えます。この例では 2 つのクラスタのみが示されていますが、このコマンドを実行して、フリートに追加できるクラスタの最大数の制限対象となる追加のクラスタでエンドポイントの検出を有効にできます。
./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
限定公開クラスタ間のエンドポイント検出の構成
API サーバーが他のクラスタの Cloud Service Mesh のコントロール プレーンにアクセスできるように、リモート シークレットを構成します。このコマンドは、Cloud Service Mesh のタイプ(クラスタ内またはマネージド)によって異なります。
A. クラスタ内 Cloud Service Mesh では、パブリック IP にアクセスできないため、パブリック 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
B. マネージド Cloud Service Mesh の場合:
PUBLIC_IP=`gcloud container clusters describe "${CLUSTER_1}" --project "${PROJECT_1}" \ --zone "${LOCATION_1}" --format "value(privateClusterConfig.publicEndpoint)"` ./istioctl x create-remote-secret --context=${CTX_1} --name=${CLUSTER_1} --server=https://${PUBLIC_IP} > ${CTX_1}.secret
PUBLIC_IP=`gcloud container clusters describe "${CLUSTER_2}" --project "${PROJECT_2}" \ --zone "${LOCATION_2}" --format "value(privateClusterConfig.publicEndpoint)"` ./istioctl x create-remote-secret --context=${CTX_2} --name=${CLUSTER_2} --server=https://${PUBLIC_IP} > ${CTX_2}.secret
新しいシークレットをクラスタに適用します。
kubectl apply -f ${CTX_1}.secret --context=${CTX_2}
kubectl apply -f ${CTX_2}.secret --context=${CTX_1}
限定公開クラスタ用の承認済みネットワークの構成
次のすべての条件がメッシュで当てはまる場合のみ、このセクションに従います。
- 限定公開クラスタを使用している。
- クラスタが同じサブネットに属していない。
- クラスタで承認済みネットワークが有効になっている。
複数の限定公開クラスタをデプロイする場合、各クラスタの Cloud Service Mesh コントロール プレーンは、リモート クラスタの GKE コントロール プレーンを呼び出す必要があります。トラフィックを許可するには、呼び出し元のクラスタ内の Pod アドレス範囲をリモート クラスタの承認済みネットワークに追加する必要があります。
各クラスタの 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)"`
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//;/,}
詳細については、承認済みネットワークを使用したクラスタの作成をご覧ください。
承認済みネットワークが更新されていることを確認します。
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)"
コントロール プレーンのグローバル アクセスを有効にする
次のすべての条件がメッシュで当てはまる場合のみ、このセクションに従います。
- 限定公開クラスタを使用している。
- メッシュ内のクラスタに異なるリージョンを使用している。
各クラスタで Cloud Service Mesh コントロール プレーンがリモート クラスタの GKE コントロール プレーンを呼び出すことができるように、コントロール プレーンのグローバル アクセスを有効にする必要があります。
コントロール プレーンのグローバル アクセスを有効にします。
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
コントロール プレーンのグローバル アクセスが有効になっていることを確認します。
gcloud container clusters describe ${CLUSTER_1} --project ${PROJECT_1} --zone ${LOCATION_1}
gcloud container clusters describe ${CLUSTER_2} --project ${PROJECT_2} --zone ${LOCATION_2}
出力の
privateClusterConfig
セクションにmasterGlobalAccessConfig
のステータスが表示されます。
マルチクラスタ接続を確認する
このセクションでは、サンプルの HelloWorld
サービスと Sleep
サービスをマルチクラスタ環境にデプロイして、クラスタ間でのロード バランシングが機能することを確認する方法について説明します。
サンプル ディレクトリの変数を設定する
asmcli
がダウンロードされた場所に移動し、次のコマンドを実行してASM_VERSION
を設定します。export ASM_VERSION="$(./asmcli --version)"
作業フォルダを、クラスタ間のロード バランシングが正常に機能することを確認するために使用するサンプルに設定します。サンプルは、
asmcli install
コマンドで指定した--output_dir
ディレクトリのサブディレクトリにあります。次のコマンドで、OUTPUT_DIR
を、--output_dir
で指定したディレクトリに変更します。export SAMPLES_DIR=OUTPUT_DIR/istio-${ASM_VERSION%+*}
サイドカー インジェクションを有効にする
リビジョン ラベルの値を特定します。この値は、後の手順で使用します。手順は Cloud Service Mesh のタイプ(マネージドまたはクラスタ内)によって異なります。
管理対象
次のコマンドを使用して、リビジョン ラベルを特定します。このラベルは、後の手順で使用します。
kubectl get controlplanerevision -n istio-system
出力は次のようになります。
NAME RECONCILED STALLED AGE asm-managed-rapid True False 89d
出力の
NAME
列にあるリビジョン ラベルの値をメモします。この例では、値はasm-managed-rapid
です。次のセクションの手順でリビジョンの値を使用します。クラスタ内
次のコマンドを使用して、リビジョン ラベルを特定します。このラベルは、後の手順で使用します。
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.
は無視しても問題ありません両方のクラスタに 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 をデプロイする
HelloWorld v1
をCLUSTER_1
にデプロイし、v2
をCLUSTER_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
次のコマンドを使用して、
HelloWorld v1
とv2
が実行されていることを確認します。次のような出力が表示されていることを確認します。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
スリープ サービスをデプロイする
両方のクラスタに
Sleep
サービスをデプロイします。この Pod は、デモ用の人為的なネットワーク トラフィックを生成します。for CTX in ${CTX_1} ${CTX_2} do kubectl apply --context=${CTX} \ -f ${SAMPLES_DIR}/samples/sleep/sleep.yaml -n sample done
各クラスタで
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 からの交互の返信を確認します。
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}')" \ -- /bin/sh -c 'for i in $(seq 1 20); do curl -sS helloworld.sample:5000/hello; done'
出力は次のようになります。
Hello version: v2, instance: helloworld-v2-758dd55874-6x4t8 Hello version: v1, instance: helloworld-v1-86f77cd7bd-cpxhv ...
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}')" \ -- /bin/sh -c 'for i in $(seq 1 20); do curl -sS helloworld.sample:5000/hello; done'
出力は次のようになります。
Hello version: v2, instance: helloworld-v2-758dd55874-6x4t8 Hello version: v1, instance: helloworld-v1-86f77cd7bd-cpxhv ...
負荷分散されたマルチクラスタの Cloud Service Mesh の検証はこれで完了です。
HelloWorld サービスをクリーンアップする
ロード バランシングの確認が完了したら、クラスタから HelloWorld
サービスと Sleep
サービスを削除します。
kubectl delete ns sample --context ${CTX_1} kubectl delete ns sample --context ${CTX_2}