🍠

酸いも甘いもある Shared VPC(共有VPC) ~ GKE をShared VPC で構築する際の苦悩~

2024/02/29に公開

このブログは、【Google Cloud】GDG Tokyo Monthly Online Tech Talksにて発表した内容を元にしています。

登壇時の資料はこちらになります。

https://speakerdeck.com/parupappa2929/suan-imogan-imoarushared-vpc-gong-you-vpc-gkewoshared-vpcdegou-zhu-suruji-noku-nao

はじめに

Google Cloud Shared VPC(共有VPC)はネットワークリソースの集中管理ができる一方で、リソース利用に関して制約があったり、エンプラ企業をはじめとするサイロ化された組織では、各チームとのコミュケーションコストや調整コストが上がってしまうリソースでもあることを案件を通じて感じました。

そんな酸いも甘いもあるShared VPC上に、GKEを中心とした一般的なアプリケーションインフラを構築した際の苦悩(注意点)をお伝えできればと思います。

Shared VPCを検討している人や、Shared VPC上にGKEをコアとしたアプリケーションインフラを構築しようとする人に参考になれば幸いです。

Shared VPC(共有VPC)とは?

Google CloudのShared VPCを所有するプロジェクトをホストプロジェクト、Shared VPCを利用するプロジェクトをサービスプロジェクトと呼びます。

Shared VPCを使用することで、ホストプロジェクトから複数のサービスプロジェクトへのネットワークリソースの共有が可能となり、リソースの集中管理が実現します。

https://cloud.google.com/vpc/docs/shared-vpc?hl=ja

Shared VPCのユースケース

Shared VPCは、特に大規模な組織(エンプラ組織)や複数のチームが関与するプロジェクトでその価値を発揮します。

例えば、複数の開発チームが異なるプロジェクトを担当しているが、全てのチームが共通のネットワークリソース(サブネット、IPアドレス範囲、ファイアウォールルールなど)にアクセスする必要がある場合などです。

IAMロールとしてもネットワーク管理者(ネットワークチーム)やセキュリティ管理者(セキュリティチーム)があることから運用は、各チームや担当者が自分たちの担当領域を独自の運用で回していることが多いと思います。(もちろん例外はあると思いますが)

前提:アーキテクチャ

  • 一般的なAPIアプリケーションのバックエンドとなる構成
    • GKE Autopilot(v1.27)
  • サービスプロジェクトはterraformによる構成管理

ホストプロジェクトは(0から構築することがなかなかないため)すでに作成されており、新しくサービスプロジェクトとして、GKEを使用したアプリケーションインフラを構築することを想定します。

また、ホストプロジェクトはネットワークチームがネットワーク管理者として全てのリソース割り当てを管理しているものとし、サービスプロジェクトにて新規アプリケーションインフラの構築を行います。

ホストプロジェクトから作成する際の事例は以下が参考になるかと思います。
https://techblog.zozo.com/entry/gcp-shared-vpc

GKEをShared VPC環境で構築する際の技術的奮闘

サービスプロジェクトの接続

下記の通り、Shared VPCを使用する場合、ホストプロジェクトにサービスプロジェクトを接続する必要があり、場合によってはネットワーク管理者に依頼が必要でしょう。

サービスプロジェクトの管理者が共有 VPC を使用するには、サービスプロジェクトがホスト プロジェクトに接続されている必要があります。

https://cloud.google.com/vpc/docs/provisioning-shared-vpc?hl=ja#create-shared

Terraform実行のための、Google Cloud サービス アカウントの権限借用

Infrastructure-as-Code を最小権限で実行するにもあるように、IAM 使用時のセキュリティを徹底するために、Terraform のコードは最小権限の原則に沿って実行する必要があります。

ユーザーのアクセス権限を Terraform コードで制限する代わりに、サービス アカウントを使用して Terraform コードを実行することができます。

https://cloud.google.com/blog/ja/topics/developers-practitioners/using-google-cloud-service-account-impersonation-your-terraform-code

サービス アカウントによる認証は、以下2つの方法があります

  • サービス アカウント キーを生成
    • ただし、この方法のデメリットとして、キーを持つユーザーなら誰でもサービス アカウントを使って認証を受けれるという点があります。それを回避するために、定期的ローテーションでキーを管理し、API キーの制限を追加する必要があります。
  • サービス アカウントの権限借用
    • この方法の場合、サービス アカウント キーへのアクセスは不要で、代わりに IAM 権限を使用して、サービス アカウントの使用を承認します。この方法だと、ユーザー アカウントに与える権限を減らすことができます。ユーザーがサービス アカウントにアクセスする必要がなくなったら、Google Cloud IAM で、そのサービス アカウントのリソースからユーザー ID を削除するだけで済みます。
    • AWSではAssumeRoleのイメージ

https://blog.g-gen.co.jp/entry/using-google-cloud-service-account-impersonation

Shared VPCのホストプロジェクトのリソースを参照するための権限を持つSAを発行して、そのSAの権限を使ってTerraformからリソースの参照を行います。

https://cloud.google.com/blog/ja/topics/developers-practitioners/using-google-cloud-service-account-impersonation-your-terraform-code

上記記事の方法でterraform providerにSAの情報を紐づけ、SharedVPC関連のリソースを取得・作成する場合にproviderを指定して使用する。

provider "google" {
  project = "YOUR_PROJECT_ID"
  alias   = "impersonation"
  scopes  = [
    "https://www.googleapis.com/auth/cloud-platform",
    "https://www.googleapis.com/auth/userinfo.email",
  ]
}

# Network管理者に権限を付与してホストプロジェクトに対する権限を付与してもらうため、Service Accountは手動で作成し、dataで取得
data "google_service_account" "default" {
  account_id = "YOUR_SERVICE_ACCOUNT@YOUR_PROJECT.iam.gserviceaccount.com"
}

data "google_service_account_access_token" "default" {
  target_service_account = data.google_service_account.default.email
  scopes                 = ["userinfo-email", "cloud-platform"] 
  lifetime               = "3600s"
}

provier "google" {
  project         = "YOUR_PROJECT_ID"
 access_token    = data.google_service_account_access_token.default.access_token
  request_timeout = 60s
}

ホストプロジェクトでの権限付与

各サービスプロジェクトの GKE サービスアカウントには、ホストプロジェクトの Host サービス エージェント ユーザーのロールを割り当てる必要があります。

このバインディングにより、サービス プロジェクトの GKE サービス アカウントは、ホストプロジェクトの GKE サービス アカウントと同様に、ホストプロジェクトでネットワーク管理オペレーションを実行できます。

このロールは、サービス プロジェクトの GKE サービス アカウントにのみ付与できます。

GKE サービス アカウントの形式は次のとおりです。

service-SERVICE_PROJECT_NUM@container-engine-robot.iam.gserviceaccount.com

※**SERVICE_PROJECT_NUM** は、サービス プロジェクトのプロジェクト番号です。

https://cloud.google.com/kubernetes-engine/docs/how-to/cluster-shared-vpc?hl=ja#grant_host_service_agent_role

Shared VPC上でGKEクラスタを構成する場合には、まずこの記事を見ることを強くオススメします!

https://cloud.google.com/kubernetes-engine/docs/how-to/cluster-shared-vpc?hl=ja#overview

サービスプロジェクトでの権限付与

Terraform 実行者アカウントには、サービスアカウントトークン作成者の権限が必要です。

これは、terraform plan実行時に、サービスアカウントとしての認証で使用するアクセストークンを取得するためです。

また、サービスプロジェクトのGKEサービスアカウントには、Hostサービスエージェントユーザーのロールを付与します。

GKEサービスアカウントの形式は以下

service-SERVICE_PROJECT_NUM@container-engine-robot.iam.gserviceaccount.com

https://cloud.google.com/kubernetes-engine/docs/how-to/cluster-shared-vpc?hl=ja#grant_host_service_agent_role

GKEに必要なサブネットを払い出す

Shared VPCを使用している場合、GKEに必要なIPアドレスはShared VPのホストプロジェクトで管理されたものを使用します。

https://cloud.google.com/blog/ja/products/containers-kubernetes/ip-address-management-in-gke

それぞれに対して必要とされるCIDRを設計します。

  • Node
  • Pod
  • Service

GKEクラスタへの割り当てるCIDR設計は以下の記事がとても参考になります。

https://future-architect.github.io/articles/20191017/

また、Autopilot(GKE: v1.27までは)では、NodeあたりのPod数の上限が32となっているため、Standardモードよりも広いCIDRが必要となるので設計に注意

不連続のマルチ Pod CIDR

SecondaryのIPが足らなければ、不連続マルチ Pod CIDRを使い、Nodeレベルで必要なIPアドレスを拡張するという手法もあります。

https://cloud.google.com/kubernetes-engine/docs/how-to/multi-pod-cidr?hl=ja#how_discontiguous_multi-pod_cidr_works

ちなみに、ServiceレベルのCIDR拡張はKubernetes 1.29 でアルファ機能として搭載されています。

https://qiita.com/uesyn/items/7768c2adb426e6ad2864#apis-to-manage-ip-address-ranges-for-services-sig-network-ip-address-range-apis


VPC Service Controlsの設定

ホストプロジェクトとサービスプロジェクトを同じ境界にいれる必要があります。

ホストプロジェクトでの作業になるため、こちらもネットワーク管理者に依頼が必要です。

共有 VPC を使用する場合、共有 VPC ネットワークに属するプロジェクトを含むサービス境界には、そのネットワークをホストするプロジェクトも含まれる必要があります。共有 VPC ネットワークに属するプロジェクトがホスト プロジェクトと同じ境界にない場合、サービスは期待どおりに動作しないか、完全にブロックされる可能性があります。

共有 VPC ネットワーク ホストが、ネットワークに接続されているプロジェクトと同じサービス境界にあることを確認してください。

https://cloud.google.com/vpc-service-controls/docs/troubleshooting?hl=ja#shared_vpc

VPC Service Controlsが適用されている場合、プロジェクトに対するAPI操作の制限がかかっている場合があります。(組織のセキュリティポリシーによる)

例えば、terraformをはじめとする外部APIからの操作ができないなど、セキュアにする取り組みを施すことができます。

VPC Service Controleのトラブルシューティングを確認してCloud Loggingにログを見てみるとどこまで制限がかかっているのかわかると思います。

https://cloud.google.com/vpc-service-controls/docs/concepts

https://cloud.google.com/vpc-service-controls/docs/troubleshooting?hl=ja

ホストプロジェクトでFirewall Ruleを設定しないとGKE Nodeに対するヘルスチェックが通らない

デフォルトFirewallルールでは、サービスプロジェクトに所属するGKEノードへのFirewallルールが許可されていないため、ホストプロジェクトにFirewallルールを作成します。(こちらもネットワーク管理者への依頼が必要です)

または、適切なIAMロールを付与することで、サービスプロジェクトのGKE クラスタで、ホスト プロジェクトのファイアウォール リソースを作成して管理することも出来ます。

https://cloud.google.com/kubernetes-engine/docs/how-to/cluster-shared-vpc?hl=ja#managing_firewall_resources

限定公開クラスタのPodのインターネット接続には、ホストプロジェクト内にCloud Natが必要

NodeのサブネットをホストプロジェクトのCloud Natのマッピングに含める必要あります。

https://cloud.google.com/nat/docs/nat-product-interactions?hl=ja#interaction-shared-vpc

例えば、Docker Hubなど外部のレジストリからDockerコンテナを取得できるようにCloud NATの対象サブネットにノードのサブネットを追加します。(こちらもネットワーク管理者への依頼が必要です)

Shared VPCの一般的なアプリケーションリソースへの制約

Memorystore for Redis

Shared VPCを使用している場合の、Memorystore for Redisの接続方法は、Private Service Accessのみになります。

https://cloud.google.com/memorystore/docs/redis/networking?hl=ja#choosing_a_connection_mode

resource "google_redis_instance" "memorystore_redis" {
  name               = "${var.prefix}-${var.service}-redis-instance-${var.env}"
  region             = var.region
  tier               = "STANDARD_HA"
  connect_mode       = "PRIVATE_SERVICE_ACCESS"
  authorized_network = "${var.your-shared-vpc}"
  redis_version      = "REDIS_7_0"
  read_replicas_mode = "READ_REPLICAS_ENABLED"
  replica_count      = var.redis_replicas

  reserved_ip_range  = "${var.private-service-access-subnet}"
  secondary_ip_range = "${var.private-service-access-subnet}"
  memory_size_gb     = 5
}

Private Service Access用にIPアドレスを確保してもらう必要があります(CIDR範囲は/24~/29)

https://cloud.google.com/memorystore/docs/redis/establish-connection?hl=ja

https://cloud.google.com/vpc/docs/private-services-access?hl=ja#example

まとめ

Shared VPCでGKEを構築する際には、さまざまな考慮すべき事項~~(苦悩)~~があります。

ネットワークリソースの一元管理やサイロ化された組織での運用には合致している点もありますが、構築おける一定の奮闘も必要であるということを理解した上で、取り組む必要があります。

参考

GitHubで編集を提案

Discussion