TECH PLAY

株式会社G-gen

株式会社G-gen の技術ブログ

744

G-gen の堂原です。Google Cloud (旧称 GCP) のマネージドなリモート開発環境である Cloud Workstations を解説します。 概要 Cloud Workstations とは 利用イメージ メリット コンポーネント 概要 Workstation cluster Workstation configuration Workstation 料金 種類 Compute Engine VM インスタンス 及び 永続ディスク Workstation 管理費用 クラスタ料金 コンテナイメージ 概要 IDE Code-OSS で構築されたベースエディタ JetBrains IDE ベースのエディタ Google Cloud サービスとの連携 概要 例 : Cloud Source Repositories で管理しているリポジトリをクローン ネットワーク プライベートクラスタ Workstation のパブリック IP アドレス 起動・停止オプション Quick start workstations Auto Sleep セキュリティ IAM ファイアウォールルール Workstation のセキュリティオプション 概要 Cloud Workstations とは Cloud Workstations は Google Cloud において、マネージドなリモート開発環境を提供するサービスです。 Cloud Workstations では、使用するコンテナイメージやマシンタイプなどを定義した構成設定を事前に用意し、その設定に基づいて必要な数だけ開発環境を作成します。開発者は IAM で許可された開発環境にブラウザやローカルの IDE を用いて接続することができます。 利用イメージ Cloud Workstations を使うと、例えばブラウザから下図のような IDE を起動できます。Visual Studio Code を使われている方は馴染み深い画面ではないでしょうか。 ブラウザで起動した IDE メリット Cloud Workstations を開発環境として利用することで、以下のようなメリットがあります。 一貫した開発環境を共有でき、開発者ごとでライブラリの有無やバージョンに差が生じることがなくなる。 ローカルに開発環境を準備することなく、必要なときにすぐに環境を用意できる。 VPC 内に Compute Engine VM インスタンスとして作成されるため、セキュアな環境が構築できる。 コンポーネント 概要 Cloud Workstations は主に 3 つのコンポーネント Workstation cluster 、 Workstation configuration 及び Workstation から構成されており、下図のような構成となっています。 Cloud Workstations の構成 参考 : Cloud Workstations の概要 Workstation cluster Workstation cluster は、Workstation configuration と Workstation をグルーピングするコンポーネントです。 Workstation cluster は VPC とリージョン、すなわちサブネットを指定して作成します。 パブリック IP アドレスでクラスタに接続できる パブリッククラスタ と、プライベート IP アドレスでしか接続できない プライベートクラスタ の 2 種類存在します。 クラスタ毎に Controller と Gateway が存在しており、ユーザはこれらを通して Workstation に接続します。 Workstation configuration Workstation configuration は、簡単に言うと Workstation (後述) のテンプレートです。Workstation を構成する以下のような設定を定義します。 コンテナイメージ 環境変数や ENTRYPOINT の設定も可能 マシンタイプ 永続ディスク サービスアカウント このサービスアカウントは Workstation 起動時に使用されるもので、開発環境から各 Google Cloud のサービスにアクセスする際は改めて認証設定が必要です。 Workstation Workstation は、Workstation configuration を基に作成される実際の開発環境です。実体は Compute Engine VM インスタンスとその中にデプロイされたコンテナで、起動中は Compute Engine のコンソール画面にて Workstation を構成しているインスタンスが確認できます。 インスタンスは起動時に、Workstation configuration で指定されたコンテナイメージを取得してコンテナを起動し、IDE やプログラム言語の設定・ライブラリはそのコンテナの中に存在するかたちとなります。 Workstation はいつでも起動・停止が可能で、停止するとインスタンスは削除されますが、作業データ自体は /home にマウントされた永続ディスクに保存されます。 Workstation が削除される際は同時に永続ディスクも削除されますが、設定で永続ディスクのみ残しておくことも可能です。 料金 種類 Cloud Workstations においては、大きく分けて 4 種類の料金が発生します。 Compute Engine VM インスタンス 永続ディスク Workstation 管理費 クラスタ料金 料金表は以下の公式ページを参照ください。 参考 : Cloud Workstations pricing Compute Engine VM インスタンス 及び 永続ディスク Compute Engine VM インスタンス及び 永続ディスクの料金は、各サービスの本来の料金に基づいて計算されます。 Workstation が停止中の間はインスタンスは作成されないので、インスタンスの料金は発生しません。 一方、永続ディスクは作業データの保存のため、Workstation が停止中の間も削除はされず残り続けます。 ただし、インスタンスのブートディスク (デフォルトだとディスクタイプは SSD 永続ディスクで、サイズは 50 GB) は Workstations 稼働中のみ作成され、停止中は削除されます。 Workstation 管理費用 Workstation 管理費用は起動中の Workstation の vCPU の数に対して発生します。 2023 年 6 月現在では、1 vCPU につき 0.05 USD ですので、例えば e2-standard-2 (2 vCPU) を 8 時間起動したとすると料金は 0.8 USD となります。 0.05 [USD] * 2 [vCPU] * 8 [時間] = 0.8 [USD] クラスタ料金 Workstation の状態に関わらず、クラスタ毎に毎時の料金が発生します。 コンテナイメージ 概要 Cloud Workstations においては、必要な IDE やプログラムの設定・ライブラリをコンテナイメージとし、このコンテナイメージをデプロイすることで開発環境を構築します。 いくつか コンテナイメージ が予め用意されており、そのイメージをそのまま使用することができます。 あるいは ベースイメージ をもとにカスタムのコンテナイメージを作成し、使用することもできます。 この場合、カスタムのコンテナイメージは Container Registry または Artifact Registry にプッシュしておく必要があります。 IDE 提供されている IDE には大きく分けて以下の 2 種類があります。 いずれの IDE にも、拡張機能として Cloud Code がプリインストールされています。 Code-OSS で構築されたベースエディタ Visual Studio Code で有名な Code-OSS で構築されたベースエディタには、以下の方法で接続することが可能です。 ブラウザ : Cloud Workstations のコンソール画面から接続可能 Visual Studio Code : SSH を利用した接続が可能( 手順が記載された公式ドキュメント ) JetBrains IDE ベースのエディタ IntelliJ IDEA や PyCharm など、各プログラム言語用に構成されたコンテナイメージが用意されています。 接続には JetBrains Gateway を用いることになります。 Google Cloud サービスとの連携 概要 Cloud Workstations の workstation からは、以下のような他の Google Cloud サービスとの連携が可能です。 Cloud Source Repositories で管理しているリポジトリをクローン Cloud Storage や BigQuery からデータを取得 これらの他サービスには、API に対する認証・認可が必要です。Workstation configuration ではサービスアカウントを指定しますが、このサービスアカウントはあくまで Workstation の起動に用いられるもので、他の Google Cloud サービスへの認証・認可には利用できません。 これはインスタンスにアタッチされたサービスアカウントの認証情報が、インスタンス内にデプロイされているコンテナの中までは伝搬されないためです。 そのため他の Google Cloud サービスと連携をする場合は、Workstation に接続後、権限付与された Google アカウントで認証をする必要があります。 認証情報は基本的にホームディレクトリ配下に保存され、また Workstation の /home には永続ディスクがマウントされることから、同じ Workstation を使用する限りは認証は一度行えば大丈夫です。 例 : Cloud Source Repositories で管理しているリポジトリをクローン 以下は、Cloud Source Repositories で管理しているリポジトリを Workstation にクローンする手順です。 リポジトリは予め作成済みとします。 Workstation にアクセスする Cloud Source Repositories への接続に使用する Google アカウントに 権限 を付与 ターミナルを起動し Google アカウントで認証 gcloud auth login --no-launch-browser リポジトリのクローンを作成 gcloud source repos clone ${Gitリポジトリ名} --project=${Gitリポジトリが存在するプロジェクトのID} /home 配下にクローンするのならば、本手順も一度実施すれば大丈夫で、次回 Workstation を起動した際も環境は維持されます。 参考 : Google Cloud CLI でユーザーとして認証する 参考 : gcloud CLI を使用してクローンを作成する ネットワーク プライベートクラスタ クラスタをプライベートクラスタとして設定すると、パブリック IP アドレスを用いた Workstation への接続が不可能になり、Private Service Connect を設定した VPC からのみ接続可能となります。 オンプレから接続する場合は、Cloud VPN や Cloud Interconnect を用いることになります。 注意点として、本設定は Cloud Workstations 経由での接続を管理するものであり、Workstation を構成する Compute Engine VM インスタンスへの直接の接続 や、 Workstation から外部への通信 を制御するものではありません。 参考 : プライベート ゲートウェイを作成する Workstation のパブリック IP アドレス 各 Workstation がパブリック IP アドレスを有するかどうかの設定が可能です。 パブリック IP アドレスを付与しない場合は、Container Registry または Artifact Registry にアクセスさせるために、Private Google Access または Cloud NAT を設定する必要があります。 起動・停止オプション Quick start workstations Workstation configuration 毎に、指定した数だけを Workstation を高速に起動することができるオプションです。 通常、停止中の Workstation は起動に数分の時間が必要です。 ところがこの設定をしておけば、早い場合は 10 秒ほどで起動状態とすることが可能です。 ただしこの設定で指定した数だけ、 常時 Workstation が起動しているときと同等の料金が発生します 。 仕組みとして、指定した数だけ常に Compute Engine VM インスタンスが起動しているためとなります。 つまり利便性とコストのトレードオフとなります。 また、現在起動している Workstation の数に関係なく常に指定した数分のインスタンスが待機しています。 例えば Quick start workstations で 2 台と指定し、更に 3 台の Workstation が起動している場合は、5 台分の Workstation の料金が発生します。 もちろん、4 台目の Workstation を起動する時は待機していたインスタンスが使用され(高速に起動)、追加で 1 台のインスタンスが待機用として作成されます。 Auto Sleep 指定した時間の間、Workstation にアクセスがなかった場合、その Workstation を自動的に停止中にするオプションです。 本オプションを設定しておくことで、停止忘れによる余分な料金発生を抑制することができます。 セキュリティ IAM Workstation configuration または Workstation 単位で、Workstation にアクセスできるユーザを管理することができます。 これによって開発者毎に適切な開発環境のみを提供することができ、管理が容易となります。 参考 : IAM を使用したアクセス制御 ファイアウォールルール Workstation への接続はクラスタに紐付いている Controller 及び Gateway を用いて行われることから、各 Workstation に対するインバウンドの通信をファイアウォールルールで許可する必要がなくなります。 そのため、直接 Compute Engine VM インスタンスへの SSH 接続は制限することがベストプラクティスとなります。 参考 : 直接 SSH アクセスを制限する Workstation のセキュリティオプション Workstation の実体が Compute Engine VM インスタンスであることから、以下のような Compute Engine のセキュリティオプションを利用することができます。 Confidential VM Shielded VM これらのオプションについては以下の記事で紹介しています。 blog.g-gen.co.jp 堂原 竜希 (記事一覧) クラウドソリューション部データアナリティクス課。2023年4月より、G-genにジョイン。 Google Cloud Partner Top Engineer 2023, 2024に選出 (2024年はRookie of the yearにも選出)。休みの日はだいたいゲームをしているか、時々自転車で遠出をしています。 Follow @ryu_dohara
アバター
G-gen の藤岡です。Terraform で VPC を作成し、デフォルトルートを削除するオプションを有効にし再度実行した際、Terraform 上ではエラーとならないものの、実際のデフォルトルートのリソースは削除されませんでした。今回はその事象の原因と解決方法を紹介します。 前提知識 実施内容 やりたかったこと 初回の VPC 作成 デフォルトルート削除の試行 事象 原因 解決策 デフォルトルート削除による影響 インターネットアクセス 内部リソースへのアクセス 限定公開の Google アクセス 前提知識 VPC のルートには以下の 4 種類があります。 システム生成ルート カスタムルート ピアリングルート ポリシーベースのルート このうち、システム生成ルートは VPC 作成時に自動で作成されるリソースです。この中には当記事で扱う システム生成のデフォルトルート が含まれています。これは 0.0.0.0/0 へのパケットを VPC のデフォルトインターネットゲートウェイに向けるルートです。 0.0.0.0/0 へのルートがあることにより、セキュリティリスクやトラフィックの誤送信、ネットワーク効率の低下などの問題もあります。そのため、ユースケースによっては削除することが好ましい場合があります。 参考: ルート 実施内容 やりたかったこと Terraform で Virtual Private Cloud (以下 VPC) を作成した後に、Terraform の delete_default_routes_on_create オプションを true にすることで自動生成されるデフォルトルート ( 0.0.0.0 ) を削除しようとしました。 想定としては、 true にすることで、既存のデフォルトルートが削除されると考えていました。 初回の VPC 作成 以下のコードを実行し、VPC とサブネットを作成しました。 delete_default_routes_on_create = true をコメントアウトし、他のオプションは最小にしています。 provider "google" { project = "$ { PROJECT_ID } " // プロジェクト ID region = "asia-northeast1" } terraform { required_version = "~> 1.3" required_providers { google = ">= 4.63.1" } } # Create vpc resource "google_compute_network" "vpc_network" { name = "vpc" auto_create_subnetworks = "false" # delete_default_routes_on_create = true // デフォルトルート作成オプション } # Create subnet resource "google_compute_subnetwork" "subnet" { name = "subnet" ip_cidr_range = "10.0.0.0/24" network = google_compute_network.vpc_network.name } 上記の Terraform を実行後、gcloud コマンドで VPC のルートを確認すると、サブネットルートとデフォルトルートが作られています。 fujioka@cloudshell:~ ( xxx ) $ gcloud compute routes list --project= ${PROJECT_ID} --filter= " network=vpc " NAME: default-route-71e4a7af65c1a91b NETWORK: vpc DEST_RANGE: 10 . 0 . 0 . 0 / 24 NEXT_HOP: vpc PRIORITY: 0 NAME: default-route-8f27e96e5eb606ae NETWORK: vpc DEST_RANGE: 0 . 0 . 0 . 0 / 0 NEXT_HOP: default-internet-gateway PRIORITY: 1000 fujioka@cloudshell:~ ( xxx ) $ tfstate ファイルは "delete_default_routes_on_create": false となっています。 { " version ": 4 , " terraform_version ": " 1.4.6 ", " serial ": 17 , " lineage ": " xxx ", " outputs ": {} , " resources ": [ { " mode ": " managed ", " type ": " google_compute_network ", " name ": " vpc_network ", " provider ": " provider[ \" registry.terraform.io/hashicorp/google \" ] ", " instances ": [ { " schema_version ": 0 , " attributes ": { " auto_create_subnetworks ": false , " delete_default_routes_on_create ": false , ~ デフォルトルート削除の試行 デフォルトルートを削除するために17行目の delete_default_routes_on_create = true を有効にします。 provider "google" { project = "$ { PROJECT_ID } " // プロジェクト ID region = "asia-northeast1" } terraform { required_version = "~> 1.3" required_providers { google = ">= 4.63.1" } } # Create vpc resource "google_compute_network" "vpc_network" { name = "vpc" auto_create_subnetworks = "false" delete_default_routes_on_create = true // デフォルトルート作成オプション } # Create subnet resource "google_compute_subnetwork" "subnet" { name = "subnet" ip_cidr_range = "10.0.0.0/24" network = google_compute_network.vpc_network.name } terraform apply を実行すると以下のように 1 changed と表示されました。 fujioka@cloudshell:~ ( xxx ) $ terraform apply google_compute_network.vpc_network: Refreshing state... [ id = projects /xxx/global/networks/vpc ] google_compute_subnetwork.subnet: Refreshing state... [ id = projects /xxx/regions/asia-northeast1/subnetworks/subnet ] Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols: ~ update in-place Terraform will perform the following actions: # google_compute_network.vpc_network will be updated in-place ~ resource " google_compute_network " " vpc_network " { ~ delete_default_routes_on_create = false - > true id = " projects/xxx/global/networks/vpc " name = " vpc " # (7 unchanged attributes hidden) } Plan: 0 to add, 1 to change, 0 to destroy. Do you want to perform these actions? Terraform will perform the actions described above. Only ' yes ' will be accepted to approve. Enter a value: yes google_compute_network.vpc_network: Modifying... [ id = projects /xxx/global/networks/vpc ] google_compute_network.vpc_network: Modifications complete after 0s [ id = projects /xxx/global/networks/vpc ] Apply complete ! Resources: 0 added, 1 changed, 0 destroyed. fujioka@cloudshell:~ ( xxx ) $ tfstate ファイルは "delete_default_routes_on_create": true となっています。 { " version ": 4 , " terraform_version ": " 1.4.6 ", " serial ": 19 , " lineage ": " xxx ", " outputs ": {} , " resources ": [ { " mode ": " managed ", " type ": " google_compute_network ", " name ": " vpc_network ", " provider ": " provider[ \" registry.terraform.io/hashicorp/google \" ] ", " instances ": [ { " schema_version ": 0 , " attributes ": { " auto_create_subnetworks ": false , " delete_default_routes_on_create ": true , ~ 事象 Terraform の実行後に VPC ルートを改めて確認すると、想定とは異なり、実際にはデフォルトルートは削除されていませんでした。 fujioka@cloudshell:~ ( xxx ) $ gcloud compute routes list --project= ${PROJECT_ID} --filter= " network=vpc " NAME: default-route-71e4a7af65c1a91b NETWORK: vpc DEST_RANGE: 10 . 0 . 0 . 0 / 24 NEXT_HOP: vpc PRIORITY: 0 NAME: default-route-8f27e96e5eb606ae NETWORK: vpc DEST_RANGE: 0 . 0 . 0 . 0 / 0 NEXT_HOP: default-internet-gateway PRIORITY: 1000 fujioka@cloudshell:~ ( xxx ) $ 原因 Terraform のドキュメントで今回のオプションを見ると以下のように記載されています。 delete_default_routes_on_create - (Optional) If set to true, default routes (0.0.0.0/0) will be deleted immediately after network creation. Defaults to false. このオプションは「VPC 作成直後にデフォルトルートを削除する」オプションであり、既に作成済みの VPC には効力を発揮しない仕様であることが分かりました。 参考: google_compute_network 解決策 今回の場合は、手動でデフォルトルートを消すことで対応しました。 当記事のケースではルート自体を Terraform で管理していないため、手動の操作がその後の Terraform 実行には影響しませんでした。 デフォルトルートを削除した状態で後述するようなインターネットアクセス、内部リソースへのアクセス等が必要な場合は、 個別で静的ルートを追加 してください。 参考: 静的ルートの追加と削除 デフォルトルート削除による影響 インターネットアクセス デフォルトルートを削除するとインターネットへの経路を失い、Web サイトや外部サービスへアクセスできなくなる場合があります。これは Google Cloud や Amazon Web Services (AWS) 等のクラウドに限ったことではなくルーティングの問題です。 例として、インスタンスの運用面から考えると以下のような影響が挙げられます。 インスタンスが Windows Server の場合、Windows Update ができなくなる インスタンスが Linux の場合、apt や yum 等のパッケージを公式リポジトリから入手できなくなる 他にも Python や Javascript のパッケージインストールでパブリックリポジトリを参照している場合も、インターネットへの経路がないため失敗します。 内部リソースへのアクセス デフォルトルートはクラウド内のリソース間通信で使われる場合もあります。そのため、リソース間の接続や上位レイヤのアプリケーション動作に影響が生じる場合があります。 例として、以下のものが挙げられます。 インスタンスが Windows Server の場合、ライセンス認証ができなくなる Cloud Load Balancing でヘルスチェックができなくなる( プローブ IP 範囲 へのルートが無くなるため) 参考: Compute EngineのWindows Serverでライセンス認証エラー(0xC004F074) 限定公開の Google アクセス デフォルトルート削除による影響として ドキュメント には以下のように記載があります。 重要: 限定公開の Google アクセスのルーティング要件を満たすカスタム静的ルートがない場合、デフォルト ルートを削除すると、限定公開の Google アクセスが無効になることがあります。 但し、限定公開の Google アクセスで利用するドメイン名によって必要になるルートは異なりますので、詳細は以下の記事を参考に、適切な静的ルートを追加してください。 blog.g-gen.co.jp 藤岡 里美 (記事一覧) クラウドソリューション部 接客業からエンジニアへ。2022年9月 G-gen にジョイン。Google Cloud 認定資格は全冠。2023 夏アニメのオススメは、ダークギャザリング。箏を習っています :) Follow @fujioka57621469
アバター
G-genの荒井です。2023/04/10に Looker Studio へ実装された サンキーグラフ の使い方について解説します。非常にグラフィカルなグラフで視覚効果が高いので、一層訴求力の高いダッシュボード作成に役立つグラフです。 概要 サンキーグラフとは ユースケース 用語 ノード ソースノード ターゲットノード リンク サンキーグラフのデータソース 必要なカラム サンプルデータソース サンキーグラフの作成方法 グラフの選択 ディメンションの設定 指標の設定 作成完了 Tips 複数ノードからなるサンキーグラフ 注意点 循環データは非対応 データの内訳までは読み取れない 概要 サンキーグラフとは サンキーグラフ とは、複数カテゴリ間のフローやデータ流量、関係性を表すために役立つグラフです。 サンキーチャート、サンキーダイアグラムと呼ばれることもあります。 参考 : サンキーグラフのリファレンス - Looker Studioのヘルプ ユースケース サンキーグラフのユースケースとして、以下のようなデータを分析したい際に利用します。 Webページを訪れたユーザーのページ遷移 人口の移動経路 マーケティングメールの開封率 用語 ノード サンキーグラフで表示されるデータの「始点」や「終点」を示します。(図①参照) ノードは「ソースノード」と「ターゲットノード」に分類されます。 ソースノード ノードの始点側を「ソースノード」と呼びます。(図②参照) ターゲットノード ノードの終点側を「ターゲットノード」と呼びます。(図③参照) リンク ノードとノードの間に存在するデータの流量を表す線を「リンク」と呼びます。(図④参照) リンクの太さは「重み付け」と呼ばれ、集計されたデータ(指標)が高いほどリンクの太さが増し、重みが高い状態となります。 サンキーグラフのデータソース 必要なカラム 実際にサンキーグラフを作る際は、どういったデータを用意したら良いか、馴染みのある棒グラフや円グラフなどとは違うため、イメージしづらいかもしれません。 サンキーグラフではデータソースとして、以下のようなカラムが必要となります。 ソースノード(ディメンション) ターゲットノード(ディメンション) データ(指標) サンプルデータソース 当記事ではサンプルデータとして、転居に伴う人口の移動数を示した表を使います。ソースノードを [転居元] 、ターゲットノードを [転居先] 、データを [移動人口数] する想定でサンプルデータを用意しました。 当記事では上記の表をスプレッドシートで作成し、Looker Studio から直接参照します。 サンキーグラフの作成方法 グラフの選択 Looker Studio へログインしたら、レポート作成画面の グラフを追加 から サンキー を選択します。 ディメンションの設定 ディメンションには、 転居元 と 転居先 を設定します。 先に配置されたディメンションが、ソースノード(グラフの左側)になります。 指標の設定 重み付けの指標には 移動人口数 を設定し、リンクで表現したい指標に変更します。 作成完了 これでサンキーグラフの作成は完了です。 静岡市からどこの市町村にどの程度の人口が転居したかが可視化されました。 リンクにカーソルを合わせると、移動した人口数がわかります。 Tips 複数ノードからなるサンキーグラフ データソース次第で、ノードが複数配置されるサンキーグラフの作成も可能です。 上記グラフのデータソースは、以下です。赤色の行が、元々のデータに追加した差分です。 注意点 循環データは非対応 Looker Studio のサンキーグラフは「サイクル」に対応していません。サイクルとは、今回のデータソースで言うと 静岡市 > 浜松市 > 静岡市 のように同じノードがフローの中に再登場し、循環してしまうようなケースです。 データソースに 浜松市 > 静岡市 といったデータ(黄セル)を追加してみます。 2行目に 静岡市 > 浜松市 (青セル)がありますのでデータがサイクル(循環)します。 この状態で Looker Studio を更新すると、以下のようなエラーになります。 Looker Studio のサンキーグラフはまだ循環するデータに対応していません。例えば Web サイトのページ遷移を分析する場合、 Aページ > Bページ > Aページ と遷移するなどサイクルが発生する可能性が十分あります。循環データへの対応については、今後のアップデートに期待です! データの内訳までは読み取れない 以下の人口移動に関するグラフは、磐田市への移動人口の総数が40人であることを示しています。 しかし上記の表からは「静岡市にいた40人が、富士市を経由して磐田市に移動した」のか「静岡市に元々いた20人と、富士市に元々いた20人が磐田市に移動した」のか、その内訳を知ることができません。 ※ マウスオーバーやドリルダウンなどで確認することもできません。 このように、Looker Studio のサンキーデータでは、データの内訳(ユニークユーザーや、Web のユニークセッション)は特定できません。 荒井 雄基 (記事一覧) クラウドソリューション部 オンプレ環境のネットワーク・サーバーシステムを主戦場としていましたが、クラウド領域にシフト。まだまだ駆け出しなので、みなさんと一緒に勉強をしていきたいです! 最近の楽しみは、子供と遊ぶこととマイホーム計画を進めること。
アバター
当記事は みずほリサーチ&テクノロジーズ × G-gen エンジニアコラボレーション企画 で執筆されたものです。 Google Maps は Google の提供するサービスの中でもなじみ深いサービスの一つです。 Google Maps Platform では Google Maps の機能やデータを簡単に利用することができ、Google Cloud 選択のきっかけになるプロダクトです。 G-gen の佐々木です。当記事では、Google Cloud のサービスではなく、同じく Google によって提供されている Google Maps Platform について解説していきます。 Google Maps Platform とは Google Maps Platform の機能 Maps Routes Places ユースケース ユーザーを支店や ATM に誘導する 住所入力の簡略化 Google Maps Platform の使用方法 Google Cloud プロジェクトの作成 API キーの発行 料金について 従量課金制 無料枠 ボリュームディスカウント Google Maps Platform Google Maps Platform とは Google Maps は Google が提供するウェブマッピングプラットフォームです。 衛星写真、航空写真、ストリートマップ、360°のストリートビューのほか、各種交通機関のルート検索、リアルタイム交通状況の検索など多数の機能が提供され、世界中で 10 億人以上のユーザーに利用されています。 Google Maps Platform では、アプリケーションに Google Maps の機能を組み込むことのできる API と SDK のセットが提供されます。 Android、iOS、ウェブブラウザから、HTTP を使用して Google Maps Platform の様々なツールを利用できます。 Google Maps Platform の機能 Google Maps Platform では、大きく分けて Maps 、 Routes 、 Places の 3 種類の機能が提供されており、それぞれいくつかのプロダクトが存在します。 ユーザーは各プロダクトの API を利用することで、アプリケーションに Google Maps の機能を実装することができます。 Maps Maps のプロダクトでは、Web ページに組み込むことのできる Google Maps の地図表示や 360° のストリートビューなど、地図に関する情報を取得する API が提供されます。 プロダクト 説明 Static Maps Google Maps の地図を静止画像として Web ページに埋め込むことができます。 標準的な HTTP リクエストで指定したパラメータに基づいて地図を作成し、Web ページに表示する画像を返す API が提供されます。 Dynamic Maps 250 種類以上のカスタマイズ項目により、ビジネスニーズに合わせてユーザーがカスタマイズした Google Maps の地図を Web ページに組み込むことができます。 Static Street View Google Maps の 360°ストリートビューを静止画像として Web ページに埋め込むことができます。 標準的な HTTP リクエストで指定した場所やカメラの向きなどのパラメータに基づいてストリートビューの画像を返す API が提供されます。 Dynamic Street View Google Maps の 360°ストリートビューを Web ページに配置することができます。 Static Street View と異なり、Web ページ上のストリートビューを Google Maps 上で使用するときと同じように操作することができます。 Maps Embed シンプルな HTML を使用して、Web ページに地図機能やストリートビューを配置することができます。 地図を画像としてページに埋め込む Static Maps と異なり、Google Maps で地図を見る場合と同様、地図の拡大や、場所に関する情報を見ることができます。 Elevation 地球上のある地点の高度のデータをクエリすることができる API が提供されます。 高度データは海底を含むあらゆる地表の地点について取得することができます。 Routes Routes のプロダクトでは、指定した複数の地点間の最適なルート検索や、移動時間、距離などの情報の計算など、経路に関する情報を取得する API が提供されます。 プロダクト 説明 Directions ある地点から別の地点への経路を検索することができる API が提供されます。 単純な移動経路だけではなく、乗換案内や各種移動手段 (車、徒歩、自動車など) を利用した場合のそれぞれの経路を検索することができます。 Distance Matrix ある地点から別の地点への移動にかかる時間と距離を計算する API が提供されます。 高速道路やフェリーなど、移動手段を詳細に指定することができます。 Roads 移動中に GPS で取得した位置データを利用し、移動経路を地図上に記録したり、通過した道路に関する追加のデータ (制限速度など) を取得することができます。 Routes ある地点から別の地点への最適な経路を計算し、距離と移動時間を返す API が提供されます。 Directions と Distance Matrix のパフォーマンスを最適化した API であり、レスポンスレイテンシの短縮、移動手段の選択肢の追加 (自動二輪車など)、エコルート (エネルギー効率の良いルート) 検索など、様々な機能強化がされています。 Places Place のプロダクトでは、Google が保持する全世界 1 億以上のスポットに関する情報から、所在地や施設名、電話番号などを検索する API が提供されます。 API 説明 Geocoding 住所を地理座標 (緯度・経度) に変換する API が提供されます。 Address Validation 入力された住所の情報を識別・検証し、正しくない住所の修正を行ったり、郵便番号などの情報を補足したりする API を提供します。 Autocomplete ユーザーがテキストボックスに住所情報を入力する際、予測された住所の候補を返すオートコンプリート機能を組み込むことができます。 Place Search 指定した位置の周辺にある施設などのスポットを返す NearBySearch 機能と、入力したキーワードに関連する位置情報の候補を返す TextSearch 機能が提供されます。 Place Details Place Search で返された place_id を入力として使用することで、場所に関する詳細な情報 (完全な住所、電話番号、ユーザー評価など) を返す API が提供されます。 Place Photos Place Search、Place Details のレスポンスとして返される photoreference の値を使用し、検索した場所に関する写真コンテンツを返す API が提供されます。 Current Place Android 、iOS デバイスの位置情報から場所を検出し、場所に関する情報を返す API が提供されます。 Geolocation デバイスが検出できる基地局や Wi-Fi ノードの情報に基づき、現在地の推定緯度、経度の情報を返す API が提供されます。 Time Zone タイムスタンプと地理座標の情報を入力として、UTC からの時差やサマータイム期間のオフセットを返す API が提供されます。 ユースケース 公式ドキュメントの 金融サービス向けのソリューション から抜粋しています。 ユーザーを支店や ATM に誘導する Directions や Distance Matrix を使用することで、Google Map 上に最寄りの店舗や ATM の情報を表示し、最新の交通情報を用いて店舗までの経路や距離、到着予定時刻をユーザーに通知することができます。 また、Static Street View で取得した店舗の画像や、Place Details で取得した店舗の営業時間を追加の情報として利用することができます。 参考: 金融サービス向けのソリューション - 支店や ATM にユーザーを誘導 住所入力の簡略化 Address Validation や Autocomplete を使用することで、新規アカウント開設やクレジットカード、ローン申し込みの際の住所入力に間違いがないようにユーザーをサポートすることができます。 Static Maps により、入力された住所の地図を確認用に表示することもできます。 参考: 金融サービス向けのソリューション - 確認済みアカウントの登録を簡略化 Google Maps Platform の使用方法 Google Cloud プロジェクトの作成 Google Maps Platform は Google Cloud とは異なる Google プロダクトですが、利用には Google Cloud のプロジェクトを作成する必要があります。 Google Cloud プロジェクトに請求先アカウントを紐付けて課金を有効にし、利用したい Google Maps Platform プロダクトの API をプロジェクトで有効化します。 API キーの発行 API キー は、Google Maps Platform の API を呼び出す際に使用される認証情報であり、請求先アカウントに紐付きます。 API キーをリクエストのパラメータに追加することで、各種 API を利用できるようになります。 API キーが漏洩してしまうと、誰でも API を呼び出すことができてしまいます。 キーの使用には以下のような制限をかけることができるので、必ず制限を設定するようにします。 制限の種類 説明 アプリケーションの制限 特定のサイト (IP アドレス、Web サイト)やプラットフォーム (Android、iOS など) のみが API キーを使用できるように制限します。 API の制限 許可された Google Maps Platform プロダクトの API に対するリクエストにのみ API キーを使用できるように制限します。 料金について 従量課金制 Google Maps Platform は従量課金制であり、各機能ごとの API リクエストの回数に応じて料金が発生します。 料金の詳細については公式ドキュメントの 料金ページ または 計算ツール を参照してください。 無料枠 Google Maps Platform では毎月 $200 の無料枠が提供されており、各 API の使用料の合計がこれを超過した時点から料金が発生します。 また、Google Cloud の $300 分の無料トライアルは Google Maps Platform の利用料金に対しても適用されます。 ボリュームディスカウント 各機能ごとに、1 ヶ月あたりの API リクエストが 10 万回を超えて以降は 20 % の割引が適用されます。 また、API リクエストが 1 ヶ月あたり 50 万回を超える場合は、追加のディスカウントが適用されます (要問い合わせ) 。 佐々木 駿太 (記事一覧) G-gen 最北端、北海道在住のクラウドソリューション部エンジニア。 2022 年 6 月に G-gen にジョイン。Google Cloud All Certifications Engineer。 好きな Google Cloud プロダクトは Cloud Run。最近は Dataflow を勉強中。 Follow @sasashun0805
アバター
G-gen の杉村です。コネクテッドシートと BigQuery を使い、技術ブログの GA4 アクセス解析をしている事例について、技術的な観点でご紹介します。 はじめに 技術ブログと GA4 データ保持期間と BigQuery コネクテッドシートの利用 エクスポートされた GA4 データの特徴 構成 構成図 ビューの活用 ネストを解き集計するためのビュー利用 マテリアライズド・ビューは断念 BigQuery へのアクセス権限 実装 やろうとしたこと コネクテッドシート接続 VLOOKUP / SUMIF の注意点 データの抽出 自動更新 BigQuery の料金 G-gen Tech Blog のアクセス傾向 記事のジャンル アクセス数が多いジャンル アクセス元 コンテンツの進化 はじめに 技術ブログと GA4 G-gen のエンジニアは、G-gen Tech Blog を通して積極的に技術情報を発信することが奨励されています。 せっかく発信活動をするのであれば、より多くの人に読まれる記事を書きたいものです。どのような記事がより読まれるのか、傾向を掴むためにアクセス解析を試みています。 G-gen Tech Blog には Google Analytics 4 (以下、GA4)のタグが埋め込まれており、アクセス情報が収集されています。このデータを分析に活用します。 当記事では主に上記分析のための データ準備に関する技術的な話題 を中心にご紹介しますが、最後に少しだけ、当ブログのよく読まれる記事の傾向などについても簡単にご紹介します。 データ保持期間と BigQuery GA4 のデータはデフォルトでは2ヶ月で削除されます。設定により 14ヶ月まで 保持することが可能です。 これを超えてデータを保持することはできません。 参考 : データの保持 GA4 ではデータが失われることを防ぐため、データを BigQuery にエクスポート することが可能です。GA4 の前身である Universal Analytics(UA)では、BigQuery のエクスポート機能の使用には有償版である アナリティクス 360 が必要でしたが、 GA4 では無償版で利用できます 。 エクスポートを設定すると、1日に1回(ストリーミングエクスポートを有効化した場合は1日を通して継続的に)データが自動的にエクスポートされ、BigQuery に集積されます。 参考 : [GA4] BigQuery Export コネクテッドシートの利用 このようにして BigQuery に集積された GA4 のデータを、 コネクテッドシート 機能を使って分析します。 blog.g-gen.co.jp G-gen では Google Workspace を使っていることから、 Google スプレッドシートからエクセルライクな操作感でデータ分析が可能なコネクテッドシートは、手軽に分析をスタートするのに最適でした。 エクスポートされた GA4 データの特徴 BigQuery にエクスポートされた GA4 データの一筋縄ではいかない点として RECORD 型 を持っていることです。 RECORD 型はネストされた型であり、1行の中に複数の値を持つ型です。つまり RECORD 型は第1正規形になっていないテーブルを実現しており、分析用データベースである BigQuery ならではです。 参考 : [GA4] BigQuery Export スキーマ GA4 データはネストされた列を持っている このデータをそのままコネクテッドシートでスプレッドシートへ読み込むと、以下のスクリーンショットのようになります。 まさに第1正規形になっていないがために、例えば event_name 列の値が page_view であるデータに対して VLOOKUP を使い、 page_location や page_referrer を取得する、といった操作が不可能であることが分かります。 コネクテッドシートでそのまま読み込む 構成 構成図 以下のような構成で、GA4 データを解析します。当記事ではこれから、この構成について解説していきます。 構成図 ビューの活用 ネストを解き集計するためのビュー利用 設計時、 No ETL/ELT で実現する ことを目指しました。専用のワークフロー管理ツールを使うこと無くデータパイプラインを実装し、可能な限り実装・運用コストを下げたい意図です。 しかし、先述のネストされたデータの特性から、ある程度は元データを加工する必要があります。 これを実現するため、元テーブルをクエリし「ネストを解く」「必要な event_name のみ抽出」「一定期間で PV 数を集計」する等の加工を行う ビュー を BigQuery で作成しました。 参考 : 論理ビューの概要 ビューは仮想的なテーブルであり実体を持ちませんが、クエリを保存する意味合いがあり、ELT の代わりとして使用しました。コネクテッドシートからはビューを参照することが可能であり、これでスプレッドシートからネストが解かれた状態のデータを扱うことができます。 マテリアライズド・ビューは断念 ビューへの問い合わせを行うと、全履歴を保持しサイズの大きい元テーブルへのクエリが発生してしまいます。もしコネクテッドシートから頻繁にビューへクエリが発せられると、体験の低下とスキャン料金の上昇に繋がってしまいます。 これを解消するため、まず マテリアライズドビュー の使用を検討しました。マテリアライズド・ビューであればデータを実体として保有し、定期的なリフレッシュも可能なので、料金節約に最適です。 参考 : マテリアライズド ビューの概要 マテリアライズドビューの活用 しかし、マテリアライズドビューには ワイルドカードテーブルを参照できない という制約があり、断念しました。 GA4 から BigQuery へエクスポートされたデータは、 ワイルドカードテーブル という形式になる仕様です。ワイルドカードテーブルは events_20230507 events_20230508 のように「接頭辞 + 接尾辞」形式のテーブル名で定義されたテーブル群です。このテーブル群は BigQuery からはまとまったテーブルと認識され、SQL の FROM 句で events_* や events_2023* のようにまとめてクエリできます。 参考 : ワイルドカード テーブルを使用した複数テーブルに対するクエリ 現在、BigQuery では、ワイルドカードテーブルではなく パーティションテーブル の使用が推奨されていますが、GA4 のエクスポートの仕組みでは依然、ワイルドカードテーブルが使われています。 参考 : BigQueryのパーティションとクラスタリングについての解説 - G-gen Tech Blog これらの事情から、今回は通常のビュー(論理ビュー)を使用することとしました。 BigQuery へのアクセス権限 コネクテッドシートでは BigQuery のテーブルに接続し、データを利用したり、 スプレッドシート上の関数操作で BigQuery にクエリを実行することもできます。そのため、シート自体へのアクセス権限に加えて、データソース(BigQuery)へのアクセス権限も考慮が必要です。 必要なアクセス権限 (委任を使わない場合) Google スプレッドシートから BigQuery へのアクセス権限は、以下のいずれかから選択します。 コネクテッドシートを作成/クエリを実行する人のアカウントの権限 アクセス権の委任 機能を利用して特定アカウントに委任 Google スプレッドシートではどのエディションでもコネクテッドシートを利用できますが、注意すべき点として、アクセス権の委任機能は Enterprise、Education Standard、Education Plus のいずれかのエディションでしか使えません。さらに、組織の管理コンソールで同機能が有効化されている必要があります。 参考 : コネクテッド シートでアクセス権の委任を使用する 今回のケースでは G-gen 従業員の全員が GA4 データの BigQuery データセットに対して閲覧権限を持っていることから、 1. を選択しました。 実装 やろうとしたこと 実施しようとした分析の一部をご紹介します。 ある Google スプレッドシートのシートには、これまでリリースした記事の一覧があります。ここでは 記事マスタ と呼ぶことにします。なおこの一覧は、現在のところ手動でメンテナンスされています。 No URL タイトル 執筆者 ジャンル リリース日 1 https://blog.g-gen.co.jp/entry/iam-explained これで分かった!Google CloudのIAMの仕組みやAWSとの違い 杉村 勇馬 徹底解説 2021-09-29 2 https://blog.g-gen.co.jp/entry/login-your-vm-with-iap 踏み台サーバはもういらない。IAP(Identity-Aware Proxy)の便利な使い方 杉村 勇馬 機能解説 2021-10-05 変更されづらい URL 列をキーにして BigQuery 上の GA4 データと突合(JOIN)し、「記事ごとの PV数」「執筆者ごとの PV 数」「ジャンルごとの PV 数」など軸を変えて PV 数を可視化したいと考えました。 コネクテッドシート接続 まずはコネクテッドシートの接続を作成します。先ほど作成したビュー monthly_page_view に対して、シートからコネクテッドシートで接続します。 以下のように、対象がビューであってもデータをプレビューできています。 コネクテッドシート接続 VLOOKUP / SUMIF の注意点 当初は記事マスタの URL をキーにしてコネクテッドシートで得た月間 PV 数等に対して VLOOKUP() や SUMIF() 関数を使って集計を行い、PV 数を表・グラフで可視化することを考えました。 始めは、コネクテッドシートで接続したデータに対して以下のように SUMIF() 関数を使用しようとしました。 =sumif(monthly_page_view!page_location,A3,monthly_page_view!count) A3 は記事マスタの URL です。コネクテッドシートで得たビュー monthly_page_view から全 PV 数を合計しようとしました。ところが、以下のように表示されます。 SUMIF() をコネクテッドシートに使うとクエリ発行になる 上のスクリーンショットように 適用 ボタンが表示されています。これを押下すると BigQuery に SQL が発行されます。関数が1個実行されるたびに BigQuery にクエリが発行されるので、これでは1記事ごとにクエリが発行されてしまいます。現在のビュー設定だと、1回のクエリで BigQuery 上の GA4 エクスポートデータ全体にフルスキャンがかかってしまうため、記事数分のフルスキャンが発生することになり、膨大なスキャン量と時間がかかってしまいます。これは RDB における N+1 問題 によく似た状態です。 データの抽出 これを解決するために、BigQuery 上のデータをスプレッドシート上に 永続化 することを考えました。ビューの結果を定期的にスプレッドシートに複製しておき、そこに対して SUMIF() や VLOOKUP() をかければ、BigQuery にクエリは発行されません。 これを実現するために データの抽出 機能を使いました。 データの抽出 データの抽出機能は、最大で 500,000 行または 10 MB まで、BigQuery データを取り出してシートに残しておけます。また抽出したデータはコネクテッドシートのデータ自動更新で 自動的にリフレッシュ されるので No ELT に近づけます。 参考 : コネクテッド シートを使用して Google スプレッドシートで BigQuery データを分析、更新する - データを抽出する 自動更新 コネクテッドシートでは自動更新を設定することができます。 今回のケースではストリーミングエクスポートを有効化していないため、GA4 から BigQuery へのエクスポートは1日1回です。コネクテッドシートの更新も、1日1回としました。 自動更新設定 この自動更新により「データの抽出」機能でシートに永続化したデータも更新されます。よって、そのシートを参照している SUMIF() や VLOOKUP() の結果も更新されます。 これをもって限りなく No ETL / No ELT で自動更新される分析シートが完成しました。 BigQuery の料金 BigQuery のコンピュート料金を見積もる方法もご紹介します。今回の場合はコネクテッドシートからビューに対してクエリを発行します。 更新時の処理量の見積もりは、更新オプションの設定画面の下部に表示されます。これを BigQuery の単価(オンデマンドもしくは Editions)に掛けることでコンピュート料金を見積もることができます。それに加え、BigQuery に保存されているデータ量に応じたストレージ料金も発生します。 参考 : BigQuery pricing 参考 : BigQueryの料金体系(BigQuery Editions)を徹底解説 - G-gen Tech Blog G-gen Tech Blog のアクセス傾向 記事のジャンル 2021年9月に開設された G-gen Tech Blog は、開設以来、日本語情報で分かりやすく Google Cloud 情報を発信することをテーマに、記事を増やしてきました。 執筆しているのは多くの Google Cloud 関連プロジェクトで経験を積んだ現役のエンジニアであり、いわゆる「やってみたブログ」「いかがでしたかブログ」に留まらない、本当に身のある技術情報を発信することを心がけています。 当社記事は、内部的に以下のように分類されています。 ジャンル名 説明 資格試験 資格試験に関する事項を扱う記事 徹底解説 特定プロダクトの全体を網羅的に解説する記事 機能解説 プロダクトの一部機能を詳細に解説する記事 手順解説 作業手順やサンプルコードの解説記事 速報記事 イベントや発表に関する速報を扱う記事 トラシュ エラー事象とトラブルシューティングを解説する記事 アーキ・事例 アーキテクチャ解説や事例紹介など抽象事項を扱う記事 動画解説 動画による手順等解説を主とする記事 アクセス数が多いジャンル 2023年5月現在、最も1記事あたりの PV 数が多いのが「資格試験」ジャンルの記事です。エンジニアを中心に資格試験に対する関心は高く、検索流入が多いことが要因です。 Google Cloud 認定資格の一覧を解説。全部で何個ある?難易度は? Cloud Digital Leader試験対策マニュアル(出題傾向・勉強方法) Associate Cloud Engineer試験対策マニュアル。出題傾向・勉強方法 次いで、Google Cloud プロダクトを網羅的に解説する「徹底解説」シリーズの記事も、多くの PV を獲得しています。こちらも、プロダクトベースで検索した方が閲覧される場合が多い記事です。 Cloud Loggingの概念と仕組みをしっかり解説 Google Cloud Storageを徹底解説 Cloud Run を徹底解説! その次に位置する「機能解説」はプロダクトの特定機能を深堀りして解説した記事で、徹底解説シリーズと同様、機能名などで検索してたどり着く方が多いです。また、特に新機能がリリースされたあと、可能な限り早く関連情報記事をリリースすることで、突発的に閲覧数が増えることがあります。 GoogleのBIツール、LookerとLooker Studioを比較してみた BigQueryの料金体系(BigQuery Editions)を徹底解説 Cloud Runのセッションアフィニティを解説 アクセス元 referer の集計によると、G-gen Tech Blog は PV 数の半分以上が Google 検索による到達です。 残りは Bing や Yahoo! といった他の検索エンジン、ブログ内回遊ですが、リファラー無しも15%程度含まれています。 コンテンツの進化 G-gen Tech Blog は、サーバーワークスグループの「クラウドで、世界を、もっと、はたらきやすく」というビジョンのもと、特に製品選定時や設計・実装などの実務に役立つコンテンツを目指しています。そのため公式ドキュメント(一次資料)へのリンクを欠かさないことにも留意しています。 長く読まれることを意識し、一度書いた記事も放置することなく機能アップデートには可能な限り追従して過去記事を更新しているほか、記事間でリンクを張り巡らせて読者が情報獲得する助けとなるよう意識するなど、有機的なコンテンツとなるように考慮されています。 Google Cloud を学習する目的の読者のために、以下のようなリンク集も用意しており、これからも情報発信を続けていきます。 blog.g-gen.co.jp 杉村 勇馬 (記事一覧) 執行役員 CTO / クラウドソリューション部 部長 元警察官という経歴を持つ現 IT エンジニア。クラウド管理・運用やネットワークに知見。AWS 12資格、Google Cloud認定資格11資格。X (旧 Twitter) では Google Cloud や AWS のアップデート情報をつぶやいています。 Follow @y_sugi_it
アバター
G-gen の杉村です。当記事は BigQuery について徹底的に解説する記事の 応用編 です。BigQuery に初めて触れる方は、まず 基本編 の記事をご参照ください。 基本編の記事 外部データ連携の概要図 外部テーブル 外部テーブルとは 用途 Cloud Storage 外部テーブル Google ドライブ外部テーブル Bigtable 外部テーブル BigLake BigLake とは データソース 通常の外部テーブルとの違い 利点 権限の持たせ方 BigQuery コネクタ BigQuery Omni BigQuery Omni とは 対応サービス 注意点 連携クエリ、連携データセット 連携クエリ(Federated query)とは 連携データセット(Federated datasets) Apache Iceberg 対応 BigLake tables for Apache Iceberg in BigQuery Apache Iceberg read-only external tables モニタリング INFORMATION_SCHEMA リソースモニタリング 管理ジョブエクスプローラ チューニング パフォーマンスのためのテーブル設計 クエリプラン(実行計画) クエリとテーブルの最適化 主キー制約・外部キー制約 レコメンデーション 同時実行とリソース クエリの同時実行 フェアスケジューリング クエリキュー コスト削減 コスト削減のベストプラクティス 上限設定 BigQuery ML BigQuery ML とは 徹底解説記事 利用例 料金 データリネージ BigQuery Sharing BigQuery Sharing 仕組み データクリーンルーム マーケットプレイスでのデータ販売 非構造化データの分析 オブジェクトテーブル 実現できること アクセス制御 高度なセキュリティ 基本編で解説したセキュリティ機能 Sensitive Data Protection 動的データマスキング 列レベル暗号化 BigQuery BI Engine BI Engine とは 料金 活用のテクニック 注意点 開発 BigQuery DataFrames BigQuery リポジトリ パイプ構文 基本編の記事 当記事は BigQueryを徹底解説する記事の「 応用編 」です。基礎的な内容は「 基本編 」で解説しています。基本編で扱った内容は、以下のとおりです。 概要 料金 コンポーネント データのロード データのクエリ エクスポート 可用性と耐久性 バックアップ データパイプライン(ELT/ETL) データカタログ(BigQuery universal catalog) アクセス制御 セキュリティ関連機能 テーブル設計 Gemini in BigQuery blog.g-gen.co.jp 当記事は応用編として、さらに詳細な内容に踏み込んでいきます。 外部データ連携の概要図 BigQuery は、内部ストレージに持っているデータだけでなく、外部サービスのデータをクエリすることが可能です。応用編記事ではいくつかの仕組みについて解説します。概要は以下の図のようになっています。 BigQuery からの外部データ取得 この図に表されている外部テーブル、連携クエリ(Federated query)、BigQuery Omni、BigLake については、それぞれ当記事内で解説します。 外部テーブル 外部テーブルとは 外部テーブル は BigQuery ストレージの外にあるデータを BigQuery から直接クエリできる仕組みです。以下のようなデータソースに対応しています。 Cloud Storage Cloud Bigtable Google ドライブ 外部テーブルではスキーマ定義とメタデータだけを持ち、データの実体は外部に置いたままです。そのため BigQuery 内部のデータをクエリするよりも、パフォーマンスは劣ります。その代わり、データを定期的に BigQuery にロードすることなく最新のデータが得られたり、ELT パイプライン構築の必要性が無いというメリットがあります。 参考 : 外部テーブルの概要 なお Cloud Storage への外部テーブル定義の場合、外部テーブルの発展系である BigLake テーブル (後述)の利用が推奨されています。BigLake テーブルのほうが、よりきめ細かい制御が可能なうえ、テーブルのアクセス制御とデータソースへのアクセス制御を分離できるため、運用の簡素化にもつながります。 参考 : オブジェクト ストア上の BigLake テーブル 用途 外部テーブルの用途として、ELT 処理の中で外部テーブルに対して CREATE TABLE xxx AS SELECT 〜 や INSERT INTO xxx SELECT 〜 を行い BigQuery へのデータロードを行ったり、あるいは頻繁に変更がある外部データを、都度 BigQuery に取り込むことなく分析するなどが挙げられます。 外部テーブルでは外部データソースにアクセスするオーバーヘッドがあるため、通常のテーブルに比べるとパフォーマンスは劣ります。外部テーブルは、外部からのデータのロードや、小規模なマスターデータの SELECT など、適切なポイントで利用する必要があります。 Cloud Storage 外部テーブル Cloud Storage に対する外部テーブルでは、以下のファイル形式に対応しています。 CSV JSON(改行区切り) Avro ORC Parquet Datastore エクスポート Firestore エクスポート 例として、業務システムから定期的に Cloud Storage へデータをアップロードするような仕組みにしておき、BigQuery からは定期的に Cloud Storage 上のファイルを外部テーブルから INSERT INTO xxx SELECT 〜 するパイプラインを構築すれば、疎結合なデータ受け渡し場所として Cloud Storage が利用可能です。 参考 : Cloud Storage 外部テーブルを作成する Google ドライブ外部テーブル Google ドライブに対する外部テーブルでは、以下のファイル形式に対応しています。 CSV JSON(改行区切り) Avro Google スプレッドシート BigQuery は、CSV や JSON のほか、Google スプレッドシートのデータもテーブルとして認識できます。そのため、従業員が普段使っているスプレッドシートの台帳をテーブルとして使い、ELT や分析に使うことが容易です。外部テーブルへのクエリでは常に最新のデータが得られるため、リフレッシュのためのバッチ処理が不要です。 参考 : ドライブデータをクエリする 以下の記事も参考にしてください。 blog.g-gen.co.jp Bigtable 外部テーブル Bigtable は、Google Cloud のマネージドな NoSQL データベースサービスです。 Bigtable は行と列の概念を持ちますが、NoSQL であるため独特のスキーマ構造をしています。詳細は以下のドキュメントをご参照ください。 参考 : Bigtable の外部テーブルを作成する BigLake BigLake とは BigLake は、アクセス権限の委任を使用して、BigQuery 外部のストレージにあるデータをクエリするための仕組みです。BigLake の仕組みで作成されたテーブルは BigLake テーブルと呼ばれ、外部テーブルの発展系とされています。 BigLake テーブルでは「ユーザからテーブルへのアクセス権限」と「テーブル (BigQuery) からデータソースへのアクセス権限」を分けて考えます。テーブルをクエリするユーザは、BigLake テーブルへのアクセス権限だけがあればよく、データソースからテーブルへデータを取得する権限は接続(Connection。基本編を参照)に紐付けられた サービスアカウント の権限で行われます。 BigLake テーブル なお BigQuery Omni を使うテーブルは、常に BigLake テーブルです。接続(Connection)に AWS の IAM ロールの認証情報を持たせることで、Amazon S3 のデータをクエリできます。 参考 : BigLake テーブルの概要 データソース BigLake テーブルに対応しているデータソースは、以下です。 Cloud Storage Amazon S3(BigQuery Omni) Azure Blob Storage(BigQuery Omni) 通常の外部テーブルとの違い BigLake テーブルと外部テーブルの違い BigLake テーブルではない通常の外部テーブルでは、テーブルをクエリするユーザは、テーブルへのアクセス権限と同時にデータソースへのアクセス権限も持つ必要があります。 すなわち、通常の外部テーブルと BigLake テーブルの違いは、クエリを行うユーザがデータソースへのアクセス権限を持っている必要があるか、ないか、という点になります。 利点 BigLake の最大の利点は、権限管理運用の簡素化です。 通常の外部テーブルでは、テーブルを新規作成したり、あるいは既存の外部テーブルへアクセスできる人を増やす、あるいは減らす場合などに「Cloud Storage 等のデータソースへのアクセス権限」と「テーブルへのアクセス権限」の 両方を編集する必要 があります。BigLake ではこれらの権限が分離され疎結合になるため、運用の簡素化に繋がります。 また、BigLake テーブルではデータソースへのアクセス権限とテーブルレベルのアクセス権限を分けられるため、以下のような機能が使えるようになります。 行レベル・列レベルセキュリティ 動的データマスキング(Cloud Storage のみ) BigLake テーブルでなければ、ユーザはデータソースのファイルへのアクセス権限をまるごと持つため、上記のような細かい制御は意味を成しません。BigLake ではユーザのアクセス権限をテーブルレベルで制御できるため、上記のような細かい粒度でのアクセス制御ができるようになります。 これらの利点から、特に Cloud Storage への外部テーブルを作成する場合は、BigLake テーブルを利用することが推奨されています。 参考 : オブジェクト ストア上の BigLake テーブル 権限の持たせ方 接続(Connection)を作成する際に、BigLake の種類を選択します。接続先環境によって、権限を持たせるための手順は異なります。 Cloud Storage 用の BigLake 接続を作成すると、サービスアカウント ID が払い出されるので、そのサービスアカウントに、対象の Cloud Storage の読み取り権限を持たせます。 Amazon S3 用の BigLake 接続では、IAM ロールの ARN を指定し、IAM ロール側の信頼関係ポリシーで接続の ID を信頼します。 Azure Blob では Federated Identity の利用有無により、方法が異なります。 それぞれ、詳細は公式ドキュメントをご参照ください。 参考 : Cloud リソース接続を作成して設定する 参考 : Amazon S3 に接続する 参考 : Blob ストレージに接続する BigQuery コネクタ BigQuery コネクタ (BigLake コネクタ)は、BigLake の追加機能の一つです。 BigQuery コネクタにより、Apache Spark、Apache Hive、TensorFlow、Trino、Presto など、BigQuery 以外のデータ処理ツールから、BigLake テーブルへのアクセスが可能になります。 参考 : コネクタ BigQuery Omni BigQuery Omni とは BigQuery Omni は他のクラウドサービスとデータ連携を行い、BigQuery でのデータ分析を可能にする仕組みです。 データは Amazon S3 や Azure Blob に置いたまま、BigQuery のコンピュート能力を活かしてデータを分析したり、あるいは BigQuery へデータをロードすることもできます。クエリは Google が管理するクラスタで行われるため、AWS や Azure 側でデータのアウトバウンド転送料金は発生しません。 認証には、BigLake の仕組みを用います。BigQuery Omni では、AWS の IAM ロール等の認証情報を接続(Connection)に登録して認証します。 参考 : BigQuery Omni の概要 対応サービス BigQuery Omni は以下のサービスに対応しています。 Amazon S3(Amazon Web Services) Azure Blob Storage(Microsoft Azure) 注意点 BigQuery Omni は、業務システムが Amazon Web Services(AWS)でホストされており、データ分析基盤は Google Cloud(BigQuery)にあるような組織で特に有用です。ただし、特に日本のユーザーにとっての注意点としては、Amazon S3、Azure Blob ともに 日本国内のリージョンに未対応である 点です。以下の公式ドキュメントをご参照ください。 参考 : BigQuery Omni locations また BigQuery Omni では、通常のクエリとは異なる料金体系が適用されることに注意が必要です。 参考 : BigQuery Omni pricing 連携クエリ、連携データセット 連携クエリ(Federated query)とは 連携クエリ (Federated query)は BigQuery から他のデータベースエンジンへクエリを送信し、結果を返してもらう機能です。 外部テーブルとの違いは、クエリの相手先がデータベースエンジンであり、相手先のコンピュートリソースを使って処理された結果を BigQuery が受け取るという点です。 連携クエリは、以下のデータソースに対応しています。 Spanner Cloud SQL AlloyDB for PostgreSQL SAP Datasphere(2025年4月現在、Preview) クエリを実行する BigQuery のロケーション(リージョン)と、連携クエリの対象データベースのロケーションは同じである必要がある、などの制限があります。詳細は公式ドキュメントを確認してください。 参考 : 連携クエリの概要 連携データセット(Federated datasets) 連携データセット (Federated datasets)、または 外部データセット (External datasets)とは、Spanner のデータベース全体を BigQuery と連携し、テーブル一覧やスキーマ情報の閲覧を可能にするほか、BigQuery から Spanner への読み取りクエリを可能にする機能です。 連携クエリでは、BigQuery で接続(Connection)を作成し、EXTERNAL_QUERY 関数で Spanner データベース内のテーブルを指定してクエリを実行する一方、連携データセットでは BigQuery で外部データセットを作成することで、FROM 句で外部データセット内のテーブルを直接指定してクエリを実行できます。 詳細は、以下の記事を参照してください。 blog.g-gen.co.jp Apache Iceberg 対応 BigLake tables for Apache Iceberg in BigQuery BigLake tables for Apache Iceberg in BigQuery の機能により、Cloud Storage に保存された Iceberg テーブルをクエリしたり、変更したりすることができます。 Apache Iceberg は、大規模な分析用テーブルを効率的に管理するために設計されたオープンソースのテーブルフォーマットです。Iceberg では、データファイルとメタデータファイルのセットを使ってテーブルを定義します。これにより、データ変更の追跡、スキーマの進化、タイムトラベルクエリ、アトミックなトランザクションなどの高度な機能を実現します。Iceberg はデータファイル形式として ORC、Avro、Parquet などをサポートしていますが、BigQuery では Parquet に対応しています。 当機能によって作成されたテーブルは BigLake Iceberg tables in BigQuery とも呼ばれます。当機能では、BigLake の仕組みにより Cloud Storage に認証し、Cloud Storage 上に保存された Iceberg データを読み書きします。 参考 : BigLake tables for Apache Iceberg in BigQuery Apache Iceberg read-only external tables Apache Iceberg read-only external tables は、前述の BigLake Iceberg tables in BigQuery とは異なり、読み取り専用の定義方法です。 Cloud Storage 上の Iceberg テーブルを指定して、読み取り専用の外部テーブルを定義します。定義方法には「BigLake metastore を使う方法」と「JSON 形式のメタデータファイルを指定する方法」の2通りがあります。 参考 : Create Apache Iceberg read-only external tables モニタリング INFORMATION_SCHEMA INFORMATION_SCHEMA は、BigQuery の各種オブジェクトの情報を格納したシステムビューです。INFORMATION_SCHEMA は読み取り専用であり、多くの場合、リアルタイムで最新の情報が格納されています。 このビューをクエリすることで、BigQuery のテーブル、ジョブ、インデックスなどの各種情報を確認することができます。以下は、代表的なものだけをピックアップしたものです。 ビュー名 内容 TABLES テーブル情報。テーブル名、スキーマ、作成時間、更新時間、DDL 等 TABLE_STORAGE テーブルのストレージ情報。テーブル名、物理バイト、論理バイト、タイムトラベルストレージ量、フェイルセーフストレージ量等 SCHEMATA データセット情報。名称、作成日時、更新日時、ロケーション、DDL 等 JOBS ジョブ情報。ID、キャッシュヒット有無、作成日時、終了日時、エラーコード、SQL 等 クエリ時は、以下のようにプロジェクト ID(省略可)やロケーション名などを付与してクエリします。 SELECT * FROM `region-asia-northeast1`.INFORMATION_SCHEMA.TABLES; INFORMATION_SCHEMA のビューには、プロジェクト単位や組織単位など、複数のレベルのビューが用意されていることがあります。 INFORMATION_SCHEMA のビューの一覧は、以下の公式ドキュメントを参照してください。 参考 : Introduction to INFORMATION_SCHEMA リソースモニタリング BigQuery の Web コンソール画面やシステムビューには、各種モニタリングツールが用意されています。 リソース使用率のモニタリング 予約(reservations)のモニタリング マテリアライズドビューのモニタリング BI Engine のモニタリング 具体的な方法については、以下のドキュメントを参考にしてください。 参考 : Introduction to BigQuery monitoring 管理ジョブエクスプローラ 管理ジョブエクスプローラ (administrative jobs explorer、または単に ジョブエクスプローラ )は、プロジェクトで実行されたジョブの一覧を確認することができる画面です。 同画面では、ジョブ一覧を各種属性でフィルタしたり、ソートすることができます。時間がかかっているジョブを確認し、最適化するために役立てることができます。 また、ジョブごとのスキャン量や占有したスロット量も確認できるため、 コスト最適化 に用いることができます。BigQuery の料金を節約したいとき、まずジョブエクスプローラでスキャン量やスロット占有量が大きいジョブを特定することが有用です。 参考 : 管理ジョブ エクスプローラを使用する 管理ジョブエクスプローラ チューニング パフォーマンスのためのテーブル設計 BigQuery においてパフォーマンスを最適化するためのテーブル設計は、まずは基本編で解説した「パーティショニング」と「クラスタリング」、また「非正規化」などのテクニックが重要です。 BigQuery では一般的な RDBMS(リレーショナルデータベースマネジメントシステム)とは異なり、インデックスを用いた検索パフォーマンス最適化は、特定のケースでのみ行われます。 詳細は、基本編の記事の「テーブル設計」の項を参照してください。 参考 : BigQueryを徹底解説!(基本編) - G-gen Tech Blog - テーブル設計 クエリプラン(実行計画) BigQuery では、他の DBMS で EXPLAIN で取得できるような クエリプラン (実行計画)の確認が可能です。 クエリジョブが投入されると、BigQuery は SQL を複数の クエリステージ に分割し、さらにクエリステージは細かい ステップ に分割されます。複数のワーカーがクエリステージを並列実行するため、高速に処理されます。またクエリジョブの進捗は タイムライン で表現され、保留中・実行中・完了済の作業単位が確認できます。 クエリプランは、Google Cloud コンソールでジョブごとに確認できます。「実行の詳細」タブでは詳細を、「実行グラフ」タブではグラフィカルなフロー図を確認できます。 実行グラフ 参考 : クエリプランとタイムライン 参考 : In-memory query execution in Google BigQuery クエリとテーブルの最適化 想定よりもクエリが遅い場合は、クエリプランやタイムラインを参考に、SQL をチューニングします。また SQL の記述方法やテーブル設計のベストプラクティスに沿うことも重要です。例として、以下のようなベストプラクティスがあります。 テーブルを非正規化して ネスト( STRUCT )列や REPEATED 列を使う WHERE 句は STRING や BYTE より、 BOOL 、 INT 、 FLOAT 、 DATE の方が高速 REGEXP_CONTAINS() ではなく LIKE を使用 不要な列を SELECT しない パーティションやクラスタリングを使用 JOIN の前にデータを減らす 複雑で長大なクエリをマルチステートメントクエリに分割し一時テーブルを活用 自己結合よりウインドウ関数 詳細は、以下の公式ドキュメントも参照してください。以下のページをはじめ、公式ドキュメントにはクエリパフォーマンスを最適化するための Tips が数多く用意されています。 参考 : クエリ パフォーマンスの最適化の概要 参考 : クエリ計算を最適化する 主キー制約・外部キー制約 BigQuery には、 主キー制約 (Primary Key Constraints)と 外部キー制約 (Foreign Key Constraints)が存在します。 参考 : Primary keys and foreign keys しかし、これらの制約に強制力は無く( NOT ENFORCED )、主キー制約を設定した列でも値が重複できますし、外部キー制約を入れた列も行の追加・削除等に制限がかかりません。これらの制約の存在理由は、 実行計画の最適化 にあります。 BigQuery で主キー制約・外部キー制約を定義すると、それらに基づいてクエリオプティマイザがクエリプランを最適化し、JOIN 処理が高速化やスキャンボリュームの最適化に繋がる可能性があります。クエリプランの最適化は、コンピュート料金の最適化にも繋がります。 ただし「主キー制約をかけた列は NULL でなく一意であること」また「外部キー制約をかけた列は参照先テーブルで主キーであること」などの本来の制約条件はシステム的には強制されないため、これを守る義務はユーザー側にあります。データが制約に違反している場合、誤った結果が返される恐れがあるとされています。 詳細な概念として、Inner Join Elimination(内部結合解除)、Outer Join Elimination(外部結合解除)、Join Reordering(結合順序変更)が以下の公式記事で紹介されています。 参考 : BigQuery の主キーと外部キーで結合を最適化 レコメンデーション BigQuery の Web コンソール(BigQuery Studio)では、パフォーマンスチューニングやコスト削減に役立つ 推奨事項 (Recommendations)を一覧表示できます。BigQuery は、AI/ML も活用し、以下のような推奨事項を表示します。 パーティショニングとクラスタリングの推奨事項 マテリアライズド ビューの推奨事項 IAM の推奨事項 例えば、「パーティショニングとクラスタリングの推奨事項」では過去30日間の利用実績に基づいて、どのテーブルのどの列にパーティショニングやクラスタリングを設定すべきかを推奨してくれます。詳細は以下のドキュメントも参照してください。 参考 : 推奨事項の概要 推奨事項はプロジェクト単位で閲覧できるほか、適切な権限を持っていれば組織レベルでも閲覧できます。 推奨事項(Recommendations) 同時実行とリソース クエリの同時実行 BigQuery はサーバーレスのサービスであり、分散アーキテクチャを採用しているため、高度な並列処理が可能です。 クエリの同時実行数は、オンデマンドモードの場合はプロジェクトごとに、BigQuery Editions を使っている場合は予約(Reservation)ごとに決まります。 オンデマンドモードのプロジェクトでは、クエリの最大同時実行数は 動的 に決まります。 一方で予約(Reservation)を使っているプロジェクト、すなわち Editions が割り当てられているプロジェクトでは、最大同時実行数を 明示的に設定 できます。 参考 : Use query queues フェアスケジューリング クエリが並列で実行される際、リソース(スロット)の割り当ては BigQuery によって動的に決まります。この割り当ての仕組みを フェアスケジューリング と呼びます。 フェアスケジューリングにより、プロジェクト間やジョブ間で自動的にスロットが分配されます。スロットを大きく要するクエリには大きなスロット数が、そうでないクエリには少ないスロット数が動的に割り当てられます。 参考 : BigQuery のフェア スケジューリング クエリキュー オンデマンドモードであるか、Editions であるかに関わらず、BigQuery には クエリキュー の概念があります。前述の仕様で決定した最大同時実行数を超えたクエリは、キューに溜まります。 インタラクティブクエリの場合は最大 1,000、バッチクエリの場合は最大 20,000 クエリがキューに滞留することができ、それを超えたクエリ投入はエラーになります。 参考 : クエリキューを使用する コスト削減 コスト削減のベストプラクティス BigQuery にはコスト削減のためのベストプラクティスも存在します。以下の記事をご参照ください。 blog.g-gen.co.jp blog.g-gen.co.jp 上限設定 BigQuery のオンデマンド課金が一定以上にならないように、クエリ量に上限設定をすることが可能です。以下をご参照ください。 blog.g-gen.co.jp BigQuery ML BigQuery ML とは BigQuery ML とは、BigQuery の SQL インターフェイスを使って機械学習モデルをトレーニングしたり、推論を実行できる機能です。トレーニングや推論には、BigQuery 内のデータをシームレスに利用できます。 機械学習の専門知識がなくても、SQL による呼び出しで、以下のような組み込みモデルをトレーニング可能です。 線形回帰 ロジスティック回帰 K-means クラスタリング 行列分解(Matrix factorization) 主成分分析 (PCA) 時系列予測 また Vertex AI や AutoML でトレーニングしたモデルを利用することも可能です。こちらではディープニューラルネットワークやランダムフォレスト、ブーストツリーなども利用できます。 BigQuery ML のモデルを Vertex AI Model Registry に登録して、オンライン推論用のエンドポイントにデプロイすることも可能です。 参考 : BigQuery ML とは 徹底解説記事 以下の記事では BigQuery ML を詳細に解説していますので、ご参照ください。 blog.g-gen.co.jp 利用例 以下のように SQL 構文で BigQuery ML を利用することができます。以下は、モデル作成の例です。 #standardSQL CREATE MODEL `bqml_tutorial.sample_model` OPTIONS(model_type= ' logistic_reg ' ) AS SELECT IF (totals.transactions IS NULL , 0 , 1 ) AS label, IFNULL(device.operatingSystem, "" ) AS os, device.isMobile AS is_mobile, IFNULL(geoNetwork.country, "" ) AS country, IFNULL(totals.pageviews, 0 ) AS pageviews FROM `bigquery- public -data.google_analytics_sample.ga_sessions_*` WHERE _TABLE_SUFFIX BETWEEN ' 20160801 ' AND ' 20170630 ' 上記は bqml_tutorial データセットの中に sample_model というモデルを作成しています。 パブリックデータセット上のデータ bigquery-public-data.google_analytics_sample.ga_sessions_* を利用してロジスティック回帰モデルをトレーニングしています。 推論は以下のように行います。 #standardSQL SELECT country, SUM (predicted_label) as total_predicted_purchases FROM ML.PREDICT(MODEL `bqml_tutorial.sample_model`, ( SELECT IFNULL(device.operatingSystem, "" ) AS os, device.isMobile AS is_mobile, IFNULL(totals.pageviews, 0 ) AS pageviews, IFNULL(geoNetwork.country, "" ) AS country FROM `bigquery- public -data.google_analytics_sample.ga_sessions_*` WHERE _TABLE_SUFFIX BETWEEN ' 20170701 ' AND ' 20170801 ' )) GROUP BY country ORDER BY total_predicted_purchases DESC LIMIT 10 モデルが推論した結果は predicted_(元カラム名) という列に出力されます。 引用元 : BigQuery ML で機械学習モデルを作成する 料金 BigQuery の課金モードがオンデマンドの場合、処理したデータ量に応じて課金が発生します。モデル作成と推論では料金単価が異なる点に注意が必要です。 BigQuery Editions を利用する場合は、BigQuery ML 内部モデルの作成・推論には Editions の QUERY 割り当てが利用されます。Vertex AI など外部モデルの利用には ML_EXTERNAL が使われます。 BigQuery ML 外部モデルの場合は Vertex AI などの利用料金も発生します。 料金表は以下をご参照ください。 参考 : BigQuery ML pricing データリネージ BigQuery では、 データリネージ を表示することができます。データリネージとは、ある BigQuery テーブルのデータがどこから来て、どこに渡されているかなど、データの出自を特定できる情報です。データリネージは、 Dataplex の1機能として提供されています。 有効化すると、テーブルごとにグラフが生成され、BigQuery のコンソール画面(BigQuery Studio)からデータの流れをグラフィカルに確認することができます。 INSERT SELECT や CREATE TABLE AS SELECT などのジョブが追跡され、以下の画像のようなリネージグラフとして表示可能になります。 参考 : データリネージについて 参考 : BigQuery テーブルのリネージを追跡する 詳細は、以下の記事を参照してください。 blog.g-gen.co.jp BigQuery Sharing BigQuery Sharing BigQuery Sharing は、BigQuery のデータセットを、他の組織に安全に共有したり、販売して収益化するための機能です。従来は Analytics Hub と呼ばれていましたが、2025年4月に改名されました。 BigQuery Sharing では、BigQuery データセットが共有可能なほか、Pub/Sub トピックを共有することで、ストリーミングデータを共有することもできます。 BigQuery Sharing には、追加の料金は発生しません。 参考 : Introduction to BigQuery sharing 仕組み データを共有する側はパブリッシャー(Publisher)と呼ばれます。パブリッシャーは、データをパブリックエクスチェンジ(Public Exchange)またはプライベートエクスチェンジ(Private Exchange)に公開することで、データを共有します。 データを共有される側は、 サブスクライバー (Subscriber)と呼ばれます。エクスチェンジからサブスクライブ(Subscribe)することで、データを利用可能になります。 サブスクライバーが BigQuery データセットをサブスクライブすると、 リンクされたデータセット (Linked datasets)という形でデータが利用可能になります。サブスクライバーは自分の環境にデータセットをコピーする必要はなく、リンクされたデータセットを通じて、直接パブリッシャーのデータにアクセスすることができます。 パブリッシャーは、 Data egress options を使うことで、サブスクライバーがデータをコピーしたり、ファイルとしてエクスポートすることを禁止することができます。このようなオプションにより、データを安全に共有することが可能です。 参考 : Introduction to BigQuery sharing - Data egress options (BigQuery shared datasets only) データクリーンルーム BigQuery Sharing の データクリーンルーム (Data clean rooms)は、セキュリティが強化された環境で、機密情報を安全に共有するための仕組みです。 パブリッシュとサブスクライブのモデルを採用している点では、通常の BigQuery Sharing によるデータ共有と同じです。しかし、データクリーンルームでは、 分析ルール や Data egress controls などの仕組みにより、よりきめ細かいアクセス制御が可能になっています。 分析ルール では、データへの直接アクセスを禁止して、集計クエリの実行のみを許可するなど、細かい制御が可能です。 参考 : Share sensitive data with data clean rooms マーケットプレイスでのデータ販売 BigQuery Sharing では、 Google Cloud Marketplace を通じて、データを販売して収益化することができます。 Google Cloud Marketplace にデータをリスティングするには、Google による審査を受ける必要があります。また、特定の要件を満たしている必要があります。 参考 : Commercialize listings on Cloud Marketplace 参考 : Google Cloud Marketplace の要件 非構造化データの分析 オブジェクトテーブル オブジェクトテーブル は、Cloud Storage 内の非構造化データを分析するための仕組みです。オブジェクトテーブルは Cloud Storage 内のオブジェクトのメタデータを格納し、1行が1オブジェクトに対応します。 非構造化データ とは、RDB のテーブルのように型(type)を含むスキーマが決まった構造化データや、CSV や JSON のように型が決まっていないが構造を有する半構造化データとは異なり、 スキーマが決まっていないデータ を指します。画像、動画、PDF、テキストファイルなどは非構造化データです。 オブジェクトテーブルはこれらのデータのメタデータを管理します。また仮想列である data にアクセスすることで、そのデータの RAW バイトにアクセスすることができます。 参考 : オブジェクト テーブルの概要 実現できること オブジェクトテーブルの利用により、Cloud Storage 内の非構造化データに対して以下のようなことが実現できます。 BigQuery ML による画像等の非構造化データを使った推論 Cloud Vision API による画像の推論 Cloud Translation API による翻訳 Cloud Natural Language API による感情分析 Apache Tika を用いた PDF のメタデータ抽出 アクセス制御 オブジェクトテーブルは、データソースへのアクセスに BigLake の仕組みを利用します。BigQuery は接続(Connection)に紐付けられたサービスアカウント権限を使って、Cloud Storage にアクセスします。 参考 : アクセス制御 高度なセキュリティ 基本編で解説したセキュリティ機能 基本編でもいくつかのセキュリティ機能を解説しています。基本編の以下の見出しをご参照ください。 参考 : BigQueryを徹底解説!(基本編) - アクセス制御 参考 : BigQueryを徹底解説!(基本編) - 暗号化 参考 : BigQueryを徹底解説!(基本編) - 列レベルのアクセス制御、行レベルのセキュリティ 参考 : BigQueryを徹底解説!(基本編) - VPC Service Controls Sensitive Data Protection Sensitive Data Protection は、機密性の高いデータを自動的に検出し、保護するためのフルマネージドサービスです。BigQuery とは別の Google Cloud プロダクトですが、BigQuery と密に連携できます。 Sensitive Data Protection を使うと、以下の方法で機密情報の所在を特定できます。 データプロファイルの作成(Sensitive Data Protection が BigQuery テーブルを自動でスキャンして作成) オンデマンド検査(明示的に指示してテーブルまたは列をスキャン) データのプロファイリングを行うと「リスクレベル」「機密性」などのインサイトがプロジェクトレベル、テーブルレベル、列レベルで得られます。 単一プロジェクトに対してプロファイルを行うことも、組織やフォルダレベルで指定して配下プロジェクト全体にプロファイルを行うことも可能です。 参考 : Sensitive Data Protection を使用した BigQuery データのスキャン 参考 : データ プロファイル 動的データマスキング 動的データマスキング は、セキュリティ目的で、クエリ結果を列レベルで動的にマスキングする機能です。 列レベルでアクセス制御をするという点で「列レベルのアクセス制御」と似ていますが、同機能では権限が無いアカウントがアクセス制御がかかった列にアクセスしようとすると権限エラーとなる一方で、動的データマスキングではエラーとならずに、難読化(null 化、マスキング、ハッシュ化など)された値が返ってきますので、既存の SQL を変更する必要がないという利点があります。 この機能は列レベルのアクセス制御と同様に分類(Taxonomy)やポリシータグを利用します。これらの概念については参考ドキュメントをご参照ください。 参考 : 動的なデータ マスキングの概要 参考 : BigQueryの列レベル・行レベルのセキュリティを解説 列レベル暗号化 Cloud KMS で暗号鍵を管理することで、透過的な暗号化とは別に、 列レベルの暗号化 をすることができます。 列レベルの暗号化は、データを暗号化するときに使う鍵、すなわち Data Encryption Key(DEK)とその暗号鍵をさらに暗号化する鍵である Key Encryption Key(KEK)を分けることで、キーの漏洩リスクを下げる手法であるエンベロープ暗号化で行われます。KEK は Cloud KMS で管理され、そのアクセス管理は IAM によって行われます。 暗号化は、INSERT/UPDATE 時の暗号化関数によって行います。確定的(deterministic)な暗号化関数と非確定的(non-deterministic)な暗号化関数があり、前者はインプットが同じであれば暗号文が同一になり、後者はインプットが同じでも、出力される暗号文が異なるものになります。前者の確定的暗号化を用いると、暗号化済みのテキストを使って集約や JOIN などの分析処理を行うことができます。 復号は SELECT 時に復号関数を用いることで行われます。クエリするユーザは、対象のテーブル・列に対するアクセス権限と同時に、Cloud KMS 鍵に対する権限も持っていなければ、データを復号することはできません。 参考 : Cloud KMS を使用した列レベルの暗号化 参考 : AEAD 暗号化のコンセプト 列レベルの暗号化の詳細は、以下の記事をご参照ください。 blog.g-gen.co.jp BigQuery BI Engine BI Engine とは BigQuery BI Engine (または単に BI Engine)とは、Looker Studio や Looker、Tableau などの BI ツールを主な対象として、クエリ結果をメモリにキャッシュすることで高速化する機能です。 BI Engine は BigQuery API と統合されており、BI ツールや API などから BigQuery が利用された際には自動的に BI Engine のキャッシュが適用されるので、アプリケーション側に変更が必要ありません。 参考 : BI Engine とは 料金 BI Engine では、 予約 というオブジェクトをプロジェクト内作成することでメモリ容量が確保されます。この確保した容量の GiB 数に応じて課金が発生します。 2025年4月現在の東京リージョンでは、$0.0499/GiB/hour です。 また BigQuery Editions でコミットメントを購入した場合、購入スロット数に応じて無料の BI Engine の GiB 枠が利用可能になります。 最新の料金は以下の公式ページをご参照ください。 参考 : BI Engine pricing 活用のテクニック BI Engine では特にキャッシュを利用させたいテーブルを指定して、 優先テーブル としてマークできます。 また BI Engine を活用するテクニックとして、BI Engine を使用させたいクエリだけを別プロジェクトに切り出し、そのプロジェクトで BI Engine 予約を作成し、キャッシュを使わせたいクエリをそのプロジェクトに集中させるという方法もあります。これを優先テーブルと組み合わせることで、確保した BI Engine 予約を有効に活用することができます。 参考 : BI Engine に関する考慮事項 注意点 注意点として、BI Engine ではワイルドカードテーブルを参照するクエリをサポートしていません。 また他にも外部テーブルへのクエリやユーザ定義関数など、サポートされていない機能もあります。 参考 : サポートされていない機能 開発 BigQuery DataFrames BigQuery DataFrames は、BigQuery API を介したデータの変換や機械学習を容易に行える Python のオープンソースパッケージです。 BigQuery DataFrames を用いると、BigQuery 上のデータを pandas のような操作感で処理ができ、scikit-learn のような操作感でモデルトレーニングと評価、予測が行えます。 参考 : BigQuery DataFrames の概要 以下の記事で詳細に解説していますので、ぜひご参照ください。 blog.g-gen.co.jp BigQuery リポジトリ BigQuery リポジトリ は、BigQuery と Git リポジトリを連携する機能です。Google Cloud コンソール(BigQuery Studio)上で、組み込みの Git リポジトリまたは GitHub 等の Git リポジトリと接続して、SQL ファイル等のバージョン管理をしたり、Git ワークフローを操作することができます。 詳細は以下の記事を参照してください。 blog.g-gen.co.jp パイプ構文 BigQuery では、標準的な SQL である Google SQL のほか、 パイプ構文 (Pipe syntax)でもクエリを記述することができます。 参考 : パイプクエリの構文を使用する 以下の例のように、構文をパイプでつなぐことで、順番に処理を記述することができます。処理の順に記述ができるため、アドホックな分析や、ログの分析の際に便利です。 FROM `myproject.mydataset.employee_master` |> WHERE location = ' Tokyo ' |> JOIN `myproject.mydataset.sales` USING (employee_id) |> WHERE sales_date >= ' 2024-04-01 ' |> AGGREGATE SUM (sales_amount) AS total_amount GROUP BY employee_id, employee_name |> WHERE total_amount > 10000 |> ORDER BY total_amount DESC |> LIMIT 2 詳細は、以下の記事を参照してください。 blog.g-gen.co.jp 杉村 勇馬 (記事一覧) 執行役員 CTO / クラウドソリューション部 部長 元警察官という経歴を持つ現 IT エンジニア。クラウド管理・運用やネットワークに知見。AWS 認定資格および Google Cloud 認定資格はすべて取得。X(旧 Twitter)では Google Cloud や Google Workspace のアップデート情報をつぶやいています。 Follow @y_sugi_it
アバター
こんにちは、G-gen の荒井です。Google Cloud を利用中に、プロジェクトを間違って消してしまった!もしくは、消しそうになって、ヒヤッとした。なんて経験がある方も多いのではないでしょうか。今回はそういった誤操作によるプロジェクト削除を防止するリーエンを設定使ってみたので記事にしてみました。 はじめに リーエンとは ユースケース リーエンに関する注意事項 リーエン設定方法 alpha コマンドである 指定できる restrictions 事前準備 プロジェクトの準備 リーエン設定方法 リーエンの作成 リーエン設定の確認 リーエンの削除 はじめに リーエンとは Google Cloud における リーエン とは、リソースに対して実行できる操作を制限する制約です。事前にリーエン設定を行っておくことで、リーエン設定後に発生した操作よりリーエン設定が優先されます。 聞き慣れない言葉だったので調べてみたところ、不動産業界ではよく使われる言葉で「先取特権(抵当権)」などと呼ばれているようです。 参考 : リーエンによるプロジェクトの偶発的削除からの保護 参考 : REST reference - Lien ユースケース リーエン設定を行うことで、プロジェクトを誤操作による削除から保護することができます。 以下のようなケースがありましたら、リーエンの出番です。 本番システムのプロジェクトを誤操作による削除から保護したい 複数人が使用する検証環境で、間違って削除されないようにしたい リーエンに関する注意事項 リーエン設定方法 リーエンの設定は2023年5月現在、Google Cloud コンソールからの設定はできず、gcloud コマンドからのみ実行することができます。 Cloud Shell からの実行、または Cloud SDK の準備 が必要となります。 alpha コマンドである gcloud コマンドは、大きく3つのリリースレベルがあります。今回使用するリーエンのコマンドは alpha コマンドとなります。(2023年5月時点) そのため今後予告なく変更がある場合もあります。ご使用の際は自己責任となりますので、ご注意ください。 なおリーエン機能は、コマンド自体は alpha リリースですが、機能としては GA (一般公開) されています。 gcloud コマンドのリリースレベルについては、以下のドキュメントをご参照ください。 cloud.google.com 指定できる restrictions ドキュメント では、restrictions の値として resourcemanager.projects.delete が記載されています。 例えば、restrictions の値を compute.instances.delete などに変更することで、プロジェクト削除防止以外の用途にも使えないかと考えるのが自然ですが、現在では利用できません。 現時点では restrictions にはプロジェクト削除防止目的の resourcemanager.projects.delete のみが指定できます。 事前準備 プロジェクトの準備 【手順1】 Google Cloud へログイン Google Cloud にログインし Cloud Shell を起動します。 Cloud SDKなど gcloud コマンドが実行できれば Cloud Shell 以外でも構いません。 【手順2】 プロジェクトの移動 対象プロジェクトに移動します。 [ 入力コマンド ] gcloud config set project <project_id> [ 出力例 ] $ gcloud config set project lien-demo Updated property [core/project]. $ 【手順3】 プロジェクトの確認 指定のプロジェクトに切り替わったことを確認します。 [ 入力コマンド ] gcloud config list [ 出力例 ] project = の項目が、対象プロジェクトになっていること。 $ gcloud config list [accessibility] screen_reader = True [component_manager] disable_update_check = True [compute] gce_metadata_read_timeout_sec = 30 [core] account = <Google アカウント> disable_usage_reporting = True project = lien-demo [metrics] environment = devshell Your active configuration is: [cloudshell-26733] リーエン設定方法 参考 : gcloud alpha resource-manager liens リーエンの作成 【手順4】 リーエンの作成 以下のコマンドを入力し、プロジェクトにリーエンを作成します。 [ 入力コマンド ] gcloud alpha resource-manager liens create \ --project < project_id > \ --restrictions=resourcemanager.projects.delete \ --reason= < 説明 > [ 出力例 ] $ gcloud alpha resource-manager liens create \ --project lien-demo \ --restrictions=resourcemanager.projects.delete \ --reason="重要プロジェクトのためリーエンで削除保護" $ 詳細なコマンドの構文は リファレンス の確認をお願いします。 リーエン設定の確認 【手順5】 リーエン設定確認 以下のコマンドを入力し、リーエンが作成されていることを確認します。 [ 入力コマンド ] gcloud alpha resource-manager liens list [ 出力例 ] $ gcloud alpha resource-manager liens list NAME: xxxxxxxxxxxxx-xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx ORIGIN: < Google アカウント > REASON: 重要プロジェクトのためリーエンで削除保護 $ 【手順6】 削除テスト 削除防止が働いているかプロジェクト削除をテストします。 ナビゲーションメニュー > IAMと管理 > リソースの管理 > プロジェクトを削除 削除エラーとなるメッセージが表示され、リーエン設定が適用されていることが確認できました。 リーエンの削除 【手順7】リーエンの削除 リーエンで保護したプロジェクトを削除したい場合、リーエンを削除する必要があります。 コマンド内の < NAME > には、 gcloud alpha resource-manager liens list を実行した際に表示されるリーエンの名前 (ID) を入力します。 [ 入力コマンド ] gcloud alpha resource-manager liens delete <NAME> [ 出力例 ] $ gcloud alpha resource-manager liens delete xxxxxxxxxxxxx-xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx Deleted [liens/xxxxxxxxxxxxx-xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx]. $ 【手順8】 プロジェクトの削除 リーエンを削除したことで、プロジェクトが削除できるようになったか確認します。 ナビゲーションメニュー > IAMと管理 > リソースの管理 > プロジェクトを削除 削除エラーが消え、プロジェクトを削除することができました。 以上で、リーエンの設定作業は完了です。 大事なプロジェクトの削除対策には、是非リーエンを作成して安全に運用をしてみてください。 荒井 雄基 (記事一覧) クラウドソリューション部 オンプレ環境のネットワーク・サーバーシステムを主戦場としていましたが、クラウド領域にシフト。まだまだ駆け出しなので、みなさんと一緒に勉強をしていきたいです! 最近の楽しみは、子供と遊ぶこととマイホーム計画を進めること。
アバター
当記事は みずほリサーチ&テクノロジーズ × G-gen エンジニアコラボレーション企画 で執筆されたものです。 はじめまして、みずほリサーチ&テクノロジーズの小野寺と申します。 本記事では、ChatGPTの事例を題材に、Cloud FunctionsでWeb APIを作る際の要点についてまとめていきます。 当ブログは G-gen × みずほRT によるコラボ記事です はじめに 概要 背景・前提 アーキテクチャ 各Google Cloudプロダクトの役割 非機能要求のポイント 詳細 クライアントと安全に通信する アクセスを社員に限定する(アクセス元IPアドレスの制限) オリジンを保護する 十分に安全な暗号スィートで通信を暗号化する 認証機能を組み込む Responseに適切なHTTPヘッダを付与する 重要データを適切に保管する コントロール可能な暗号鍵で暗号化する データを国内に保管する 外部サービス(Azure OpenAI)と安全に通信する secret(アクセスキー)を適切に管理する outbound IPアドレスの固定 まとめ はじめに さて、突然ですが私は弊社内で実績のないサービスや技術を検証し、知見を共有するミッションがあります。 ときおり、検証した結果を文字情報だけではなく実際に触れるサービスとして社内に公開したいことがあります。 直近でもChatGPTを社員限定で利用できるAPIとして公開するために、設計・実装を行いました。 本記事では、このChatGPTの事例を題材に、Cloud FunctionsでWeb APIを作る際の要点についてまとめていきます。 システムアーキテクチャの解説をメインにし、コードの実装や各プロダクトの詳細なパラメーターには触れません。 Google CloudでServerlessなAPIを作る際のヒントになればと思います。 概要 背景・前提 ChatGPT は Azure OpenAI Service を利用しています。 Azure OpenAI Service 自体も API を公開していますので、単純に API を利用するだけであればわざわざ Cloud Functions を利用する必要はありません。 しかし、今回のケースでは以下の要件があり直接 Azure OpenAI Service の API を社内に公開するのではなく Cloud Functions でラップしてオリジナルのAPIとして公開することになりました。 予算超過となるような高額課金にならないよう、無制限に利用されないように利用する部署ごとにクォータを設けたい 監査のため、ChatGPTとの対話について誰が、いつ、どんな対話をしたか記録したい 初期コストを抑えたい(構築済の Google CloudでホストしているWebサイトとAPIの仕組みを流用したい) なお、安価かつ速やかなAPI提供のため、専用線やVPNなどは使用せず、通信はインターネットを利用しています。 アーキテクチャ Cloud Load Balancing → Cloud Functions → Azure OpenAI Service という流れでAPIを実行しつつ、Cloud Functionsで必要なデータを参照・保存する仕組みです。 アーキテクチャ 各Google Cloudプロダクトの役割 詳細な解説に入る前に利用する主なGoogle Cloudプロダクトの役割・利用目的をまとめます。 Google Cloud プロダクト 役割・利用目的 Cloud Load Balancing クライアントとの通信やオリジン(Cloud Functions)へのルーティング Cloud Armor アクセス元IPアドレスベースのアクセス制御や通信暗号化アルゴリズムの指定 Cloud Functions OpenAIのAPIを実行、認証認可、ログの保管など業務アプリケーションを動かす Cloud Firestore セッションなどアプリケーションの制御に必要な情報を保持しておく BigQuery ChatGPTとの対話ログを保管する Identity Platform 認証機能を担う Secret Manager OpenAIのAPIキーを安全に管理する Cloud KMS BigQuery のデータやSecret Managerのsecretを暗号化する鍵を管理する 非機能要求のポイント 最適なGoogle Cloudプロダクトの選択では非機能要求が重要になります。 以降の解説では非機能要求のポイントごとにどのような考えで Google Cloud プロダクトを選択していったかを中心に解説していきます。 クライアントと安全に通信する アクセスを社員に限定する(アクセス元IPアドレスの制限) オリジンを保護する 十分に安全な暗号スィートで通信を暗号化する 認証機能を組み込む Response に適切なHTTPヘッダを付与する 重要データを適切に保管する コントロール可能な暗号鍵で暗号化する データを国内に保管する 外部サービス(Azure OpenAI)と安全に通信する secret(アクセスキー)を適切に管理する outbound IPアドレスの固定 非機能要求のポイント 詳細 クライアントと安全に通信する アクセスを社員に限定する(アクセス元IPアドレスの制限) クライアントの外部IPアドレスが固定されている場合、選択できる手法です。 Cloud Load Balancing で公開している場合、Cloud Armor の security policy でアクセス元IPアドレスベースのアクセス制御が実装できます。 なお、 security policy には Edge security policy と Backend securiy policy があります。 前者は CDN 上のキャッシュ配信に対しても評価するのに対し、後者は評価しません。 外部に公開してはならない静的コンテンツを配信する場合などは Edge security policy を利用するよう注意してください。 オリジンを保護する これまで、Cloud Load Balancing や Cloud Armor でさまざまな防御を記載してきました。 しかしながら、Cloud Load Balancing を迂回して直接オリジン( Cloud Functions や Cloud Run )にアクセス出来てしまえば台無しです。 たとえば Cloud Armor の security policy でIPアドレスベースのアクセス制限を実装していても、直接 Cloud Functions にアクセスされてしまえばIPアドレスベースのアクセス制限はききません。 オリジンのGoogle Cloud プロダクトによって対応が異なります。 Cloud Runの場合はアクセス元をCloud Load Balancingに限定することができます。 一方、Cloud Functionsではそういった設定は不可能です。 ワークアラウンドとして、アプリケーション内で許可された経路でアクセスされていることを確認します。具体的には X-Forwarded-for ヘッダを確認し、許可されたクライアントやロードバランサからのアクセスであることを検証します。 十分に安全な暗号スィートで通信を暗号化する クライアントとAPIとの通信はインターネットを介して行われますので中間者攻撃や通信中のデータの漏洩を防ぐために通信を暗号化する必要があります。 具体的には Cloud Load Balancing に証明書を配置し、 https 通信のみ受け付けるようにします。 証明書は3種類あることを念頭に置く必要があります。これらは認証局(CA)が証明書を発行するためにチェックする項目に違いがあります。 種類 説明 DV証明書 ドメインを所持していることをDNSサーバへの問い合わせで検証される OV証明書 DV証明書の項目に加え、組織が法的に実在することなどが検証される EV証明書 OV証明書の項目に加え、企業の公開電話番号や事業の継続期間なども検証される Cloud Load Balancing で自動生成できる「Google管理の証明書」はDV証明書です。 特にBtoCの場合はOV証明書以上のものが必要になることが多いです。チェックしておきましょう。 また、 SSLポリシー を利用することで、クライアントが接続を確立するために使用できる TLS プロトコルの最小バージョンを指定できます。TLS 1.2以上が必須、などの要件がある場合はSSLポリシーを利用します。 認証機能を組み込む 認証機能では Firebase Auth が Enterprise 向けに拡張された Identity Platform を利用しています。 メールやSMS、各IDプロバイダとの連携など、簡単に認証機能を組み込むことができます。 簡単に利用できる反面、 Identity Platform の利用には制約があります。 認証後、Identity Platform で払い出される JWT は有効期限が1時間固定 一度払い出された JWT は期限が切れるまで破棄することが現実的にできない これらの制約が飲めない場合、アプリケーションによるセッション管理の検討や認証機能の差し替えが必要になります。 Identity Platform を利用する際はこれらの制約を飲める非機能要求となっているか確認します。 なお、今回の事例では制約を許容できなかったため、セッション管理の仕組みをアプリケーションに組み込んでいます。 セッション情報は Cloud Firestore に保存し、Cloud Functions から参照させる仕組みにしています。 Responseに適切なHTTPヘッダを付与する 'access-control-allow-origin'など、 API のレスポンスに適切な Header を設定します。 Header はアプリケーションで定義することもできますが、 Backend Service のcustom header で定義する事も可能です。 固定値を設定する場合はどちらでも実現可能ですが、動的にヘッダを変更する場合はアプリケーションでの実装が必要です。 たとえば2つのオリジン(localhost とクラウド上にデプロイしたFrontendなど)からはアクセスを許可しつつ、他のドメインは拒否したいケースを考えてみます。 'access-control-allow-origin'では全ドメイン許可(*)か一つのオリジンしか設定出来ませんので、リクエストの送信元オリジンに応じて'access-control-allow-origin'を設定する必要が出てきます。 アプリケーションでヘッダ付与の処理を共通化しておくことで自由度とメンテナンスを両立できるので個人的にはアプリケーション内での実装がオススメです。 静的コンテンツを返却する場合や、席二人分誕生ヘッダ付与の処理を terraformで管理したい場合は Backend Service の custom header のほうがよいかもしれません。 重要データを適切に保管する 今回の事例では監査のためにChatGPTとの対話ログを保管する必要がありました。 対話ログには業務情報が含まれる可能性があり、情報漏洩のリスクをコントロールする必要があります。 コントロール可能な暗号鍵で暗号化する 単純に保管時にデータが暗号化されていればよい、というだけであればGoogle Cloud管理の暗号鍵でも要件を満たせます。 しかし、CMEKの利用と暗号鍵への適切なアクセス管理を実施することで、データ漏洩のリスクをさらに軽減することができます。 Google Cloud管理の暗号鍵の場合、暗号鍵へのアクセスはプロダクト(例えばBigQueryのテーブルやFirestoreのレコード)と透過的に権限付与されます。データそのもののアクセス権限に完全に依存するため、万が一誤ったアクセス権限をプロダクトに設定てしまった場合は即座に情報漏洩につながります。 一方、CMEKの場合は暗号鍵への権限(IAM)を明示的に設定する必要があります。万が一データへのアクセス権を誤って設定したとしても暗号鍵へのアクセスを持たない場合はデータを復号できず、エラーとなります。つまり暗号鍵の厳格な管理によってデータ漏洩のリスクを低減することができます。 Cloud Firestore は CMEKに対応していません。上述のような暗号鍵の要件が必須の場合は利用できませんので注意しましょう。 また、利用する暗号鍵と暗号化対象のデータは同じリージョンに存在する必要があります。 今回の事例では ChatGPT との対話ログは監査のための保管が目的であり、リアルタイムに複雑なクエリを実行する必要がないので CMEK で暗号化した BigQuery のテーブルに保存しています。 データを国内に保管する クラウドサービスのリージョン選定ではプロダクトの単価やエンドユーザの地理的ロケーションの観点がありますが、それら以上に特に重要なのが法規制です。 とくに金融や公共系の業務・サービスを扱う場合、法規制を意識する必要があります。 日本国内に限定する場合、東京リージョンと大阪リージョンが利用可能です。 マルチリージョン構成とする場合、Google Cloudプロダクトごとに選択できるマルチリージョン構成に差異があるので注意が必要です。 特にCMEKを利用してデータを暗号化する場合は注意が必要です。 たとえば Cloud Storage でデュアルリージョンの「ASIA1」(東京・大阪リージョン)を選択した場合、キーリングをASIA1(東京、大阪、ソウル)で作成する必要があります。 暗号鍵自体は国外にも保管されますので非機能要求に違反していないか注意が必要です。 外部サービス(Azure OpenAI)と安全に通信する ChatGPTに限らず、外部APIを組み込む場合は利用者で果たすべき責任があります。 Azure OpenAI側の設定については割愛します。 secret(アクセスキー)を適切に管理する Azure OpenAIの場合、API呼び出しにはアクセスキーを使用します。 アクセスキーの漏洩は不正利用に直接つながる重大なインシデントであり、おきてはいけません。 ソースコードにアクセスキーを埋め込んでおり、リポジトリから誰でも参照できる アクセスキーを環境変数で定義しており、Cloud Functionsのコンソールから誰でも参照できる 上記のような状態は適切にアクセスキー(secret)が管理されているとは言えません。 secret の管理には、Secret Managerを利用します。 Secret Manager にsecretを保存し、アプリケーションから参照させます。 登録したsecretはIAMによって権限設定が可能なため、一部の管理者とCloud Functions(で利用するサービスアカウント)のみに権限を付与することで最小権限で運用することが可能です。 これにより、リポジトリの権限が侵害されたり、開発者や運用者が不正を行った場合であってもsecretの値を取得できなくなります。 outbound IPアドレスの固定 連携するサービスによっては送信元IPアドレスを限定する仕組みがあることがあります。 Cloud Functions についても、VPC と Cloud NAT を組み合わせることで Cloud Functions からの outbound アクセスのIPアドレスを固定することができます。(今回の事例では実施しませんでした) まとめ Cloud Functions 単体でもAPIを簡単に作ることができますが、非機能要求に応じて他の GoogleCloud プロダクトと連携していく必要があります。 この記事でご紹介したポイントは当然すべてを網羅するものでも、あらゆるシーンで必要な物でもありませんが、みなさまのアーキテクチャ設計やプロダクト選定の際にそういえば昔何か読んだな、と思い出していただければ幸いです。 小野寺 律文 みずほリサーチ&テクノロジーズ 2019年よりクラウドアーキテクトとして、主にAWSへのマイグレーションプロジェクトで活動。 2023年現在はGoogle Cloudの社内向け環境や教育コンテンツの整備を実施中。
アバター
G-gen の杉村です。Google Cloud (旧称 GCP) で Pub/Sub を中心とした疎結合アーキテクチャについて解説します。 はじめに 疎結合アーキテクチャとは 非同期処理 同期と非同期 同期処理 非同期処理 疎結合アーキテクチャと非同期処理 メリット メッセージングサービスが必要な理由 拡張性の向上 保守性の向上 可用性の向上 なぜクラウドらしいのか 用語 コンポーネントの用語 挙動の用語 pull と push FIFO (message ordering) At-least-once (最低1回の配信) アーキテクチャの用語 Publish/Subscribe 方式 Fan-out (ファンアウト) Google Cloud での疎結合アーキテクチャ キーとなるサービス「Pub/Sub」 AWS との比較 Pub/Sub vs Cloud Tasks サンプルアーキテクチャ データの並列処理 サーバーレス 他のクラウドサービスとの連携 はじめに 疎結合アーキテクチャとは 疎結合アーキテクチャとは、当記事では以下のものを指すこととします。 「 メッセージングサービスを用いた連携により、疎結合化されたアーキテクチャ 」 Google Cloud では、以下のようなアーキテクチャを意味します。 Google Cloud の場合 図の左側の「画像アップロード受付 API」は、システム利用者からの画像のアップロードを受け付けて「画像ストレージ (変換前)」に画像ファイルの生データを保存します。その後、画像の変換処理を依頼するメッセージを図中央のメッセージキューに投入します。図の右側の「画像変換処理」はメッセージキューから仕事の依頼を見つけると、画像に所定の変換処理をかけてから「画像ストレージ (変換後)」に保存します。 画像のアップロード受け付けと、画像の変換処理という異なる仕事を、別々のシステム (サービス) が分担しているイメージです。 比較のために、最大シェアを誇るパブリッククラウドである Amazon Web Services (AWS) でも同様のアーキテクチャを構成図にしてみました。 Amazon Web Services (AWS) の場合 このような疎結合なアーキテクチャは、「サーバーレスアーキテクチャ」や「マイクロサービスアーキテクチャ」が一般化するにつれて、理解しているのが当然とされるようになってきています。当記事では、このようなアーキテクチャについて論じていきます。 非同期処理 同期と非同期 疎結合アーキテクチャを理解するうえで重要なのが、 同期処理 と 非同期処理 の違いについてです。 なお当記事では、プログラミングの背景で語られる同期処理と非同期処理ではなく、Web API へのリクエストとレスポンスの背景における同期処理と非同期処理について説明します (ただし、本質的にはこれらは同じです)。 同期処理 同期処理 とは、クライアントが API に対して処理を依頼するリクエストを行ったあと、期待するレスポンスが返ってくるまで待機してから次の処理に進む方式を指します。 例えば gcloud コマンドで、ある Cloud Storage バケットの中にあるオブジェクト一覧を表示させるとします。 コマンドを実行すると、すぐにオブジェクトの一覧 (期待する処理結果) を得ることができ、その後にコマンドラインが操作できるようになります。これは同期処理です。例えば Google Cloud の参照系の API リクエストは、ほとんどの場合、同期処理です。 非同期処理 一方で、仕事の依頼のリクエストをしてもすぐ期待する処理結果が返ってくるわけではなく、処理の受付だけが行われ、結果は後から取りにいくような方式が、 非同期処理 です。 例えば、ある画像処理システムを考えます。画像ファイルを指定して処理開始を命令すると「処理を受け付けました」というメッセージと、その処理を一意に示す ID だけが返ってきます。画像の処理には数分かかるので、処理が完了したかどうか、ID を示してたびたび問い合わせる必要があります。このような処理が非同期処理です。 処理開始を指示する最初のリクエストを、 ジョブの投入 と表現する場合もあります。 疎結合アーキテクチャと非同期処理 メッセージングサービスを用いた疎結合アーキテクチャは、この非同期処理を実現するためのものと言えます。 ジョブ投入者 (仕事を依頼する側) と、ジョブ処理者 (仕事を行う側) に分かれて、その間にメッセージングサービスが仲介役として入るような構成です。 ジョブ投入者は、仕事の依頼をメッセージとしてメッセージキューに投入します。メッセージキューに入った仕事を実際に処理するのは、ジョブ処理者です。 非同期処理の実現 メリット メッセージングサービスが必要な理由 上記のようなアーキテクチャでは、左側のシステム (システム A とします) と右側のシステム (システム B とします) の間に Pub/Sub や Amazon SQS といったメッセージングサービス、すなわちシステム間のメッセージを中継するためのサービスが入っています。 このような構成図を初めて見る方にとっては、以下のような疑問が生まれるかもしれません。 なぜ、わざわざ間に Pub/Sub を挟むのだろうか。システム A から B へ直接メッセージを送れば早いのではないか。 システム A と B を一つのシステムにしてしまえば早いのではないか。 メッセージグサービスの意義 しかしながら、メッセージングサービスをシステム間に挟み込むことには、明確なメリットがあります。それは、以下で表すことができます。 拡張性の向上 保守性の向上 可用性の向上 拡張性の向上 メリットの1つは、 拡張性の向上 です。 間にメッセージングサービスを挟むことで、仕事を依頼する側であるシステム A と、仕事をこなす側であるシステム B を別々のシステムとして分離することができています。そのため「最近、1件あたりの仕事が重くなってきたから、システム B だけ処理能力を向上させたい。システム B だけサーバ (コンテナ) の数を増やそう」というように、システム拡張の柔軟性が増します。 システム B (右側) だけを性能拡張する 仮にシステム A と B が単一のサーバ上で稼働する単一のシステムであれば、このような柔軟な処理能力拡張は不可能です。 保守性の向上 もう1つのメリットは、 保守性の向上 です。 仕事を依頼するシステム A と、仕事をこなすシステム B を別のシステムとして分離することで、プログラムの修正があった場合の影響範囲を狭めることができます。 ビジネスの変化のスピードが早いこんにちでは、システムの保守性を高め、デプロイの頻度を上げることが望まれます。保守性の向上は、ビジネスメリットをもたらすと言えます。 システム A の改修はシステム B に影響しない またこれは、 モジュール独立性の向上 と言い換えることもできます。モジュール独立性という用語は、基本情報技術者試験に向けた学習で目にしたことがある方も多いかもしれません。仕事を依頼する側とされる側でモジュールを分けることで、 モジュール強度が向上 し、仕事の内容を「メッセージ」としてやり取りし合うことで、 モジュール結合度を下げる ことができます。これが、保守性の向上に繋がっています。 可用性の向上 無視できないメリットとして、 可用性の向上 も挙げられます。 システム A と B が単一システムであれば、システム障害が起こった際には両方の機能が停止します。 しかしこれらが別々のシステムであれば、例えばシステム A が停止しても、システム B は既に依頼された分の仕事は継続してこなせますし、逆の場合なら、仕事の受け付けだけは継続できます。 なぜクラウドらしいのか 疎結合アーキテクチャは、AWS や Google Cloud といったプラットフォームを採用するシステムによく見られ、「クラウドライクなアーキテクチャである」と認識されています。 しかし、このようなアーキテクチャ自体はオンプレミスでも実現可能であり、事実、IBM MQ のようなメッセージングのためのソフトウェアは何十年も前から存在します。 このアーキテクチャがクラウドで最も用いられる理由は、前述の拡張性メリットが クラウドではより効果的に得やすいから です。 パブリッククラウドでは、仮想サーバやコンテナといったコンピューティングリソースを API コールでプログラマブルに、また即時に増強することができます。前述の例で言えば、システム B だけを増やしたいと思えばボタン1つで、あるいはボタンを押すことすらなく、負荷に応じて自動的にリソースを増強することができるのです。 つまり、疎結合アーキテクチャ自体はクラウドに特有のアーキテクチャというわけではなく、クラウドで特にメリットが得やすいアーキテクチャなのです。 用語 コンポーネントの用語 メッセージングサービスを使った疎結合アーキテクチャでは、登場するコンポーネント (登場人物) の役割を「仕事を依頼する主体」「仕事を仲介する主体」「仕事を処理する主体」と大きく分けることができます。 多くの場合、仕事を依頼する主体を producer と呼びます。ジョブやメッセージを生成 (produce) する側だからです。 仕事を仲介する主体は queue (Message Queue) と呼ばれます。 仕事を処理する主体は consumer と呼ばれます。ジョブやメッセージを消費 (consume) して処理するからです。 Producer / Queue / Consumer 挙動の用語 pull と push producer が生成したメッセージを consumer に届ける方式は、複数あります。 consumer から queue にポーリングを行いメッセージを取りに行く pull 方式と、メッセージが consumer に直接届けられる push 方式があります。処理やプログラムの性質に応じて、適したほうが選択されます。 Pull の挙動 (consumer から取りに行く) Push の挙動 (consumer に届けられる) FIFO (message ordering) また queue の性質として FIFO という用語があります。First-In First-Out の略で「先入れ先出し方式」とも表現されます。 一般的なメッセージングサービスでは、メッセージは FIFO ではなく、順番が入れ替わって出てきてしまう場合があります。サービスによってはオプションで FIFO を保証する設定が可能で、有効化することでメッセージが順番通りに出てくるようになります。ただし一般的には、FIFO を有効化するとメッセージのスループット (一定時間に配信可能な数量) は低下します。 また FIFO の代わりに message ordering のような別の用語が用いられる場合があります。 FIFO の挙動 At-least-once (最低1回の配信) 一般的なメッセージングサービスでは、メッセージングの配信は At-least-once (最低1回の配信) という性質で行われます。 At-least-once とは、「メッセージは、最低1回配信される。しかし、逆に言うと2回以上配信される場合もある」という性質です。メッセージングサービスが分散システムであること、また一時的なネットワーク障害や遅延でメッセージに対する ack (受信したことを伝えるレスポンス) がうまく伝達されない場合もあることなどに起因します。 同じメッセージが2回以上配信されてもシステム全体で不具合が起きないよう、consumer が何回同じ処理をしても、その結果としての状態が同じになるように、処理の設計を工夫する必要があります。このように、処理を複数回行っても結果が同じになる性質のことを 冪等性 (べきとうせい) と呼びます。分散システムと非同期処理が普通である現代 IT では、重要な概念とみなされています。 サービスによっては、 Exactly-once (1回限りの配信) というオプションが用意されている場合があります。FIFO の場合と同じく、有効化すると通常はスループットが低下します。 アーキテクチャの用語 Publish/Subscribe 方式 producer、queue、consumer という用語を紹介しましたが、よく似たアーキテクチャの場合でも、違う用語が用いられることがあります。 Publish/Subscribe 方式 というアーキテクチャがあります。Pub/Sub 方式と略される場合もあります。これは、Google Cloud プロダクトの Pub/Sub の名前の由来となっています。 Pub/Sub 方式とは、1つのメッセージキューに対して、複数の subscriber が存在する構成を指します。 このとき producer は publisher (発行者)、queue は topic (トピック)、consumer は subscriber (購読者) と呼ばれます。 publisher / topic / subscriber Fan-out (ファンアウト) 冒頭で紹介したような画像処理のシステムでは、画像変換のジョブが投入されたあと、どれか1つのsubscriber (consumer) が処理をすれば用が足りるような仕組みでした。 しかしそうではない場合、例えば「subscriber 1 はサムネイル画像を作る」「subscriber 2 は画像フォーマットを変換する」「subscriber 3 は AI/ML 推論を行い画像の内容を説明するメタデータを付与する」のように、別々の目的の subscriber に同時にメッセージを送りたい場合もあります。 このように複数の subscriber に同時にメッセージを送るようなアーキテクチャを、 Fan-out (ファンアウト) と言います。ここでの Fan は「扇」であり、扇の形のようにメッセージがばらまかれることから来ています。 Fan-out (ファンアウト) なお、Google Cloud の Pub/Sub では、上記の Fan-out アーキテクチャを、1つの topic に対して複数の「subscription リソース (topic の "出口" の役割)」を作成することで実現します。AWS では、Amazon SNS と Amazon SQS の組み合わせで実現します。 参考 : 1 対多の Pub/Sub システムを構築する - Google Cloud 参考 : Amazon SNS とは - 一般的な Amazon SNS シナリオ - Amazon Web Services Google Cloud での疎結合アーキテクチャ キーとなるサービス「Pub/Sub」 Google Cloud では、疎結合アーキテクチャのキーとなるプロダクトとして、 Pub/Sub が挙げられます。 参考 : Pub/Sub とは - Google Cloud Pub/Sub はフルマネージドなメッセージングサービスであり、インフラの管理が全く必要ありません。高いスケーラビリティを持ち、Publisher/Subscriber 形式で非同期メッセージングを行うことができます。 AWS との比較 AWS を既にご存知の方に分かりやすいよう表現すると、Pub/Sub は「 Amazon SNS と Amazon SQS と Amazon Kinesis Data Streams を合体させたようなサービス 」と言えます。 Pub/Sub では Amazon SNS のような Push 型 / ファンアウト型のメッセージングを行うことができるのに加えて、 Amazon SQS と同じ Pull 型 (ポーリング型) のメッセージングを扱うこともできます。 また Pub/Sub には、 Amazon Kinesis Data Streams のようなストリーミングバッファの側面もあります。Pub/Sub は大量のスループットを処理でき、またメッセージの data フィールドの最大サイズも 10 MB と大きいものになっています。 また Google Cloud の各種サービスから発動したイベントの配信時に、イベント連携プロダクトである Eventarc のバックエンドとして用いられる点は、 Amazon EventBridge と役割が重複するところもあります。 Pub/Sub は、以下のようなユースケースに対応できます。AWS であれば、Amazon SNS、Amazon SQS、Amazon Kinesis Data Streams、Amazon EventBridge 等から適切なサービスを選択するべきところ、Pub/Sub はこれらすべてに対応できます。 ユースケース AWS サービス Google Cloud サービス ジョブの非同期・並列処理 Amazon SQS Pub/Sub ユーザーイベントやサーバイベントの取り込み Amazon Kinesis Data Streams Pub/Sub IoT デバイスからのデータストリーミング Amazon Kinesis Data Streams Pub/Sub イベントドリブン処理の実行 (イベントバス) Amazon EventBridge Pub/Sub ※表のサービス名は一例であり、実際には他のサービスも使用されます Pub/Sub vs Cloud Tasks Google Cloud には Pub/Sub とは別に、メッセージングと非同期処理を実現するプロダクトとして、 Cloud Tasks があります。 参考 : Cloud Tasks について理解する - Google Cloud Pub/Sub と Cloud Tasks は、どちらも内部にキューを持っており、publisher が依頼する処理を非同期にするという点でよく似たサービスではありますが、想定アーキテクチャが異なっています。 Pub/Sub では publisher と subscriber を分離してアーキテクチャを疎結合にすることが前提とされていることに対し、Cloud Tasks は subscriber を明示的に呼び出し、subscriber による処理を制御できることがポイントとなっています。配信タイミングや、配信レートを管理することも可能です。詳細は以下のドキュメントをご参照ください。 参考 : Cloud Tasks か Pub/Sub かの選択 - Google Cloud サンプルアーキテクチャ データの並列処理 データの並列処理 当記事の冒頭でも引き合いに出したアーキテクチャです。 ユーザから画像のアップロードを受け付けた左側のシステムは、画像を Cloud Storage にアップロードしたあと、Pub/Sub にメッセージを投入します。メッセージにはバケット名や画像ファイルのパスが入っています。右側のシステムは Pub/Sub (の subscription) をポーリングし、pod (subscriber) のうち誰か一人がメッセージを取得すると、対象の画像を処理します。 投入されたジョブを大量の Pod (subscriber) で次々に処理するので、高いスループットを維持することができます。 publisher (左側) と subscriber (右側) はそれぞれ Google Kubernetes Engine の pod であり、高いスケーラビリティを持っています。 サーバーレス サーバーレス 高いスケーラビリティは、サーバーレスアーキテクチャで特に発揮されます。サーバーレスと疎結合アーキテクチャは、切っても切れない関係性があります。 サーバーレスアーキテクチャの基本については、以下の記事もご参照ください。 blog.g-gen.co.jp 他のクラウドサービスとの連携 他のクラウドサービスとの連携 Pub/Sub のようなメッセージングサービスは、クラウドサービス間の連携にもよく用いられます。 上記の図は、以下のような処理を示しています。項番は図中の数字と対応しています。 動画ファイルが Cloud Storage に設置されると、Cloud Run functions が起動する (イベントドリブン) Cloud Run functions は Transcoder API (フルマネージドの動画変換サービス) へ非同期ジョブを投入する 元動画ファイルのパスや変換後動画ファイルの出力先 Cloud Storage パスを指定 Transcoder API は与えられたパラメータに基づき Cloud Storage から元動画ファイルを取得し、変換処理を実行 動画変換処理が終わると、Transcoder API は変換後動画ファイルを別の Cloud Storage に配置 Transcoder API はジョブ完了を Pub/Sub に通知 Pub/Sub をサブスクライブしていた別の Cloud Run functions が、後続処理を実行 杉村 勇馬 (記事一覧) 執行役員 CTO / クラウドソリューション部 部長 元警察官という経歴を持つ現 IT エンジニア。クラウド管理・運用やネットワークに知見。AWS 12資格、Google Cloud認定資格11資格。X (旧 Twitter) では Google Cloud や AWS のアップデート情報をつぶやいています。 Follow @y_sugi_it
アバター
G-gen の杉村です。Google Cloud のフルマネージドな分析用データベースである BigQuery について、徹底的に解説します。当記事は 基本編 であり、当記事を読み終わったあとは 応用編 もご参照ください。 概要 BigQuery とは 利用方法 フルマネージド(サーバーレス) 他の Google Cloud サービスとの連携 他クラウドサービスとの連携 料金 料金体系の基本 ストレージ料金 ストレージ料金の基本 Physical Storage と Logical Storage Active Storage と Long-term Storage コンピュート料金 無料枠 オンデマンド課金に制限をかける コンポーネント BigQuery の構成要素 データセット テーブル テーブルとは 標準テーブル 外部テーブル ビュー ビュー(通常) マテリアライズドビュー ルーティン ルーティンとは ストアドプロシージャ ユーザー定義関数(UDF) ストアドプロシージャと UDF の違い テーブル関数 ジョブ 接続(Connection) データのロード バッチロード ストリーミング データのクエリ クエリと宛先テーブル クエリ結果のキャッシュ クエリのドライラン パラメータ化クエリ Optional job creation mode パイプ構文 エクスポート テーブルデータのエクスポート クエリ結果のエクスポート 可用性と耐久性 概要 マルチリージョンの意味 クロスリージョンデータセットレプリケーション 災害対策(DR) バックアップ 耐久性に関する考え方 タイムトラベル フェイルセーフ スナップショットとクローン データパイプライン(ELT/ETL) データパイプライン(ELT/ETL)とは BigQuery Data Transfer Service スケジュールされたクエリ(Scheduled queries) 継続的クエリ(Continuous queries) BigQuery pipelines Cloud Workflows Dataform データカタログ(Dataplex Universal Catalog) アクセス制御 ネットワーク IAM によるアクセス制御 Dataplex によるアクセス制御 承認済みビューと承認済みデータセット セキュリティ関連機能 デフォルトの暗号化・CMEK 暗号化 列レベルの暗号化 列レベルのアクセス制御、行レベルのセキュリティ VPC Service Controls その他のセキュリティ テーブル設計 非正規化 パーティショニング・クラスタリング Search Index Gemini in BigQuery Gemini in BigQuery とは 料金と利用制限 各機能の詳細 自然言語でのクエリ 応用編の内容 概要 BigQuery とは BigQuery とは、Google Cloud(旧称 GCP)の分析用データベースです。BigQuery は フルマネージドサービス であるため、「従量課金(初期投資がゼロ)」「フルマネージド(インフラ構築・管理が不要)」であることが最大の特徴です。 また自動的にスケーリング(拡大・縮小)するため、高度なパフォーマンスを容易に得ることができます。 分析用データベース(あるいはデータウェアハウスとも呼ばれます)として企業や官公庁、あるいは個人によって世界中で広く使われており、Google Cloud を特徴づける代表的なプロダクトでもあります。同種の他社製品としては、Amazon Redshift や Snowflake、Azure Synapse Analytics が挙げられます。 参考 : BigQuery の概要 BigQuery のデータは表形式で保存され、 GoogleSQL (旧称 Google 標準 SQL)という SQL や、Web API 経由でデータを操作可能です。扱うデータは表形式ではありますが、一般的な業務アプリ用の RDBMS とは異なり「列指向ストレージ」「キー制約の不在」「インデックス管理不要」「非正規化のプラクティス」などの特性があります。 参考 : BigQuery での SQL の概要 利用方法 BigQuery は Google Cloud のプロダクトです。利用開始するには、ユーザーを認証するための Google アカウント と、Google Cloud の環境を配置するための Google Cloud プロジェクト が必要です。 BigQuery を操作するユーザーインターフェイスはいくつか存在します。Web ブラウザで操作できる Web コンソール (Google Cloud コンソールの一部であり、BigQuery の画面は BigQuery Studio とも呼ばれます)、 CLI コマンドラインである bq コマンドライン 、Python などのプログラミング言語用の クライアントライブラリ などです。 また、BigQuery 用の ODBC/JDBC ドライバも用意されています。 BigQuery コンソール画面 参考 : Google Cloud Console を使用する 参考 : bqコマンドライン ツール リファレンス 参考 : BigQuery API クライアント ライブラリ 参考 : BigQuery 用の ODBC ドライバと JDBC ドライバ まだ Google Cloud プロジェクトをお持ちでない場合、手軽に Google Cloud を利用開始する方法は、以下もご参照ください。 参考 : Google Cloud(旧GCP)無料で使ってみた!クラウド初心者もかんたんに開設、始め方大解説(前編:説明編) - G-gen Tech Blog フルマネージド(サーバーレス) BigQuery は フルマネージド であり、 サーバーレス です。これは BigQuery のインフラ管理が Google によって行われており、我々ユーザーは関与しなくてよいことを示しています。 次の図は、BigQuery の基盤アーキテクチャを模式的に示した図です。 BigQuery のアーキテクチャ BigQuery のインフラは Google によって管理されています。コンピュート処理を行うワーカーはコンテナ群で形成されており(Borg と呼ばれる技術が使われています)、ユーザーによる ジョブ投入 に応じて自動的にスケーリングします。 また、ストレージレイヤも仮想化されており、コンピュート能力とは切り離されています。 我々ユーザーは、BigQuery API を経由して BigQuery にデータを投入したり、SQL を投入するなどして、Google の持つ大量のリソースを柔軟に使うことができます。課金は、使った分だけの従量課金です。ただし、選択するプランによっては料金のある程度の固定化も可能です。 参考 : BigQuery explained: An overview of BigQuery's architecture 他の Google Cloud サービスとの連携 BigQuery の優れている点は、高度なスケーリング能力や保守性に留まりません。Google Cloud の他のサービスと連携させることで、容易に業務アプリケーションのデータを BigQuery にロード(読み込み)し、分析に繋げることができるのです。 例えば、応用編で解説する 外部テーブル や BigLake テーブル を用いると、オブジェクトストレージサービスである Cloud Storage に保存された CSV や Parquet などのファイルや、Google ドライブ上のスプレッドシートを BigQuery にロードすることなくそのままクエリできます。 連携クエリ (Federated query)を用いると、Cloud Spanner や Cloud SQL などの運用 RDBMS のテーブル上のデータを、BigQuery から直接クエリすることができます。 またメッセージングサービスである Cloud Pub/Sub と BigQuery を連携することで、IoT 機器や Web アプリからニアリアルタイムでデータを BigQuery に投入することが可能です。 このように、BigQuery と他の Google Cloud サービスを組み合わせることで、全ての情報システムからデータを途切れなく BigQuery に流し込むことができます。データの発生から利用までをスピーディ・低工数で繋げられるため、BigQuery がデータ戦略の中心と成り得ると言えます。 BigQuery を中心としたデータ分析基盤 他クラウドサービスとの連携 BigQuery には、Amazon Web Services(AWS)の Amazon S3 や Microsoft Azure の Azure Blob Storage など、他のクラウドサービスのデータを取り込むための機能も備わっています。 当記事の「データパイプライン(ELT/ETL)」の見出しで解説する BigQuery Data Transfer Service や、応用編で紹介する BigQuery Omni を使うことで、それらの外部データソースから BigQuery にデータを取り込むことが可能です。 ただし各パブリッククラウドサービスは、ネットワークを経由してデータが外部に出ていくときにデータサイズ(GB 数)に応じて課金が発生するので、その点には注意が必要です。 参考 : BigQuery Data Transfer Service とは 参考 : BigQuery Omni の概要 料金 料金体系の基本 BigQuery の料金体系の原則は使った分だけ支払いが発生する 従量課金 です。ただし、設定により固定料金とすることもできます。 BigQuery の利用料金は以下の2軸で決定されます。 ストレージ料金(格納したデータサイズに応じた課金) コンピュート料金(コンピュート処理能力に対する課金) BigQuery 利用料金の構成 料金表や課金の仕組みの詳細は、以下公式ページもご参照ください。 参考 : BigQuery pricing ストレージ料金 ストレージ料金の基本 ストレージ料金は、BigQuery に格納されているデータのサイズで決定し、月次で請求されます。 ディスクの拡張や追加といった概念はなく、ユーザーとしてはデータを挿入したり削除するだけで、利用したストレージ分だけが課金される仕組みです。また最初の 10 GB は無料枠となっています。 ストレージの課金体系の詳細は、以下の記事を参照してください。 blog.g-gen.co.jp Physical Storage と Logical Storage データセットごとに、Logical Storage 課金モデルか Physical Storage 課金モデルかを選択することができます。 BigQuery に格納したデータは透過的に圧縮されています。Logical Storage 課金を選択すると、圧縮 前 の額面データサイズに課金されます。Physical Storage 課金だと、圧縮 後 の実データサイズに対して課金されます。 単価は Physical Storage 課金のほうが高いのですが、BigQuery では通常それを超える圧縮率で圧縮されますので、Physical Storage 課金のほうが安価になる傾向があります。 Active Storage と Long-term Storage Physical Storage と Logical Storage の両方で Active Storage と Long-term Storage と呼ばれる保存領域があります。 格納してすぐのデータは Active Storage の単価で課金されます。その後、90 日間連続で変更がされなかったデータは Long-term Storage という扱いになり、より安価な単価で課金されます。 コンピュート料金 一方のコンピュート料金の課金方法は「 オンデマンド 」と「 BigQuery Editions 」という2つの仕組みからどちらかを選択することができます。デフォルトはオンデマンドです。 オンデマンドの場合、BigQuery が1ヶ月にスキャンしたデータサイズに応じて料金が決定します。無料枠があり、毎月最初の 1TB のスキャンは無料です。料金表は、以下の通りです。 一方の BigQuery Editions では、確保した スロット の量と時間に応じて課金されます。スロットとは、BigQuery がクエリの実行に使う CPU のリソース量を図るための仮想的な単位です。 参考 : スロットについて BigQuery Editions は、 Standard 、 Enterprise 、 Enterprise Plus という3つのエディションから選択でき、それぞれ使用可能な機能が異なります。一方のオンデマンドは、ほとんどの機能が利用可能です。しかしながら無料枠を超える場合は、適切なエディションを選択したほうが、オンデマンド課金よりも安価になる可能性があります。Editions の概念のより詳細な解説は、以下の記事をご参照ください。 blog.g-gen.co.jp 無料枠 ストレージ課金は、 最初の10GB は無料となります。 コンピュート料金は、オンデマンド課金を選択している場合のみ無料枠があります。月の 最初の1TB のスキャン までは無料です。 無料枠は Google Cloud のプロジェクトごとではなく、請求先アカウントごとに適用される点にご注意ください。 オンデマンド課金に制限をかける 突発的な大量課金を防ぐために、API の割り当て(Quota)機能を利用する方法があります。以下の記事でご紹介していますので、ご参照ください。 blog.g-gen.co.jp コンポーネント BigQuery の構成要素 BigQuery の論理的な構成要素(コンポーネント)を解説します。 参考 : BigQuery リソースの整理 なお、Google Cloud プロジェクトや組織といった概念についてはこの記事では解説しません。代わりに、以下の記事もご参照ください。 blog.g-gen.co.jp BigQuery の構成要素を図にすると、以下のようになります。 BigQuery の構成要素 データセット BigQuery の データセット とは、テーブルをグルーピングするための論理的な入れ物です。データセットは一つの Google Cloud プロジェクトに所属します。 データセット作成時に、データを配置する ロケーション を選択します。東京リージョンなどの個別リージョンや、あるいは US マルチリージョンなどが選択できます。 またデータセットの単位で IAM 権限の設定やテーブル有効期限の設定、暗号鍵の指定が可能です。データセットで設定した設定値が配下のテーブルのデフォルトの設定値になります。ほとんどの設定値はテーブルの設定でオーバーライド(上書き)が可能ですが、ロケーションだけはオーバーライドできません。 データセットにはテーブルだけでなく、ビュー、関数、プロシージャなどが所属します。 テーブル テーブルとは BigQuery の テーブル は、データを格納するためのコンポーネントです。テーブルはスキーマ定義を持っており、通常のリレーショナル・データベースと同じように、型(タイプ)が決まった列(カラム、またはフィールド)と、行(レコード)を持ちます。テーブルは単一のデータセットに所属します。 参考 : テーブルの概要 標準テーブル 後述の外部テーブル等と区別するため、通常のテーブルは 標準テーブル もしくは単にテーブルと呼ばれます。 標準テーブルは、一般的な RDBMS とは異なり、 列指向 ( カラムナ )でデータを保持します。そのため集計処理など、カラム単位で行う処理を高速に行うことができます。なお列指向は多くの分析用データベースが持つ特徴であるほか、一部のファイルフォーマット(Apache Parquet や ORC)でも採用されています。 BigQuery のテーブルは列指向であるため、クエリ時は SELECT 文で必要な列だけを選択することで、料金に関わる要素であるスキャン量を節約し、パフォーマンスを向上させることが可能であるなどの特性があります。 外部テーブル 外部テーブル はテーブルの一種ですが、BigQuery のストレージの外にある外部データソースをクエリするための仮想的なテーブルです。 Cloud Storage をはじめ、Cloud Bigtable、Google ドライブを外部テーブルのソースとして定義することが可能です。 外部テーブル定義を利用することで外部データソースから データを BigQuery にロードすること無く クエリが実行できるのが利点です。ただしデータソースへのストレージ I/O が発生するほか、例えば Cloud Storage であれば、オブジェクトストレージへの I/O となるため、BigQuery 内のデータをクエリするよりもパフォーマンスは劣ります。 参考 : 外部データソースの概要 また外部テーブルを BigLake テーブル として定義することで、アクセス権限管理を高度化・簡素化することもできます。外部テーブルの詳細や、BigLake の概要は「 応用編 」記事で解説します。BigLake テーブルでは BigQuery Omni 機能により Amazon S3 や Azure Blob Storage 上のデータをクエリすることもできます。 参考 : BigLake テーブルの概要 ビュー ビュー(通常) ビュー は実データを持たない、読み取り専用の仮想的なテーブルです。ベースとなるテーブルへの SELECT 文を書くことで定義します。副問合せを含む複雑なクエリを単純化して可読性をよくしたり、再利用性を高めるために利用します。 ビューと、そのビューのベーステーブルは同じロケーションに存在している必要があるなど、いくつかの制限が存在します。 参考 : ビューの概要 マテリアライズドビュー ビューでありながら実データを持つ マテリアライズドビュー も存在します。マテリアライズドビューには使える JOIN 句に制限がある等しますが、内部にデータを持つためベーステーブルへのクエリが発生しません。自動リフレッシュが設定できるため、管理コストの低い ELT(データ変換)とみなすこともできます。 参考 : マテリアライズド(実体化)ビューの概要 ルーティン ルーティンとは BigQuery では以下のリソースを総称して ルーティン (routines)と呼んでいます。 ストアドプロシージャ ユーザー定義関数(UDF) テーブル関数 これらは SQL やプログラミング言語によって事前に処理を定義しておき、後から呼び出せるようにしておくためのコンポーネントです。ルーティンは単一のデータセットに所属します。 参考 : ルーティンを管理する ストアドプロシージャ ストアドプロシージャ は SQL で記述するステートメント群です。他のクエリや他のストアドプロシージャから呼び出すことができ、引数として値をインプットし、戻り値を返すことができます。 システムプロシージャと呼ばれる、組み込みのストアドプロシージャも存在します。 参考 : SQL ストアド プロシージャを使用する ユーザー定義関数(UDF) ユーザー定義関数 (User Defined Functions、UDF)は SQL、JavaScript、Python で記述できるステートメント群です。 永続 UDF と一時 UDF があり、永続 UDF は一度定義すると複数のクエリから使い回せる一方、一時 UDF は一つのクエリの中で完結し、クエリが完了すると消滅します。 また リモート関数 という種類の UDF も存在します。リモート関数では Cloud Run functions または Cloud Run に関数をデプロイできることから、SQL や JavaScript、Python 以外でも関数を記述することができます。 参考 : User-defined functions 参考 : User-defined functions in Python 参考 : リモート関数の操作 ストアドプロシージャと UDF の違い ストアドプロシージャと UDF はどちらも事前に処理を記述しておき、再利用するための仕組みです。 ストアドプロシージャは主に ELT 処理など、データに対して処理を行った後、その結果を DML でテーブルに書き込む用途が想定されます。 一方の UDF はユーザー定義の「関数」ですので、組み込み関数である SUM() や AVG() のように、SQL 中で呼び出され、インプットに応じた値を返すことに使うことが想定されています。 テーブル関数 テーブル関数 (TVF)は、戻り値として値ではなくテーブルを返すユーザー定義関数です。テーブルを返すという点ではビューとも類似していますが、ビュー定義ではパラメータを受け取ることができない一方で TVF ではパラメータを受け取れるというのが違いです。 参考 : テーブル関数 ジョブ ジョブ とはBigQuery が実行するアクションの総称であり、「データのロード」「データのクエリ」「データのエクスポート」「データのコピー」などを指します。 利用者は、Google Cloud コンソール(BigQuery Studio)や bq コマンドラインなどから非同期にジョブを投入し、あとからステータスや結果を取得できます。 コンピュート課金は ジョブが投入されたプロジェクトにおいて発生 します。そのため、コンピュート処理課金を負担するプロジェクトとストレージ課金を負担するプロジェクトを別にすることができます。これにより、例えば自社の BigQuery データセットをグループ会社の Google Cloud プロジェクトに共有した場合、ストレージ料金を負担するのは自社ですが、データの利用(クエリ)によって発生する課金はグループ会社、という立て付けにすることが可能です。 参考 : BigQuery ジョブの概要 なおジョブの実行にもロケーションの概念が存在します。ジョブが対象とするデータセットに基づいて、クエリを実行するロケーションも決定されます。データセットの参照が無いクエリの場合は US マルチリージョン で実行されます。明示的にクエリの実行ロケーションを指定することもできますが、ジョブと同じロケーションのデータセットしか参照・更新できません。 参考 : ロケーションを指定する 接続(Connection) Biglake テーブルや連携クエリなど、BigQuery の外部のデータへのクエリを行うための接続設定や認証情報を管理するために 接続 (Connection)というコンポーネントが存在します。接続には、以下の種類があります。 Biglake テーブル 連携クエリ リモート関数 Apache Spark ストアドプロシージャ 接続(Connection)により、接続先データベース(連携クエリにおける Cloud SQL 等)への接続設定や認証情報を管理したり、リモート関数として実装された Cloud Run functions や Cloud Run の情報を管理します。なお、接続(Connection)はデータセットにではなく、プロジェクトに所属します。 参考 : 接続の概要 データのロード バッチロード BigQuery にデータを投入することを ロード (読み込みまたは積み込み)と言います。いくつかの方法がありますが、以下は代表的なものです。 Cloud Storage 等からファイルで一括ロード SQL の結果をテーブルに書き込み BigQuery Data Transfer Service BigQuery Storage Write API 他のマネージドサービスやサードパーティ製品 これらは、ファイルの単位である程度のデータをまとまりでロードするため、バッチロード(一括ロード)とも呼ばれます。 1. の方法では、Avro、CSV、JSON、ORC、Parquet などのファイルを、Cloud Storage やローカル環境から読み込ませることができます。CSV や JSON のような半構造化データ(スキーマが自己記述されていないデータ)のロード時には、スキーマを明示的に BigQuery に指示することもできますし、スキーマの自動検知も利用可能です。その他の方法も豊富に用意されており、AWS や Azure など他のクラウドプラットフォームのストレージからもデータをロードすることができます。 参考 : データの読み込みの概要 / バッチ読み込み ストリーミング BigQuery にはバッチロードのほか、ストリーミングでデータをロードすることもできます。 BigQuery に用意されている Storage Write API 経由でデータをストリーミングしたり、Google Cloud のフルマネージドサービスである Dataflow、Datastream、Pub/Sub 経由でデータを投入することができます。 IoT 機器や Web アプリの行動履歴データなど、ニアリアルタイムにデータを BigQuery に投入して分析したい場合にストリーミングロードが利用されます。 参考 : データの読み込みの概要 / ストリーミング データのクエリ クエリと宛先テーブル BigQuery で利用できる SQL の方言は、 GoogleSQL (旧称 Google 標準 SQL)です。 参考 : BigQuery での SQL の概要 Google Cloud コンソール(BigQuery Studio)や bq コマンドライン、あるいは各種プログラミング言語向けのクライアントライブラリ(Cloud SDK)などからクエリを実行可能です。 SELECT 文の実行結果はコンソールやコマンドラインに返されますが、裏の処理で同じ結果が 一時テーブル に書き込まれます。一時テーブルはクエリ結果の書き込み先として使われる、24時間だけ持続するテーブルで、作成したアカウントからのみアクセスできます。一時テーブルに料金は発生しません。 クエリを実行すると、前述のジョブが作成されます。ジョブは一意の ID を持ち、ログから情報を確認することができます。 なおコンソールや bq コマンドで SELECT 文を実行する際に、クエリ結果の書き込み先を既存の標準テーブルへ向けることもできます。このとき「宛先テーブルが空の場合にだけ書き込む」「洗い替え」「追記」から選択できます。それぞれ、SQL でいうところの CREATE TABLE xxx AS SELECT 〜 CREATE OR REPLACE TABLE xxx AS SELECT 〜 INSERT INTO xxx SELECT 〜 を実行した場合と同等です。 参考 : クエリ結果の書き込み クエリ結果のキャッシュ 先述の通り SELECT 文の結果はデフォルトで一時テーブルに書き込まれますが、この一時テーブルはクエリ結果の キャッシュ としても使われます。 クエリの文字列が完全一致する場合のみ、BigQuery は結果を一時テーブルから返します。 参考 : キャッシュに保存されているクエリ結果を使用する また一時テーブルは、前述の通り、クエリ実行者のユーザー(Google アカウント、サービスアカウント)からしかアクセスできないため、原則的には別のユーザーのキャッシュを参照することはできません。ただし Enterprise または Enterprise Plus edition の課金体系の場合のみ、別のユーザーのキャッシュを参照することができます。 参考 : Cross-user caching クエリのドライラン クエリの ドライラン によって、どれくらいのサイズのデータにスキャンがかかるかを、クエリ実行前に知ることができます。 Google Cloud コンソール(BigQuery Studio)を利用している場合、SQL を記述すると自動的に想定スキャンバイト数が表示されます。bq コマンドラインであれば --dry_run オプションを明示的に指定します。 参考 : クエリのドライランの発行 パラメータ化クエリ パラメータ化クエリ (Parameterized query)を使うことで SQL 内にプレイスホルダを置いて、動的な SQL 生成が可能です。 プレイスホルダとして、 @param_name のように @ 記号とパラメータ名を使います。 SQL へパラメータを引き渡すには、BigQuery Studio 画面のクエリ設定で渡す値を設定したり、bq コマンドの --parameter オプションでパラメータ名と値を指定したりします。 参考 : パラメータ化されたクエリの実行 Optional job creation mode Optional job creation mode (旧称 Short query optimized mode)を使うことで、処理量が小さいクエリの実行時間を最適化することができます。データ探索やダッシュボード表示などの処理時間が短いクエリへの利用が想定されています。 参考 : Run a query ‐ Optional job creation mode コンソール画面(BigQuery Studio)や bq コマンドライン、SDK などでのクエリ実行時に当モードを明示的に有効化すると、クエリ実行時に BigQuery が最適化の可否を自動的に判断し、可能であれば自動的に最適化されます。最適化が適用されていない通常のクエリ実行では、クエリを実行するたびにジョブが生成され非同期に処理が行われますが、最適化が適用されると、そのクエリにはジョブが作成されず、結果がダイレクトに返ります。また通常の API レスポンス body には jobReference という要素が含まれますが、最適化が適用された場合には、この要素が含まれません。 最適化オプションを有効化した場合でも、キャッシュが利用できる場合は、通常クエリと同様に利用されます。 同機能の詳細や検証結果については、以下の記事もご参照ください。 blog.g-gen.co.jp パイプ構文 BigQuery では、通常の SQL に加えて、 パイプ構文 (Pipe syntax)を使うことができます。 パイプ構文は、パイプ演算子(|>)で各操作をつなげることで、データの流れを明確にしながらクエリを作成、修正、デバッグができる記述方式です。以下は、パイプ構文を使って記述したクエリの例です。 FROM mydataset.produce |> WHERE sales > 0 |> AGGREGATE SUM (sales) AS total_sales, COUNT (*) AS num_sales GROUP BY item; 参考 : Pipe syntax 詳細は以下の記事を参照してください。 blog.g-gen.co.jp エクスポート テーブルデータのエクスポート データは Cloud Storage 上のファイルに エクスポート することができます。1ファイル最大 1GB となり、それを超える場合は複数ファイルに分割されます。 ファイル形式は Avro / CSV / JSON / Parquet に対応しており、それぞれ対応した形式で圧縮されます。 エクスポートは Google Cloud コンソールや bq コマンドラインで行えるほか、SQL の EXPORT DATA ステートメントでも実行可能です。 参考 : テーブルデータをエクスポートする 参考 : EXPORT DATA statement クエリ結果のエクスポート Google Cloud コンソール(BigQuery Studio)を利用してクエリを実行している場合、 クエリ結果をエクスポート することも可能です。 コンソールからは、ローカル PC のファイル、Google スプレッドシート(英名 : Google Sheets)、Google ドライブにクエリ結果をダウンロードできます。 bq コマンドやクライアントライブラリからクエリを実行した場合は、上記のような結果ダウンロードはできませんが、前述の通りクエリ結果は一時テーブルまたは通常のテーブルに書き込めるため、それらの宛先テーブルから 前述の Cloud Storage へのエクスポートが実施できます。 参考 : Google Cloud コンソールからクエリ結果をダウンロードして保存する クエリ結果のエクスポート 可用性と耐久性 概要 BigQuery は Compute Engine や Amazon Redshift 等とは異なり、ユーザごとにインスタンスを構築して利用するようなサービス ではありません 。Borg と呼ばれる分散コンテナ管理基盤や Colossus という分散ストレージ基盤といった Google の技術を用いており、ユーザはバックエンドのインフラを全く気にすることなく利用することができます。 これにより 99.99% の可用性 SLA が提供されています。一定の条件下で可用性がこれを下回った場合、Google Cloud で利用可能なクレジットの払い戻しがあります。詳細な条件は公式ページをご参照ください。 参考 : BigQuery、業界トップクラスとなる 99.99% の稼働時間の SLA を提供 参考 : BigQuery Service Level Agreement (SLA) BigQuery のストレージに保存したデータは、指定したリージョン内の2つのゾーンに複製されます。これにより高可用性と耐久性の両方を実現しています。 参考 : 可用性と耐久性 マルチリージョンの意味 データセット作成時に、配置先のロケーション(リージョン)を選択します。このとき US や EU といったマルチリージョンを選択することもできます。 「マルチリージョン」という表現が誤解を呼びやすいのですが、これらのマルチリージョンを選択してもデータは 1つのリージョン内 にしか保存されません。選択されたマルチリージョンの中の いずれか1つのリージョン にデータが保存されます。 マルチリージョンを選択するメリットは 可用性ではなく 、より大きな 割り当て(Quota)が利用可能 になる点にあります。 参考 : BigQuery のロケーション クロスリージョンデータセットレプリケーション BigQuery のデータは複数ゾーンに保存されているため、マシンレベルやゾーンレベルの障害は、サービスの可用性に影響しません。ただし、リージョンレベルの障害時に対しては クロスリージョンデータセットレプリケーション などの利用を検討する必要があります。 クロスリージョンデータセットレプリケーションではデータセットを別のリージョンに非同期レプリケーションすることで、データの読み取り可用性を高めることができます。 参考 : クロスリージョン・データセットレプリケーション 以下の当社記事もご参照ください。 blog.g-gen.co.jp 災害対策(DR) BigQuery では災害対策(DR)のために Managed disaster recovery 機能を利用することができます。ただし、当機能は BigQuery Enterprise Plus エディション以上のみで利用できることに注意してください。 当機能を使うと、BigQuery がリージョンレベルで機能停止した際に、コンピュートリソースやクエリを自動で他のリージョンにフェイルオーバーすることができます。詳細は、公式ドキュメントをご参照ください。 参考 : Managed disaster recovery バックアップ 耐久性に関する考え方 前述の通り BigQuery では、必ず2つの異なるゾーンにデータを保存することから、ユーザー側で何も考慮しなくても、データの物理障害への耐久性は高いといえます。 ただし、大災害などによって Google Cloud のリージョンレベルでデータの消失が起きる可能性を考慮するのであれば、BigQuery Data Transfer Service のデータセットコピー機能などを用いて、別のリージョンにデータセットの複製を作っておく、などの対策を検討します。 また誤削除、誤更新などの論理的な障害に備えて、後述のタイムトラベル、クローン、スナップショットなどの機能を利用することができます。 参考 : データセットのコピー 参考 : Scalable BigQuery backup automation タイムトラベル BigQuery には タイムトラベル と呼ばれる機能が存在します。BigQuery のテーブルには、デフォルトで過去7日間のデータが保存されており、データが更新されたり削除されたりしても、保存期間内であればどの時点のデータにもアクセスすることができます。 過去データへのアクセスは、以下のような SQL によって行います。 SELECT * FROM `mydataset.mytable` FOR SYSTEM_TIME AS OF ' 2024-01-01 10:00:00 ' 参考 : タイムトラベル 参考 : 過去のデータへのアクセス フェイルセーフ BigQuery にはタイムトラベルとは別に、 フェイルセーフ と呼ばれるデータ保存期間があります。タイムトラベル期間に加えて、さらに7日間、データが保存されます。 フェイルセーフは緊急復旧用のデータであり、復元するには Google Cloud カスタマーケア(サポート窓口)に問い合わせる必要があります。また、復元はテーブル単位になります。 保存期間はデータセットの単位で2〜7日間の範囲で設定できます。 参考 : フェイルセーフ スナップショットとクローン スナップショット や クローン という機能を使うことで、低価格で、テーブルの論理的なバックアップを作成したり、検証用の複製テーブルを作成することができます。 詳細は、以下の記事をご参照ください。 blog.g-gen.co.jp 参考 : テーブル スナップショットの概要 参考 : テーブル クローンの概要 データパイプライン(ELT/ETL) データパイプライン(ELT/ETL)とは BigQuery を中心としたデータ分析基盤は、以下のように単純な模式図にすることができます。 データパイプライン 各種データを収集して BigQuery に投入・保存し、そのデータに SQL で変換をかけ、利用に適した形にしてから BI ツールや AI/ML 等に利用します。 この一連のデータの流れを、人手でやるのではなく、プログラムで自動化したものが データパイプライン です。 またこの一連の処理を指して、Extract(データの抽出・収集)/ Load(データベースへの読み込み)/ Transform(データの変換)の頭文字を取り ELT と呼びます。アルファベットの並び順が違う ETL という用語もあり、こちらはデータベースへの読み込み前にデータを変換するという、ELT とは順番の異なる処理を指します。BigQuery では高度な並列処理能力と SQL という学習コストの少ない言語が使えるメリットがあることから、まず BigQuery にデータを読み込んでから SQL で変換をかける「E "LT"」の順で処理されることが多いデータベースです。 この項では、BigQuery での ELT に使える各種機能や Google Cloud プロダクトを紹介します。 BigQuery Data Transfer Service BigQuery Data Transfer Service は BigQuery に備え付きの、フルマネージドなデータ転送サービスです。設定したスケジュールに基づいて、各種取得元からデータを取得して BigQuery のテーブルにデータを取り込みます。容易に実装することができるうえ、料金も一部のコネクタを除いて無料です。 以下は、利用可能なデータソースの一部抜粋です。 Cloud Storage Amazon S3 Azure Blob Storage Amazon Redshift MySQL PostgreSQL Microsoft SQL Server Oracle Salesforce、Salesforce Marketing Cloud Google Ads(旧 AdWords) YouTube Channel / YouTube Content Owner 上記のように、Google Cloud 内の Cloud Storage はもちろん、Amazon S3 上のファイル等からデータを引き抜いて BigQuery にデータをロードしたり、SaaS からデータを抽出したりできます。 この機能も、後述のスケジュールされたクエリと同様に、簡易的な ELT 処理に利用することが可能です。実行失敗通知等は、Pub/Sub へ通知したり、メールへ通知することもできます。 参考 : BigQuery Data Transfer Service とは 参考 : BigQuery pricing / Data Transfer Service pricing 以下の記事も参考にしてください。 blog.g-gen.co.jp スケジュールされたクエリ(Scheduled queries) BigQuery では、簡単な設定で、事前定義したクエリを定期実行できます。この機能は スケジュールされたクエリ (Scheduled Queries)あるいはクエリのスケジューリングと呼ばれます。 組み込みパラメータである @run_time (実行時刻の TIMESTAMP)や @run_date (実行時刻の DATE)をクエリに渡すことができるため、簡易的な ELT 処理の実装に使えます。また過去の日付に基いて実行させるバックフィルも可能です。 デフォルトではクエリはスケジュールを作成した人の Google アカウント権限で実行されますが、サービスアカウント権限で実行するよう設定することも可能です。 参考 : クエリのスケジューリング クエリの自動実行が失敗した場合の通知方法については、以下の記事も参考にしてください。 blog.g-gen.co.jp 継続的クエリ(Continuous queries) 継続的クエリ (Continuous queries)は、事前に定義した SQL を BigQuery 上で断続的に実行し、ニアリアルタイムなデータ変換やリバース ETL を実現するための機能です。 継続的クエリを使うと、BigQuery テーブルに追加されたレコードに対して、数秒〜数十秒の遅延で、ほぼリアルタイムに SQL による加工を施すことができます。加工されたデータは、他の BigQuery テーブルや Pub/Sub トピック等に転送できます。 詳細は、以下の記事を参照してください。 blog.g-gen.co.jp BigQuery pipelines BigQuery pipelines は、BigQuery に備え付けの簡易的なパイプライン(ワークフロー)ツールです。以前は BigQuery workflows と呼ばれていましたが、2025年3月20日に改名されて BigQuery pipelines になりました。 BigQuery pipelines では、事前定義した SQL や BigQuery notebook(Colab Enterprise のノートブック)、また後述の Data preparation(Gemini in BigQuery 機能の1つ)を、スケジュールに基づいて定期的に実行できます。実行には前後関係を持たせることができるので、簡易的なデータパイプラインを実装可能です。 実行権限はサービスアカウントに持たせることができます。また、バックエンドでは後述の Dataform が使われています。 前述のスケジュールされたクエリ(Scheduled queries)が単一のクエリを実行するだけであるのに対して、BigQuery pipelines は複数のクエリもしくは notebook に前後関係を持たせて実行することができます。 参考 : Introduction to BigQuery pipelines BigQuery pipelines Cloud Workflows Cloud Workflows (または単に Workflows)はGoogle Cloud のジョブ自動化サービスです。フルマネージドかつサーバーレスであるためインフラの管理は必要なく、また非常に安価に利用できるのが特徴です。 スケジュールされたクエリ(Scheduled queries)や BigQuery pipelines よりもワークフローが複雑なとき、ワークフローをコード(yaml)で管理したいとき、また BigQuery だけでなく他の Google Cloud サービスとも統合したいときに、Cloud Workflows を選択します。 以下の記事も参照してください。 参考 : Cloud Workflowsを徹底解説 - G-gen Tech Blog 参考 : Cloud Workflowsで簡易的なデータパイプラインを構築してみる - G-gen Tech Blog Dataform Dataform は、BigQuery における ELT の T(Transform = データ変換)を担う Google Cloud サービスです。Dataform core(SQLX)という SQL の拡張言語で変換処理を記述します。また Dataform は、無料で利用できます。 Dataform では SQLX により、テスト(assertions)を記述したり、複雑な依存関係を定義できるのが強みです。 詳細は以下の記事をご参照ください。 blog.g-gen.co.jp データカタログ(Dataplex Universal Catalog) データカタログ とは、メタデータをカタログ化し、利用者が必要なデータを探し出しやすくするためのツールです。BigQuery のデータカタログ管理は、 Dataplex Universal Catalog (旧称 Dataplex Catalog)で行うことができます。 BigQuery のデータセットやテーブルのメタデータは、自動的に Dataplex Universal Catalog に登録され、検索可能になります。BigQuery では、以下のようなメタデータが自動的にカタログに登録されます。 データセット・テーブルの一覧 データセット・テーブルの説明(description) テーブルの列の説明(description) テーブルのスキーマ(列名、型、その他の基本的な情報) Dataplex Universal Catalog(Dataplex Catalog)の詳細は、以下の当社記事を参照してください。 blog.g-gen.co.jp アクセス制御 ネットワーク BigQuery は Web API で操作するクラウドサービスであり、API エンドポイントはインターネットからアクセス可能です。そのため BigQuery は 原則的にインターネット経由で利用する サービスです。API との通信は HTTPS プロトコルであり、SSL/TLS で暗号化されます。 ただし、 限定公開の Google アクセス 機能や Private Service Connect 機能を利用することで、専用線や IPSec VPN で Google Cloud と接続されたオンプレミス環境から、プライベート IP アドレスで API を利用することが可能です。また、VPC 内の Compute Engine VM からも、Google のネットワーク内に閉じた通信でアクセスすることができます。 限定公開の Google アクセスや Private Service Connect の詳細は、以下の記事をご参照ください。 blog.g-gen.co.jp blog.g-gen.co.jp IAM によるアクセス制御 BigQuery の認証・認可は他の Google Cloud サービスと同様、Identity and Access Management( IAM )の仕組みで制御されます。 BigQuery 自体はインターネットから利用可能なサービスですが、IAM により厳密な認証・認可制御がされますので、アクセスすべきでない人がデータへアクセスすることはできません。クラウドサービスは従来型の境界型セキュリティではなく、「あるべき人があるべき方法でアクセスしているか」を検査する方法でセキュリティが担保されます。 IAM の仕組みの詳細は以下の記事をご参照ください。 blog.g-gen.co.jp BigQuery のテーブルやデータセット、またジョブ投入に関する IAM 権限管理の仕組みについては、以下の記事をご参照ください。 blog.g-gen.co.jp Dataplex によるアクセス制御 BigQuery を使ったデータ基盤が大規模になると、IAM によるアクセス制御を管理する運用工数が肥大化します。 Dataplex を使うと、アクセス権限の管理・運用の工数を節減できる可能性があります。Dataplex では、データのアクセスユースケースに基づいた データメッシュ を構築することができます。 Dataplex によるアクセス制御の詳細については、以下の記事も参考にしてください。 blog.g-gen.co.jp blog.g-gen.co.jp 承認済みビューと承認済みデータセット ビューにも IAM アクセス権限が存在します。通常は、利用者がビューへクエリを投げるとき、ビューへのアクセス権限を持っていたとしても、ビューのベーステーブルへのデータアクセス権限も持っていなければクエリは権限エラーで失敗します。 しかし、 承認済みビュー や 承認済みデータセット の仕組みを使うと、ベーステーブルへのアクセス権限が必要なくなり、アクセス権限管理をビューに集中させることができます。 機能の詳細は、以下を参照してください。 blog.g-gen.co.jp セキュリティ関連機能 デフォルトの暗号化・CMEK 暗号化 Google Cloud では、保存される 全てのデータが自動的・透過的に暗号化されます 。BigQuery も例外ではなく、データは透過的に暗号化・復号されます。これをデフォルトの暗号化といいます。 デフォルトでは、暗号化は Google が管理する AES 暗号鍵で行われます。この鍵は厳密に監査された堅牢な鍵管理システムで管理されています。 一方で、監査要件への対応やより厳密なセキュリティを求める場合、顧客管理の暗号鍵(Customer-managed Encryption Key、略称 CMEK)を用いることができます。CMEK は Cloud KMS で管理されます。 参考 : 保存時の暗号化 Cloud KMS の詳細は、以下の記事を参照してください。 blog.g-gen.co.jp 列レベルの暗号化 列レベルの暗号化 は、Cloud KMS で管理する暗号鍵を使って、データ自体を暗号化してテーブルに格納する方法です。 デフォルトの暗号化や CMEK 暗号化とは異なり、透過的なアクセスはできず、関数で復号しなければデータにアクセスできません。詳細は以下の記事をご参照ください。 blog.g-gen.co.jp 列レベルのアクセス制御、行レベルのセキュリティ IAM でデータセットレベル、あるいはテーブルレベルでアクセス制御が可能なことは先に述べたとおりです。 列レベルのアクセス制御 や、 行レベルのセキュリティ を使うと、さらに細かい粒度、すなわち列レベルおよび行レベルでアクセス制御を行うこともできます。 詳細は、以下を参照してください。 blog.g-gen.co.jp VPC Service Controls VPC Service Controls は Google Cloud の API アクセス制御とデータ移動の制限のためのサービスです。BigQuery にも対応しており、BigQuery にアクセス可能な主体を制限したり、プロジェクト外へのデータ移動を制限することができます。 以下の記事もご参照ください。 blog.g-gen.co.jp その他のセキュリティ 応用編 の記事では、以下のような追加のセキュリティ機能を解説しています。 Sensitive Data Protection 動的データマスキング 列レベル暗号化 テーブル設計 非正規化 BigQuery をはじめとする分析用データベースでは、第3正規形以上に正規化されたテーブル設計を基本とする運用データベースの OLTP 処理とは対象的に、分析処理(OLAP)を最適化するため 非正規化 したデータモデリングを行うこともあります。 当記事ではデータウェアハウス(データマート)の非正規化のベストプラクティスまでは踏み込みませんが、通常のトランザクション処理(OLTP)向けデータベースとは異なる考えでデータモデリングを行う必要がある点にご留意ください。 参考 : BigQuery 特集: 結合データ、繰り返しおよびネストされたデータの処理 パーティショニング・クラスタリング BigQuery のテーブル設計では パーティショニング と クラスタリング の概念が重要です。 テーブルへのクエリは、通常はフルスキャンとなります。数 TB の規模のテーブルをフルスキャンすると処理時間がかかるほか、スキャン容量 / コンピュート処理量が多くなり、利用料金も跳ね上がります。 これを避けるため、パーティショニング列やクラスタリング列を適切に設定すると、スキャン範囲を狭め、効率的な処理に繋がります。 詳細は以下の記事を参照してください。 blog.g-gen.co.jp Search Index BigQuery のテーブルには、通常の RDBMS におけるインデックスの概念に近いものとして Search Index (検索インデックス)が存在します。 ただし、RDBMS で使われるように汎用的なクエリでスキャン効率を良くしたりする目的ではなく、非構造化テキストや半構造化 JSON データから特定の文字列を検索・抽出するようなクエリの効率化の目的で用いられます。 いかのようなユースケースが挙げられます。 システムログ、ネットワークログ、アプリケーションログ等から特定文字列を検索 法的規制に準拠するため削除対象データを特定 セキュリティ監査 検索フィルタのダッシュボード用 前処理されたデータの完全一致検索 以下の公式ドキュメントもご参照ください。 参考 : BigQuery での検索の概要 また、以下の当社記事でも紹介されています。 blog.g-gen.co.jp Gemini in BigQuery Gemini in BigQuery とは Gemini in BigQuery は、BigQuery での分析・可視化や、SQL の開発を生成 AI がアシストする機能群です。Gemini in BigQuery の料金は通常の BigQuery 利用料金の中に含まれていますので、無料(追加コストなし)で利用できます。 参考 : Gemini in BigQuery の Gemini の概要 Gemini in BigQuery には、以下のような機能があります。これらの機能により、データ分析者が 可視化や分析を効率化・高速化 したり、あるいは SQL を習得していないビジネスユーザーが BigQuery のデータに 自然言語でアクセス できるようになります。 Conversational Analytics(会話形分析)/ データエージェント SQL コーディング支援 Python コーディング支援(BigQuery ノートブック) データキャンバス(Data canvas。自然言語によるデータ分析) データ準備(Data preparation。自然言語によるデータ変換) データ分析情報(Data insights) メタデータ自動生成 SQL 変換(他方言から GoogleSQL への変換)支援 料金と利用制限 Gemini in BigQuery は、オンデマンドモードと、すべての BigQuery Editions で利用可能です。ただし、Standard エディションの場合のみ、以下の2機能が利用できません。 データインサイト メタデータの自動生成(Automated metadata generation) Gemini in BigQuery 機能を利用しても、通常の BigQuery 料金以外の料金は発生しませんが、1日あたりの実行可能回数に制限(割り当て)があります。詳細は以下を参照してください。 参考 : Gemini in BigQuery Pricing Overview 参考 : Quotas and limits - Quotas for Gemini in BigQuery 各機能の詳細 Gemini in BigQuery の各機能の詳細は以下の記事で扱っています。 blog.g-gen.co.jp 自然言語でのクエリ Gemini in BigQuery を使うことで、SQL の知識がなくても、日本語や英語などの 自然言語を使って BigQuery からデータを抽出 することができます。 自然言語でのクエリには、最も難易度の低い Conversational Analytics(会話形分析)やデータキャンバンスなど、複数の手法があります。以下の記事では、BigQuery に自然言語を使ってクエリする方法をまとめていますので参照してください。 blog.g-gen.co.jp 応用編の内容 当記事は「基本編」です。続編記事である「 応用編 」では以下のような事項を扱います。 外部テーブル BigLake BigQuery Omni 連携クエリ、連携データセット Apache Iceberg 対応 モニタリング チューニング 同時実行とリソース コスト削減 BigQuery ML データリネージ 非構造化データの分析 高度なセキュリティ Cloud DLP 動的データマスキング 列レベル暗号化 BigQuery BI Engine 開発 BigQuery Dataframes BigQuery リポジトリ パイプ構文 blog.g-gen.co.jp 杉村 勇馬 (記事一覧) 執行役員 CTO 元警察官という経歴を持つ IT エンジニア。クラウド管理・運用やネットワークに知見。AWS 認定資格および Google Cloud 認定資格はすべて取得。X(旧 Twitter)では Google Cloud や Google Workspace のアップデート情報をつぶやいています。 Follow @y_sugi_it
アバター
G-gen の堂原です。Google Cloud(旧称 GCP)の Cloud Load Balancing で利用できる Google マネージド SSL/TLS 証明書 を、 Certificate Manager で作成する方法を紹介します。 Certificate Manager について Certificate Manager とは Google マネージド証明書 構成 コンポーネント Certificate Certificate Map Certificate Map Entry DNS Authorization 証明書の発行方法 Google マネージド 証明書の認証方法 Google マネージド 証明書 ロードバランサー認証 DNS 認証 gcloud を用いた作成手順 はじめに 1. DNS Authorization 作成 2. CNAME レコード登録 3. Certificate 作成 4. ロードバランサにアタッチ Certificate Manager について Certificate Manager とは Certificate Manager は Cloud Load Balancing(以下、ロードバランサ)用の SSL/TLS 証明書(以下、証明書)の作成・管理・デプロイが行えるサービスです。 Certificate Manager で管理できる証明書には「 Google マネージド証明書 」と「 セルフマネージド証明書 」があります。前者は Google により発行されるドメイン認証(DV)証明書であり、当記事で紹介する DNS 認証の方法であれば 100 枚まで無料で作成 できます。 参考 : Certificate Manager の概要 Google マネージド証明書 Google マネージド証明書 は、Google が発行するドメイン認証(DV)の証明書です。無料で発行でき、Web サイトや Web アプリケーションを HTTPS 化することができます。 Google マネージド証明書の発行には、自組織で管理するドメインの所有権を認証する必要があります。ドメイン認証には「DNS 認証」と「ロードバランサー認証」の2つの方法があります。 参考 : Google マネージド証明書のドメインの承認 当記事で紹介する DNS 認証の場合、2025年9月現在、Google Cloud コンソール(Web UI)で証明書を作成・管理することはできます。しかし、その証明書をロードバランサーにアタッチすることは、 Google Cloud コンソールではできません 。ロードバランサーへのアタッチは、Google Cloud CLI(gcloud コマンド)や Terraform で実施する必要があります。Google Cloud コンソールのロードバランサー設定画面では、DNS 認証で作成した Google マネージド証明書は選択肢に表示されませんのでご注意ください。 参考 : DNS 認証を使用して、グローバル Google マネージド証明書をデプロイする なお当記事では紹介しませんが、ロードバランサー認証の Google マネージド証明書であれば、Google Cloud コンソールで作成からアタッチまでを行うことができます。 構成 コンポーネント Certificate Manager においては大きく 4 つのコンポーネントが登場します。それぞれの関わりは以下の図のとおりです。 参考 : How Certificate Manager works Certificate Manager の構成図 Certificate Certificate Manager で管理される証明書です。 「 Google マネージド証明書 」と「 セルフマネージド証明書 」の2種類があります。 単一ドメイン用の証明書のほか、ワイルドカード証明書も発行・管理できます。 Certificate Map ロードバランサ(Target HTTPS Proxy)と証明書を紐づけるための中間エンティティです。 Certificate Map の中には、複数の Certificate Map Entry が所属できます。 ロードバランサの Target HTTPS Proxy については以下の記事で紹介しています。 blog.g-gen.co.jp Certificate Map Entry ドメイン名と Certificate の紐づけをするコンポーネントです。 www.example.com には証明書 A を、 xxx.example.com には証明書 B を適応させるといった設定が可能です。 他のどの Certificate Map Entry でも指定されてないドメインへのアクセスをまとめて処理する Certificate Map Entry の作成も可能です。 DNS Authorization 証明書の認証情報を管理するコンポーネントで、DNS 認証に用いられます。 作成時に 1 つの CNAME レコードが指定され、それをネームサーバに登録することで、ドメインの所有者であることが認証されます。 認証後、DNS Authorization を Certificate にアタッチすることで、認証済みの証明書を作成することができます。 DNS Authorization をアタッチしていない Certificate は自動的に、後述する Load balancer 認証になります。 証明書の発行方法 ロードバランサにアタッチするための 証明書(セルフマネージド・Google マネージド)には 2 つの 構成方法 があります。 Compute Engine SSL 証明書リソース Certificate Manager のコンソール画面では「従来の証明書」タグに表示される 認証方法は Load balancer 認証のみ (後述) コンソールから作成が可能 Certificate Manager Certificate Manager のコンソール画面では「証明書」タグに表示される 認証方法は Load balancer 認証 と DNS 認証から選択 コンソールから作成が不可 (2023年5月現在)。gcloud コマンド等を利用 当記事では後者の紹介をしています。後者であれば DNS 認証が利用でき、ワイルドカード証明書の発行も可能であるなどの利点があります。 Google マネージド 証明書の認証方法 Google マネージド 証明書 Google マネージド 証明書 は先述の通り、Google が無償で発行する DV 証明書です。ロードバランサにこの証明書をアタッチすることで、Google Cloud で公開する Web サイト / Web アプリケーションを無料で HTTP (SSL/TLS) 化することができます。 この証明書を発行する際は、ドメインの正当な所有者であることを証明するため、 ドメイン認証 を行う必要があります。ドメインの認証方法には、「DNS 認証」と「ロードバランサー認証」の2つがあります。 参考 : Google マネージド証明書のドメインの承認 ロードバランサー認証 証明書に設定したドメインでロードバランサーにアクセス可能であることをもって認証する方法です。以下のような DNS レコードを作成する必要があります。 設定名 値 レコード名 証明書のドメイン名 レコードタイプ A または AAAA レコード値 ロードバランサーの IP アドレス この方法はシンプルですが、 証明書が利用可能になる前に ドメインをロードバランサの IP アドレスに向ける必要があるため、既存サービスを Google Cloud にマイグレーションする際などには利用できません。 また、この方法は ワイルドカードの証明書の作成には対応していません 。 DNS 認証 Google Cloud から指定された CNAME レコードを DNS に登録することで認証する方法です。参考として、Amazon Web Services(AWS)の Certificate Manager の DNS 認証と似た方式と言えます。 以下のような DNS レコードを作成します。 設定名 値 レコード名 _acme-challenge.(ドメイン名) レコードタイプ CNAME レコード値 (Google が指定する英数字).authorize.certificatemanager.goog. この方法であれば ロードバランサへの疎通設定をする前に証明書の作成ができる ため、マイグレーションの際にも利用することができます。 また、こちらの方法であればワイルドカード証明書の作成に対応しています。 gcloud を用いた作成手順 はじめに 当手順では、ドメイン名 www.example.com のための Google マネージド証明書を発行します。 繰り返しにはなりますが、Certificate Manger の証明書はコンソールから作成できないため、以下の手順は gcloud を用いています。 参考 : Deploy a Google-managed certificate with DNS authorization 1. DNS Authorization 作成 まずは Certificate (証明書) にアタッチするための DNS Authorization を作成します。 DNS Authorization の名前は test-dns-auth とします。 gcloud certificate-manager dns-authorizations create test-dns-auth --domain =" www.example.com " もし、ワイルドカードの証明書 *.example.com を作成したい場合、 --domain は example.com とします。 2. CNAME レコード登録 DNS Authorization 作成後、以下のコマンドで指定された CNAME レコードの確認をします。 gcloud certificate-manager dns-authorizations describe test-dns-auth レコード名・タイプ・値が出力されるので、その情報を元にネームサーバに CNAME レコードを登録します。 3. Certificate 作成 Certificate、つまり証明書の作成を行います。このタイミングで DNS Authorization もアタッチします。 Certificate の名前は test-cert とします。 gcloud certificate-manager certificates create test-cert \ --domains =" www.example.com " \ --dns-authorizations = test-dns-auth 作成処理には 10 分ほどかかります。 以下のコマンドを実行、またはコンソールで test-cert を確認し、ステータスが ACTIVE (コンソールだと「正常」)となっていたら、証明書が正しく作成されたことになります。 gcloud certificate-manager certificates describe test-cert createTime: ' xxx ' expireTime: ' xxx ' managed: authorizationAttemptInfo: - domain: www.example.com state: AUTHORIZED dnsAuthorizations: - projects/xxx/locations/global/dnsAuthorizations/test-dns-auth domains: - www.example.com state: ACTIVE ... 4. ロードバランサにアタッチ 上記の手順で証明書の作成自体は完了しましたので、以降はロードバランサにアタッチするまでの流れとなります。 まずは Certificate Map を作成します。名前は test-cert-map とします。 gcloud certificate-manager maps create test-cert-map 次に test-cert-map の中に、ドメイン www.exmple.com と Certificate test-cert を紐づける Certificate Map Entry を作成します。名前は test-cert-map-entry とします。 gcloud certificate-manager maps entries create test-cert-map-entry \ --map =" test-cert-map " \ --certificates =" test-cert " \ --hostname =" www.example.com " これで Certificate Manager 側の設定は完了です。 あとは、Certificate Map をロードバランサの Target HTTPS Proxy にアタッチすれば完了です。 以下のコマンドは既に存在している Traget HTTPS Proxy を更新するコマンドです。Target HTTPS Proxy の名前を test-https-proxy とします。 gcloud compute target-https-proxies update test-https-proxy \ --certificate-map =" test-cert-map " 以上で、証明書をロードバランサにアタッチすることができました。 堂原 竜希 (記事一覧) クラウドソリューション部クラウドエクスプローラ課。2023年4月より、G-genにジョイン。 Google Cloud Partner Top Engineer 2023, 2024, 2025に選出 (2024年はRookie of the year、2025年はFellowにも選出)。休みの日はだいたいゲームをしているか、時々自転車で遠出をしています。 Follow @ryu_dohara
アバター
G-gen の杉村です。組織の集約ログシンクを作成する際に「適切な権限を付与できませんでした」というメッセージが出力されました。原因と対処法を紹介します。 はじめに・前提知識 事象 原因調査 調査 判明した原因 詳細な原因 対処法 概要 1. 顧客 ID の確認 1-A. 管理コンソールで顧客 ID を確認 1-B. gcloud コマンドで顧客 ID を確認 2. ホワイトリストへの追加 3. 権限の再付与 3-A. 書き込み ID に IAM ロールを付与 3-B. シンクの再作成 はじめに・前提知識 Cloud Logging の ログシンク の仕組みを使うことで、組織内で監査ログやその他の重要なログを一つのプロジェクトに集約し、監査対応や発見的統制に役立てることができます。 ログシンクの仕組みについては以下の記事の ログルーティングとログの保存 の項をご参照ください。 blog.g-gen.co.jp 事象 ある組織において、組織レベル (またはフォルダレベル) で集約ログシンクを作成することを試みました。 作業は Web コンソール画面から行いました。ログシンクの作成自体は成功したものの、以下のメッセージが表示されました。 手動構成が必要 手動構成が必要 シンクは正常に作成されました。 しかしながら、宛先に適切な権限を付与できませんでした。シンクのサービス アカウント (サービスアカウント名) に、宛先 (ログバケット ID) へのログ書き込み権限を手動で付与して、オペレーションを完了してください。適切な権限がサービス アカウントに付与されるまで、この宛先にログは書き込まれません。 メッセージにあるように、権限不足により集約ログシンクは正常に稼働していません。 原因調査 調査 Cloud Audit Logs により、権限付与に失敗した際のエラーログが出ているはずだと考え、Cloud Logging のログエクスプローラからログを確認しました。 すると、以下のようなログが出力されていました。 ログエクスプローラでのログ検索 One or more users named in the policy do not belong to a permitted customer. このエラーメッセージを Google Cloud 公式ドキュメント内で検索すると、以下のドキュメントに当たりました。このエラーメッセージは 組織のポリシー の ドメイン制限の制約 (iam.allowedPolicyMemberDomains) という制約によって、IAM に追加しようとしたドメインが拒否された際に表示されるものでした。 参考 : エラー メッセージの例 組織のポリシーについては、以下の記事をご参照ください。 blog.g-gen.co.jp 判明した原因 今回の事象が発生した組織では、確かにドメイン制限の制約 (iam.allowedPolicyMemberDomains) を設定したばかりでした。 この制約は、事前にホワイトリストに登録した Google Workspace (Cloud Identity) 組織のアカウント以外は IAM 権限を持てないようにする制約です。 このときは、ホワイトリストとして Google Cloud 環境の構築を行う外部ベンダーのドメインしか登録していませんでした。つまり、 自分自身の組織の登録をしていなかった のです (なお、制約は遡及的に効果を発揮しませんので、既に IAM 権限付与済みのアカウントには影響しません)。 自分自身の組織をホワイトリストとして登録していなかったため、自組織のサービスアカウントも拒否され、エラーとなったのでした。 詳細な原因 ログシンクが他プロジェクトのログバケットにログを書き込むためには、 書き込み ID と呼ばれるサービスアカウントが、宛先ログバケットの所属するプロジェクトに対して ログバケット書き込み (roles/logging.bucketWriter) ロールを持つ必要があります。 この権限付与は、今回のようにコンソール画面からログシンク作成を行うと、通常は裏で Google 側が自動的に実施してくれます。 しかし今回は「ドメイン制限の制約」に自組織が登録されていなかったことで、裏で Google が権限付与をしようとした際に、自組織内のサービスアカウントであるにも関わらず権限付与が拒否されてしまい、今回の事象となったわけです。 対処法 概要 ドメイン制限の制約 (iam.allowedPolicyMemberDomains) のホワイトリストに対して、自組織の 顧客 ID (お客様 ID、customer ID) を追加します。 顧客 ID は以下いずれかの方法で確認できます。 Google Workspace (Cloud Identity) の管理コンソールで確認 gcloud コマンドで確認 1. 顧客 ID の確認 1-A. 管理コンソールで顧客 ID を確認 Google Workspace (Cloud Identity) の管理コンソールにログインし、 アカウント > アカウント設定 > プロファイル から顧客 ID を確認することができます。 Google Workspace (Cloud Identity) 管理コンソールでの確認 この手順で顧客 ID を確認するには、管理コンソールにログインして情報を表示する権限が必要です。Google Workspace (Cloud Identity) の特権を持っていない場合は、次の gcloud コマンドの手順で ID を取得します。 1-B. gcloud コマンドで顧客 ID を確認 Google Workspace (Cloud Identity) の管理コンソールに入れない場合は、以下のコマンドで組織の顧客 ID を確認することができます。 ${DOMAIN_NAME} の部分は自環境のドメイン名 ( example.com 等) に置き換えてください。 gcloud organizations list --filter= " displayName= ${DOMAIN_NAME} " --format=json 当該 Google Cloud 組織に対してコマンド実行アカウントが resourcemanager.organizations.get 権限を持っていればこのコマンドが成功します。この権限を得るためには、組織トップノードのレベルで 参照者 (roles/browser) や 組織の管理者 (roles/resourcemanager.organizationAdmin) が必要です。 なお組織のポリシーの制約を管理するのに必要な 組織ポリシー管理者 (roles/orgpolicy.policyAdmin) ロールには、組織情報を得るための resourcemanager.organizations.get 権限が 含まれていません ので、ご注意ください。 2. ホワイトリストへの追加 Web コンソールの IAM と管理 > 組織のポリシー で Domain restricted sharing または constraints/iam.allowedPolicyMemberDomains で検索して当該の制約を編集します。 制約のホワイトリストへ顧客 ID を追加 + 値を追加 を押して、顧客 ID を追加します。 なお、 ドキュメント では顧客 ID の文字列の前に接頭辞 is: をつけることが指示されていますが、接頭辞がなくても挙動が変わらないことが検証できています。 3. 権限の再付与 3-A. 書き込み ID に IAM ロールを付与 シンクの書き込み ID に権限を与えるため、シンクの書き込み ID 名を確認します。 Cloud Logging > ログルーター でシンクの一覧を表示させ、当該のシンク名の右の三点リーダから シンクの詳細を表示する を選択します。 シンクの書き込み ID を確認 書き込み ID (サービスアカウント) 名をクリップボードにコピーします。なおサービスアカウント名の頭にある serviceAccount: は接頭辞であり、今回は不要です。 次に、ログルーティング先のログバケットのあるプロジェクトの IAM 画面へ遷移します。 IAM と管理 > IAM に遷移し、プロジェクトセレクタが 宛先バケットと同じプロジェクト になっていることを確認します。この画面で、先程のサービスアカウントをプリンシパルとして、 ログバケット書き込み (roles/logging.bucketWriter) ロールを付与してください。 これにより、ログシンクによるログのルーティングができるようになります。 3-B. シンクの再作成 上記の手順では書き込み ID に手動で権限を付与したことで権限を解決しました。 代わりに、ログシンクを削除して再作成することでも Google が裏で自動で権限を付与してくれますので、問題を解決することができます。 杉村 勇馬 (記事一覧) 執行役員 CTO / クラウドソリューション部 部長 元警察官という経歴を持つ現 IT エンジニア。クラウド管理・運用やネットワークに知見。AWS 12資格、Google Cloud認定資格11資格。Twitter では Google Cloud や AWS のアップデート情報をつぶやいています。 Follow @y_sugi_it
アバター
当記事は みずほリサーチ&テクノロジーズ × G-gen エンジニアコラボレーション企画 で執筆されたものです。 Cloud Armor は Google Cloud でセキュアな Web アプリケーションを構築するために欠かせないプロダクトです。 代表的なアプリケーションへの攻撃である SQL インジェクションを題材に、CloudArmor の機能を体験できるハンズオンを整備しました。 G-gen の片岩です。 当記事ではサーバレスな Web アプリケーションを構築し、SQL インジェクション攻撃から保護するまでの手順をご紹介します。 Cloud Armor はじめに Cloud Armor とは 当記事の概要 関連記事 構成図 作成するアプリケーション 事前準備 Google Cloud にアクセス プロジェクトIDの確認 Cloud Shell の起動 変数PROJECT_IDの設定 Cloud SQL で RDB を準備 インスタンスの作成 データベースの作成 ユーザーの作成 サービスアカウントの作成 サービスアカウントの作成 サービスアカウントに権限を付与 コンテナイメージの作成 概要 エディタの起動 ファイルの配置 ターミナルの表示 コンテナイメージのビルド Cloud Run でアプリケーションを実行 デプロイ 動作確認 HTTP(S) ロードバランサの作成 ロードバランサの作成 動作確認 SQL インジェクション対策 SQL インジェクション攻撃 Cloud Armor による保護 保護確認 WAF を迂回したアクセスの禁止 はじめに Cloud Armor とは Cloud Armor は Google 製のクラウド型 WAF (Web Application Firewall) です。 WAF とは SQL インジェクション、クロスサイトスクリプティング、DDoS など、 Web アプリケーションに対するアプリケーションレイヤの攻撃を検知・防御するための仕組みです。 アクセス元の IP アドレスを制限することも可能です。 当記事の概要 今回は以下の手順に沿って Web アプリケーションの構築および SQL インジェクション対策を実施しました。 Cloud SQL 構築 サービスアカウントの作成 Docker コンテナの作成 Cloud Run 上で動作する Web アプリケーションの作成 ロードバランサの作成 Cloud Armor による SQL インジェクション対策の確認 関連記事 利用するサービスについては以下の記事にて詳しく解説しています。 参考: Cloud SQL 参考: Cloud Run 参考: Load Balancing 参考: Cloud Armor 構成図 Web アプリケーションの構成図を以下に示します。 作成するアプリケーション 作成するアプリケーションの画面です。 事前準備 Google Cloud にアクセス Google Cloud へアクセスします。 プロジェクトIDの確認 はじめに、ご自身のプロジェクトIDを確認します。 Cloud Shell の起動 今回は Cloud Shell を利用して実装します。 Cloud Shell では専用の仮想マシンが払い出され、開発用のクライアントとして使用できます。 gcloud コマンドリファレンスは こちら です。 それでは、コンソール画面右上の Cloud Shell を起動するアイコンをクリックします。 変数PROJECT_IDの設定 以降のコマンドにて PROJECT_ID を設定する手間を省くため、変数 PROJECT_ID を設定します。 プロジェクトIDをご自身のプロジェクトIDに読み替えて以下のコマンドを実行します。 PROJECT_ID =プロジェクトID Cloud SQL で RDB を準備 インスタンスの作成 まず Cloud SQL のインスタンスを作成します。 インスタンス ID は「my-cloudsql」とします。パスワードには任意のパスワードを設定してください。 なお、Cloud SQL インスタンスの作成には 2 〜 3 分掛かります。 gcloud sql instances create my-cloudsql \ --database-version=POSTGRES_14 \ --cpu=1 \ --memory=4GB \ --region=asia-northeast1 \ --root-password=rootpassword APIが有効でない場合、APIを有効にするか確認されるので「y」を入力します。 API [sqladmin.googleapis.com] not enabled on project [xxxxxxxxxxxx]. Would you like to enable and retry (this will take a few minutes)? (y/N)? ※Cloud SQL 以外のサービスでも同様です。API を有効にして以降の手順を進めてください。 データベースの作成 次にデータベースを作成します。データベース名は「my-db」とします。 gcloud sql databases create my-db --instance=my-cloudsql ユーザーの作成 ユーザーを作成します。ユーザー名は「my-user」とします。 パスワードは任意で構いません。値を忘れないようメモします。 ※当記事では以降 password と記載しますが、安易なパスワードの設定は非推奨です。 gcloud sql users create my-user \ --instance=my-cloudsql \ --password=password サービスアカウントの作成 サービスアカウントの作成 Cloud Run から Cloud SQL にアクセスできる権限を持ったサービスアカウントを作成します。サービスアカウント名を「cloudsql-client」とします。 gcloud iam service-accounts create cloudsql-client \ --display-name= " cloudsql-client " サービスアカウントに権限を付与 Cloud SQL クライアントのロールを付与します。 gcloud projects add-iam-policy-binding ${PROJECT_ID} \ --member= " serviceAccount:cloudsql-client@ ${PROJECT_ID} .iam.gserviceaccount.com " \ --role= " roles/cloudsql.client " コンテナイメージの作成 概要 Python と Flask を利用したオリジナルのアプリを作成します。 ここでは以下の 5 つのファイルを準備します。 ファイル名 説明 index.html 画面レイアウトを記述したファイル app.py アプリケーションの挙動を記述したファイル connect_unix.py Cloud SQL に接続するためのファイル (*1) requirements.txt 利用するパッケージを記述したファイル (*1) Dockerfile コンテナをビルドするときに使用するファイル (*1) (*1) Google Cloud が提供している Python の サンプルアプリ の コード を利用。 エディタの起動 それではファイルの作成に着手します。 まずは「エディタを開く」をクリックし、コードの編集画面を表示します。 ファイルの配置 次に以下の構成でファイルを配置します。 コードはコピー&ペーストしてください。 index.html 画面レイアウトを記述したファイルです。 <!DOCTYPE html> < html lang = "ja" > < head > < meta charset = "UTF-8" > < link href = "https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel = "stylesheet" integrity= "sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin = "anonymous" > < title > テストWebアプリ </ title > </ head > < body > < div class = "container mt-4" > < h2 > 商品登録 </ h2 > < form action = "/add" method = "POST" > < input name = "product" class = "form-control" type = "text" placeholder = "商品名" ></ input > < button type = "submit" class = "btn btn-primary" > 商品登録 </ button > </ form > </ div > < div class = "container mt-4" > < h2 > 商品削除 </ h2 > < form action = "/delete" method = "POST" > < input name = "id" class = "form-control" type = "text" placeholder = "#" ></ input > < button type = "submit" class = "btn btn-primary" > 商品削除 </ button > </ form > </ div > < div class = "container mt-4" > < h2 > 商品一覧 </ h2 > < table class = "table table-striped" > < thead > < tr > < th scope = "col" > # </ th > < th scope = "col" > 名前 </ th > < th scope = "col" ></ th > </ tr > </ thead > < tbody > {% for product in products %} < tr > < th scope = "row" > {{ product.id }} </ th > < td > < a class = "text-decoration-none" > {{ product.product }} </ a > </ td > </ tr > {% endfor %} </ tbody > </ table > </ div > </ body > </ html > app.py アプリケーションの挙動を記述したファイルです。 import os import sqlalchemy from typing import Dict from flask import Flask, render_template, request, Response, redirect, url_for from connect_unix import connect_unix_socket app = Flask(__name__) db = None def init_connection_pool () -> sqlalchemy.engine.base.Engine: return connect_unix_socket() @ app.before_first_request def init_db () -> sqlalchemy.engine.base.Engine: global db db = init_connection_pool() with db.connect() as conn: conn.execute( "DROP TABLE IF EXISTS products;" "CREATE TABLE products ( id SERIAL NOT NULL, product VARCHAR(30) , PRIMARY KEY (id) );" ) @ app.route ( "/" , methods=[ "GET" , "POST" ]) def render_index () -> str : products = [] with db.connect() as conn: results = conn.execute( "SELECT id, product FROM products" ).fetchall() for row in results: products.append({ "id" : row[ 0 ], "product" : row[ 1 ]}) context = { "products" : products} return render_template( "index.html" , **context) @ app.route ( "/add" , methods=[ "POST" ]) def product_add (): with db.connect() as conn: sample_sql= "INSERT INTO products(product) VALUES('" + request.form.get( 'product' ) + "');" conn.execute(sample_sql) return redirect(url_for( 'render_index' )) @ app.route ( "/delete" , methods=[ "POST" ]) def product_delete (): with db.connect() as conn: sample_sql= "DELETE FROM products WHERE id = '" + request.form.get( 'id' ) + "'" conn.execute(sample_sql) return redirect(url_for( 'render_index' )) if __name__ == "__main__" : app.run(host= "127.0.0.1" , port= 8080 , debug= True ) connect_unix.py Cloud SQL に接続するためのファイルです。 import os import sqlalchemy def connect_unix_socket () -> sqlalchemy.engine.base.Engine: db_user = os.environ[ "DB_USER" ] db_pass = os.environ[ "DB_PASS" ] db_name = os.environ[ "DB_NAME" ] unix_socket_path = os.environ[ "INSTANCE_UNIX_SOCKET" ] pool = sqlalchemy.create_engine( sqlalchemy.engine.url.URL.create( drivername= "postgresql+pg8000" , username=db_user, password=db_pass, database=db_name, query={ "unix_sock" : "{}/.s.PGSQL.5432" .format(unix_socket_path)}, ), pool_size= 5 , max_overflow= 2 , pool_timeout= 30 , pool_recycle= 1800 , ) return pool requirements.txt 利用するパッケージを記述したファイルです。 Flask==2.1.0 pg8000==1.24.2 SQLAlchemy==1.4.38 cloud-sql-python-connector==1.0.0 gunicorn==20.1.0 Dockerfile コンテナをビルドするときに使用するファイルです。 FROM python:3 COPY requirements.txt ./ RUN set -ex; \ pip install -r requirements.txt; \ pip install gunicorn ENV APP_HOME /app WORKDIR $APP_HOME COPY . ./ CMD exec gunicorn --bind :$PORT --workers 1 --threads 8 app:app ターミナルの表示 ファイル配置後はエディタ右上の「ターミナルを開く」をクリックします。 コンテナイメージのビルド Cloud Build を使用して、Dockerfile を元に Docker コンテナのイメージをビルドします。 cd コマンドにより Dockerfile のあるディレクトリに移動し、以下のコマンドを実行します。コンテナのイメージ名は「my-container」とします。 cd handson-cloudarmor gcloud builds submit --tag gcr.io/ ${PROJECT_ID} /my-container ビルドしたコンテナイメージは、指定したプロジェクトの Container Registry に格納されます。 Cloud Run でアプリケーションを実行 デプロイ Cloud Run はサーバーレスなコンテナ実行基盤を提供するサービスです。 先程作成したコンテナイメージを Cloud Run にデプロイします。 Cloud SQLの DB の password を読み替えて以下のコマンドを実行します。 gcloud run deploy my-run --image gcr.io/ ${PROJECT_ID} /my-container \ --add-cloudsql-instances my-cloudsql \ --region=asia-northeast1 \ --service-account=cloudsql-client \ --allow-unauthenticated \ --set-env-vars INSTANCE_UNIX_SOCKET = " /cloudsql/ ${PROJECT_ID} :asia-northeast1:my-cloudsql " \ --set-env-vars DB_NAME = " my-db " \ --set-env-vars DB_USER = " my-user " \ --set-env-vars DB_PASS = " password " 動作確認 ターミナルに表示された URL をクリックし、サンプルアプリケーションにアクセスします。 以下のとおりアプリの挙動を確認します。 任意の商品名を入力して商品を登録できること 削除したい商品番号を入力して商品を削除できること HTTP(S) ロードバランサの作成 ロードバランサの作成 Cloud Run サービスの前段に配置する HTTP(S) ロードバランサを作成します。 Cloud Armor の利用にはロードバランサが必要になります。 また、それ以外にもオリジンの保護や静的IPアドレスの払い出し、CDN との連携などのメリットがあります。 ネットワークエンドポイントグループを作成します。 ネットワークエンドポイントには先程作成した Cloud Run サービスを指定します。 gcloud compute network-endpoint-groups create my-neg \ --region=asia-northeast1 \ --network-endpoint-type=serverless \ --cloud-run-service=my-run バックエンドサービスを作成し、ネットワークエンドポイントグループを関連付けます。 gcloud compute backend-services create my-backend --global gcloud compute backend-services add-backend my-backend --global \ --network-endpoint-group=my-neg \ --network-endpoint-group-region=asia-northeast1 以下の3つのコマンドを順次実行し、ロードバランサーを作成します。 gcloud compute url-maps create my-lb --default-service my-backend gcloud compute target-http-proxies create my-http-proxy --url-map my-lb gcloud compute forwarding-rules create my-forwarding-rule --global \ --target-http-proxy my-http-proxy \ --ports 80 動作確認 ロードバランサが作成されたら 以下のコマンドを実行してロードバランサの IP アドレスを確認します。 gcloud compute forwarding-rules describe my-forwarding-rule --global ブラウザからアクセスすると画面が表示されることを確認します。 ロードバランサの作成にはしばらく時間がかかります。アクセスできない場合は、しばらく待機してから再度アクセスしてください。 SQL インジェクション対策 SQL インジェクション攻撃 この Web アプリでは、削除したい商品の番号を入力して商品を削除します。 具体的には以下の SQL を発行しています。詳細は app.py の product_delete 関数をご確認ください。 DELETE FROM products WHERE id = ' 入力した番号 ' この実装は SQL インジェクションが可能で、脆弱です。 '入力した番号'に、WHERE 句が必ず True になるような値を設定すると全データが削除できてしまいます。 例えば、以下のような SQL は WHERE 句が必ず True になり、全データが削除対象になってしまいます。 DELETE FROM products WHERE id = ' 1 ' or ' 1 ' = ' 1 ' 上記の SQL を実行させるためにテキストボックスに 1' or '1' = '1 と入力して商品を削除すると、すべてのデータが削除されてしまうことを確認してください。 Cloud Armor による保護 それでは Cloud Armor により SQL インジェクション攻撃から保護します。 gcloud compute security-policies create my-policy gcloud compute security-policies rules create 100 --project= ${PROJECT_ID} --action=deny-403 --security-policy=my-policy --expression=evaluatePreconfiguredExpr\(\ ' sqli-v33-stable\ ' \) gcloud compute backend-services update my-backend --security-policy my-policy --global 今回は expression=evaluatePreconfiguredExpr\(\'sqli-v33-stable\'\) を設定することにより、SQLインジェクション攻撃に対処しています。 ほかの値を設定することも可能です。詳しくは コチラ をご参照ください。 保護確認 それでは確認用のデータを登録し、先程と同様にテキストボックスに 1' or '1' = '1 と入力して攻撃します。 すると403エラーが発生するはずです。 Cloud Armor により、SQL インジェクションをもくろむリクエストが検知され、403 エラーが返却されています。 再度画面にアクセスすると、データは削除されていないことがわかります。 これは Cloud Run で実行している Web アプリケーションを呼び出すことなく、ロードバランサ+Cloud Armor によって 403 エラーが返却されるためです。 WAF を迂回したアクセスの禁止 ここまでの手順で Cloud Armor によって SQL インジェクションを対策できました。しかし、これだけでは不十分です。Web アプリケーションにアクセスするには以下 2 つのルートがあります。 ①ロードバランサを経由するルート ②直接 Cloud Run サービスの URL にアクセスするルート 先程は①のルートを保護しましたが、②のルートを保護できていません。 --ingress=internal-and-cloud-load-balancing の指定を追加した Cloud Run のデプロイコマンドを実行します。 gcloud run deploy my-run --image gcr.io/ ${PROJECT_ID} /my-container \ --add-cloudsql-instances my-cloudsql \ --region=asia-northeast1 \ --ingress=internal-and-cloud-load-balancing \ --service-account=cloudsql-client \ --allow-unauthenticated \ --set-env-vars INSTANCE_UNIX_SOCKET = " /cloudsql/ ${PROJECT_ID} :asia-northeast1:my-cloudsql " \ --set-env-vars DB_NAME = " my-db " \ --set-env-vars DB_USER = " my-user " \ --set-env-vars DB_PASS = " password " --ingress=internal-and-cloud-load-balancing の指定は、internal-and-cloud-load-balancing と記述されているとおり、Cloud Run サービスに対して Google Cloud 内部やロードバランサからのアクセスのみを許可する設定で、インターネットからの直接のアクセスを許可しません。 これで ②直接 Cloud Run サービスの URL にアクセスするルート を禁止できました。 SQL インジェクション対策ができたと言えます。 片岩 裕貴 (記事一覧) データアナリティクス準備室 2022年5月にG-gen にジョイン。 AI/ML系に関心が強く、ディープラーニングE資格とProfessional Machine Learningを取得。最近話題のGenerative AIにも興味がある。毎日の日課は三人乗りの電動自転車で子供を幼稚園に送り迎えすること。和歌山県在住。
アバター
G-genでセールスを担当している村上です。本日は Cloud Identity Free Edition の環境で AppSheet (無償版) が利用できるのか試してみました。結論としては「Cloud Identity Free Edition だけでは AppSheet は利用できないが、Google Workspace と併用することで組織にストレージ容量割り当てがあれば可能」です。その検証の経緯をご紹介します。 はじめに Cloud Identity の登録方法 検証の結果 前提条件 利用するライセンス ライセンス割り当て状況 検証1. Cloud Identity でスプレッドシート作成は不可 追加検証 - アプリが作成できたように見える事象 検証2. 共有されたスプレッドシートを AppSheet 側から指定する場合 検証2-1. (参考) スプレッドシートからアプリ作成を試す 検証2-2. AppSheet 側からスプレッドシートを指定する 検証3. 自分がオーナーであるスプレッドシートを AppSheet 側から指定する場合 検証の前提 検証3-1. (参考) スプレッドシートからアプリ作成を試す 検証3-2. AppSheet 側からスプレッドシートを指定する はじめに Cloud Identity の登録方法 当記事では Cloud Identity の無償版である Free Edition 環境を前提にして検証を行っています。 Cloud Identity の環境準備については、ぜひ以下の記事をご参照ください。 blog.g-gen.co.jp 検証の結果 結果からお伝えしますと、 Cloud Identity Free Edition ライセンスだけでは AppSheet は利用できません 。 Cloud Identity はストレージ領域を持っていないため、AppSheet のデータソースを配置できないことがその理由です。よって AppSheet の運用・開発には有償の Google Workspace ライセンスが必須となります。 ただし Google Workspace と Cloud Identity を併用しており、 組織にストレージプールの割り当てがあれば 、AppSheet 用のデータソースを配置できますので、アプリの作成が可能です。 当記事では、その検証の経緯をご紹介します。 また検証を進める中である条件で設定を進めると一見連携が出来てしまうように見えた (が、事実上はアプリとして利用できない) 事象に遭遇したため、これも参考情報としてご紹介します。 前提条件 利用するライセンス Cloud Identity Free Edition AppSheet Free Edition ライセンス割り当て状況 ユーザーである takenobu murakami に Cloud Identity Free Edition のライセンスだけが割り当てられている状態で検証を行います。 以下のスクリーンショットでは Google Workspace Business Standard も表示されていますが、未割り当ての状態です。 検証1. Cloud Identity でスプレッドシート作成は不可 Google ドライブを開き、データソースとなるスプレッドシートを新規で作成します。 以下の通りエラーになります。 これは、Cloud Identity にはストレージ領域がないために、ファイルが作成できないことを意味しています。 このように Cloud Identity のアカウントは、自らの領域に Google Sheets や Google Slides などのファイルを作成することはできません。ただし有償版の Google Workspace のテナントで作成されたファイルへのアクセスは可能で、権限さえあれば閲覧・編集することができます。 そのため Sheets を自組織の領域に作成はできず、よってその Sheets をベースにして AppSheet アプリを作成することもできません。 追加検証 - アプリが作成できたように見える事象 ここで話が終わってしまいそうですが、冒頭に記載したとおり、上記のエラーが出ず AppSheet でアプリ作成までできてしまう状況もご紹介します。 例えば以下のような状況が該当します。 検証2. 共有されたスプレッドシートを AppSheet 側から指定する場合 検証3. 自分がオーナーであるスプレッドシートを AppSheet 側から指定する場合 以降、この2点について説明していきます。 検証2. 共有されたスプレッドシートを AppSheet 側から指定する場合 検証2-1. (参考) スプレッドシートからアプリ作成を試す 有償版 Google Workspace テナントから共有されたスプレッドシートであれば、操作者が Cloud Identity アカウントでも、AppSheet アプリを作成できるのかどうかを試してみます。 まずは有償版 Google Workspace でシートを作成し、私の Cloud Identity アカウントに共有します。 Cloud Identity アカウントで、共有されたシートを開きます。Cloud Identity アカウントでも問題なく、シートを閲覧・編集できています。機能拡張メニューから「アプリを作成」を選択してみます。 スプレッドシートからアプリを作成 以下のとおり「アプリ所有者に連絡してね」のようなメッセージが表示され、アプリは作成出来ません。 エラー表示 検証2-2. AppSheet 側からスプレッドシートを指定する 今度はその反対に、AppSheet 側から同じスプレッドシートを開いてみます。オーナーが私ではないことが表示されています。 AppSheet からスプレッドシートを選択 そのまま選択して進むと、エラーになる訳では無く、アプリが作成出来てしまいます。 AppSheet アプリ開発画面 以下のスクリーンショットを見ると、ストレージ領域が無いはずの Cloud Identity アカウントの Google ドライブ (マイドライブ) に、AppSheet フォルダが出来ています。これは Google Workspace アカウントでスプレッドシートと AppSheet を連携した場合の挙動と同じです。データは開発者アカウントの Google ドライブに保管されます。 これは、ストレージ領域が無い Cloud Identity では、想定外の挙動と言えます。 Google ドライブに AppSheet フォルダが作成された 一見、これで Cloud Identity アカウントで AppSheet アプリが作成・運用できるようにも見えます。しかし、実際にはそうでないことが以下の操作で分かりました。 作成したアプリで試しに写真をアップロードしてみます。 作成したアプリで写真のアップロードを試行 しばらく処理中のままとなり、ある程度時間が経過した後、以下のエラーが返ってきました。メッセージを要約すると、以下の通りです。 テーブル (スプレッドシート) である "シート1" にレコードが追加できない 画像が保存できない ユーザーの Google Drive の上限を超過している アップロード時のエラーメッセージ Unable to add row to table 'シート1'. → Unable to save image → The user's Drive storage quota has been exceeded. → Error id (略) この検証で、Google Workspace で作成されたシートを利用しても、やはり Cloud Identity アカウントでは AppSheet アプリが作成できないことが分かりました。 検証3. 自分がオーナーであるスプレッドシートを AppSheet 側から指定する場合 検証の前提 もともと有償の Google Workspace ライセンスを割り当てていたがその後不要になり、Cloud Identity Free Edition ライセンスにダウングレードしたアカウントがあるとします。この場合でも、ライセンス削除の前に作成したスプレッドシートなどのファイルは、ライセンス割り当ての削除後も、自分がオーナーのままGoogle ドライブ (マイドライブ) に残るという仕様があります。 ここではその状況を再現するため、事前に以下の作業を実施しました。 takenobu murakami に Google Workspace Business Standard ライセンスを付与 スプレッドシートを作成 takenobu murakami から Google Workspace Business Standard ライセンス割り当てを削除 Google Workspace ライセンスの割り当てを削除する際、以下のように影響について警告する表示がされますが、構わず続行します。 ライセンス割り当てを削除 検証3-1. (参考) スプレッドシートからアプリ作成を試す スプレッドシートは Google ドライブ (マイドライブ) に保管されたまま残っています。このファイルを開くと、以下のとおり「空き容量がありません」と表示され、また「拡張機能」タブがグレーアウトしている等の状態になっています。 このスプレッドシートから AppSheet のアプリ作成ができないのは、一目瞭然です。 シートを開いた状況 検証3-2. AppSheet 側からスプレッドシートを指定する 反対に AppSheet 側からシートを指定してみます。今回はオーナーが私である事が表示されています。 AppSheet 側からスプレッドシートを指定 ここでも操作を進めることが可能であり、指定ができたように見えます。しかしながら、アプリから写真をアップロードすると、やはりエラーが表示されます。 写真ファイルのアップロードを試行 先程とはエラーメッセージが異なりますが、アプリとして実用はできません。メッセージは以下のような内容です。 テーブル (スプレッドシート) である "シート1" にレコードが追加できない 呼び出し側に権限がない エラーメッセージ Unable to add row to table 'シート1'. → The service sheets has thrown an exception. HttpStatusCode is Forbidden. The caller does not have permission: Message[The caller does not have permission] Location[ - ] Reason[forbidden] Domain[global] これらのことから、Cloud Identity アカウントでは AppSheet との実用的な連携は不可能である、ということが分かりました。 ただし、Google Workspace ライセンスがひとつでも割り当てられており、組織にストレージプールが存在していれば、App Sheet の作成は可能ですのでご留意ください。 村上 丈伸 (記事一覧) ビジネス推進部 営業2課 アパレル、工場の作業員を経てITの世界へ。弊社では Google Workspace をメインに活動しています!
アバター
G-gen の佐々木です。当記事では、Cloud Run の マルチコンテナ (サイドカー) 機能 のユースケースの 1 つである、AlloyDB Auth Proxy をサイドカーコンテナとして使用した Alloy DB への接続を試してみます。 前提知識 Cloud Run マルチコンテナ (サイドカー) 機能の概要 AlloyDB for PostgreSQL AlloyDB Auth Proxy 構成 VPC リソースの作成 VPC、サブネットの作成 サービスプロデューサー VPC の IP アドレス範囲を作成 プライベートサービスアクセスの構成 サーバーレス VPC アクセスの構成 AlloyDB クラスタ、インスタンスの作成 サービスアカウントの作成 データベースの作成 踏み台 VM の作成 psql クライアントのインストール データベース、テーブルの作成 データベースの作成 テーブルの作成 Cloud Run サービスの作成 コンテナイメージの作成 使用するコード コンテナイメージのビルド YAML ファイルの作成 使用する YAML ファイル コンテナの起動順序について サービスのデプロイ Cloud Run サービスからの接続 前提知識 Cloud Run Cloud Run とは、Google Cloud のマネージドなコンテナ実行環境でアプリケーションを実行することができる、サーバレス コンテナコンピューティング サービスです。 Cloud Run には Cloud Run services 、 Cloud Run jobs 、 Cloud Run functions(旧称 : Cloud Functions) の3種類がありますが、マルチコンテナ(サイドカー)機能はいずれの種類でも利用することが可能です。 当記事では、HTTP リクエストベースでアプリケーションを実行できる Cloud Run services を例として進めていきます。 Cloud Run の詳細については、以下の記事をご一読ください。 blog.g-gen.co.jp マルチコンテナ (サイドカー) 機能の概要 Cloud Run services でマルチコンテナ (サイドカー) 機能を使用すると、外部からの HTTP リクエストを受信・処理する Ingress コンテナ と、1 つ以上の サイドカーコンテナ でサービスを構成することができます。 サイドカーコンテナは外部からの HTTP リクエストを受信することはできませんが、ローカルホスト ポートを使用して Ingress コンテナや他のサイドカーコンテナと通信することができます。また、それぞれのコンテナから 共有メモリ内ボリューム にアクセスすることも可能です。 Cloud Run services におけるマルチコンテナ構成 公式ドキュメントで紹介されているマルチコンテナ機能のユースケースは以下の通りです。 Prometheus や OpenTelemetry を使用したアプリケーションのモニタリング、ロギング、トレース。 Nginx、Envoy、または Apache2 をリバースプロキシとして使用する。 認証および認可フィルターを追加する (Open Policy Agent など)。 Alloy DB Auth プロキシなど、アウトバウンド接続用のプロキシを実行する。 マルチコンテナ機能は Cloud Run の第 1 世代、第 2 世代いずれの実行環境でも利用することができます。 参考: Deploying multiple containers to a service (sidecars) AlloyDB for PostgreSQL AlloyDB for PostgreSQL (以下、AlloyDB)は Google Cloud が提供する PostgreSQL 互換のフルマネージド データベース サービスです。 同じくマネージドな PostgreSQL データベースを提供する Cloud SQL と比較すると、パフォーマンスや可用性、スケーラビリティに優れ、大規模ワークロードに対応したサービスとなっています。 AlloyDB の詳細については以下の記事で解説しています。 blog.g-gen.co.jp AlloyDB Auth Proxy AlloyDB Auth Proxy (以下、Auth Proxy)は AlloyDB の接続に使用することができるプロキシソフトウェアであり、Google Cloud の IAM でデータベースの認証・認可 (ログイン) をすることができます。 また、Auth Proxy を使用することで、アプリケーションからデータベースへの通信が TLS で暗号化され、より安全なデータベース接続を構成することができます。 参考: About the AlloyDB Auth proxy 構成 当記事では、Cloud Run のマルチコンテナ機能を利用し、AlloyDB Auth Proxy をサイドカーとして使用するコンテナアプリケーションを作成します。 Cloud SQL 同様、AlloyDB のインスタンスは Google Cloud が管理する VPC (サービスプロデューサー VPC) に作成されます。AlloyDB のインスタンスはプライベート IP しかエンドポイントを持たないため、サービスプロデューサー VPC に接続できる VPC を作成し、プライベートサービスアクセスを構成する必要があります。 Cloud Run から AlloyDB へのアクセスは、サーバーレス VPC アクセスコネクタを VPC に作成し、コネクタ経由でサービスプロデューサー VPC に到達できるようにします。 Auth Proxy を 使用して Cloud Run から AlloyDB に接続する VPC リソースの作成 VPC、サブネットの作成 asia-northeast1 にサブネットをもつ VPC を作成します。 この VPC を、AlloyDB が作成されるサービスプロデューサー VPC にピアリング接続します。 # VPC の作成 $ gcloud compute networks create my-vpc --subnet-mode = custom サーバーレス VPC アクセスコネクタを作成するサブネットは /28 の CIDR 範囲である必要があります。 Auth Proxy がプライベート接続で AlloyDB のメタデータを取得する際に Google Cloud APIs にアクセスできる必要があるため、 --enable-private-ip-google-access で 限定公開の Google アクセス を有効化します。 # サブネットの作成 $ gcloud compute networks subnets create my-subnet \ --network = my-vpc \ --range = 192 . 168 . 100 . 0 / 28 \ --region = asia-northeast1 \ --enable-private-ip-google-access サービスプロデューサー VPC の IP アドレス範囲を作成 サービスプロデューサー VPC で使用する IP アドレス範囲を確保します。 $ gcloud compute addresses create my-iprange \ --global \ --purpose = VPC_PEERING \ --addresses = 192 . 168 . 200 . 0 \ --prefix-length = 24 \ --network = my-vpc プライベートサービスアクセスの構成 作成した IP アドレス範囲をサービスプロデューサー VPC で使用し、VPC とのピアリングを構成します。 # プライベートサービスアクセスの構成 $ gcloud services vpc-peerings connect \ --service = servicenetworking.googleapis.com \ --ranges = my-iprange \ --network = my-vpc サーバーレス VPC アクセスの構成 Cloud Run が VPC を経由してサービスプロデューサー VPC にある AlloyDB にアクセスできるように、サーバーレス VPC アクセスコネクタを作成します。 # サーバーレス VPC アクセスコネクタの作成 $ gcloud compute networks vpc-access connectors create my-connector \ --region = asia-northeast1 \ --subnet = my-subnet \ --min-instances = 2 \ --max-instances = 3 \ --machine-type = f1-micro AlloyDB クラスタ、インスタンスの作成 AlloyDB は基本的な管理単位である クラスタ と、クラスタ内に作成され実際にデータベースを持つ プライマリインスタンス 、読み取り専用の リードプールインスタンス で構成されます。 当記事ではリードプールインスタンスは使用しないため、クラスタとプライマリインスタンスのみ作成していきます。 # クラスタの作成 $ gcloud alloydb clusters create my-alloycls \ --password = mypassword \ --region = asia-northeast1 \ --network = my-vpc # プライマリインスタンスの作成 $ gcloud alloydb instances create my-alloyins \ --cluster = my-alloycls \ --cpu-count = 2 \ --instance-type = PRIMARY \ --region = asia-northeast1 サービスアカウントの作成 AlloyDB インスタンスに接続するための権限を付与したサービスアカウントを作成します。 # サービスアカウントを作成する $ gcloud iam service-accounts create my-serviceaccount --project = { プロジェクト名 } AlloyDB に接続するために、 roles/alloydb.client ロールをサービスアカウントに付与します。 # AlloyDB クライアントのロールを付与 $ gcloud projects add-iam-policy-binding { プロジェクト名 } \ --role =" roles/alloydb.client " \ --member = serviceAccount:my-serviceaccount@ { プロジェクト名 } .iam.gserviceaccount.com データベースの作成 踏み台 VM の作成 psql クライアントを使用して AlloyDB にデータベースを作成します。 AlloyDB クラスタにはプライベート IP を使用して接続するため、プライベートサービスアクセスを設定した VPC に踏み台 VM を作成し、そこから AlloyDB に接続します。 踏み台 VM には AlloyDB クライアントのロールを付与したサービスアカウントを紐付けます。 # 踏み台 VM の作成 $ gcloud compute instances create my-bastion \ --machine-type = e2-micro \ --subnet = my-subnet \ --zone = asia-northeast1-b \ --image-family = debian-11 \ --image-project = debian-cloud \ --metadata = enable-oslogin =true \ --service-account = my-serviceaccount@ { プロジェクト名 } .iam.gserviceaccount.com ファイアウォールルールを設定し、踏み台への SSH 接続を許可します。 $ gcloud compute firewall-rules create my-firewall-rule \ --network = my-vpc \ --allow = tcp:22 \ --direction = INGRESS \ --target-service-accounts = my-serviceaccount@ { プロジェクト名 } .iam.gserviceaccount.com 踏み台 VM で作業する際に AlloyDB インスタンスの IP アドレスが必要になるため、以下のコマンドで確認しておきます。 # AlloyDB インスタンスの IP アドレスを確認 $ gcloud alloydb instances describe my-alloyins \ --cluster = my-alloycls \ --region = asia-northeast1 \ | grep ipAddress # 出力例 ipAddress: 192 . 168 . 200 . 2 psql クライアントのインストール 踏み台 VM に psql クライアントをインストールします。 以下のコマンドは踏み台 VM に SSH 接続してから実行します。 # 踏み台 VM で実行 # psql クライアントのインストール $ sudo apt-get update $ sudo apt-get install -y postgresql-client データベース、テーブルの作成 踏み台 VM から AlloyDB にデータベースとテーブルを作成します。 作成したテーブルには、Cloud Run にデプロイするアプリケーションから接続確認するために、適当なレコードを挿入しておきます。 データベースの作成 # 踏み台 VM で実行 # AlloyDB に接続(パスワードは AlloyDB クラスタ作成時に指定したもの) $ psql -h { AlloyDBのプライベートIPアドレス } -U postgres # データベースを作成 > CREATE DATABASE mydb; テーブルの作成 # 踏み台 VM で実行 # データベースの切り替え > \c mydb # users テーブルを作成 > CREATE TABLE users ( id varchar ( 128 ) , name varchar ( 128 )) ; # 適当なデータを挿入 > INSERT INTO users VALUES ( ' 3 ' , ' sasashun ' ) ; 以降、踏み台 VM は使用しないため、消し忘れないようにここで削除してしまっても良いです。 Cloud Run サービスの作成 コンテナイメージの作成 Cloud Run にデプロイするアプリケーションのコンテナイメージを作成します。 Auth Proxy のコンテナイメージについては、Google Cloud から提供されているものをそのまま利用します。 使用するコード PostgreSQL ドライバを使用して AlloyDB の mydb データベースに接続し、 users テーブルのデータを取得する処理を実装します。 当記事では Go 言語で記述していきます。 package main import ( "database/sql" "fmt" "log" "net/http" "os" _ "github.com/jackc/pgx/v4/stdlib" // PostgreSQLドライバ ) type User struct { ID string NAME string } // AlloyDB Auth Proxy に接続する関数 func connectTCPSocket() (*sql.DB, error ) { mustGetenv := func (k string ) string { v := os.Getenv(k) if v == "" { log.Fatalf( "Warning: %s environment variable not set." , k) } return v } // Cloud Run の環境変数に設定するデータベース接続情報 var ( dbUser = mustGetenv( "DB_USER" ) // データベースユーザ dbPwd = mustGetenv( "DB_PASS" ) // データベースユーザのパスワード dbTCPHost = mustGetenv( "INSTANCE_HOST" ) // 127.0.0.1 (Auth Proxy コンテナ) dbPort = mustGetenv( "DB_PORT" ) // Port 5432 dbName = mustGetenv( "DB_NAME" ) // データベース名 ) // データベース接続情報 dbURI := fmt.Sprintf( "host=%s user=%s password=%s port=%s database=%s" , dbTCPHost, dbUser, dbPwd, dbPort, dbName) // Auth Proxy 経由で AlloyDB に接続 dbPool, err := sql.Open( "pgx" , dbURI) if err != nil { return nil , fmt.Errorf( "sql.Open: %v" , err) } return dbPool, nil } // SELECT * を実行する関数 func selectAll() ([]User, error ) { db, err := connectTCPSocket() if err != nil { return nil , fmt.Errorf( "connectTCPSocket: %v" , err) } defer db.Close() rows, err := db.Query( "SELECT * FROM users" ) if err != nil { return nil , fmt.Errorf( "db.Query: %v" , err) } var users []User for rows.Next() { var u User rows.Scan(&u.ID, &u.NAME) users = append (users, u) } return users, nil } // SELECT オペレーションを実行するハンドラ func selectHandler(w http.ResponseWriter, r *http.Request) { users, err := selectAll() if err != nil { log.Fatal(err) } fmt.Fprintf(w, "Select Users: %v \n " , users) } // Cloud Runで Webサーバを実行する func main() { log.Print( "starting server..." ) http.HandleFunc( "/" , selectHandler) port := os.Getenv( "PORT" ) if port == "" { port = "8080" log.Printf( "defaulting to port %s" , port) } log.Printf( "listening on port %s" , port) if err := http.ListenAndServe( ":" +port, nil ); err != nil { log.Fatal(err) } } 参考: Connect an application to a database using the AlloyDB Auth proxy コンテナイメージのビルド 当記事では Dockerfile を使用せず、Buildpack を使用してコンテナイメージをビルドします。 Buildpack を使用することで、ソースコードを自動でパッケージ化し、デプロイ可能なコンテナイメージを生成することができます。 コンテナイメージは Artifact Registry のリポジトリにプッシュします(リポジトリがない場合は作成してください)。 # Buildpack を使用してコンテナイメージをビルド $ gcloud builds submit --pack image =asia-northeast1-docker.pkg.dev/ { プロジェクト名 } / { リポジトリ名 } /run-alloydb 参考: Buildpack を使用してアプリケーションをビルドする YAML ファイルの作成 使用する YAML ファイル 当記事ではYAML ファイルを使用して Cloud Run サービスをデプロイします。Google Cloud コンソール、Google Cloud CLI からもマルチコンテナ機能を使用したサービスをデプロイすることが可能です。 サイドカーコンテナに AlloyDB Auth Proxy のコンテナイメージを指定したファイル service.yaml を作成します。 なお、当記事では AlloyDB の作成時に自動で作成される postgres ユーザを使用していますが、実運用では専用のユーザを使用することが推奨されます。 また実運用では、ユーザやパスワードなどの認証情報は Secret Manager に格納し、それを環境変数から読み込む方式にしたほうが良いでしょう。 apiVersion : serving.knative.dev/v1 kind : Service metadata : # サービスの名前 name : service-alloyrun spec : template : metadata : annotations : run.googleapis.com/execution-environment : gen1 # Cloud Run からの外向き通信をサーバーレス VPC アクセスコネクタ経由にする run.googleapis.com/vpc-access-egress : all-traffic # サーバーレス VPC アクセスコネクタを指定する run.googleapis.com/vpc-access-connector : projects/{プロジェクト名}/locations/asia-northeast1/connectors/my-connector # AlloyDB Auth Proxy コンテナの起動後にアプリケーションコンテナを起動するようにする run.googleapis.com/container-dependencies : '{"alloydb-app":["alloydb-proxy"]}' autoscaling.knative.dev/maxScale : '10' spec : # AlloyDB に接続できるサービスアカウント serviceAccountName : my-serviceaccount@{プロジェクト名}.iam.gserviceaccount.com containers : # Ingress Container - name : alloydb-app # アプリケーションのコンテナイメージを指定する image : asia-northeast1-docker.pkg.dev/{プロジェクト名}/{リポジトリ名}/run-alloydb:latest ports : - containerPort : 8080 # 環境変数にデータベース接続情報を設定する env : - name : DB_USER value : postgres # AlloyDB のデフォルトのユーザ - name : DB_PASS value : mypassword # AlloyDB 作成時に設定したパスワード # サイドカーコンテナの Auth Proxy に接続するため 127.0.0.1(localhost)を指定する - name : INSTANCE_HOST value : 127.0.0.1 - name : DB_PORT value : '5432' - name : DB_NAME value : mydb # Sidecar Container - name : alloydb-proxy # AlloyDB Auth Proxy のコンテナイメージを指定する image : gcr.io/alloydb-connectors/alloydb-auth-proxy:latest args : # AlloyDB インスタンスの接続文字列を設定する - "projects/{プロジェクト名}/locations/asia-northeast1/clusters/my-alloycls/instances/my-alloyins" - --address=0.0.0.0 - --port=5432 # Startup Probe の設定 startupProbe : tcpSocket : port : 5432 initialDelaySeconds : 5 timeoutSeconds : 1 failureThreshold : 10 periodSeconds : 3 コンテナの起動順序について Auth Proxy コンテナが起動していない状態ではアプリケーションコンテナが正常に動作しないため、 spec.template.metadata.annotations の run.googleapis.com/container-dependencies でコンテナの依存関係を指定しています。 それに加えて Auth Proxy コンテナに Startup Probe を設定することで、Auth Proxy コンテナの TCP 5432 ポートと疎通が取れるようになるのを待ってからアプリケーションが実行されるようにしています。 サービスのデプロイ YAML ファイルからのデプロイは、新規であっても gcloud run services replace コマンドを使用します。 # 新しいサービスのデプロイ $ gcloud run services replace service.yaml --region = asia-northeast1 YAML ファイルからのデプロイでは metadata.annotations.run.googleapis.com/ingress に all を設定しても 未認証の呼び出しを許可 の設定ができないようなので、別途 allUsers に対して Cloud Run 起動元 のロールを付与します。 # 未認証で Cloud Run サービスを呼び出せるようにする $ gcloud run services add-iam-policy-binding service-alloyrun \ --role =" roles/run.invoker " \ --member =" allUsers " \ --region = asia-northeast1 Cloud Run サービスからの接続 gcloud run services replace コマンドで Cloud Run サービスをデプロイした後、エンドポイントとなる URL が出力されているので、ブラウザからアクセスします。 # 出力例 Applying new configuration to Cloud Run service [ service-alloyrun ] in project [ myproject ] region [ asia-northeast1 ] ✓ Deploying new service... Done. ✓ Creating Revision... Creating Service. ✓ Routing traffic... Done. New configuration has been applied to service [ service-alloyrun ] . URL: https://service-alloyrun-xxxxxxxxxx-an.a.run.app アプリケーションから AlloyDB のデータベースに接続され、SELECT * クエリの結果が画面に表示されます。 ブラウザから Cloud Run サービスにアクセスする 佐々木 駿太 (記事一覧) G-gen最北端、北海道在住のクラウドソリューション部エンジニア 2022年6月にG-genにジョイン。Google Cloud Partner Top Engineer 2025 Fellowに選出。好きなGoogle CloudプロダクトはCloud Run。 趣味はコーヒー、小説(SF、ミステリ)、カラオケなど。 Follow @sasashun0805
アバター
G-gen の藤岡です。当記事では、Google Cloud (旧称 GCP) の Cloud DNS を使うことで複数のリージョンにあるインスタンスの内部 IP アドレスで HTTP(S) 通信を振り分ける方法を紹介します。環境の作成には Terraform を使います。 Cloud DNS とは 検証の背景 複数リージョンにまたがるバックエンドへの負荷分散 Cloud DNS による負荷分散 実施内容 構成図 前提 ディレクトリ構成 構築 プロジェクトの作成と請求先アカウントの紐づけ デフォルトプロジェクトのセット Terraform の実行 確認 正常時の振り分けの確認 東京リージョンのインスタンスが停止した時の挙動 フェイルオーバの方法 Terraform のコード Cloud DNS とは Cloud DNS は Google Cloud が提供するマネージドな DNS サービスです。Cloud DNS に関する用語については以下の記事をご参照ください。 また当記事で設定するゾーン名やレコード名は、わかりやすいように以下の記事と同一にしています。 blog.g-gen.co.jp Cloud DNS では一般的な DNS と同様に、1つのドメイン名(例: www.g-gen.local )に対して複数の A レコードを持つことができます。当記事では、複数のリージョンにある Compute Engine インスタンスの内部 IP アドレスへの振り分けを行います。 参考: DNS レコードの概要 検証の背景 複数リージョンにまたがるバックエンドへの負荷分散 HTTP(S) 通信の負荷分散や災害対策として、一般的にはロードバランサーが利用されます。Google Cloud で提供されているロードバランサーは Cloud Load Balancing で、以下の 9 種類があります。 画像は 公式 より引用 バックエンドの VM が複数のリージョンにまたがる場合は External HTTP(S) Load Balancing が選択肢になります。 しかし External なロードバランサーのエンドポイントはインターネットに公開されます。そのため例えば以下のようにオンプレミスと Google Cloud を Cloud VPN や Cloud Interconnect で接続している場合で、オンプレミスのクライアントから複数リージョンの Compute Engine VM に対して HTTP(S) 通信を振り分けたい場合は、 選択できる Cloud Load Balancing が存在しません。その理由は、Internal なロードバランサーは単一リージョンのバックエンドにしかトラフィックを振り分けられないことに起因します。また、Internal なロードバランサー自体がリージョンに所属するリソースであるため、ロードバランサーが存在するリージョンが障害になるとバックアップリージョンへの振り分けが不可になることも問題の一つです。 →2023年8月に Cross-region internal Application Load Balancer がリリースされ、現在では内部ロードバランサー(Internal なロードバランサー)でも、複数リージョンのバックエンドにトラフィックを振り分けられるようになりました。 参考 : Internal Application Load Balancer overview オンプレミスと Google Cloud の構成例 Cloud DNS による負荷分散 よって、インターナルな通信でバックエンドが複数のリージョンにまたがる場合、当記事で紹介する Cloud DNS による負荷分散が選択肢の 1 つとなります。 ただし、以下の条件・制約が存在します。 RTO は数時間 Cloud DNS には IP アドレス/VM に対するヘルスチェック・自動フェイルオーバが無いため ただし internal passthrough Network Load Balancer と internal Application Load Balancer へのヘルスチェック機能は存在するため、各リージョンに LB を設置すれば自動フェイルオーバが可能 Cloud Load Balancing の詳細なトラフィック制御やインスタンスグループ機能は使用できない なお当記事では Google Cloud 内で完結する構成となっていますが、オンプレミスと Cloud DNS を組み合わせたベストプラクティスは ドキュメント をご参照ください。 実施内容 構成図 今回作成する構成は以下の通りです。 Compute Engine インスタンスに Apache をインストールし、東京と大阪リージョンのインスタンスで /var/www/html/index.html の内容を変えることでどちらのインスタンスに振り分けられているか確認します。 構成図 前提 実行環境 Cloud Shell で後述の Terraform のコード を実行 外部 IP アドレス 振り分けの確認用でインスタンスに Apache や dig をインストールするため、外部 IP アドレスを付与 インスタンスへの接続 インスタンスへの接続は Cloud IAP を使用 当記事で扱わないこと 各リソースの作成に必要な権限 ディレクトリ構成 以下 2 つの .tf ファイルを用意しました。コードの内容は後述します。 fujioka@cloudshell:~/terraform (xxxx)$ tree . ├── main.tf └── variables.tf 0 directories, 2 files fujioka@cloudshell:~/terraform (xxxx)$ 構築 プロジェクトの作成と請求先アカウントの紐づけ プロジェクトを作成し、作成したプロジェクトに請求先アカウントを紐づけます。 $ gcloud projects create ${PROJECT_ID} --name=${PROJECT_NAME} --organization=${ORGANIZATION_ID} && \ gcloud beta billing projects link ${PROJECT_ID} --billing-account=${BILLING_ACCOUNT_ID} デフォルトプロジェクトのセット 作成したプロジェクトをデフォルトプロジェクトとしてセットし、結果を確認します。 $ gcloud config set project ${PROJECT_ID} && \ gcloud config list project Terraform の実行 Terraform を実行します。 # .tf ファイルのあるディレクトリに移動 $ cd terraform/ # 初期化 $ terraform init # 確認 (今回は 22 個のリソースが作成されます) $ terraform plan ... Plan: 22 to add, 0 to change, 0 to destroy. ... # 適用 $ terraform apply 参考: TerraformをGoogle Cloudで使ってみた 確認 正常時の振り分けの確認 vpc-a の vm-soul から watch -d -n 3 "curl www.g-gen.local | tee -a output.log" コマンドを実行すると、東京リージョンと大阪リージョンにあるインスタンスそれぞれにアクセスが振り分けられています。 東京リージョンへ振り分け 大阪リージョンへ振り分け output.log の中身 東京リージョンのインスタンスが停止した時の挙動 vm-tokyo を手動で停止し、挙動を確認します。Cloud DNS には IP アドレスや Compute Engine VM に対してヘルスチェックをする機能がないため、 www.g-gen.local が vm-tokyo の IP アドレスに名前解決されてしまった場合、レスポンスが返ってきません。 東京リージョン停止時の挙動 フェイルオーバの方法 あるリージョンのサービスが停止してしまった場合、サービス停止時にアラートを飛ばして人が対処する、もしくは自動的に A レコードを削除する仕組みを作る、等の対策が必要です。 また、 Cloud DNS にはグローバルアクセスが有効化された internal passthrough Network Load Balancer と internal Application Load Balancer に限った ヘルスチェック機能 が用意されています。 自動的な DNS フェイルオーバを実装したい場合、各リージョンにロードバランサを配置することも検討します。 Terraform のコード 今回使用したコードは以下の 2 つのファイルです。 variables.tf の ${PROJECT_ID} は置き換えてください。 main.tf ############################################## # 共通設定 # ############################################## # terraform 設定 terraform { required_version = "~> 1.3" required_providers { google = ">= 4.63.1" } } # provider 設定 provider "google" { project = var.project } # api 有効化 resource "google_project_service" "enabled_apis" { for_each = toset (var.enabled_apis_list) service = each.key disable_on_destroy = true } # サービスアカウント作成 resource "google_service_account" "service_account_for_vm" { account_id = "service-account-for-vm" display_name = "VM 用サービスアカウント" depends_on = [ google_project_service.enabled_apis ] } # サービスアカウント権限付与 resource "google_project_iam_member" "service_account_for_vm_role" { project = var.project role = "roles/compute.admin" member = "serviceAccount:$ { google_service_account.service_account_for_vm.email } " } ############################################## # vpc-a の設定 # ############################################## /****************************** ネットワーク設定 ******************************/ # vpc 作成 resource "google_compute_network" "vpc_a" { name = "vpc-a" auto_create_subnetworks = "false" routing_mode = "GLOBAL" depends_on = [ google_project_service.enabled_apis ] } # subnet 作成 resource "google_compute_subnetwork" "subnet_soul" { name = "subnet-soul" ip_cidr_range = var.subnet_cidr_soul region = var.region_soul network = google_compute_network.vpc_a.name } # firewall 作成 resource "google_compute_firewall" "allow_ssh_from_iap_a" { name = "allow-ssh-from-iap-a" network = google_compute_network.vpc_a.name direction = "INGRESS" allow { protocol = "tcp" ports = [ "22" ] } source_ranges = [ var.iap_ip_range ] } /****************************** gce 作成 ******************************/ resource "google_compute_instance" "vm_soul" { name = "vm-soul" machine_type = var.instance_type zone = var.zone_soul service_account { email = google_service_account.service_account_for_vm.email scopes = [ "cloud-platform" ] } boot_disk { initialize_params { image = var.instance_os } } metadata_startup_script = <<EOF #! /bin/bash apt update apt -y install dnsutils EOF network_interface { network = google_compute_network.vpc_a.name subnetwork = google_compute_subnetwork.subnet_soul.name network_ip = var.internal_ip_soul access_config {} } allow_stopping_for_update = true } /****************************** Cloud DNS ******************************/ # ピアリングゾーン作成 resource "google_dns_managed_zone" "g_gen_local_peering_zone" { name = "g-gen-local-peering-zone" dns_name = "g-gen.local." depends_on = [ google_project_service.enabled_apis ] visibility = "private" private_visibility_config { networks { network_url = google_compute_network.vpc_a.id } } peering_config { target_network { network_url = google_compute_network.vpc_b.id } } } ############################################## # vpc-b の設定 # ############################################## /****************************** ネットワーク設定 ******************************/ # vpc 作成 resource "google_compute_network" "vpc_b" { name = "vpc-b" auto_create_subnetworks = "false" routing_mode = "GLOBAL" depends_on = [ google_project_service.enabled_apis ] } # subnet 作成 resource "google_compute_subnetwork" "subnet_tokyo" { name = "subnet-tokyo" ip_cidr_range = var.subnet_cidr_tokyo region = var.region_tokyo network = google_compute_network.vpc_b.name } resource "google_compute_subnetwork" "subnet_osaka" { name = "subnet-osaka" ip_cidr_range = var.subnet_cidr_osaka region = var.region_osaka network = google_compute_network.vpc_b.name } # firewall 作成 resource "google_compute_firewall" "allow_http_from_vpc_a" { name = "allow-http-from-vpc-a" network = google_compute_network.vpc_b.name direction = "INGRESS" allow { protocol = "tcp" ports = [ "80" , "443" ] } source_ranges = [ var.internal_ip_range_vpc_a ] } resource "google_compute_firewall" "allow_ssh_from_iap_b" { name = "allow-ssh-from-iap-b" network = google_compute_network.vpc_b.name direction = "INGRESS" allow { protocol = "tcp" ports = [ "22" ] } source_ranges = [ var.iap_ip_range ] } /****************************** gce 作成 ******************************/ resource "google_compute_instance" "vm_tokyo" { name = "vm-tokyo" machine_type = var.instance_type zone = var.zone_tokyo service_account { email = google_service_account.service_account_for_vm.email scopes = [ "cloud-platform" ] } boot_disk { initialize_params { image = var.instance_os } } metadata_startup_script = <<EOF #! /bin/bash apt update apt -y install apache2 cat <<EOF > /var/www/html/index.html <html><body><p>Tokyo Instance</p></body></html> EOF network_interface { network = google_compute_network.vpc_b.name subnetwork = google_compute_subnetwork.subnet_tokyo.name network_ip = var.internal_ip_tokyo access_config {} } allow_stopping_for_update = true } resource "google_compute_instance" "vm_osaka" { name = "vm-osaka" machine_type = var.instance_type zone = var.zone_osaka service_account { email = google_service_account.service_account_for_vm.email scopes = [ "cloud-platform" ] } boot_disk { initialize_params { image = var.instance_os } } metadata_startup_script = <<EOF #! /bin/bash apt update apt -y install apache2 cat <<EOF > /var/www/html/index.html <html><body><p>Osaka Instance</p></body></html> EOF network_interface { network = google_compute_network.vpc_b.name subnetwork = google_compute_subnetwork.subnet_osaka.name network_ip = var.internal_ip_osaka access_config {} } allow_stopping_for_update = true } /****************************** Cloud DNS ******************************/ # ゾーン作成 resource "google_dns_managed_zone" "g_gen_local_zone" { # project = var.project name = "g-gen-local-zone" dns_name = "g-gen.local." depends_on = [ google_project_service.enabled_apis ] visibility = "private" private_visibility_config { networks { network_url = google_compute_network.vpc_b.id } } } # レコード作成 resource "google_dns_record_set" "www" { project = var.project name = "www.$ { google_dns_managed_zone.g_gen_local_zone.dns_name } " managed_zone = google_dns_managed_zone.g_gen_local_zone.name type = "A" ttl = 3600 rrdatas = [ var.internal_ip_osaka, var.internal_ip_tokyo ] } ############################################## # vpc peering vpc-a <--> vpc-b # ############################################## resource "google_compute_network_peering" "peering1" { name = "peering1" network = google_compute_network.vpc_a.self_link peer_network = google_compute_network.vpc_b.self_link } resource "google_compute_network_peering" "peering2" { name = "peering2" network = google_compute_network.vpc_b.self_link peer_network = google_compute_network.vpc_a.self_link } variables.tf ############################################## # プロジェクトレベル # ############################################## variable "project" { type = string default = "$ { PROJECT_ID } " // プロジェクト ID } variable "enabled_apis_list" { description = "有効化するAPI" type = list (string) default = [ "cloudresourcemanager.googleapis.com" , "iam.googleapis.com" , "compute.googleapis.com" , "dns.googleapis.com" , ] } ############################################## # リージョン / ゾーン # ############################################## variable "region_tokyo" { description = "東京リージョンを指定" type = string default = "asia-northeast1" } variable "region_osaka" { description = "大阪リージョンを指定" type = string default = "asia-northeast2" } variable "region_soul" { description = "ソウルリージョンを指定" type = string default = "asia-northeast3" } variable "zone_tokyo" { description = "東京リージョンのゾーンを指定" type = string default = "asia-northeast1-a" } variable "zone_osaka" { description = "大阪リージョンのゾーンを指定" type = string default = "asia-northeast2-a" } variable "zone_soul" { description = "ソウルリージョンのゾーンを指定" type = string default = "asia-northeast3-a" } ############################################## # サブネット # ############################################## variable "subnet_cidr_tokyo" { description = "東京リージョンのサブネット範囲" type = string default = "10.0.0.0/24" } variable "subnet_cidr_osaka" { description = "大阪リージョンのサブネット範囲" type = string default = "10.0.10.0/24" } variable "subnet_cidr_soul" { description = "ソウルリージョンのサブネット範囲" type = string default = "172.16.0.0/24" } ############################################## # ipアドレス # ############################################## variable "internal_ip_tokyo" { description = "東京リージョンのインスタンス内部IPアドレス" type = string default = "10.0.0.10" } variable "internal_ip_osaka" { description = "大阪リージョンのインスタンス内部IPアドレス" type = string default = "10.0.10.10" } variable "internal_ip_soul" { description = "ソウルリージョンのインスタンス内部IPアドレス" type = string default = "172.16.0.10" } variable "internal_ip_range_vpc_a" { description = "project-aの内部IPアドレス範囲" type = string default = "172.16.0.0/24" } variable "iap_ip_range" { description = "IAP用のIPアドレス範囲" type = string default = "35.235.240.0/20" } ############################################## # インスタンス # ############################################## variable "instance_os" { description = "OSイメージ" type = string default = "debian-cloud/debian-11" } variable "instance_type" { description = "インスタンスタイプ" type = string default = "e2-micro" } 藤岡 里美 (記事一覧) クラウドソリューション部 数年前までチキン売ったりドレスショップで働いてました!2022年9月 G-gen にジョイン。ハイキューの映画を4回は見に行きたい。 Google Cloud All Certifications Engineer / Google Cloud Partner Top Engineer 2024 Follow @fujioka57621469
アバター
G-gen の佐々木です。当記事では、Google Cloud (旧称 GCP) が提供するメッセージングサービスである Cloud Pub/Sub と、そのクライアントライブラリを使用することで、継続的に送信されるデータ(メッセージ)をリアルタイムで処理する仕組みを実装していきます。 構成 使用するサービスの解説 Cloud Pub/Sub Cloud Run jobs Google Kubernetes Engine(GKE) StreamingPull API とは Pub/Sub の設定 パブリッシャーの作成(Cloud Run jobs) コンテナイメージの作成 使用するコード コンテナイメージのビルド ジョブの作成 サブスクライバーの作成(GKE) コンテナイメージの作成 使用するコード コンテナイメージを Artifact Registry にプッシュ GKE クラスタの作成 Workload Identity の設定 GSA の作成 GKE クラスタに ServiceAccount リソースを作成 KSA と GSA の紐付け アプリケーションのデプロイ(GKE) 動作確認 ジョブの実行(Cloud Run jobs) Pod のログを確認(GKE) 構成 今回は Cloud Run jobs をパブリッシャーとして、 Pub/Sub トピック にメッセージを送信するジョブを並列して実行する、という構成を検証します。 GKE クラスタ上に展開した Pod をサブスクライバーとし、Pub/Sub の API である StreamingPull API を使用することで、Pub/Sub に送信されたメッセージをリアルタイムで受信・処理します。 Pub/SubとGKEを使用したストリーミング処理 使用するサービスの解説 Cloud Pub/Sub Cloud Pub/Sub(以下、Pub/Sub)は、メッセージを生成する パブリッシャー とそれを処理する サブスクライバー(コンシューマー) を切り離すマネージドな メッセージング サービス です。 Pub/Sub を使用することで、パブリッシャーとサブスクライバーの互換性・拡張性が担保された粗結合なシステムを構成することができます。 参考: 公式ドキュメント Cloud Run jobs Cloud Run jobs とは、サーバーレス コンテナコンピューティングサービスである Cloud Run の 1 機能です。 Cloud Run jobs を使用することで、コンテナイメージとして実装したジョブを、手動、スケジュール、ワークフローによる任意タイミングで並列して実行することができます。 Cloud Run jobs の詳細については以下の記事で解説しています。 blog.g-gen.co.jp Google Kubernetes Engine(GKE) Google Kubernetes Engine(以下、GKE)は、コンテナ オーケストレーションツールである Kubernetes の Google マネージドなクラスタを利用することができるサービスです。 GKE の詳細については以下の記事で解説しています。 blog.g-gen.co.jp StreamingPull API とは Pub/Sub クライアントライブラリで StreamingPull API を使用すると、アプリケーションと Pub/Sub との間に永続的な双方向接続が維持され、Pub/Sub でメッセージが利用可能になるとすぐに pull されます。 この仕様により、1 つの pull リクエストで 1 つの pull レスポンスが返る通常の 単項 Pull と比較すると、高スループット・低レイテンシでメッセージを処理することができます。 StreamingPull API は以下の言語のクライアントライブラリで使用することができます。 C++ C# Go(当記事で使用) Java Node.js Python Ruby Pub/Sub のクライアントライブラリは上記言語のほか PHP でも利用できますが、StreamingPull API については PHP ではサポートされていません。 参考: StreamingPull API Pub/Sub の設定 Pub/Sub のトピックとサブスクリプションをそれぞれデフォルトの状態で作成します。 トピック名、サブスクリプション名は後ほど環境変数で使用します。 # Pub/Subトピックの作成 $ gcloud pubsub topics create {トピック名} # 実行例 $ gcloud pubsub topics create mytopic # Pub/Subサブスクリプションの作成 $ gcloud pubsub subscriptions create {サブスクリプション名} --topic={トピック名} # 実行例 $ gcloud pubsub subscriptions create mysubscription --topic=mytopic パブリッシャーの作成(Cloud Run jobs) Pub/Sub にメッセージをパブリッシュするジョブを Cloud Run jobs で作成します。 コンテナイメージの作成 使用するコード 当記事では 公式ドキュメント のサンプルコードを参考に、Go 言語で処理を実装していきます。 メッセージのパブリッシュには Pub/Sub のクライアントライブラリである cloud.google.com/go/pubsub を使用します。 package main import ( "context" "fmt" "log" "os" "time" "cloud.google.com/go/pubsub" // Pub/Subクライアントライブラリ "github.com/google/uuid" ) type Attributes struct { taskNum string uuid string creationTime string } // メッセージの内容を生成する関数 func generateAttributes() Attributes { // タスク番号を取得(Cloud Run jobsのデフォルトの環境変数) taskNum := os.Getenv( "CLOUD_RUN_TASK_INDEX" ) attr := Attributes{ taskNum: taskNum, uuid: fmt.Sprint(uuid.New()), creationTime: time.Now().Format( "2006-01-02 15:04:05" ), } return attr } // メッセージをパブリッシュする関数 func publishMessage(c context.Context, attr Attributes) error { // 環境変数からプロジェクトIDとPubSubトピックID を取得 projectId := os.Getenv( "PROJECT_ID" ) topicId := os.Getenv( "TOPIC_ID" ) // クライアント作成 client, err := pubsub.NewClient(c, projectId) if err != nil { return fmt.Errorf( "pubsub.NewClient: %v" , err) } defer client.Close() // Pub/Sub トピックの参照 t := client.Topic(topicId) // トピックにメッセージをパブリッシュ result := t.Publish(c, &pubsub.Message{ Attributes: map [ string ] string { "taskNum" : attr.taskNum, "uuid" : attr.uuid, "creationTime" : attr.creationTime, }}) id, err := result.Get(c) if err != nil { return fmt.Errorf( "result.Get: %v" , err) } fmt.Printf( "Published a messsage; msg ID: %v \n " , id) return nil } func main() { // 空のコンテキストを作成 ctx := context.Background() // メッセージ内容の生成 attr := generateAttributes() // メッセージのパブリッシュ err := publishMessage(ctx, attr) if err != nil { log.Fatal(err) } } コンテナイメージのビルド ここでは Dockerfile を使用せず、Buildpack を使用してコンテナイメージをビルドします。 Buildpack を使用することで、ソースコードを自動でパッケージ化し、デプロイ可能なコンテナイメージを生成することができます。 コンテナイメージは Artifact Registry のリポジトリにプッシュします(リポジトリがない場合は作成してください)。 # Buildpackを使用してイメージをビルドする $ gcloud builds submit --pack image={リポジトリのURL}/{コンテナイメージ名} # 実行例(リポジトリにArtifact Registryを使用) $ gcloud builds submit --pack image=asia-northeast1-docker.pkg.dev/myproject/pubsub-container/pubsub-publisher 参考①: Google Cloud の Buildpack 参考②: Cloud Run で Go ジョブをビルドして作成する ジョブの作成 コンテナイメージを使用して Cloud Run jobs でジョブを作成します。 環境変数として プロジェクトID と トピック名 を設定し、同時に実行する Task の数を 50 に設定しています。 # Cloud Run jobsのジョブを作成する(実行Task数=50) $ gcloud run jobs create {ジョブ名} \ --image {イメージのURL} \ --region {リージョン} \ --tasks 50 \ --set-env-vars PROJECT_ID={プロジェクトID},TOPIC_ID={トピック名} # 実行例 $ gcloud run jobs create jobs-pubsub-publisher \ --image asia-northeast1-docker.pkg.dev/myproject/pubsub-container/pubsub-publisher \ --region asia-northeast1 \ --tasks 50 \ --set-env-vars PROJECT_ID=myproject,TOPIC_ID=mytopic 当記事では認証にデフォルトのサービスアカウントを使用します。サービスアカウントを個別に設定する場合、トピックに対する「Pub/Sub パブリッシャー( roles/pubsub.publisher )」ロールが紐付いたサービスアカウントを使用してください。 サブスクライバーの作成(GKE) Pub/Sub からメッセージを Pull するアプリケーションを GKE クラスタにデプロイします。 コンテナイメージの作成 使用するコード 公式ドキュメント のサンプルコードを参考に、こちらも Go 言語で処理を実装していきます。 パブリッシュの処理と同様に cloud.google.com/go/pubsub ライブラリを使用し、メッセージをストリーミングで Pull するようにコードを記述します。 ここでは検証のため、Pull したメッセージはそのまま標準出力に出力します。 package main import ( "context" "fmt" "io" "log" "os" "cloud.google.com/go/pubsub" // Pub/Subクライアントライブラリ ) // メッセージをStreamingPullする関数 func pullMessages(w io.Writer , c context.Context, projectId, subId string ) error { // クライアント作成 client, err := pubsub.NewClient(c, projectId) if err != nil { return fmt.Errorf( "pubsub.NewClient: %v" , err) } defer client.Close() // サブスクリプションの参照 sub := client.Subscription(subId) // メッセージをpullし続ける err = sub.Receive(c, func (_ context.Context, msg *pubsub.Message) { fmt.Fprintf(w, "%v \n " , msg.Attributes) // メッセージを標準出力に出力 msg.Ack() }) if err != nil { return fmt.Errorf( "sub.Receive: %v" , err) } return nil } func main() { // 空のコンテキスト作成 ctx := context.Background() // プロジェクトID と PubSubトピックID を取得 projectId := os.Getenv( "PROJECT_ID" ) subId := os.Getenv( "SUBSCRIPTION_ID" ) err := pullMessages(os.Stdout, ctx, projectId, subId) if err != nil { log.Fatal(err) } } コンテナイメージを Artifact Registry にプッシュ アプリケーションを GKE にデプロイするために、コンテナイメージを作成します。 以下の Dockerfile を使用します。 FROM golang:1.20 WORKDIR /usr/src/app COPY go.mod go.sum main.go ./ RUN go mod download && go mod verify RUN go build -v -o /usr/local/bin/app ./... CMD ["app"] Cloud Build を使用し、ビルドしたコンテナイメージを Artifact Registry のリポジトリにプッシュします。 # コンテナイメージ $ gcloud builds submit --tag={リポジトリのURL}/{コンテナイメージ名} # 実行例 $ gcloud builds submit --tag=asia-northeast1-docker.pkg.dev/myproject/pubsub-container/pubsub-subscriber GKE クラスタの作成 当記事では Autopilot モードの GKE クラスタを使用します。 # AutopilotモードのGKEクラスタを作成する $ gcloud container clusters create-auto {クラスタ名} \ --region {リージョン} \ --network {VPC名} --subnetwork {サブネット名} # 実行例 $ gcloud container clusters create-auto mycluster-autopilot \ --region asia-northeast1 \ --network myvpc \ --subnetwork mysubnet Workload Identity の設定 アプリケーションが Pub/Sub からメッセージを Pull できるように、Workload Identity によって Pod から IAM サービスアカウントを使用できるようにします。 当記事では便宜上、Google Cloud APIs にアクセスするためのサービスアカウントを GSA 、Kubernetes の ServiceAccount リソースを KSA と呼びます。 GSA の作成 Pub/Sub に対するアクセス権限を持った GSA を作成します。 # GSA を作成する $ gcloud iam service-accounts create {GSAの名前} --project {プロジェクトID} # 実行例 $ gcloud iam service-accounts create my-gsa --project myproject 作成した Pub/Sub サブスクリプションからメッセージを Pull できるように、「Pub/Sub サブスクライバー( roles/pubsub.subscriber )」を付与します。 # GSA に IAM ロールを紐付ける $ gcloud pubsub subscriptions add-iam-policy-binding {サブスクリプション名} \ --role "roles/pubsub.subscriber" \ --member "serviceAccount:{GSAの名前}@{プロジェクトID}.iam.gserviceaccount.com" # 実行例 $ gcloud pubsub subscriptions add-iam-policy-binding mysubscription \ --role "roles/pubsub.subscriber" \ --member "serviceAccount:my-gsa@myproject.iam.gserviceaccount.com" GKE クラスタに ServiceAccount リソースを作成 以下のマニフェストファイルを使用して、KSA を GKE クラスタに作成します。 apiVersion : v1 kind : ServiceAccount metadata : name : my-ksa annotations : # Workload Identity で紐付ける GSA を指定する iam.gke.io/gcp-service-account : my-gsa@myproject.iam.gserviceaccount.com KSA と GSA の紐付け GSA に対する Workload Identity User (roles/iam.workloadIdentityUser)  ロールを KSA に紐付け、KSA が GSA の権限を借用できるようにします。 # KSA と GSA を紐付ける $ gcloud iam service-accounts add-iam-policy-binding {GSAの名前}@{プロジェクトID}.iam.gserviceaccount.com \ --role roles/iam.workloadIdentityUser \ --member "serviceAccount:{プロジェクトID}.svc.id.goog[{KSAを作成したNamespace}/{KSAの名前}]" # 実行例(default名前空間を使用している場合) $ gcloud iam service-accounts add-iam-policy-binding my-gsa@myproject.iam.gserviceaccount.com \ --role roles/iam.workloadIdentityUser \ --member "serviceAccount:myproject.svc.id.goog[default/my-ksa]" アプリケーションのデプロイ(GKE) 以下のマニフェストを使用して、GKE クラスタにアプリケーションをデプロイします。 複数の Pod を使用してメッセージを Pull できるように Deployment リソースを作成します。 apiVersion : apps/v1 kind : Deployment metadata : name : pubsub-subscriber spec : replicas : 3 selector : matchLabels : app : subscriber template : metadata : labels : app : subscriber spec : containers : - name : subsc-container image : "asia-northeast1-docker.pkg.dev/myproject/pubsub-container/pubsub-subscriber:latest" # コンテナイメージのURL env : - name : "PROJECT_ID" value : "myproject" # Pub/Subを作成したプロジェクトのID - name : "SUBSCRIPTION_ID" value : "mysubscription" # Pub/Subサブスクリプションの名前 serviceAccountName : my-ksa # Workload Identityで使用するServiceAccount Pod のステータスがすべて Running になるまで待機します。 # Podのステータスを確認する $ kubectl get po NAME READY STATUS RESTARTS AGE pubsub-subscriber-555ffcb5df-fxm2z 1/1 Running 0 3m3s pubsub-subscriber-555ffcb5df-sz4xl 1/1 Running 0 3m3s pubsub-subscriber-555ffcb5df-xmrr2 1/1 Running 0 3m3s 動作確認 ジョブの実行(Cloud Run jobs) Cloud Run jobs のジョブを実行し、Pub/Sub にメッセージをパブリッシュします。 # ジョブを実行する $ gcloud run jobs execute {ジョブ名} --region {リージョン} # 実行例 $ gcloud run jobs execute jobs-pubsub-publisher --region asia-northeast1 Pod のログを確認(GKE) kubectl logs コマンドで Pod のログを確認すると、Cloud Run jobs のジョブがパブリッシュしたメッセージが記録されています。 複数の Pod を展開しているため、Pod ごとにメッセージが分散処理されています。 # Podのログを確認する $ kubectl logs {Pod名} # 実行例 $ kubectl logs pubsub-subscriber-555ffcb5df-fxm2z map[creationTime:2023-05-03 15:15:49 taskNum:35 uuid:c38275b5-da20-4527-9e28-cec2744f6374] map[creationTime:2023-05-03 15:15:49 taskNum:2 uuid:f5ab93a7-6a93-4637-b50e-ee491cdfcc8d] map[creationTime:2023-05-03 15:15:49 taskNum:30 uuid:e82aa1ef-c53b-4812-a3a3-e74437b436ef] map[creationTime:2023-05-03 15:15:49 taskNum:23 uuid:8bc9d8c0-cc4d-49d8-b565-c3e26570c92c] map[creationTime:2023-05-03 15:15:50 taskNum:39 uuid:1594b54e-2ed0-4a2e-b248-6613677c4cf3] map[creationTime:2023-05-03 15:15:49 taskNum:21 uuid:b81177da-d6de-4ee2-acb9-bf1442286cd5] map[creationTime:2023-05-03 15:15:50 taskNum:31 uuid:c4950a18-5fba-4a8c-aae1-6c628db22c65] map[creationTime:2023-05-03 15:15:50 taskNum:49 uuid:20b3ca60-8a83-4116-ab54-a26e187bd8d0] map[creationTime:2023-05-03 15:15:50 taskNum:16 uuid:ede7ab24-d246-485f-8bcb-2a71fcf9e3a5] map[creationTime:2023-05-03 15:15:49 taskNum:48 uuid:166a1bd9-f0ac-4c93-8129-7110b2429f55] map[creationTime:2023-05-03 15:15:50 taskNum:42 uuid:2faa0967-46f6-4a86-84f5-c1e821607c83] map[creationTime:2023-05-03 15:15:52 taskNum:46 uuid:0eea95d7-a673-4e44-b214-ca49dd34be91] $ kubectl logs pubsub-subscriber-555ffcb5df-sz4xl map[creationTime:2023-05-03 15:15:48 taskNum:41 uuid:86be2dd1-196f-4d04-8990-fbd1fa6bc0db] map[creationTime:2023-05-03 15:15:48 taskNum:5 uuid:b0f957d8-e113-451d-ba9e-19dc061b1632] map[creationTime:2023-05-03 15:15:48 taskNum:0 uuid:e3690426-4c7d-4fe8-b927-8a6343f23a54] map[creationTime:2023-05-03 15:15:49 taskNum:18 uuid:760c5c63-d41f-4d78-b2fe-9d839d872859] map[creationTime:2023-05-03 15:15:49 taskNum:12 uuid:dbfdab23-687e-4dce-9ae9-8c775e88d873] map[creationTime:2023-05-03 15:15:48 taskNum:17 uuid:f13c4842-5503-4e0b-a91b-57879563448b] map[creationTime:2023-05-03 15:15:49 taskNum:33 uuid:f307f4f9-4735-4abd-8232-b2793f6dd08c] map[creationTime:2023-05-03 15:15:49 taskNum:1 uuid:e5f5a0d2-7430-4816-8a24-4bc0464c1c9c] map[creationTime:2023-05-03 15:15:49 taskNum:44 uuid:9d23a4f4-4c3e-40b9-be85-ea191936ecb6] map[creationTime:2023-05-03 15:15:48 taskNum:14 uuid:5389088c-0452-46a2-ba96-255f79404741] map[creationTime:2023-05-03 15:15:48 taskNum:32 uuid:171f0b3a-a19f-4574-b75d-87ddf30e9b7f] map[creationTime:2023-05-03 15:15:48 taskNum:47 uuid:6cc0f34c-d9ac-4ab9-8308-45c21a7c1c52] map[creationTime:2023-05-03 15:15:49 taskNum:24 uuid:9fef79ac-a637-451f-9c3c-5ddbfd99631d] map[creationTime:2023-05-03 15:15:50 taskNum:40 uuid:8ac572b6-e02a-4505-b7bf-d491410f28e3] map[creationTime:2023-05-03 15:15:51 taskNum:19 uuid:59f52dc0-56e5-4780-a6ac-8e0b9d0ffad8] map[creationTime:2023-05-03 15:15:51 taskNum:28 uuid:6cf456b1-b234-4847-a8f7-7ee8c6e06c97] map[creationTime:2023-05-03 15:15:51 taskNum:37 uuid:cad50885-e41e-4d97-ae99-24c92507cfcf] map[creationTime:2023-05-03 15:15:51 taskNum:25 uuid:54fc2bec-c510-4ed4-a247-64a1141dd1e0] map[creationTime:2023-05-03 15:15:51 taskNum:7 uuid:1ee32849-261a-4e2e-82d8-04426675b88b] map[creationTime:2023-05-03 15:15:50 taskNum:15 uuid:bae917a3-01e5-4c20-b678-6f21bfe878e2] map[creationTime:2023-05-03 15:15:50 taskNum:27 uuid:d6a5a28f-4e24-460f-8d09-5f79af09f74c] map[creationTime:2023-05-03 15:15:51 taskNum:43 uuid:f873af32-edc5-45fc-9752-3f87cc3309e1] map[creationTime:2023-05-03 15:15:51 taskNum:10 uuid:120f48fb-3377-4896-9cb2-a32dfe00f25b] $ kubectl logs pubsub-subscriber-555ffcb5df-xmrr2 map[creationTime:2023-05-03 15:15:49 taskNum:26 uuid:39cbb823-592c-40e9-99cb-bf3ce52f2989] map[creationTime:2023-05-03 15:15:48 taskNum:38 uuid:447a296c-564d-4dc9-a04a-155f67c760e9] map[creationTime:2023-05-03 15:15:49 taskNum:11 uuid:0989fe2c-2fc0-4668-95d6-b1829fa6d92d] map[creationTime:2023-05-03 15:15:48 taskNum:29 uuid:db6e7299-47d8-473d-aebb-e18f2c846de6] map[creationTime:2023-05-03 15:15:49 taskNum:8 uuid:501f74cf-ccb8-4b1e-b388-32f565da889d] map[creationTime:2023-05-03 15:15:49 taskNum:6 uuid:108e22df-629d-4d88-9ab0-5758f608237f] map[creationTime:2023-05-03 15:15:49 taskNum:36 uuid:6e85faa5-d2ed-48b6-8866-d0114386303c] map[creationTime:2023-05-03 15:15:49 taskNum:20 uuid:aa07a78a-e478-4df7-9fef-835e215bc2ee] map[creationTime:2023-05-03 15:15:49 taskNum:45 uuid:4a43bad3-5a3d-4315-a565-ca7137104aa8] map[creationTime:2023-05-03 15:15:49 taskNum:9 uuid:f3f4c132-80ff-4850-8bea-94371b9cdfe5] map[creationTime:2023-05-03 15:15:50 taskNum:34 uuid:d026ad8e-f1cb-49df-bd33-9cf03cb0ee55] map[creationTime:2023-05-03 15:15:50 taskNum:13 uuid:f6731fca-d48b-41d7-a505-61e44425d817] map[creationTime:2023-05-03 15:15:51 taskNum:22 uuid:6ade1f2a-229b-4664-bf82-55aec79ba8fa] map[creationTime:2023-05-03 15:15:49 taskNum:3 uuid:95291f1f-94d3-44bb-a22c-97bfdd59fa25] map[creationTime:2023-05-03 15:15:51 taskNum:4 uuid:469e54b9-de42-4fb2-9c38-3f5c3b625d7b] 佐々木 駿太 (記事一覧) G-gen 最北端、北海道在住のクラウドソリューション部エンジニア。 2022 年 6 月に G-gen にジョイン。Google Cloud All Certifications Engineer。 好きな Google Cloud プロダクトは Cloud Run。最近は Dataflow を勉強中。 Follow @sasashun0805
アバター
G-gen の杉村です。Compute Engine の VM で Squid 等の HTTP プロキシサーバ経由でインターネットへ出るような構成を取った際、gcloud コマンドが不可解なエラーメッセージと共に失敗しました。今回はその事象と、解決方法をご紹介します。 事象 やろうとしたこと エラーメッセージ 前提知識 VM とプロキシサーバについて サービスアカウントと VM 原因調査 サービスアカウントは認識できている エラーメッセージ内の URL メタデータサーバはローカルからのみクエリできる 解決策 さらに深堀り curl でメタデータサーバへクエリしてみる プロキシ経由でメタデータサーバへクエリしてみる 事象 やろうとしたこと ある Compute Engine の Linux VM があります。この VM のログインユーザのプロファイルでは HTTP_PROXY および HTTPS_PROXY 環境変数を設定しており、他の VM 上で動作するプロキシサーバ (Squid) 経由でインターネットへ出られるようになっています。 この Linux VM にログインし、 gcloud storage コマンドを実行しようとしました。なお認証情報として VM にアタッチされているサービスアカウントを利用する想定です。 エラーメッセージ この環境で gcloud storage ls gs://my-test-bucket を実行しました。 すると、以下のようなエラーメッセージが出力され、コマンドが失敗しました。※バケット名やサービスアカウント名などは仮の値に置き換えてあります。 $ gcloud storage ls gs://my-test-bucket ERROR: ( gcloud.storage. ls ) There was a problem refreshing your current auth tokens: ( ' Failed to retrieve http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/1234567890-compute@developer.gserviceaccount.com/?recursive=true from the Google Compute Engine metadata service. Status: 403 Response:\nb\ '<! DOCTYPE html > \\n < html lang =en > \\n < meta charset =utf - 8> \\n < meta name =viewport content = " initial-scale=1, minimum-scale=1, width=device-width "> \\n < title > Error 403 ( Forbidden ) !! 1 < /title > \\n < style > \\n *{margin:0 ; padding:0}html,code{font:15px/22px arial,sans-serif}html{background: #fff;color:#222;padding:15px}body{margin:7% auto 0;max-width:390px;min-height:180px;padding:30px 0 15px}* > body{background:url(//www.google.com/images/errors/robot.png) 100% 5px no-repeat;padding-right:205px}p{margin:11px 0 22px;overflow:hidden}ins{color:#777;text-decoration:none}a img{border:0}@media screen and (max-width:772px){body{background:none;margin-top:0;max-width:none;padding-right:0}}#logo{background:url(//www.google.com/images/branding/googlelogo/1x/googlelogo_color_150x54dp.png) no-repeat;margin-left:-5px}@media only screen and (min-resolution:192dpi){#logo{background:url(//www.google.com/images/branding/googlelogo/2x/googlelogo_color_150x54dp.png) no-repeat 0% 0%/100% 100%;-moz-border-image:url(//www.google.com/images/branding/googlelogo/2x/googlelogo_color_150x54dp.png) 0}}@media only screen and (-webkit-min-device-pixel-ratio:2){#logo{background:url(//www.google.com/images/branding/googlelogo/2x/googlelogo_color_150x54dp.png) no-repeat;-webkit-background-size:100% 100%}}#logo{display:inline-block;height:54px;width:150px}\\n </style>\\n <a href=//www.google.com/><span id=logo aria-label=Google></span></a>\\n <p><b>403.</b> <ins>That\\xe2\\x80\\x99s an error.</ins>\\n <p>Your client does not have permission to get URL <code>/computeMetadata/v1/instance/service-accounts/1234567890-compute@developer.gserviceaccount.com/?recursive=true</code> from this server. <ins>That\\xe2\\x80\\x99s all we know.</ins>\\n\'', <google.auth.transport.requests._Response object at 0x7f0701657970>) Please run: $ gcloud auth login to obtain new credentials. If you have already logged in with a different account: $ gcloud config set account ACCOUNT to select an already authenticated account to use. 上記コードブロックだと改行がなく読みづらいのと、記号がエンコードされてしまっているので、以下に要所だけ抜き出して整形したメッセージを記載します。 ERROR: (gcloud.storage.ls) There was a problem refreshing your current auth tokens: ('Failed to retrieve http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/1234567890-compute@developer.gserviceaccount.com/?recursive=true from the Google Compute Engine metadata service. Status: 403 403. That's an error. Your client does not have permission to get URL /computeMetadata/v1/instance/service-accounts/1234567890-compute@developer.gserviceaccount.com/?recursive=true from this server. That's all we know. Please run: $ gcloud auth login to obtain new credentials. If you have already logged in with a different account: $ gcloud config set account ACCOUNT to select an already authenticated account to use. There was a problem refreshing your current auth tokens ... や Your client does not have permission to get URL ...というメッセージから想像するに、認証情報に関連するエラーメッセージのようです。 設定を確認したところ、VM には正しくサービスアカウントがアタッチされており、またサービスアカウントは適切に IAM ロールが付与されています。VM の アクセススコープ設定 も適切でした。 また Please run: $ gcloud auth login とありますが、このコマンドは gcloud コマンドに明示的に Google アカウントの認証情報を設定するコマンドであり、今回は VM にアタッチしたサービスアカウントを使いたいため、要件と合わなくなってしまいます。 前提知識 VM とプロキシサーバについて 今回の事象を理解するためには、前提となる環境を理解する必要があります。 Compute Engine VM からインターネットに出るには、いくつかの方法があります。 VM のパブリック IP アドレスを使って VPC の Default Internet Gateway から直接インターネットへ出る パブリック IP アドレスを持たない VM から Cloud NAT 経由でインターネットへ出る パブリック IP アドレスを持たない VM から HTTP プロキシサーバ経由でインターネットへ出る このうち 3. のプロキシサーバを使う場合、以下のような方法でプロキシサーバの宛先を設定することになります。 (Linux の場合) HTTP_PROXY / HTTPS_PROXY 環境変数を設定する (Windows の場合) コントロールパネルのインターネットオプションで設定する アプリケーション固有の設定でプロキシサーバを指定する 今回の事象は Linux サーバで起きており、上記のうち HTTP_PROXY / HTTPS_PROXY 環境変数を使用する方法でプロキシサーバを指定しています。また gcloud コマンドは、これらの環境変数が設定されている場合は、変数の内容を読み取ってプロキシサーバを利用してくれる仕様となっています。 サービスアカウントと VM サービスアカウント とは、Google Cloud で管理されるアカウントの一種です。 サービスアカウントは人間が使う Google アカウントとは区別され、プログラムが Google API や Google Cloud API を呼び出すために用いるアカウントを指します。 Compute Engine の VM にはそのサービスアカウントをアタッチ (日本語ドキュメントでは “接続” と表現) することができます。 参考 : サービス アカウントとインスタンスの接続 サービスアカウントをアタッチすると、VM 上で動作する gcloud 等のプログラムは、そのサービスアカウントの権限を使って各種 Google Cloud サービスの API を実行することができます。Google アカウントを使って認証したり、サービスアカウントキーをダウンロードする必要はありません。 余談ですが Amazon Web Services (AWS) でも「EC2 インスタンスへの IAM ロールのアタッチ」というよく似た概念が存在しています。 原因調査 サービスアカウントは認識できている エラーメッセージには以下の文言が含まれています。 Your client does not have permission to get URL /computeMetadata/v1/instance/service-accounts/1234567890-compute@developer.gserviceaccount.com/?recursive=true from this server. 1234567890-compute@developer.gserviceaccount.com はこの VM にアタッチされているサービスアカウント名であり、VM の中からはサービスアカウントの名称は正しく認識できていることが分かります。 それにも関わらず「認証情報が得られていない」ようなエラーメッセージが出力されていることになります。 エラーメッセージ内の URL ヒントは、引用した上記メッセージにありました。 /computeMetadata で始まる URL に注目します。 Cloud SDK は、前述のように ADC (Application Default Credentials) の仕組みにより認証情報を探します。 今回の実行時は GOOGLE_APPLICATION_CREDENTIALS 環境変数に認証情報を設定しておらず、gcloud コマンドにも認証情報を設定していなかったため、VM にアタッチされたサービスアカウントへ認証情報を取りにいきます。 実は gcloud 等が VM のサービスアカウントから認証情報を取得するときは、VM の メタデータサーバ というサーバへ HTTP リクエストを投げることで認証情報を取得する仕様です。 参考 : 接続されたサービス アカウント メタデータサーバは http://metadata.google.internal/ という URL であり、これを VM 上で名前解決すると 169.254.169.254 というリンクローカル IP アドレスへ解決されます (hosts ファイルで静的に指定されている)。 このメタデータサーバ (169.254.169.254) は VM のローカルからのみアクセス可能な特殊なサーバとなっており、我々ユーザも curl コマンドなどを用いて VM 内からリクエストすることで、VM やプロジェクトの情報を得ることができます。 参考 : VM メタデータにクエリを実行する 改めてエラーの1行目を見てみると以下のようになっています。 ERROR: (gcloud.storage.ls) There was a problem refreshing your current auth tokens: ('Failed to retrieve http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/1234567890-compute@developer.gserviceaccount.com/?recursive=true from the Google Compute Engine metadata service. 前述のエラーメッセージと合わせて見てみても、どうもこのメタデータサーバへのクエリが失敗している ( Your client does not have permission ) らしいことが分かります。 メタデータサーバはローカルからのみクエリできる 実は、VM のメタデータサーバへは、その VM の ローカルからしかアクセスできない という制限があります。プロキシサーバ等、他のノードを経由してメタデータサーバへアクセスしようとすると、セキュリティ上の理由から拒否されます。 今回利用しようとしていたプロキシサーバ (Squid) のアクセスログを見てみると、以下のように、当該 VM からメタデータサーバへ プロキシ経由で接続しようと していました。 1671421064 . 582 1 10 . 146 .x.x TCP_MISS/ 403 2037 GET http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/1234567890-compute@developer.gserviceaccount.com/? - HIER_DIRECT/ 169 . 254 . 169 . 254 text/html 本来ローカルでルーティングされるべき 169.254.169.254 へのリクエストがプロキシ経由でリクエストされてしまっていたのです。 今回はプロキシサーバも Compute Engine VM でしたが、ローカルからのアクセスではないためメタデータサーバ側で弾かれていました。なおプロキシサーバがオンプレミスであれば、169.254.169.254 は HTTP レスポンスを受け付けないはずなので、タイムアウト等になるかもしれません。 解決策 以下のように NO_PROXY 環境変数を設定し「プロキシへフォワードしない IP アドレス/ドメイン名一覧」を定義してあげます。 $ export NO_PROXY = 127 . 0 . 0 . 1 ,localhost, 169 . 254 . 169 . 254 ,metadata,metadata.google.internal これにより localhost (127.0.0.1) や metadata.google.internal (169.254.169.254) への HTTP リクエストはプロキシサーバに転送されなくなり、ローカルから直接リクエストされるようになります。 上記を設定したところ、以下のように正しく gcloud コマンドが使えるようになりました。 $ gcloud storage ls gs://my-test-bucket gs://my-test-bucket/my-private-obj-01.txt gs://my-test-bucket/my-private-obj-02.txt さらに深堀り curl でメタデータサーバへクエリしてみる メタデータサーバの挙動を理解するために、curl コマンドを使い手動でメタデータサーバをクエリしてみます。 curl コマンドは HTTP_PROXY / HTTPS_PROXY / NO_PROXY 環境変数に影響を受けません。代わりに -x オプションで明示的にプロキシサーバを指定できます。 まずは以下のように、プロキシを使わずにメタデータをクエリしてみます。 metadata.google.internal は hosts ファイルにより 169.254.169.254 に解決され、メタデータサーバへルートされます。 $ curl -v -H ' Metadata-Flavor:Google ' http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/1234567890-compute@developer.gserviceaccount.com/email * Trying 169 . 254 . 169 .254:80... * Connected to metadata.google.internal ( 169 . 254 . 169 . 254 ) port 80 ( #0) > GET /computeMetadata/v1/instance/service-accounts/1234567890-compute@developer.gserviceaccount.com/email HTTP/ 1 . 1 > Host: metadata.google.internal > User-Agent: curl/ 7 . 74 . 0 > Accept: */* > Metadata-Flavor:Google > * Mark bundle as not supporting multiuse < HTTP/ 1 . 1 200 OK < Metadata-Flavor: Google < Content-Type: application/text < ETag: ae98be0f811e76b9 < Date: Mon, 19 Dec 2022 23:23:36 GMT < Server: Metadata Server for VM < Content-Length: 48 < X-XSS-Protection: 0 < X-Frame-Options: SAMEORIGIN < * Connection #0 to host metadata.google.internal left intact 1234567890-compute@developer.gserviceaccount.com 上記のようにメタデータサーバから正しくレスポンスが返ってきました。 なお -H オプションで Metadata-Flavor:Google を付与するのは、メタデータをクエリするための決まりごとであり、必須です。 参考 : VM メタデータについて 参考 : VM メタデータにクエリを実行する プロキシ経由でメタデータサーバへクエリしてみる 次にオプション -x http://myproxy.local:3128 を指定して、あえてプロキシ経由でメタデータサーバにクエリを投げてみます。 myproxy.local は Squid が動作している別の VM です。 $ curl -v -x http://myproxy.local:3128 -H ' Metadata-Flavor:Google ' http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/1234567890-compute@developer.gserviceaccount.com/email * Trying 10 . 146 . 15 .212:3128... * Connected to myproxy. local ( 10 . 146 . 15 . 212 ) port 3128 ( #0) > GET http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/1234567890-compute@developer.gserviceaccount.com/email HTTP/ 1 . 1 > Host: metadata.google.internal > User-Agent: curl/ 7 . 74 . 0 > Accept: */* > Proxy-Connection: Keep-Alive > Metadata-Flavor:Google > * Mark bundle as not supporting multiuse < HTTP/ 1 . 1 403 Forbidden < Metadata-Flavor: Google < Date: Mon, 19 Dec 2022 23:24:18 GMT < Content-Type: text/html ; charset =UTF -8 < Server: Metadata Server for VM < Content-Length: 1678 < X-XSS-Protection: 0 < X-Frame-Options: SAMEORIGIN < X-Cache: MISS from squid < X-Cache-Lookup: MISS from squid:3128 < Via: 1 . 1 squid ( squid/ 4 . 13 ) < Connection: keep-alive < <! DOCTYPE html > < html lang =en > < meta charset =utf -8> < meta name =viewport content = " initial-scale=1, minimum-scale=1, width=device-width "> < title > Error 403 ( Forbidden ) !! 1 < /title > < style > * { margin:0 ; padding:0 } html,code { font:15px/22px arial,sans-serif } html { background: #fff;color:#222;padding:15px}body{margin:7% auto 0;max-width:390px;min-height:180px;padding:30px 0 15px}* > body{background:url(//www.google.com/images/errors/robot.png) 100% 5px no-repeat;padding-right:205px}p{margin:11px 0 22px;overflow:hidden}ins{color:#777;text-decoration:none}a img{border:0}@media screen and (max-width:772px){body{background:none;margin-top:0;max-width:none;padding-right:0}}#logo{background:url(//www.google.com/images/branding/googlelogo/1x/googlelogo_color_150x54dp.png) no-repeat;margin-left:-5px}@media only screen and (min-resolution:192dpi){#logo{background:url(//www.google.com/images/branding/googlelogo/2x/googlelogo_color_150x54dp.png) no-repeat 0% 0%/100% 100%;-moz-border-image:url(//www.google.com/images/branding/googlelogo/2x/googlelogo_color_150x54dp.png) 0}}@media only screen and (-webkit-min-device-pixel-ratio:2){#logo{background:url(//www.google.com/images/branding/googlelogo/2x/googlelogo_color_150x54dp.png) no-repeat;-webkit-background-size:100% 100%}}#logo{display:inline-block;height:54px;width:150px} < /style > < a href =//www.google.com/ >< span id =logo aria-label = Google >< /span >< /a > < p >< b > 403 . < /b > < ins > That’s an error. < /ins > < p > Your client does not have permission to get URL < code > /computeMetadata/v1/instance/service-accounts/1234567890-compute@developer.gserviceaccount.com/email < /code > from this server. < ins > That’s all we know. < /ins > * Connection #0 to host myproxy.local left intact 上記のように Your client does not have permission to get URL <code>/computeMetadata/v1/instance/service-accounts/1234567890-compute@developer.gserviceaccount.com/email</code> from this server. というメッセージが表示されました。ステータスコードは 403 Forbidden です。 これは、当初 gcloud コマンドで出力されたエラーメッセージと全く同じです。この検証によって、今回の事象がよく理解できるのではないでしょうか。 杉村 勇馬 (記事一覧) 執行役員 CTO / クラウドソリューション部 部長 元警察官という経歴を持つ現 IT エンジニア。クラウド管理・運用やネットワークに知見。AWS 12資格、Google Cloud認定資格11資格。Twitter では Google Cloud や AWS のアップデート情報をつぶやいています。 Follow @y_sugi_it
アバター
当記事は みずほリサーチ&テクノロジーズ × G-gen エンジニアコラボレーション企画 で執筆されたものです。 みずほリサーチ&テクノロジーズ株式会社の舘山です。本日は Artifact Registry リモートリポジトリ機能について、検証した結果を共有します。 Artifact Registry はじめに 前提知識 Artifact Registryリモートリポジトリ Artifact Registry とプライベート接続 注意点 プライベートパッケージとの併用 Cloud Buildプライベートプール 検証1. プライベートプールでのDockerイメージビルド 検証内容 改修前のビルド構成ファイルとDockerfile ビルド構成ファイル Dockerfile リモートリポジトリの作成 Dockerfileとビルド構成ファイルの改修方針 (1) キーリングによる認証を用いた改修 検証手順 Dockerfile ビルド構成ファイル(抜粋) (2) サービスアカウントキーによるパスワード認証を用いた改修 検証手順 Dockerfile ビルド構成ファイル ビルド実行後のリモートリポジトリ 検証2. Cloud Functions 関数のビルド 検証内容 (1) Python関数 (2) Node.js関数 (3) Java関数 はじめに セキュリティ上の理由で開発環境からインターネット接続が制限されている場合、Dockerのベースイメージやプログラム言語パッケージの取得が課題になります。 また、プロキシサーバでパブリックリポジトリへの通信を単純に許可した場合、アップロードによる情報持ち出しリスクが生じます。 Artifact Registryリモートリポジトリ機能を利用することで、インターネット接続が制限されたVPCから間接的に、パブリックリポジトリへの読み取り専用アクセスが可能になります。本記事では具体的に以下 2 つのテーマについてご紹介します。 インターネット接続が制限された環境で Docker コンテナをビルドできること インターネット接続が制限された環境で Cloud Functions をビルドできること 注意点としてArtifact Registryリモートリポジトリ機能は2023/4/3現在、正式リリース前のプレビュー版です。正式リリースまで本番ワークロードでの利用は、推奨されません。 →2023/10/27に GA になりました。 前提知識 Artifact Registryリモートリポジトリ Artifact Registry とプライベート接続 Artifact Registry はコンテナイメージと言語パッケージを管理するサービスです。 インターネット接続が制限されたVPCからArtifact Registryのプライベートリポジトリには、 限定公開のGoogleアクセス 、 Private Service Connect を利用したアクセスが可能です。 参考 : 限定公開の Google アクセスの仕組みと手順をきっちり解説 参考 : Private Service Connect機能解説。Google Cloud APIにプライベート接続 Artifact Registryの リモートリポジトリ 機能により、プライベートリポジトリから間接的に Docker Hub Maven Central npmレジストリ PyPI に対する読み取り専用アクセスが可能になります。 Docker Hubをソースとするリモートリポジトリ 注意点 現状、リモートリポジトリではApt、Yumは、未サポートです。 OSパッケージについては、別環境でダウンロードしたパッケージを持ち込んで、オフラインインストールする等の代替手段が必要です。 プライベートパッケージとの併用 Artifact Registryの 標準リポジトリ とリモートリポジトリに対する単一のアクセス ポイントを提供する、 仮想リポジトリ 機能を利用できますが、当記事では扱いません。 Cloud Buildプライベートプール Cloud Build はサーバレスのビルド実行サービスです。 Cloud Buildのデフォルト設定では、自由にインターネット接続が可能なデフォルトプールでビルドが実行されます。 ビルドスクリプトによる外部へのデータ持ち出しを予防するため、 組織ポリシーでデフォルトプールの利用を制限 し、 パブリックIPアドレスを無効化 した プライベートプール でビルドを実行させることも可能です。 参考 : プライベート ネットワークで Cloud Build を使用する 参考 : VPC Service Controls を使用する 検証1. プライベートプールでのDockerイメージビルド 検証内容 パブリックIPアドレスを無効化したプライベートプールで、PythonアプリケーションのDockerイメージのビルドを実行してみました。 インターネット接続が制限された環境でのビルド 改修前のビルド構成ファイルとDockerfile ビルド構成ファイル Cloud Buildのビルド構成ファイルのスキーマはCloud Buildのガイドを参照してください。 ビルド構成ファイルのスキーマ Cloud Buildの各ビルドステップは、Dockerコンテナで実行されます。 Dockerのビルド用途に利用できるDockerイメージはGoogleがContrainer Registry('gcr.io/cloud-builders/docker')で公開しています。 Cloud Buildでビルドした成果物のDockerイメージは、Artifact Registry標準リポジトリへ格納します。 steps: - name: 'gcr.io/cloud-builders/docker' args: [ 'build', '-t', 'asia-northeast1-docker.pkg.dev/poc01-xxxx/gcf-artifacts/test', '.' ] images: - 'asia-northeast1-docker.pkg.dev/poc01-xxxx/gcf-artifacts/test' options: logging: CLOUD_LOGGING_ONLY pool: name: 'projects/poc01-xxxx/locations/asia-northeast1/workerPools/no-external-ip-pool' Dockerfile Docker HubからPythonのベースイメージを取得し、プログラムをCOPYで配置。PyPIから依存ライブラリを取得します。 FROM python: 3.9 -slim ENV APP_HOME / app WORKDIR $APP_HOME COPY . . / # Install production dependencies. RUN pip install -r requirements.txt パブリックIPアドレス無効化したプライベートプールでは、Docker HubとPyPIにアクセスできないため、ビルドエラーになります。 プライベートプールからDocker Hubへのアクセスがタイムアウト なお、指定するベースイメージによっては、Docker Hubへのアクセスエラーが発生しませんでした。 cloud-builders/dockerが Container RegistryのDocker Hubイメージキャッシュ を先にチェックしているためと思われます。 リモートリポジトリの作成 ガイドの記載に従いDocker HubとPyPIをソースとするリモートリポジトリを作成します。 - リモート リポジトリを作成する Terraformを利用する場合、以下のような記載になります。 resource "google_artifact_registry_repository" "docker-proxy" { provider = google-beta location = "asia-northeast1" repository_id = "docker-proxy" description = "Docker Hubをソースとするリモートリポジトリ" format = "DOCKER" mode = "REMOTE_REPOSITORY" remote_repository_config { description = "docker hub" docker_repository { public_repository = "DOCKER_HUB" } } } resource "google_artifact_registry_repository" "pypi-proxy" { provider = google-beta location = "asia-northeast1" repository_id = "pypi-proxy" description = "PyPIをソースとするリモートリポジトリ" format = "PYTHON" mode = "REMOTE_REPOSITORY" remote_repository_config { description = "pypi" python_repository { public_repository = "PYPI" } } } google-betaプロバイダーのバージョン4.57.0 (March 13, 2023)以降の利用が必要です。 リポジトリをVPC Service Controlsで保護する場合、 アップストリームソースへのアクセス許可設定 が必要ですが、2023/4/3現在、Terraformで設定する方法は見つかりませんでした。 Dockerfileとビルド構成ファイルの改修方針 Artifact RegistryのPythonリポジトリ認証方式は2つあります。 認証方式によってDockerfileとビルド構成ファイルの改修内容が異なるため、それぞれの手順を解説します。 参考 : Pythonキーリングライブラリ 参考 : サービスアカウントキーを利用したパスワード認証 (1) キーリングによる認証を用いた改修 検証手順 Artifact RegistryのガイドではPythonキーリングライブラリはPyPIからインストールしていますが、今回はPyPIに直接アクセスできない前提のため、キーリングライブラリは別環境で事前にダウンロードし、ソースと一緒にビルド環境へ持ち込みます。 ビルド対象のDockerイメージにはライブラリをCOPY配置してオフラインインストールします。 参考サイト: オフライン環境: pipでファイルからpythonパッケージインストール Dockerfile、ビルド構成ファイルを以下の様に修正します。 Dockerfile ベースイメージの取得先をArtifact Registryリモートリポジトリに変更します。 COPYで持ち込んだキーリングライブラリをオフラインインストールします。 Pythonライブラリの取得先をArtifact Registryリモートリポジトリに変更します。 --index-url はrequirements.txt内で指定することも可能です。 # Docker HubをソースとするArtifact Registryリモートリポジトリを参照 FROM asia-northeast1-docker .pkg.dev / poc01-xxxx / docker-hub-proxy / python: 3.9 -slim ENV APP_HOME / app WORKDIR $APP_HOME COPY . . / # Artifact Registry認証用キーリングライブラリはCOPYで持ち込み、オフラインインストールする RUN pip install -- no-index -- find-links = . / keyring keyring RUN pip install -- no-index -- find-links = . / keyring keyrings. google-artifactregistry-auth # PyPIをソースするArtifact Registryリモートリポジトリを参照。 # RUNコマンド実行時にデフォルト認証情報(Cloud Buildサービスアカウント)を取得可能にしておくため、 # docker buildコマンドのオプションで--network=cloudbuild指定する。 # https://cloud.google.com/build/docs/build-config-file-schema?hl=ja#network RUN pip install -- index-url https: // asia-northeast1-python .pkg.dev / poc01-xxxx / pypi-proxy / simple / -r requirements.txt ビルド構成ファイル(抜粋) RUN命令でビルド対象のコンテナ内でのpipコマンド実行時に、キーリングライブラリがCloud Buildサービスアカウント認証情報を取得できるように、docker buildコマンドのオプションに --network=cloudbuild を追加します。 - Cloud Build ネットワーク steps: - name: 'gcr.io/cloud-builders/docker' args: [ 'build','--network=cloudbuild', '-t', 'asia-northeast1-docker.pkg.dev/poc01-xxxx/gcf-artifacts/test', '.' ] 今回検証していませんが、キーリングライブラリは実行環境には不要なので、マルチステージビルドにして最終的なイメージから除外してもよいでしょう。 (2) サービスアカウントキーによるパスワード認証を用いた改修 検証手順 リモートリポジトリ参照のみ可能な最小権限のサービスアカウントを作成し、ガイドの記載に従いサービスアカウントキーからベーシック認証でリポジトリへアクセスするURLを生成します。 Pyhon:サービス アカウント キーによる認証 以下のような、ベーシック認証でリポジトリへアクセスするURLが取得できます。{key}部分は実際には長大な文字列になります。 https://_json_key_base64:{KEY}@{LOCATION}-python.pkg.dev/{PROJECT}/{REPOSITORY}/simple/ リモートリポジトリ参照のみ可能な認証情報漏洩の影響は限定的(リモートリポジトリをVPC Service Controlsで保護する場合、外部からのアクセス不可)ですが、今回はベーシック認証のURLをSecret Managerに保持することにします。 シークレットの作成方法は、Secret Managerのガイドを参照してください。 - シークレットを作成する Dockerfile、ビルド構成ファイルを以下の様に修正します。 Dockerfile ベースイメージの取得先をArtifact Registryリモートリポジトリに変更します。 Pythonライブラリの取得先はbuild-arg:PIP_INDEX_URLとして実行時に指定します。 # DockerHubをソースとするArtifact Registryリモートリポジトリを参照 FROM asia-northeast1-docker .pkg.dev / poc01-hn-audit-115243084873 / docker-hub-proxy / python: 3.9 -slim # PyPIをソースとするArtifact Registryリモートリポジトリをサービスアカウントキーで認証するベーシック認証URL ARG PIP_INDEX_URL ENV APP_HOME / app WORKDIR $APP_HOME COPY . . / RUN pip install -- index-url $PIP_INDEX_URL -r requirements.txt ビルド構成ファイル ベーシック認証でリモートリポジトリへアクセスするURLをSecret Managerから取得し、docker buildコマンドのbuild-arg:PIP_INDEX_URLに引き渡します。 Cloud BuildでのSecret Manager利用方法はCloud Buildのガイドを参照してください。 - Secret Manager のシークレットを使用する steps: - name: 'gcr.io/cloud-builders/docker' entrypoint: 'bash' args: [ '-c', 'docker build --build-arg PIP_INDEX_URL=$$PIP_INDEX_URL -t asia-northeast1-docker.pkg.dev/poc01-xxxx/gcf-artifacts/test .' ] secretEnv: [ 'PIP_INDEX_URL' ] images: - 'asia-northeast1-docker.pkg.dev/poc01-xxxx/gcf-artifacts/test' availableSecrets: secretManager: - versionName: projects/xxxxxxxxx/secrets/pip_index_url/versions/1 env: PIP_INDEX_URL options: logging: CLOUD_LOGGING_ONLY pool: name: 'projects/poc01-xxxx/locations/asia-northeast1/workerPools/no-external-ip-pool' 注意点として、この方法で最終イメージを作成した場合、ARGに渡したベーシック認証のURLは docker history で露出します。 参考サイト: Dockerイメージビルド時の秘密情報の扱い方に関するまとめ 前述のように漏洩の影響は限定的ですが、許容できない場合には別ステップでライブラリを取得し、結果だけを最終イメージに取り込むような手当が必要になります。 ビルド実行後のリモートリポジトリ コンソールでソースリポジトリから取得したパッケージを確認できます。 ビルド実行後のリモートリポジトリ(Docker Hub) ビルド実行後のリモートリポジトリ(PyPI) 検証2. Cloud Functions 関数のビルド 検証内容 AWS Lambda と異なり、Cloud Functions 関数のビルドはユーザープロジェクトの Cloud Build 環境で実行されます。 インターネット接続制限のため、組織ポリシーでデフォルトプールの利用を制限した場合、関数のビルドにもデフォルトプールを利用できなくなります。 今回、Python、Node.js、Javaについて、ライブラリの取得先をArtifact Registryリモートリポジトリへ変更することで、パブリックIP無効化したプライベートプールでビルドする方法を調査しました。 (1) Python関数 Cloud Functionsのガイド「 Python での依存関係の指定:プライベート依存関係を使用する 」を参考に、 requirements.txt を --index-url https://asia-northeast1-python.pkg.dev/poc01-xxxx/pypi-proxy/simple/ functions-framework==3.* boto3 beautifulsoup4 のように変更することで、パブリックIP無効化したプライベートプールでのビルドが成功しました。 Artifact Registryの認証には、Cloud Buildサービスアカウントが自動で利用されます。 なお、かつてBuildpackのPython関数ビルドプロセスには最初にpip、setuptools、wheelをPyPIからアップデートする処理が入っていましたが、2023/3末の更新でロジックが修正されています。 参考 : GoogleCloudPlatform/buildpacks - do not upgrade pip, wheel and setuptools in the python/runtime buildpack 参考として、以前 (2023/3/30時点) の挙動では、pip、setuptools、wheelをPyPIからアップデートする処理が走り、パブリックIP無効化したプライベートプールからPyPIへ接続できないためビルドエラーとなりました。 ビルドログ (2) Node.js関数 Cloud Functionsのガイド「 Node.js での依存関係の指定:Artifact Registry の非公開モジュール 」を参考に package.json と同じ階層に .npmrc ファイルを配置して、参照先リポジトリを切り替えることで、パブリックIP無効化したプライベートプールでのビルドが成功しました。 registry=https://asia-northeast1-npm.pkg.dev/poc01-xxxx/npm-proxy //asia-northeast1-npm.pkg.dev/poc01-xxxx/npm-proxy:always-auth=true (3) Java関数 Cloud Functionsのガイド「 Java での依存関係の指定 」にはArtifact Registryの利用方法が言及されていませんが、 Artifact Registryのガイド「 Maven と Gradle 用の認証を設定する:Maven を構成する 」を参考に、 pom.xml にrepositoriesセクション、buildセクションの記載を追加することで、パブリックIP無効化したプライベートプールでのビルドが成功しました。 <repositories> <repository> <id> central </id> <name> Maven Central remote repository </name> <url> artifactregistry://asia-northeast1-maven.pkg.dev/poc01-xxxx/maven-proxy </url> <layout> default </layout> <releases> <enabled> true </enabled> </releases> <snapshots> <enabled> true </enabled> </snapshots> </repository> </repositories> <build> <extensions> <extension> <groupId> com.google.cloud.artifactregistry </groupId> <artifactId> artifactregistry-maven-wagon </artifactId> <version> 2.2.0 </version> </extension> </extensions> </build> 舘山 浩之 みずほリサーチ&テクノロジーズ 先端技術研究部に所属。個人のキャリアではAWSの利用経験が長く、Google Cloudは2022年より利用開始。
アバター
G-gen の杉村です。Google Cloud(旧称 GCP)には組織(Organization)という概念があります。ガバナンスとセキュリティのために重要なこの機能を解説します。 組織の基本 組織(Organization)とは リソースの階層構造 組織リソース 組織 ID(顧客 ID) フォルダ、プロジェクト 組織のメリットとユースケース 組織を使う理由 複数プロジェクトの管理 利用可能なサービス・機能 組織を使わないリスク 組織の作成 Google Workspace と Cloud Identity 組織の作成方法 組織作成直後の特権 組織の表示 階層構造(ツリー)の表示 表示に必要な権限 組織の管理 組織の管理者ロール 強力な管理権限 管理の委任 監査 組織と監査ログ(Cloud Audit Logs) 監査ログの収集 組織(Resource Manager)自体の監査ログ 構成情報管理 組織のポリシー 詳細な管理 タグ(tags) プロジェクトの移動 通知の連絡先 エッセンシャルコンタクト 連絡先の継承 組織内アクセス制限 関連する Google Cloud サービス Identity and Access Management(IAM) 階層型ファイアウォールポリシー VPC Service Controls ベストプラクティス フォルダ構造の決め方 組織作成直後にやるべきこと その他のベストプラクティス セキュリティスイート for Google Cloud 組織の基本 組織(Organization)とは Google Cloud の 組織 (Organization)とは、Google Cloud リソースの集中管理と整理のための仕組みです。Google Cloud プロジェクトを始めとする Google Cloud リソースは組織に所属して、管理下に入ることができます。 組織は、Google Workspace(Cloud Identity)のドメインと1:1で紐付きます。 example.com という Google Workspace ドメインがある場合、そこには Google Cloud 組織 example.com が作成できます。 参考 : 組織リソース リソースの階層構造 Google Cloud において、Compute Engine の VM(仮想サーバ)や BigQuery のテーブルなど、1つ1つのオブジェクトは リソース と呼ばれます。 そして、全てのリソースは階層構造を取ります。つまり、リソースには 親子関係 があり、リソース同士の関係はツリー構造で表現することができます。 ツリーの頂点には組織リソースがあり、このツリーにリソースが所属している状態を「このプロジェクトは組織 my-domain.com に所属する」のように表現します。 組織とリソース階層構造 参考 : リソース階層 組織リソース 組織自体も1つのリソース です。ツリーのトップレベルノード(最上位の節)が組織リソースであり、前掲の図で言うと、最上位にある my-domain.com が組織リソースです。組織リソースは、組織ノード、あるいはルートノードともいいます。 組織自体がリソースであるため、組織は IAM 許可ポリシーを持つことができます。また、組織に対して CRUD(Create、Read、Update、Delete)操作をする API が存在します。 参考 : Google CloudのIAMを徹底解説! - G-gen Tech Blog - IAM ポリシーの構造 なお、組織リソースを操作する Google Cloud API は Resource Manager API( cloudresourcemanager.googleapis.com )です。 組織 ID(顧客 ID) 組織はドメイン名の他に10桁〜13桁程度の数字で表現される、一意の 組織 ID を持ちます。また Google Workspace(Cloud Identity)ドメインと共通の 顧客 ID (お客様 ID)も存在します。 ID は、後述の「組織のポリシー」機能を使う際や、gcloud CLI などで組織リソースを対象とした操作を行うときに必要です。これらの ID を調べる方法については、以下の記事を参照してください。 blog.g-gen.co.jp フォルダ、プロジェクト リソース階層を構成する要素として フォルダ と プロジェクト があります。 フォルダ は、下位のフォルダやプロジェクトをグルーピングするためのリソースです。 プロジェクト は、ほとんどの Google Cloud リソースが所属する最も基本的な管理単位です。Google Cloud におけるプロジェクトは、Amazon Web Services(AWS)でいうところの「AWS アカウント」や Microsoft Azure でいうところの「サブスクリプション」と似ています。BigQuery テーブルや Compute Engine の VM など、ほとんどの Google Cloud リソースは1つのプロジェクトに所属します。 フォルダやプロジェクトも IAM 許可ポリシーを持っているので、 権限管理の単位 として使うことができます。また請求先アカウントのレポートでは、組織単位、フォルダ単位、プロジェクト単位で請求を分類することができるので、 請求の管理単位 としても利用できます。 フォルダやプロジェクトも、Resource Manager API のリソースです。 組織のメリットとユースケース 組織を使う理由 Google Cloud は組織なしでも利用することが可能です。 無料の Google アカウント(Gmail アカウント)で Google Cloud プロジェクトを作成すると、そのプロジェクトは組織に所属せず「 組織なし 」となります。この状態でも Compute Engine や BigQuery といった Google Cloud サービスは利用できます。 ただし、ほとんどの企業や官公庁では、組織機能を使うべきと言えます。以下にそのいくつかの理由と、逆に組織を使わない場合のリスクを解説します。 複数プロジェクトの管理 組織を使うメリットは、複数プロジェクトを利用するときに出てきます。 企業や官公庁において、複数のシステムを Google Cloud で開発・運用する際、それぞれのシステムを異なる部署やチームで管理し、またチームごとに異なる権限を付与したい場合があります。 組織機能を使って複数プロジェクトを 組織下に束ねる ことにより、組織のポリシーや IAM、VPC Service Controls などで 継承 の性質を利用したセキュリティ・統制の管理ができるようになります。 継承 (inheritance)とは、ここではリソースツリーの親から子へ、IAM 権限や組織ポリシーの設定が引き継がれることを指します。これによって、組織の管理者は、多数存在するプロジェクトなどの Google Cloud リソースに1つ1つ設定を施していく運用オーバヘッドを節約することができます。工数を削減することで、効果的に、また現実的な工数でガバナンスを実現できるようになります。 組織レベルで参照者権限を付与することでツリー全体が参照できる 利用可能なサービス・機能 後述の組織のポリシー機能をはじめ、重要なセキュリティ機能の多くでは、組織を有効化していることが利用条件となっています。以下は、その例です。 フォルダ グループでの IAM 管理 組織のポリシー VPC Service Controls Security Command Center ログの集約(ログルーティング) 上記のような機能については、以下の当社記事でも解説していますので、参考にしてください。 参考 : Google CloudのIAMを徹底解説! - G-gen Tech Blog 参考 : 組織のポリシーを解説 - G-gen Tech Blog 参考 : VPC Service Controlsを分かりやすく解説 - G-gen Tech Blog 参考 : Security Command Centerを徹底解説。Google Cloud(GCP)の脆弱性を自動検知 - G-gen Tech Blog 参考 : Cloud Loggingの概念と仕組みをしっかり解説 - G-gen Tech Blog 組織を使わないリスク 逆に組織を利用しない場合、個別のプロジェクトに対して権限管理や統制を行う必要があるため、運用工数が大きくなります。また、Google Workspace や Cloud Identity で利用可能な Google アカウントの集約管理やグループ機能も使えないため、組織内で誰がどのように Google Cloud や Google サービスを使っているのか、把握する術がありません。 また企業や官公庁内で、いわゆる「野良プロジェクト」の存在を許すことになり、意図しないセキュリティ事故のリスクが高まります。 企業で Google Cloud を利用する場合は、たとえ最初は Google Cloud プロジェクトを1つしか使わない場合でも、 将来の拡張性も見込んで最初から組織を作成しておき 、組織下でプロジェクトを管理することが望ましいと言えます。 組織の作成 Google Workspace と Cloud Identity Google Cloud 組織は、Google Workspace または Cloud Identity の組織(ドメイン)と 必ず1:1 になります。そのため、Google Cloud 組織を作成するには Google Workspace(Cloud Identity)ドメインを作成します。 Google Workspace とは、Google 製のグループウェアツールです。ディレクトリ管理機能(アカウントやグループ、組織部門の管理機能)と、Gmail や Google ドライブなどのグループウェア機能を持ちます。 Cloud Identity とは、Google Workspace と同等のディレクトリ管理機能を持つサービスです。Cloud Identity は Google Workspace と同じようにアカウントやグループの管理が可能ですが、Gmail などのグループウェア機能がありません。Cloud Identity には Free と Premium の2エディションがあり、Premium ではセキュリティ関連の機能がより強化されています。 Google Cloud の視点からは、Google Workspace と Cloud Identity のどちらを使っても、機能の差はありません。すでに Microsoft 365 などのグループウェアツールを導入済みの組織が Google アカウントを集約管理したい場合に、Cloud Identity が採用されることがあります。 なお、他のクラウドを例に取ると、Amazon Web Services(AWS)ではクラウドリソースとして IAM ユーザーを作成します。一方で Google Cloud には、クラウドリソースとしての IAM ユーザーは存在しません。Google Workspace で管理される Google アカウントが、Google Cloud 管理・利用のためのアカウントとなります。つまり、アカウントは Google Cloud の外で管理されます。 参考 : Google CloudのIAMを徹底解説! - G-gen Tech Blog - AWS IAM との比較と連携 組織の作成方法 Google Cloud 組織を作成するには、まず Google Workspace または Cloud Identity の組織(テナント)を作成します。 Web の利用申込み画面から組織の情報やドメイン名などを記入し、発行された TXT もしくは CNAME レコードをそのドメイン名を管理する DNS に追加してドメインを認証することで、利用開始できます。 ある Google Workspace(Cloud Identity)組織の Google アカウントで初めて Google Cloud コンソールにログインした際に、表示される利用規約に同意すると、そのタイミングで Google Cloud 組織が自動的に作成 されます。 Cloud Identity の組織作成の方法は、以下の記事をご参照ください。 blog.g-gen.co.jp 組織作成直後の特権 Google Workspace(Cloud Identity)の管理者権限と、Google Cloud の管理者権限は、それぞれ別々のものです。 Google Workspace(Cloud Identity)には「特権管理者」「グループ管理者」「ユーザー管理者」などの 管理者ロール があり、Google アカウントにロールをアサインできます。 一方で Google Cloud では、 Identity and Access Management ( IAM )の仕組みで、リソースベースで権限管理します。 Google Workspace(Cloud Identity)の権限管理と Google Cloud の権限管理は、本来は完全に別ものであり、管理体系も異なっていますが、「特権管理者」だけは関連があります。Google Workspace(Cloud Identity)の特権管理者ロールを持つアカウントは、Google Cloud 組織に対しても、 組織の管理者 ( roles/resourcemanager.organizationAdmin )ロールと同等の権限を持ちます。これは暗黙的な権限であり、Google Cloud 組織の IAM 許可ポリシーの一覧画面には表示されません。組織作成直後には、一部のアカウントのみ、このロールバインディングが表示されていることがあります。しかし、このロールバインディングを Google Cloud 側で削除しても、暗黙的な権限はなくなりません。 Google Workspace の特権管理者は Google Cloud の組織の管理者と同等 運用体制として、Google Workspace(Cloud Identity)の管理体制と、Google Cloud の管理体制が別である場合は、Google Cloud 組織を作成した直後に Google Workspace(Cloud Identity)の特権管理者アカウントを使って、Google Cloud を管理する担当者の Google アカウントやグループに対して、Google Cloud 組織レベルで組織の管理者ロール等を付与することで、Google Cloud 環境の管理を委任することが推奨されています。 参考 : 組織リソースを作成、管理する 参考 : 特権管理者アカウントのベスト プラクティス 組織の表示 階層構造(ツリー)の表示 Google Cloud コンソールで リソースの管理 (Manage resources)という画面に遷移すると、組織階層がツリー形式で表示されます。 Google Cloud コンソールにログインし、 IAM と管理 の画面の左部メニューから リソースの管理 をクリックするか、Google Cloud コンソール上部の検索ボックスに Manage Resources と入力することで、この画面に遷移できます。 Manage Resources での階層構造表示 この画面を使う方法以外にも、gcloud コマンドや REST API 等で組織情報を取得することが可能です。 表示に必要な権限 組織とその配下のフォルダやプロジェクトを全て表示するには、組織・フォルダ・プロジェクトに対する get list 権限が必要です。 組織のトップノードに対して、Google アカウントが参照者( roles/browser )ロールや組織の管理者( roles/resourcemanager.organizationAdmin )ロールを持っていれば、組織とその配下の全てのフォルダやプロジェクトを閲覧することができます。 組織レベルで参照者権限を付与することでツリー全体が参照できる 参照者ロールや組織の管理者ロールは、組織・フォルダ・プロジェクトのツリー構造を見られる権限はありますが、プロジェクト内のリソース(Compute Engine VM や BigQuery テーブル等)を見る権限は持っていません。これにより、クラウド環境の全体管理をするチームと、実際の開発・構築を行うチームの権限を分けることができます。 一方で、基本ロールである閲覧者( roles/viewer )ロールやオーナー( roles/owner )は、組織やフォルダに対する get・list 権限を持っていません。組織トップノードにこれらのロールを付与しても、「 組織なし (No organization)」としてプロジェクトが平坦に一覧表示されます。これらのロールには、プロジェクトに対する get・list 権限を持つためプロジェクトの情報は得られるのですが、組織やフォルダの情報は得られないためです。 組織レベルでオーナー権限を持っていても組織情報は得られない つまり、閲覧者ロールやオーナーロールはあくまでプロジェクトの 中 を管理するためのロールであり、組織構成を閲覧したり、管理することはできない点に注意しましょう。 参考 : 階層内のすべてのリソースの一覧表示 参考 : IAM basic and predefined roles reference 参考 : IAM permissions reference 組織の管理 組織の管理者ロール 組織の管理に必要な IAM 権限の考え方についても解説します。 管理担当者の Google アカウントに対して、組織のトップノードレベルで、組織の管理者( roles/resourcemanager.organizationAdmin )ロールを付与することで、以下のような管理タスクを実行することができます。 組織ツリー全体の表示 組織トップノードの IAM ポリシーの編集 組織配下の全フォルダの IAM ポリシーの編集 組織配下の全プロジェクトの IAM ポリシーの編集 組織の管理者ロールは「何でもできる権限」を持っているわけではありませんが、組織と配下の全フォルダ・全プロジェクトの IAM を編集できることから、自分に対して「どんな権限でも付与できる」ことになります。よって実質的に、最も強い権限を持っていると言えます。 強力な管理権限 組織の管理者ロールは、フォルダの作成・削除などは行えません。それが行えるのはフォルダ管理者( roles/resourcemanager.folderAdmin )等です。またプロジェクトの作成には、プロジェクト作成者( roles/resourcemanager.projectCreator )が必要ですし、組織のポリシーの管理には組織ポリシー管理者( roles/orgpolicy.policyAdmin )が必要です。 ある Google アカウントが、以下のロールを組織トップノードレベルで持っていれば、その Google アカウントは組織全体で ほとんどの管理・構築タスク を行うことができます。 フォルダ管理者( roles/resourcemanager.folderAdmin ) プロジェクト作成者( roles/resourcemanager.projectCreator ) 組織の管理者( roles/resourcemanager.organizationAdmin ) 組織ポリシー管理者( roles/orgpolicy.policyAdmin ) 実際には 最小権限の原則 に従い最小限のロールのみを付与することが望ましいですが、常にクラウド環境全体を管理するような管理者の場合は、上記のようなロールを持つことを検討します。 ただし、上記ロールにはプロジェクトの中のリソース(VM や BigQuery データセット等)を操作する権限は入っていません。それらを行うには、プロジェクトレベルでオーナー( roles/owner )ロールや、サービスごとの事前定義ロールを持つ必要があります。 管理の委任 システム単位の管理者や部署のクラウド管理者に対して、フォルダレベルで IAM ロールを付与することで、 管理を委任 することができます。 例えばフォルダ system-a 以下は john@my-domain.com に管理を委任し、フォルダ system-b 以下は mary@my-domain.com に管理を委任する、という場合は、それぞれのフォルダレベルでそれぞれの担当者に対して フォルダ管理者 ロールを付与することで管理を委任できます。 部署 (システム) 単位で管理を委任 フォルダ管理者ロールは、フォルダレベルの IAM ポリシーの編集に加え、そのフォルダ配下のプロジェクトの IAM 権限を編集することができます。 また、プロジェクト単位で管理を委任することも可能です。その場合はプロジェクトレベルでオーナーロールを付与することなどで実現できます。 どのロールにどのような権限があるかは、以下のドキュメントで確かめることができます。 参考 : IAM を使用した組織リソースのアクセス制御 参考 : IAM を使用したフォルダのアクセス制御 参考 : IAM を使用したプロジェクトのアクセス制御 監査 組織と監査ログ(Cloud Audit Logs) Google Cloud の API 実行履歴は Cloud Audit Logs で収集されます。Cloud Audit Logs の詳細は以下の記事をご参照ください。 blog.g-gen.co.jp Cloud Audit Logs はデフォルトで有効であり、管理アクティビティ監査ログ(Admin Activity audit logs)と呼ばれる変更系オペレーションは、必ず記録されるようになっています。 しかし、データアクセス監査ログ(Data Access audit logs)と呼ばれる参照系オペレーションを含むすべての API 履歴を残すためには、明示的に設定する必要があります。 この明示的な設定も、組織内で親リソースから子リソースに継承されます。そのため組織トップノードでデータアクセス監査ログを有効化することで、組織内の全てのプロジェクトで有効化できます。 参考 : データアクセス監査ログを有効にする - デフォルト構成を設定する 監査ログの収集 ログの生成自体は、前述のデフォルト構成の設定で実施できますが、デフォルtおではログの保管先はそれぞれのプロジェクト、フォルダ、組織レベルのログバケット( _Default や _Required )です。 ログルーティング (シンク)の仕組みを利用すると、監査ログなどをログ集約用のプロジェクトに集約し、社内のルールや監査基準に沿ったログの保持や、必要なときのエクスポートを行うことができます。 参考 : Cloud Loggingの概念と仕組みをしっかり解説 - ログルーティングとログの保存 - G-gen Tech Blog 参考 : Cloud Loggingの概念と仕組みをしっかり解説 - プロジェクトをまたいだログの集約 - G-gen Tech Blog 組織(Resource Manager)自体の監査ログ 組織リソースを管理するための API である Resource Manager API 自体のログも、Cloud Audit Logs に保存されます。 この監査ログには、フォルダやプロジェクト、組織ポリシーなどの作成、削除、変更が記録されます。 参考 : Resource Manager の監査ロギングの情報 参考 : 組織のポリシーの監査ロギング情報 構成情報管理 Google Cloud 組織やプロジェクトのリソース構成情報は、 Cloud Asset Inventory というサービスに記録されます。 詳細は、以下の記事を参照してください。 blog.g-gen.co.jp 組織のポリシー 組織のポリシー (Organization Policy)機能では、組織で管理されたプロジェクトに対してルールを課し、統制を効かせることができます。組織のポリシーは、組織を使うにあたって最も基本的なセキュリティ機能です。一般的に、このようなルールを、セキュリティガードレイルと呼ぶこともあります。 例として、以下のような組織のポリシーの制約があります。 Cloud Storage で公開アクセスを禁止する 利用可能な Google Cloud サービスを制限する リソースを作成できるリージョンを制限する 組織のポリシーはリソース階層の上から下へ継承されていくため、フォルダを適切に分けることによって、システムごとに適用するポリシーを変えることができます。 組織のポリシーについては以下の記事で詳細に解説しています。 blog.g-gen.co.jp 詳細な管理 タグ(tags) Google Cloud には タグ という仕組みがあります。タグは Key-Value の文字列であり、それ自体が Google Cloud リソースです。IAM の条件(Condition)として利用可能であり、親リソースから子リソースへ継承されます。 タグをうまく使うことで、権限が及ぶ範囲をコントロールすることができます。 よく似た概念で「ラベル」という概念もありますが、タグとラベルは全くの別ものです。 タグの詳細は、以下の記事もご参照ください。 blog.g-gen.co.jp プロジェクトの移動 Google Cloud プロジェクトは、組織直下からフォルダ内へ、あるいはあるフォルダから別のフォルダへと、移動することができます。移動を行う際は、継承の性質を持つ IAM や組織ポリシーなどの影響範囲が変わることに注意が必要です。設定の継承元が変わるため、予期せぬ結果を生まないよう確認しましょう。 また、プロジェクトは「組織なし」から組織に所属させたり、ある組織から違う組織に移動することもできます。 ただし、組織間でプロジェクトを移行する場合、さまざまな考慮事項があります。以下に代表的なもののみを記載します。 上位リソースから継承される IAM 権限は移行されない プロジェクトに紐づくサポートケースが閲覧できなくなる(サポートケースを移行するには、Google サポートに連絡してメタデータ移行を依頼する必要がある) VPC Service Controls 境界にプロジェクトが入っている場合、移行ができない(移行するには、プロジェクトを境界の保護から外す必要がある) より詳細なリストは、以下をご参照ください。 参考 : 特殊なケースを処理する プロジェクトの組織間移動の手順等は、以下のドキュメントをご参照ください。 参考 : 組織リソース間のプロジェクトの移行 通知の連絡先 エッセンシャルコンタクト Google Cloud から「料金改定」「サービス仕様」「法令に関連する通達」「セキュリティ関連通知」など重要なお知らせが通知される場合があります。 こういったお知らせの通知先メールアドレスは、 エッセンシャルコンタクト という仕組みで指定することができます。 なお、エッセンシャルコンタクトで明示的に通知先を指定しない場合、お知らせは所定の IAM ロールを付与されている Google アカウントのメールアドレスに通知されます。 例えば課金に関するお知らせや法令遵守に関するお知らせは請求先アカウント管理者( roles/billing.admin )ロールを持つ Google アカウントに通知され、Google プロダクトの変更点に関するお知らせはオーナー( roles/owner )ロールを持つアカウントに届きます。 通知カテゴリとロールのデフォルトの対応表は、以下のドキュメントをご参照ください。 参考 : 通知の連絡先の管理 - 通知のカテゴリ 連絡先の継承 連絡先設定にも継承の概念があります。組織レベルで連絡先を設定すると、その設定が下位のフォルダ・プロジェクトにまで継承されます。下位プロジェクトで発生したお知らせは組織レベルで設定した連絡先へ通知されます。 「課金」「法務」「セキュリティ」カテゴリの通知は組織レベルで設定することが推奨されています。 参考 : 通知の連絡先の管理 - 連絡先の割り当てに関するベスト プラクティス 組織内アクセス制限 組織内アクセス制限 (organization restrictions)は、指定された Google Cloud 組織以外の組織へのアクセスを制限する機能です。 当機能は、企業や官公庁のネットワークで HTTP プロキシサーバを利用しており、そのプロキシサーバ以外にはインターネットに出る手段がないことが前提になります。プロキシサーバにおいて、Google Cloud に向けた HTTP リクエストの HTTP ヘッダに特定の Key-Value を強制的に追加することで、そこで指定された組織 ID 以外の組織にアクセスできなくさせます。 詳細は以下の公式ドキュメントをご参照ください。 参考 : 組織内アクセス制限の概要 また実際の利用方法については、以下の当社記事もご参照ください。 blog.g-gen.co.jp 関連する Google Cloud サービス Identity and Access Management(IAM) 組織と最も関連が強い Google Cloud サービスとして、 Identity and Access Management (IAM)が挙げられます。 IAM は Google Cloud の認証・認可を担う仕組みであり、Google Cloud におけるセキュリティの最も基本的な要素です。 IAM 権限はリソースに対して設定されます。上位リソースに対して設定された権限は、下位リソースに 継承 されます。組織トップノードに設定された権限は最下位レベルのリソースである VM や BigQuery テーブル等にまで継承されます。この仕組みを使って、プロジェクトをフォルダ分けしたうえで、フォルダ単位で権限を付与するなどして、権限管理の運用を効率化し、よりセキュアに運用できるようになります。 IAM の仕組みについては、以下の記事を参照してください。 blog.g-gen.co.jp 階層型ファイアウォールポリシー Google Cloud の VPC にはファイアウォールの仕組みがあります。ファイアウォールは通常、プロジェクトの VPC ネットワークのレベルで管理しますが、 階層型ファイアウォールポリシー を使うと、組織の上位から下位に向けてファイアウォールのルールを継承させることができます。 例えば、組織ルートもしくは上位フォルダで「TCP ポート 22 番は 拒否」というポリシーを作れば、下位のプロジェクトの VPC ファイアウォールルールで許可したとしても、パケットは拒否されます。 ファイアウォールポリシーの詳細については、以下の記事を参照してください。 blog.g-gen.co.jp VPC Service Controls VPC Service Controls は、Google Cloud サービスの API を保護する仕組みです。 接続元 IP アドレスやデバイス情報等のコンテキストに基づいて、Google Cloud プロジェクトへのアクセスを制御できます。 VPC Service Controls の設定自体の管理権限は、アクセスポリシーというリソースにより、フォルダもしくはプロジェクトの範囲に限定できます。これによりフォルダやプロジェクトの管理者に、VPC Service Controls の設定権限を委任することができます。 VPC Service Controls の詳細については、以下の記事を参照してください。 blog.g-gen.co.jp ベストプラクティス フォルダ構造の決め方 組織配下ではフォルダを使って Google Cloud プロジェクトを整理することができますが、どのようなフォルダ構成とすべきかは悩みの種です。しかしながら、どのような環境でも通用するような 絶対の答えはありません 。 「IAM」「組織のポリシー」「階層型ファイアウォール」など、継承によって影響範囲が決まる機能を考慮に入れ、どの単位でまとめて管理したいか、という観点でフォルダ構成を決めます。 一例として、以下の図は 組織 > 環境別フォルダ > システム別フォルダ > 機能別プロジェクト という構成になっています。 組織>環境別フォルダ>システム別フォルダ>機能別プロジェクト この会社、あるいは官公庁では、クラウドを横断管理する CCoE チームが存在し、本番環境には本番環境のための統制を、開発環境には開発環境のための統制を、というように環境別に異なったセキュリティポリシーを割り当てるために、このような構成になっています。 別のパターンでは、以下のような構成も取ることができます。以下の例は 組織 > システム別フォルダ > 環境別フォルダ > 機能別プロジェクト という構成になっています。 組織>システム別フォルダ>環境別フォルダ>機能別プロジェクト この環境では、それぞれのシステムはそれぞれの部門が管理していて、あるシステムでは「本番環境」「開発環境」の2面しか用意されていないが、別の部門の別のシステムでは「本番」「UAT」「テスト」「開発」の4面構成であり、セキュリティポリシーも異なる...というように、システムごとに統制の設計が大きく異なります。そのため、システム別フォルダ以下において、担当部署に IAM 権限を与えて、裁量を持たせています。 上記の例でも分かるように、組織の構成を設計する際は、クラウド環境がどのような運用体制でどのように統制されるか、という観点を持つ必要があります。 組織作成直後にやるべきこと 組織作成直後には、組織トップノードの IAM 許可ポリシーにおいて、以下のロールバインディングが存在します。 プリンシパル ロール (組織のドメイン名) プロジェクト作成者( roles/resourcemanager.projectCreator ) (組織のドメイン名) 請求先アカウント作成者( roles/billing.creator ) (組織のドメイン名) とは、その 組織のドメインの Google アカウント全員に 権限が与えられていることを意味します。組織レベルの IAM 画面では、以下のように表示されます。 ドメインの全アカウントに対してプロジェクト作成者が割り当てられている この状態だと、組織の すべてのアカウント がプロジェクトを作成したり、請求先アカウントを作成できます。この状態では、クラウド管理者が気づかないうちに「野良アカウント」が作成できてしまいます。クラウド環境を集中管理するのであれば、この IAM ロールバインディングは削除するべきといえます。 以下のように My First Project というプロジェクトが乱立している状態を見たことがあるでしょうか。 My First Project が乱立 前掲のように組織のアカウントの全員がプロジェクト作成権限を持っている場合、Google Cloud 画面に初めてアクセスした際や、チュートリアルのドキュメントからコンソール画面に遷移した際に、My First Project という名称のプロジェクトが自動的に作成されてしまいます。前述の、プロジェクト作成者ロールの割り当てが削除してあれば、この状況は起こりません。 以下の記事もご参照ください。 blog.g-gen.co.jp その他のベストプラクティス 以下の公式ドキュメントで、Google Cloud 組織のベストプラクティスが紹介されています。 組織の構成、アカウント管理、ネットワークセキュリティ、ロギング、請求などに関して重要な観点が示唆されていますので、ぜひご確認ください。 参考 : エンタープライズ企業のベスト プラクティス また上記のドキュメントで言及されている Google Cloud の機能の多くが、当社記事によって解説されています。記事を見つけるために、以下のリンク集もご活用ください。 参考 : Google Cloud サービスカット学習コンテンツ集 セキュリティスイート for Google Cloud 組織リソース構成や組織のポリシーを含む、Google Cloud の推奨セキュリティ設定を提供する G-gen のサービスである「セキュリティスイート for Google Cloud」について、以下の記事をぜひご参照ください。 blog.g-gen.co.jp 杉村 勇馬 (記事一覧) 執行役員 CTO / クラウドソリューション部 部長 元警察官という経歴を持つ現 IT エンジニア。クラウド管理・運用やネットワークに知見。AWS 12資格、Google Cloud認定資格11資格。X (旧 Twitter) では Google Cloud や AWS のアップデート情報をつぶやいています。 Follow @y_sugi_it
アバター