TECH PLAY

株式会社G-gen

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

744

G-gen の佐々木です。当記事では Google Cloud ( 旧称 GCP ) の Google Kubernetes Engine ( GKE ) で作成した Ingress に対して、 Cloud Armor セキュリティポリシー を構成することで、ワークロードに対するアクセス元 IP アドレス制限を実装します。 前提知識 Google Kubernetes Engine とは Ingress とは Cloud Armor とは GKE クラスタの準備 GKE クラスタにサンプルアプリケーションをデプロイする Ingress を作成してアプリケーションを公開する Cloud Armor のセキュリティポリシーを作成する BackendConfig を作成する Service を更新して BackendConfig に紐づける 許可していない IP アドレスからアクセスしてみる GKE & Cloud Armor 前提知識 Google Kubernetes Engine とは Google Kubernetes Engine (以下、GKE ) は、Google Cloud のインフラストラクチャ上に構築された マネージドな Kubernetes クラスタ を利用することができるサービスです。 サービスの詳細は以下の記事で解説しています。 blog.g-gen.co.jp Ingress とは Ingress は Kubernetes の API リソースの 1 つで、Kubernetes クラスタ内のワークロードに対する L7 ロードバランシングを提供します。 Ingress は Service に関連付けることで、その背後にある Pod にデプロイされたアプリケーションを外部に公開することができます。 GKE では、組み込みのコントローラである GKE Ingress Controller によって、Kubernetes によって管理される HTTP(S) ロードバランサを GKE クラスタ外に作成することができます。 Ingressを用いたL7ロードバランサによるワークロードの外部公開 当記事では省略しますが、Ingress によって作成される HTTP(S) ロードバランサで HTTPS を使用する場合、Ingress と SSL 証明書を紐づける設定をする必要があります。 詳細な手順については以下の記事をご一読ください。 blog.g-gen.co.jp Cloud Armor とは Cloud Armor は Google 製のクラウド型 WAF であり、Cloud Armor の セキュリティポリシー を HTTP(S) ロードバランサのバックエンドに紐づけることで、バックエンドに対するアクセス元の制限をかけることができます。 Cloud Armor の詳細については、以下の記事で解説しています。 blog.g-gen.co.jp GKE クラスタの準備 当記事では Autopilot モードの限定公開クラスタを使用していきます。 クラスタの作成方法については 公式ドキュメント 、または Terraform を使用した以下の記事を参照してください。 blog.g-gen.co.jp GKE クラスタにサンプルアプリケーションをデプロイする Google Cloud が提供するサンプルのコンテナイメージを使用し、アプリケーションを GKE クラスタに登録します。 マニフェストファイルは以下のようになります。 # deployment.yaml apiVersion : apps/v1 kind : Deployment metadata : name : hello namespace : default spec : replicas : 3 selector : matchLabels : app : hello template : metadata : labels : app : hello spec : containers : - name : hello image : us-docker.pkg.dev/google-samples/containers/gke/hello-app:1.0 ports : - containerPort : 8080 protocol : TCP 続いて、アプリケーションを公開するための Service を GKE クラスタに登録します。 Service のマニフェストファイルは後に編集するため、当記事ではファイル名を service.yaml と定めます。 # service.yaml apiVersion : v1 kind : Service metadata : name : hello namespace : default annotations : cloud.google.com/neg : '{"ingress": true}' spec : ports : - port : 8080 protocol : TCP targetPort : 8080 selector : app : hello type : NodePort それぞれのプロビジョニングが完了するまで待ちます。 $ kubectl get deployments hello NAME READY UP-TO-DATE AVAILABLE AGE hello 3 / 3 3 3 15m $ kubectl get services hello NAME TYPE CLUSTER-IP EXTERNAL-IP PORT ( S ) AGE hello NodePort 172 . 31 . 236 . 34 < none > 8080:32678/TCP 10m Ingress を作成してアプリケーションを公開する まずは Cloud Armor のセキュリティポリシーを構成せずに Ingress を作成してみます。 この Ingress によって作成される HTTP(S) ロードバランサはアクセス元の制限がされておらず、ロードバランサの IP アドレスを知っていれば誰でもアプリケーションにアクセスすることができてしまいます。 # ingress.yaml apiVersion : networking.k8s.io/v1 kind : Ingress metadata : name : hello-ingress namespace : default annotations : kubernetes.io/ingress.class : "gce" # 外部 HTTP(S) ロードバランサを指定 spec : defaultBackend : service : name : hello port : number : 8080 Ingress によって作成された HTTP(S) ロードバランサの外部 IP アドレスを確認し、ブラウザからアクセスしてみます。 $ kubectl get ingresses hello-ingress NAME CLASS HOSTS ADDRESS PORTS AGE hello-ingress < none > * 34 . 117 . 255 . 169 80 19m サンプルアプリケーションの画面 Cloud Armor のセキュリティポリシーを作成する アプリケーションに対するアクセス元 IP アドレスを制限するセキュリティポリシーを作成します。 ここで設定したセキュリティポリシーの名前は後で使用します。 セキュリティポリシーは Cloud Armor の機能ですが、 gcloud compute コマンドグループを使用して作成、編集を行うことができます。 当記事では hello-sec-policy という名前のセキュリティポリシーを作成します。 # セキュリティポリシーの作成 $ gcloud compute security-policies create hello-sec-policy ポリシーのデフォルトルール(優先度 2,147,483,647 )を更新し、すべてのアクセス元を拒否します。 # デフォルトルールの更新 $ gcloud compute security-policies rules update 2147483647 \ --security-policy hello-sec-policy \ --action " deny-403 " 特定の IP アドレス範囲からのアクセスを許可するルールを追加します。 ルールの優先度はデフォルトのルールより高ければよいので、ここでは優先度 1000 で設定します。 # 許可ルールの追加 $ gcloud compute security-policies rules create 1000 \ --security-policy hello-sec-policy \ --src-ip-ranges " <許可する IP アドレス範囲> " \ --action " allow " BackendConfig を作成する Ingress に対して Cloud Armor セキュリティポリシーを紐づけるため、GKE のカスタムリソースである BackendConfig リソースをクラスタに登録します。 BackendConfig リソースを使用することで、セキュリティポリシーだけではなく、Cloud CDN や Identity-Aware Proxy など、HTTP(S) ロードバランサで利用できる様々な機能を Kubernetes から有効化することができます。 BackendConfig のマニフェストファイルでは、先ほど作成したセキュリティポリシーの名前を使用します。 # backendconfig.yaml apiVersion : cloud.google.com/v1 kind : BackendConfig metadata : name : hello-backend-config spec : securityPolicy : name : hello-sec-policy # 作成したセキュリティポリシーの名前 Service を更新して BackendConfig に紐づける BackendConfig は、Ingress に対してではなく、HTTP(S) ロードバランサのバックエンドとなる Service に対して紐づけます。 バックエンドが使用するポートと BackendConfig を組み合わせた cloud.google.com/backend-config アノテーションを service.yaml に追記します。 annotations のキー 値 cloud.google.com/backend-config '{"ports": {"8080":"hello-backend-config"}}' # service.yaml apiVersion : v1 kind : Service metadata : name : hello namespace : default annotations : cloud.google.com/backend-config : '{"ports": {"8080":"hello-backend-config"}}' # 追記 cloud.google.com/neg : '{"ingress": true}' spec : ports : - port : 8080 protocol : TCP targetPort : 8080 selector : app : hello type : NodePort 許可していない IP アドレスからアクセスしてみる Ingress によって作成された HTTP(S) ロードバランサに Cloud Armor セキュリティポリシーが構成されたので、ポリシーで許可していない IP アドレス範囲からのアクセスが拒否されることを確認します。 ブラウザで、HTTP(S) ロードバランサの外部 IP アドレスに再度アクセスしてみます。 許可されていない IP アドレス範囲からのアクセスは拒否される 最後に、ポリシーで許可された IP アドレスからアクセスし、サンプルアプリケーションの画面が表示されることを確認します。 許可された IP アドレス範囲からのアクセス 佐々木 駿太 (記事一覧) G-gen最北端、北海道在住のクラウドソリューション部エンジニア 2022年6月にG-genにジョイン。Google Cloud Partner Top Engineer 2024に選出。好きなGoogle CloudプロダクトはCloud Run。 趣味はコーヒー、小説(SF、ミステリ)、カラオケなど。 Follow @sasashun0805
アバター
こんにちは!G-genで営業をしている遠目塚です。当記事では、Google Cloud (旧称 GCP) のノーコードツールである Google AppSheet (以下AppSheet) を Google Sheets (以下Sheets) から簡単に作成する方法をご紹介します。 AppSheetの概要 AppSheetの始め方 アプリ化したい業務を考えてみよう こんな業務ありませんか? アプリをつくってみよう Sheetsに紙で入力している項目を洗い出してみよう 入力の簡素化についても考えてみる Sheetsの画面上から簡単にアプリ作成ができる! 設定を変更してみよう TYPEの変更 シート同士(テーブル)を連携させよう アプリ操作を確認する 便利な機能、「エミュレータ」 アプリで入力した項目が、反映されているか確認する AppSheetの概要 ノーコードでモバイルアプリケーションやウェブアプリケーションを構築することができるサービスです。 AppSheetの始め方 AppSheetの概要およびはじめ方については以下の記事で解説しています。 blog.g-gen.co.jp アプリ化したい業務を考えてみよう こんな業務ありませんか? 現場で紙で記録して、システムに再入力している 拠点からFAXで報告を受け、本社で基幹システムに再入力している 専用のシステムを構築するまでもないけれども、手間がかかっているので簡素化したい!そんな業務をアプリ化してみましょう。 アプリをつくってみよう Sheetsに紙で入力している項目を洗い出してみよう Sheetsをアプリのデータソースにすることができます。今回は「棚卸業務」を想像して項目を洗い出してみました。 シート名がアプリで利用するテーブル名になりますので、わかりやすいものに変更します。 入力の簡素化についても考えてみる 商品名を手入力してしまうと、間違いの元になるため今回は「商品名」のシートを別で作成してみました。 表計算ソフトのLUUKUPのようなイメージです。 Sheetsの画面上から簡単にアプリ作成ができる! 上部のメニュー一覧の「拡張機能」を選択すると「AppSheet」が表示されますので「アプリを作成」をクリックします。 アプリ作成中です AppSheetが表示され、アプリができました! 設定を変更してみよう 今回は、一部AppSheet上での設定変更についてご紹介します。詳細は今後ブログで発信していくので割愛させていただきます。 TYPEの変更 日付はカレンダー形式で入力出来たほうがよいので、「TYPE」を「Data型(日付)」に変更します シート同士(テーブル)を連携させよう 商品名の入力を簡素化するために、Sheets上に「商品名」のシートを準備していました。 まだテーブルとして追加されていないので、追加します。 画面左上の「+」ボタンをクリックします。 データソースの追加画面が表示されますので、画面下の「Add Table "商品名"」を選択します。 直感的でわかりやすいですね! 画面右下の「Add This Table」を選択します。 テーブルとして追加されました。 棚卸し業務テーブルの商品名の設定詳細を見てみましょう、先程までTYPEが「Text」だったのが「ref」に変わっています! NAME:商品名の左にある「edit」を選択してください。 SorceTableが自動的に「商品名」となっています。これでアプリで入力する際にあらかじめ準備された項目から商品名を選択できます。 アプリ操作を確認する 便利な機能、「エミュレータ」 画面右側の「エミュレータ」で、実際のアプリの操作を確認することができます。エミュレータの右下の、「Add」を選択します。 カレンダー入力ができます。 商品名もプルダウンで選ぶことができました! 必要事項を入力して「Save」を選択しましょう。 アプリで入力した項目が、反映されているか確認する データソースである Sheets を確認すると・・・? 入力項目が、反映されました! 今回は、Sheetsから簡単に棚卸しアプリを作成する方法を紹介しました。今後はもっと細かい機能やアプリ作成方法についても紹介していきます。 遠目塚美優希 (記事一覧) ビジネス推進部 G-genに飛び込んで、GoogleCloudの営業として日々奮闘中! 福岡でフルリモートワーク勤務、在宅なのに毎朝お手製弁当を作成中
アバター
当記事は みずほリサーチ&テクノロジーズ × G-gen エンジニアコラボレーション企画 で執筆されたものです。 当社では業務系システムを開発する機会が多く、定期的に実行する処理が多くあります。慣れ親しんだCron式でジョブの実行定義ができる Cloud Scheduler は利用機会も多く、優先的に仕様を調査することにしました。 G-gen の佐々木です。当記事では Google Cloud (旧称 GCP) のフルマネージドの cron ジョブスケジューラである Cloud Scheduler を解説します。 Cloud Scheduler とは ユースケース cron ジョブのターゲット cron ジョブスケジュールの設定 日時フィールド 特殊文字 設定例 ターゲットの認証 ジョブ失敗時の対処 ジョブ失敗の通知 失敗したジョブの再実行 使用上の注意点 サマータイムによる影響 べき等性を考慮する 料金 Cloud Scheduler とは Cloud Scheduler は サーバーレスなジョブスケジューラサービスです。 Google Cloud コンソールや gcloud コマンドを使用してジョブのスケジュールとターゲットを指定するだけで cron ジョブを簡単に作成することができ、一括で管理することができます。 ジョブ実行のためのインフラストラクチャのプロビジョニングや運用管理をする必要はありません。 ユースケース Cloud Scheduler は、cron ジョブを使用して Cloud Functions や Cloud Run などのコンピューティングプロダクトや、Pub/Sub のようなメッセージングのプロダクトにリクエストを送信することができます。 一般的なユースケースとしては、 cron ジョブを起点とした Cloud Functions によるバッチ処理 があります。 たとえば、Cloud Functions から他の Google Cloud プロダクトの API を使用することで、以下のような処理を定期実行タスクとして実装することができます。 Cloud SQL や Cloud Storage にあるデータを読み込んで変換し、BigQuery に書き込む ( ETL 処理 ) Compute Engine インスタンスのマシンイメージを取得する ( バックアップ ) Web サイトから収集したデータを Cloud Storage や BigQuery に出力する ( Web スクレイピング ) すべてのコンポーネントにサーバーレスのプロダクトを使用するため、インフラストラクチャの運用管理をすることなく定期バッチ処理を実装することができます。 Cloud Scheduler の一般的なユースケース cron ジョブのターゲット cron ジョブのターゲットには、以下のタイプが指定できます。 一般公開されている HTTP/S エンドポイント Cloud Pub/Sub トピック Google App Engine の HTTP/S アプリケーション Cloud Pub/Sub トピックを cron ジョブのターゲットとして利用することで、Pub/Sub サブスクライバーとなる Cloud Functions や Cloud Run にリクエストを送信することができ、定期実行タスクの柔軟性がかなり高まります。 なお、Cloud Functions や Cloud Run では HTTPS エンドポイントが提供されるため、Pub/Sub を間に挟むことなく使用することもできます。 しかし、Pub/Sub を使用することで、cron ジョブと実際の処理部分を疎結合にすることができ、同じ cron ジョブから複数のサブスクライバーにリクエストを送信するなど、処理の柔軟性が向上します。 また、Pub/Sub ターゲットを使用する場合のみ、ジョブを VPC Service Controls の境界に含めることができます。 cron ジョブスケジュールの設定 Cloud Scheduler では、cron ジョブを unix-cron 形式 で指定します。 日時フィールド cron 形式は、空白で区切られた 5 つの日時フィールドで構成されます。 cron 形式の 5 つの日時フィールド フィールド 有効な値 分 0~59 時間 0~23 日 1~31 月 1~12 ( JAN ~ DEC でも可 ) 曜日 0~6 ( SUN ~ SAT でも可。また日曜日は 7 でも可 ) 特殊文字 日時フィールドには先述した有効な値のほか、特殊文字を使用することができます。 特殊文字 説明 * (アスタリスク) 日時フィールドに設定できる すべての値 を表します。たとえば、曜日フィールドに * を設定した場合、 日曜日~土曜日までの全ての曜日 を意味します。 - (ハイフン) 範囲 を指定することができます。たとえば、月フィールドに 4-8 を設定した場合、 4 月から 8 月までの毎月 ( 8 月も含む ) を意味します。 /{数値} 範囲の指定とともに使用することで、指定した範囲内での 実行間隔 (ステップ) を指定することができます。たとえば、時間フィールドに 0-23/2 を設定した場合、ジョブは 0,2,4,6,8,10,12,14,16,18,20,22 時 に実行されます。 , (カンマ) 値の リスト を使用することができます。たとえば、日フィールドに 7,17,27 を設定した場合、 7 日、17 日、27 日 にジョブが実行されます。 設定例 設定したいスケジュール 値 毎週月曜日の午前 9 時 0 9 * * 1 毎平日の午前 0 時 30 分 30 0 * * 1-5 毎月 5, 15, 25 日の午後 6 時 0 18 5,15,25, * * 毎年 8 月 5 日 の午後 9 時 0 21 5 8 * ターゲットの認証 ターゲットに HTTP/S エンドポイントを使用し、認証が必要なターゲットを Cloud Scheduler から呼び出す場合、ジョブに対してサービスアカウントを設定します。 たとえば、認証が必要な Cloud Functions の HTTPS エンドポイントをターゲットとする場合、 Cloud Functions 起動元 ロール ( roles/cloudfunctions.invoker ) を紐付けたサービスアカウントをジョブに設定します。 Pub/Sub をターゲットとして使用する場合、Cloud Scheduler ジョブに対しては追加の権限を設定する必要はありません。 Pub/Sub 経由で Cloud Functions や Cloud Run などを使用する場合は、サブスクリプションに対してサービスアカウントを設定します。 ジョブ失敗時の対処 ジョブ失敗の通知 Cloud Scheduler ジョブの実行は、デフォルトで Cloud Logging に記録されます。 Cloud Logging に記録されたジョブのエラーログを ログベースの指標 として Cloud Monitoring で取得し、 アラートポリシー を設定することで、メールや Slack による通知や、Pub/Sub を使用した後続処理などを行うことができます。 失敗したジョブの再実行 Cloud Scheduler のジョブ実行が失敗した場合、 デフォルトでは再実行されません。 ジョブの再実行が必要な場合、最大実行回数 (1~5) や最大実行時間 (1s ~ 実質無制限)、再実行間隔などを指定し、 指数バックオフ による再実行を設定することができます ( 参考 )。 また、ジョブはコンソールや gcloud コマンドから手動で実行することも可能です。 使用上の注意点 サマータイムによる影響 ジョブの実行時間を設定する際、基準となるタイムゾーンを選択することができますが、 一部のタイムゾーンにおいて、夏時間の影響でジョブが予期せず実行される、もしくはジョブが実行されない可能性があります。 したがって、基本的には 夏時間のないタイムゾーン を選択します。 ドキュメントでは UTC を選択することが推奨されていますが、 JST もサマータイムの影響を受けないため、いずれかのタイムゾーンを選択するとよいでしょう。 べき等性を考慮する Cloud Scheduler では最低 1 回の実行 ( At-least Once ) を保証しており、そのため同じ cron ジョブが複数回実行されることが稀にあります。 したがって、cron ジョブを起点とする処理全体を べき等 に実装する必要があります。 ジョブの作成時に ヘッダー を設定し、ジョブ名や cron で設定した実行時刻をターゲットへのリクエストに含めることで、リクエストの重複削除に役立てることができます ( 参考 )。 料金 Cloud Scheduler の料金は、 ジョブの実行回数ではなく、ジョブの個数で決定されます。 1 日あたり $0.003 課金され、たとえばジョブを作成して 10 日後に削除した場合は $0.03 の課金となります。 無料枠はプロジェクト単位ではなく、請求先アカウント単位で集計される点に注意しましょう。 ジョブの料金 無料枠 ジョブあたり $0.10 / 月 請求先アカウントあたり 3 つのジョブ 佐々木 駿太 (記事一覧) G-gen最北端、北海道在住のクラウドソリューション部エンジニア 2022年6月にG-genにジョイン。Google Cloud Partner Top Engineer 2025 Fellowに選出。好きなGoogle CloudプロダクトはCloud Run。 趣味はコーヒー、小説(SF、ミステリ)、カラオケなど。 Follow @sasashun0805
アバター
G-gen 又吉です。今回は BigQuery に備わる機械学習機能である BigQuery ML で、2 項ロジスティック回帰を用いた分類モデルを作成してみました。 BigQuery ML とは サポートされているモデル 今回使用するデータ 準備 各種ファイルのアップロード データセットの作成 テーブルの作成 テーブルにデータをロード データの確認 モデルの作成 データの前処理を行います。 モデル構築 予測 評価 BigQuery ML とは BigQuery ML とは、BigQuery 上で機械学習モデルを作成、評価、実行ができるGoogle Cloud の機械学習プロダクトのひとつです。 通常、機械学習を行うには機械学習フレームワークに対する高度なプログラミングスキルと知識を必要としますが、BigQuery ML を用いれば SQL を用いて機械学習の一連の工程を実装できます。 BigQuery 自体はフルマネージドサービスであり、また BigQuery をデータウェアハウス (DWH) として使われている場合はデータをエクスポートする必要がないため機械学習モデルの開発効率を向上させることができます。 以下の記事では BigQuery ML を詳細に解説していますので、ご参照ください。 blog.g-gen.co.jp サポートされているモデル BigQuery ML では、2023年3月現在以下のモデルをサポートしています。 Linear regression Binary logistic regression Multiclass logistic regression K-means clustering Matrix Factorization Time series Boosted Tree Deep Neural Network Vertex AI AutoML Tables TensorFlow model importing Autoencoder 今回使用するデータ 今回使用するデータは、 kaggle で提供されている Loan Approval Prediction の Traning Dataset.csv を使用します。 このデータセットは、住宅ローン全般を取り扱っている会社がモデルとなっており、その会社でローンの審査を受ける個人の情報 (性別、婚姻状況、教育、扶養家族の数、収入、ローン金額、信用履歴など) と「実際にローンの審査が通ったのか否か」が記載されています。 今回は、BigQuery ML を用いて「各個人の情報と審査の結果を 教師データ として、ローンの審査が通るか通らないかを予測する分類モデル」を作成したいと思います。 準備 準備の工程は Cloud Shell 環境にて gcloud コマンドを用いて行います。 各種ファイルのアップロード 先程ダウンロードした Traning Dataset.csv データを、traning_dataset.csv にリネームし、 Cloud Shell にアップロードします。 また、schema.json というファイルを新規作成し、以下を記入します。 [ { " name ": " Loan_ID ", " type ": " STRING ", " mode ": " Required ", " description ": " Unique Loan ID " } , { " name ": " Gender ", " type ": " STRING ", " mode ": " Nullable ", " description ": " Male/ Female " } , { " name ": " Married ", " type ": " BOOLEAN ", " mode ": " Nullable ", " description ": " Applicant married (Y/N) " } , { " name ": " Dependents ", " type ": " STRING ", " mode ": " Nullable ", " description ": " Number of dependents " } , { " name ": " Education ", " type ": " STRING ", " mode ": " Nullable ", " description ": " Applicant Education (Graduate/ Under Graduate) " } , { " name ": " Self_Employed ", " type ": " BOOLEAN ", " mode ": " Nullable ", " description ": " Self-employed (Y/N) " } , { " name ": " ApplicantIncome ", " type ": " INTEGER ", " mode ": " Nullable ", " description ": " Applicant income " } , { " name ": " CoapplicantIncome ", " type ": " FLOAT ", " mode ": " Nullable ", " description ": " Coapplicant income " } , { " name ": " LoanAmount ", " type ": " INTEGER ", " mode ": " Nullable ", " description ": " Loan amount in thousands " } , { " name ": " Loan_Amount_Term ", " type ": " INTEGER ", " mode ": " Nullable ", " description ": " Term of the loan in months " } , { " name ": " Credit_History ", " type ": " INTEGER ", " mode ": " Nullable ", " description ": " Credit history meets guidelines " } , { " name ": " Property_Area ", " type ": " STRING ", " mode ": " Nullable ", " description ": " Urban/ Semi-Urban/ Rural " } , { " name ": " Loan_Status ", " type ": " BOOLEAN ", " mode ": " Nullable ", " description ": " (Target) Loan approved (Y/N) " } ] Cloud Shell 上のディレクトリが以下のように構成できれば必要なファイルの準備は揃っています。 root ├── training_dataset.csv └── schema.json データセットの作成 BigQuery のデータセットを作成します。 bq mk --dataset --location=asia-northeast1 <DATASET_NAME> DATASET_NAME:データセット名 テーブルの作成 BigQuery のテーブルを作成します。 bq mk --table <PROJECT_ID>:<DATASET_NAME>.<TABLE_NAME> schema.json PROJECT_ID:プロジェクト ID TABLE_NAME:テーブル名 テーブルにデータをロード 作成したテーブルにデータをロードします。 bq --location=asia-northeast1 load \ --source_format=CSV \ --skip_leading_rows=1 \ <PROJECT_ID>:<DATASET_NAME>.<TABLE_NAME> training_dataset.csv データの確認 BigQuery を開いてクエリエディタで以下 SQL を実行します。(614行/13列) SELECT * FROM `<PROJECT_ID>.<DATASET_NAME>.<TABLE_NAME>` クエリ結果 モデルの作成 データの前処理を行います。 ここからは BigQuery のコンソール画面 (クエリエディタ) から作業を行います。 まずはデータの前処理を行い、前処理を終えたデータをビューとして定義します。 CREATE OR REPLACE VIEW `<PROJECT_ID>.<DATASET_NAME>.<TABLE_NAME>` AS SELECT  * EXCEPT(Loan_ID), CASE WHEN MOD ( ABS (FARM_FINGERPRINT(Loan_ID)), 10 ) < 8 THEN ' training ' WHEN MOD ( ABS (FARM_FINGERPRINT(Loan_ID)), 10 ) >= 8 THEN ' prediction ' END AS dataframe FROM `<PROJECT_ID>.<DATASET_NAME>.<TABLE_NAME>` WHERE Gender is not null AND Married is not null AND Dependents is not null AND Education is not null AND Self_Employed is not null AND ApplicantIncome is not null AND CoapplicantIncome is not null AND LoanAmount is not null AND Loan_Amount_Term is not null AND Credit_History is not null AND Property_Area is not null AND Loan_Status is not null 「Loan_Status」列には、 True (審査が通った) と False (審査に落ちた) の 2 つの値が入っており、「Loan_Status」列と 関連性のある属性 を用いてモデルを構築します。 したがって、「Loan_ID」列は一意の値を持ち「Loan_Status」列とは関係がないため除外してます。 また、トレーニングデータと予測データを分けるために、「dataframe」列を作成しています。 最後に、各列に null が含まれる行を除外しています。 前処理を終えると 480行13列 のデータになります。 「dataframe」列を元に、トレーニングデータと予測データの割合は以下のようになっています。 No 種別 数量 全体 (480) に占める割合 [%] 1 training 360 75 % 2 prediction 120 25 % モデル構築 モデル構築を行います。 CREATE OR REPLACE MODEL `<PROJECT_ID>.<DATASET_NAME>.<TABLE_NAME>` OPTIONS ( model_type= ' LOGISTIC_REG ' , auto_class_weights= TRUE , data_split_method= ' NO_SPLIT ' , enable_global_explain= TRUE , input_label_cols=[ ' Loan_Status ' ]) AS SELECT * EXCEPT(dataframe) FROM `<PROJECT_ID>.<DATASET_NAME>.<TABLE_NAME>` WHERE dataframe = ' training ' CREATE OR REPLACE MODEL 構文を用いてモデルを構築していきます。 OPTIONS の中に各種パラメータを定義します。 model_type には [LOGISTIC_REG] (ロジスティック回帰モデル) を選択します。 デフォルトでは、ロジスティック回帰モデルの作成に使用されるトレーニング データは重み付けされていないため、 auto_class_weights を True に設定することでラベルに偏りが出てもバランスを取ってくれます。 data_split_method はトレーニングデータと評価データでデータを分けることができますが、今回は「dataframe」列を追加しデータ分割をしているため、明示的に不要としています。 enable_global_explain はモデルに対するグローバルな特徴の重要性を評価するために必要です。本検証の最後に使用するため True にしておきます。 input_label_cols はトレーニングデータのラベル列名となるため、今回は「 Loan_Status 」を記入します。 トレーニングが完了すると、モデルの結果を確認していきます。 トレーニング結果には、今回使ったモデルの詳細やトレーニングオプションについて確認することができます。 トレーニング結果 モデルの評価を確認する前に分類モデルの評価指標について軽く触れておきます。 分類モデルは主に以下の 4 つの評価指標を持ちます。 真陽数 (True Positive : TP ):正解が「◯」のものを、「◯」と予測した回数 真陰数 (True Nagative : TN ) :正解が「✕」のものを、「✕」と予測した回数 偽陽数 (False Pasitive : FP ):正解が「✕」のものを、「◯」と予測した回数 偽陰数 (False Nagative : FN ):正解が「◯」のものを、「✕」と予測した回数 上記の評価指標の組み合わせから以下のような指標が算出できます。 No 用語 概要 1 適合率 (Precision) 「◯」と予測したデータのうち、正しく分類できたデータの割合。式は TP / (TP+FP) 2 再現率 (Recall) または 真陽性率 (True Positive Tate) 正解が「◯」だったデータのうち、正しく「◯」と予測できた割合。式は TP / (TP + FN) 3 偽陽性率 (False Positive Rate) 正解が「✕」だったデータを、誤って「◯」と予測した割合。 式は FP / (FP + TN) 3 精度 (Accuracy) 全データ数のうち、正しく分類できたデータ数の割合。よく正解率ともいいます。式は (TP + TN) / (TP + TN + FP + FN) 4 F1 スコア (f1-score) 適合率と再現率の調和平均。式は 2×適合率×再現率 / (適合率 + 再現率) 5 ログ損失 (Log loss) モデル予測とターゲット値の間の交差エントロピー。 値が小さいほど高品質のモデル であることを示す。 6 ROC AUC 横軸に偽陽性率、縦軸に真陽性率を取った曲線の下の面積。0 ~ 1 の範囲を取り、 値が高いほど高品質 のモデルであることを示す。 今回のモデルの評価を確認してみます。 値が 0 に近ければ高品質とされる LogLoss が 0.4766 をとり、0 ~ 1 の範囲で値が高いほど高品質とされる ROC AUC は 0.8009 をとっております。 モデルの評価① モデルの評価② また、しきい値を変えることで適合率や再現率を調整することができます。 適合率は正解が「◯」を「✕」として分類してもよいが、正解が「✕」のものを確実に「✕」として分類したい場合に有用なので、今回のようなローンの審査において結果が False のものは極力 False と分類したいという要件があるなら重要な指標となるかと思います。 逆に再現率は正解が「✕」を「◯」と分類しても問題なく、かつ正解が「◯」は確実に「◯」として分類したい場合に有用なので、病気の診断などで再現率が重視されるケースが多いです。 このように、適合率と再現率は性質が異なるため、 ユースケースに合わせて調整 していく必要があります。 しかし、適合率と再現率はトレード・オフの関係であるため、適合率を極端に高めると再現率が極端に下がるのであまり良いモデルとは言えません。そこで、適合率と再現率の調和平均である F1 スコアも考慮しつつ 最適なしきい値 を決めていくことをおすすめします。 今回は特に要件がないため、しきい値を 0.5 として予測を行っていきたいと思います。 予測 以下の SQL を実行し予測を行います。 SELECT * FROM ML.PREDICT (MODEL `<PROJECT_ID>.<DATASET_NAME>.<TABLE_NAME>`, ( SELECT * EXCEPT(dataframe) FROM `<PROJECT_ID>.<DATASET_NAME>.<TABLE_NAME>` WHERE dataframe = ' prediction ' ), STRUCT( 0.5 as threshold) ) ML.PREDICT 関数 を用いて予測を行います。 STRUCT 型で threshold (しきい値) を 0.5 として設定しています。 元の列に加え、以下の 3 つの列が追加されます。 predicted_ label_column_name :ラベルの予測結果 predicted_ label_column_name _probs.label:ラベル (今回は True or False) predicted_ label_column_name _probs.prob:ラベルに対応する予測確率 結果は以下のようになりました。 予測結果 赤枠部分が今回の予測結果です。ローンの審査が通るか通らないかに対して、一番左の predicted_Loan_Status 列で、 True or False が表示されています。 このように過去の教師データをもとに、機械学習モデルで予測が簡単にできました。 評価 先程予測に用いたデータには正解のラベルもあるので、予測データに対して評価も行ってみます。 以下の SQL から評価を実行できます。 SELECT * FROM ML.EVALUATE (MODEL `<PROJECT_ID>.<DATASET_NAME>.<TABLE_NAME>`, ( SELECT * FROM `<PROJECT_ID>.<DATASET_NAME>.<TABLE_NAME>` WHERE dataframe = ' prediction ' ) STRUCT( 0.5 as threshold) ) ML.EVALUATE関数 で評価を行います。 評価結果 評価に用いる複雑な計算式が SQL 一発で出せるのは魅力的ですね。 正しく分類できたデータの割合を示す精度 (accuracy) が 81.7% でした。 他にも、適合率 (precision) や F1 スコア (f1_score) もともに 8 割を超えています。 最後に、特徴量の重要度スコアを見てみましょう。 SELECT *, ROUND (attribution * 100 / ( SELECT SUM (attribution) FROM ML.GLOBAL_EXPLAIN(MODEL `<PROJECT_ID>.<DATASET_NAME>.<TABLE_NAME>`) ), 1 ) AS percentage FROM ML.GLOBAL_EXPLAIN(MODEL `<PROJECT_ID>.<DATASET_NAME>.<TABLE_NAME>`) ML . GLOBAL_EXPLAIN関数 を用いて特徴量の重要度スコアを算出します。 特徴量の重要度スコアとは、各特徴量がどれほど影響を及ぼしているか数値化されています。 グローバル変数の説明 特に、以下の特徴量の影響が大きいようです。 Credit_History (Credit history meets guidelines) Property_Area (Urban/ Semi-Urban/ Rural) Married (Applicant married) いかがでしたでしょうか。BigQuery ML を使えば SQL を用いて、機械学習モデルの作成、予測、評価が簡単に実行できました。 又吉 佑樹 (記事一覧) クラウドソリューション部 はいさい、沖縄出身のクラウドエンジニア! セールスからエンジニアへ転身。Google Cloud 全 11 資格保有。Google Cloud Champion Innovator (AI/ML)。Google Cloud Partner Top Engineer 2024。Google Cloud 公式ユーザー会 Jagu'e'r でエバンジェリスト。好きな分野は生成 AI。 Follow @matayuuuu
アバター
G-gen の荒井です。今回はコスト削減にも繋がる、Compute Engine VM の自動起動・停止のスケジューリングについて紹介します。 概要 VM の起動・停止のスケジューリング メリット スナップショットへの影響 シャットダウン時の動作 制限事項 設定方法 Compute Engine サービスエージェントへの権限付与 スケジュール作成 スケジュールの編集 スケジュールへ VM をアタッチ 新規 VM 作成時にスケジュールをアタッチ インスタンスのデタッチ スケジュールの削除 ログの確認 参考リンク 概要 VM の起動・停止のスケジューリング Google Cloud の VM インスタンス(Compute Engine)では、VM インスタンスの起動・終了をスケジューリングすることができます。 スケジュールは、Google Cloud コンソールや gcloud コマンドラインで設定できます。ソースコードを記述する必要はありません。 設定するには、起動時間や停止時間を指定して インスタンススケジュール を作成します。その後、作成したインスタンススケジュールに対象の VM を紐づけます。 参考 : VM インスタンスの起動と停止をスケジュールする メリット Google Cloud をはじめとするパブリッククラウドでは、リソースを使用しただけ課金が発生する従量課金方式となっていることがほとんどです。 Compute Engine も、VM を起動した時間に応じた従量課金であり、インスタンスを起動したままだと、常に課金が発生します。業務時間外には VM をシャットダウンしておくことで、コスト削減を図ることができます。 スナップショットへの影響 VM をシャットダウンさせておくと心配されるのがスナップショット(バックアップ)ですが、Google Cloud のスナップショットは VM がシャットダウンされていても、正しく取得されます。 むしろ、VM を停止した状態でスナップショットを取得すれば、ファイルシステムに読み書きがない状態でバックアップを取るので、VM が起動している状態でスナップショットを取るよりも、確実な整合性が担保されます。 シャットダウン時の動作 Compute Engine VM を停止すると、Google Cloud から VM インスタンスの OS に ACPI シャットダウン信号が送信され、OS がシャットダウンを開始します。そのため物理サーバーで言う「電源を引き抜く」イメージではなく、OS からシャットダウンをさせるイメージになります。 シャットダウン動作に関する詳細については、以下をご参照ください。 参考 : VM を停止する 制限事項 インスタンススケジュールには、OS 上のプロセス(サービス)を順番に終了させるような機能はありません スケジュールのオペレーションは最大15分程度遅延することがあります 指定時間に VM を起動させたい、または停止したい場合、指定時間の15分程度前にスケジューリング設定をする必要があります また起動、終了のオペレーションは最短でも15分以上の間隔を空けてスケジューリングします 1つの VM に対して紐付けることができるインスタンススケジュールは、1つだけです 反対に、1つのインスタンススケジュールには最大1,000個の VM を紐付けることができます インスタンススケジュールの作成時に、リージョンを指定します。スケジュールと同じリージョンの VM を対象にできます その他、詳細な制限事項については以下をご参照ください。 参考 : 制限事項 設定方法 Compute Engine サービスエージェントへの権限付与 VM の自動起動・終了は Google Cloud のシステム専用のアカウントである サービスエージェント の認証情報で実行されます。そのため、まずサービスエージェントに必要な権限を付与します。 サービス エージェントの詳細は、以下の記事を参照してください。 blog.g-gen.co.jp Compute Engine のサービスエージェントの詳細は、以下を参照してください。 参考 : Compute Engine サービス エージェント 付与する権限は以下のとおりです。 compute.instances.start : VM インスタンスの起動をスケジュールします。 compute.instances.stop : VM インスタンスの停止をスケジュールします。 上記を包括する権限であればどういったものでも構いません。今回は事前定義ロールとして用意されている Compute インスタンス管理者(v1)(roles/compute.instanceAdmin.v1) を付与します。 Cloud Console > IAM と管理 > IAM をクリック Google 提供のロール付与を含める を有効にする service-(PROJECT_NUMBER)@compute-system.iam.gserviceaccount.com の 編集 をクリック Compute インスタンス管理者(v1)(roles/compute.instanceAdmin.v1) の事前定義ロールを追加し 保存 をクリック Compute インスタンス管理者(v1) が付与されていることを確認 スケジュール作成 Compute Engine > VMインスタンス をクリック インスタンス スケジュール をクリック スケジュールを作成 をクリック 「新しいスケジュールの作成」ウインドウで、パラメータを入力し 送信 をクリック スケジュールが作成されていることを確認 スケジュールの編集 現在のところ、作成済みのスケジュールを編集することはできません。 スケジュールを変更したい際は、既存のインスタンススケジュールを削除してから、インスタンススケジュールを新規に作成します。 なお、インスタンススケジュールを削除する前に、そのスケジュールが持つ VM との紐づきをすべて削除する必要があります。また、1つの VM には複数のスケジュールを紐付けられませんので、一度既存のスケジュールと紐づきを削除してから、新しいスケジュールを作成することになります。 スケジュールへ VM をアタッチ 作成したスケジュールをクリック スケジュールにインスタンスを追加 をクリック スケジュールに追加したい インスタンス を選択し 追加 をクリック ※ 追加したいインスタンスが表示されていない場合、スケジュールとインスタンスのリージョン(ゾーン)が一致しているか確認をしてください。 指定インスタンスが追加されていることを確認 新規 VM 作成時にスケジュールをアタッチ 新規 VM 作成の際にスケジュールをアタッチしたい場合、gcloud コマンドを使用します。Google Cloud コンソールでは、2025年2月現在、新規 VM の作成時にスケジュールを紐付けることはできません。 gcloud compute instances create (INSTANCE_NAME) \ --resource-policies=(SCHEDULE_NAME) \ [--zone=(ZONE)] \ [--image (IMAGE) | --image-family (IMAGE_FAMILY)] \ [--image-project (IMAGE_PROJECT)] ※ ( ) 内のパラメータは任意で指定します。 参考 : 新しい VM インスタンスの作成時にインスタンス スケジュールを接続する 参考 : gcloud compute instances create インスタンスのデタッチ 作成したスケジュールをクリック デタッチしたいインスタンスを選択し スケジュールからインスタンスを削除 をクリック スケジュールの削除 作成したスケジュールをクリック 削除 をクリック 削除 をクリック スケジュールが削除されていることを確認 ログの確認 インスタンススケジュールによる VM の起動・停止のログは、Cloud Logging のログエクスプローラーで確認することができます。 Google Cloud コンソールでログエクスプローラ画面へ遷移して、以下のクエリを実行します。 最後の行の (PROJECT_NUMBER) の部分は、プロジェクト番号に置き換えてください。プロジェクト ID ではなく、数字で表されるプロジェクト番号であることに注意してください。プロジェクト番号は、Google Cloud コンソールの「IAM と管理 > 設定」で確認できます。 ( protoPayload.methodName= " v1.compute.instances.stop " OR protoPayload.methodName= " v1.compute.instances.start " ) AND protoPayload.authenticationInfo.principalEmail= " service-(PROJECT_NUMBER)@compute-system.iam.gserviceaccount.com " 参考リンク Compute Engine に関する詳細は、以下の記事を参照してください。 blog.g-gen.co.jp blog.g-gen.co.jp 荒井 雄基 (記事一覧) クラウドソリューション部 オンプレ環境のネットワーク・サーバーシステムを主戦場としていたが、クラウド領域にシフト。 Google Cloud 認定資格 7冠 現在は Google Workspace を中心に企業の DX 推進をサポート。 最近頑張っていることは、子どもがハマっている戦隊モノの踊りを踊れるようになること。
アバター
G-gen の佐々木です。当記事では Google Cloud ( 旧称 GCP ) の Google Kubernetes Engine ( GKE ) で Ingress を使用し、 Google マネージド SSL 証明書が構成された HTTPS ロードバランサ を Kubernetes のリソースとして作成していきます。 前提知識 Google Kubernetes Engine とは Ingress とは GKE クラスタの準備 GKE クラスタにサンプルアプリケーションをデプロイする 証明書を使用せずに Ingress を作成する Google マネージド SSL 証明書を使用して Ingress を作成する 静的外部 IP アドレスを払い出す A レコードを登録する ManagedCertificate リソースを作成する 証明書を使用するように Ingress を更新する Google マネージド SSL 証明書のステータスを確認する サンプルアプリケーションに HTTPS でアクセスできることを確認する 前提知識 Google Kubernetes Engine とは Google Kubernetes Engine (以下、GKE ) は、Google Cloud のインフラストラクチャ上に構築された マネージドな Kubernetes クラスタ を利用することができるサービスです。 サービスの詳細は以下の記事で解説しています。 blog.g-gen.co.jp Ingress とは Ingress は Kubernetes の API リソースの 1 つで、Kubernetes クラスタ内のワークロードに対する L7 ロードバランシングを提供します。 Ingress は Service に関連付けることで、その背後にある Pod にデプロイされたアプリケーションを外部に公開することができます。 GKE では、組み込みのコントローラである GKE Ingress Controller によって、Kubernetes によって管理される HTTP(S) ロードバランサを GKE クラスタ外に作成することができます。 Ingressを用いたL7ロードバランサによるワークロードの外部公開 GKE クラスタの準備 当記事では Autopilot モードの限定公開クラスタを使用していきます。 クラスタの作成方法については 公式ドキュメント 、または Terraform を使用した以下の記事を参照してください。 blog.g-gen.co.jp GKE クラスタにサンプルアプリケーションをデプロイする gcloud container clusters get-credentials コマンドを使用して GKE クラスタに接続した後、アプリケーションを GKE クラスタにデプロイします。 ここでは、Google Cloud が提供するサンプルのコンテナイメージを使用します。 また、Ingress に紐づける Service も同時にデプロイしていきます。 マニフェストファイルは以下のようになります。 # workload.yaml apiVersion : apps/v1 kind : Deployment metadata : name : hello namespace : default spec : replicas : 3 selector : matchLabels : app : hello template : metadata : labels : app : hello spec : containers : - name : hello image : us-docker.pkg.dev/google-samples/containers/gke/hello-app:1.0 ports : - containerPort : 8080 protocol : TCP --- apiVersion : v1 kind : Service metadata : name : hello namespace : default annotations : cloud.google.com/neg : '{"ingress": true}' spec : ports : - port : 8080 protocol : TCP targetPort : 8080 selector : app : hello type : NodePort このマニフェストファイルにより、3 つの Pod を含む Deployment と、それらを公開するための Service ( NodePort ) が作成されます。 # デプロイ後 $ kubectl get deployments hello NAME READY UP-TO-DATE AVAILABLE AGE hello 3/3 3 3 3m45s $ kubectl get services hello NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE hello NodePort 172.31.113.220 <none> 8080:30989/TCP 4m24s 証明書を使用せずに Ingress を作成する まずは、最小限のマニフェストを使用して Ingress リソースを作成してみます。 ここで作成されるロードバランサは SSL 証明書が構成されておらず、HTTP トラフィック(ポート 80)を GKE クラスタ内のワークロードにルーティングします。 当記事では、Ingress リソースを作成するマニフェストファイルの名前を ingress.yaml とします。 # ingress.yaml apiVersion : networking.k8s.io/v1 kind : Ingress metadata : name : hello-ingress namespace : default annotations : kubernetes.io/ingress.class : "gce" # 外部 HTTP(S) ロードバランサを指定 spec : defaultBackend : service : name : hello port : number : 8080 # デプロイ後 $ kubectl get ingresses hello-ingress NAME CLASS HOSTS ADDRESS PORTS AGE hello-ingress <none> * 34.117.255.169 80 4m55s Ingress のデプロイが完了すると、Google Cloud コンソールで HTTP(S) ロードバランサが作成されていることを確認できます。 ここで作成されたロードバランサが使用できるプロトコルは HTTP のみ となっています。 Ingress から作成された HTTP(S) ロードバランサ ブラウザでロードバランサの IP アドレスにアクセスすると、サンプルアプリケーションのページが表示されます。 サンプルアプリケーションのページ Google マネージド SSL 証明書を使用して Ingress を作成する 静的外部 IP アドレスを払い出す 当記事では、 hello-app-address という名前で IP アドレスを払い出します。 この IP アドレスは、HTTP(S) ロードバランサに紐づけ、証明書に使用するドメインで解決できるようにします。 # 静的外部 IP アドレスの払い出し $ gcloud compute addresses create hello-app-address --global 払い出した IP アドレスは次の手順で使用するため、値を確認しておきます。 # 払い出した IP アドレスを確認する $ gcloud compute addresses describe hello-app-address --global address: 34.160.155.24 A レコードを登録する SSL 証明書に使用するドメインで、先ほど作成した IP アドレスを解決できるように DNS を構成します。 当記事では Cloud DNS に作成された DNS ゾーンに A レコードを作成していきます。 ここでは仮の値として、DNS ゾーンを hogehoge-zone 、ドメインを hogehoge.com とします。 # トランザクションの開始 $ gcloud dns record-sets transaction start --zone=hogehoge-zone Transaction started [transaction.yaml]. # Aレコードの登録 $ gcloud dns record-sets transaction add 34.160.155.24 \ --name=hogehoge.com \ --ttl=300 \ --type=A \ --zone=hogehoge-zone Record addition appended to transaction at [transaction.yaml]. # トランザクションの終了 $ gcloud dns record-sets transaction execute --zone=hogehoge-zone Executed transaction [transaction.yaml] for managed-zone [hogehoge-zone]. Created [https://dns.googleapis.com/dns/v1/projects/myproject/managedZones/hogehoge-zone/changes/32]. ID START_TIME STATUS 32 2023-02-21T14:38:43.813Z pending ManagedCertificate リソースを作成する ManagedCertificate カスタムリソースを使用して、Google マネージド SSL 証明書を Kubernetes リソースとして作成します。 マニフェストファイルには、先ほど DNS に登録したドメインを記述します。 # cert.yaml apiVersion : networking.gke.io/v1 kind : ManagedCertificate metadata : name : hello-managed-cert spec : domains : - hogehoge.com # デプロイ後 $ kubectl get managedcertificate.networking.gke.io hello-managed-cert NAME AGE STATUS hello-managed-cert 6m21s Provisioning 証明書を使用するように Ingress を更新する 作成した Google マネージド SSL 証明書を使用するように Ingress を更新します。 始めに作成した ingress.yaml の annotations に以下の 2 つを追記して GKE クラスタに再適用します。 annotations のキー 値 kubernetes.io/ingress.global-static-ip-name ロードバランサ用に払い出した静的外部 IP アドレスの名前 networking.gke.io/managed-certificates ManagedCertificate の名前 # ingress.yaml apiVersion : networking.k8s.io/v1 kind : Ingress metadata : name : hello-ingress namespace : default annotations : kubernetes.io/ingress.global-static-ip-name : hello-app-address # 追記 networking.gke.io/managed-certificates : hello-managed-cert # 追記 kubernetes.io/ingress.class : "gce" spec : defaultBackend : service : name : hello port : number : 8080 Ingress を更新すると、Ingress が使用する(ロードバランサで使用する) IP アドレスが、払い出した静的外部 IP アドレスに変更されます。 $ kubectl get ingresses hello-ingress NAME CLASS HOSTS ADDRESS PORTS AGE hello-ingress <none> * 34.160.155.24 80 19m Google Cloud コンソールから、HTTP(S) ロードバランサが HTTPS プロトコルを使用するように構成され直したことが確認できます。 HTTP(S) ロードバランサに HTTPS プロトコルが追加されている Google マネージド SSL 証明書のステータスを確認する Google マネージド SSL 証明書のステータスが Active になるまで待ちます。 ※ 証明書のステータス反映は Google Cloud コンソールのほうが早いようです。 # 証明書のステータス確認 $ kubectl get managedcertificate.networking.gke.io hello-managed-cert --watch NAME AGE STATUS hello-managed-cert 31m Active 証明書のステータスが ACTIVE になっていることを確認 サンプルアプリケーションに HTTPS でアクセスできることを確認する 証明書を構成したあと、DNS に設定したドメインに接続すると、サンプルアプリケーションに HTTPS でアクセスすることができます。 サンプルアプリケーションに HTTPS でアクセスできることを確認 佐々木 駿太 (記事一覧) G-gen 最北端、北海道在住のクラウドソリューション部エンジニア。 2022 年 6 月に G-gen にジョイン。Google Cloud All Certifications Engineer。 好きな Google Cloud プロダクトは Cloud Run。最近は Dataflow を勉強中。 Follow @sasashun0805
アバター
G-gen の藤岡です。当記事では、Google Cloud(旧称 GCP)の Compute Engine(以下 GCE)において、Windows Server のオンデマンドライセンスを使用した場合のライセンス認証の設定編について紹介します。 はじめに 検証 構成図 前提条件 Terraform のコード リソースの確認 結果 パターン1 パターン2 パターン3 パターン4 おわりに はじめに 以下の記事で Windows Server のオンデマンドライセンスを使用した場合のライセンス認証についての概要や確認方法を紹介しました。 blog.g-gen.co.jp 上記を踏まえ、 外部 IP アドレスを持たない Windows Server VM でライセンス認証を行うには、 限定公開の Google アクセス (以下 Private Google Access)を有効にする必要があります。 そのため当記事では、 外部 IP アドレスの有無と Private Google Access の On/Off の観点 から4つのパターンの検証結果について記載します。 なお、以下の記事を理解しておくことで、後述の検証内容への理解が深まります。 Compute EngineのWindows Serverでライセンス認証エラー(0xC004F074) 限定公開の Google アクセスの仕組みと手順をきっちり解説 検証 構成図 構成は以下の通りです。通信経路については後述します。 構成図 * 参考: 限定公開の Google アクセス / IAP の TCP 転送の仕組み 前提条件 ここでは、以下を前提条件とし、Terraform を用い環境を構築します。 請求アカウントが紐付いたプロジェクトが作成済みである 本検証で必要な API が有効化されている 実行ユーザーに適切な権限が付与されている Private Google Access では デフォルトのドメイン名を利用 する Terraform はCloudShell で実行する Terraform のコード 実行する Terraform のコードは以下のとおりです。 variable "project" の ${PROJECT_ID} を自身のプロジェクト ID に置き換えた上で実行します。 /* 変数定義 https://developer.hashicorp.com/terraform/language/values/variables */ variable "project" { type = string default = "$ { PROJECT_ID } " // プロジェクト ID } variable "region" { type = string default = "asia-northeast1" } variable "zone" { type = string default = "asia-northeast1-a" } variable "subnet-cidr-a" { type = string default = "10.0.10.0/24" } variable "subnet-cidr-b" { type = string default = "10.0.20.0/24" } variable "instance_os" { type = string default = "windows-cloud/windows-2022" } variable "instance_type" { type = string default = "n2-standard-2" } /* プロバイダー定義 https://developer.hashicorp.com/terraform/language/providers/configuration */ provider "google" { project = var.project region = var.region zone = var.zone } /* terraform 設定 https://developer.hashicorp.com/terraform/language/settings */ terraform { required_version = "~> 1.2" required_providers { google = ">= 4.32.0" } } /* vpc 作成 https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/compute_network */ resource "google_compute_network" "vpc" { name = "vpc" auto_create_subnetworks = "false" routing_mode = "GLOBAL" } /* subnet 作成 https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/compute_subnetwork */ resource "google_compute_subnetwork" "subnet_a" { // Private Google Access: ON name = "subnet-a" ip_cidr_range = var.subnet-cidr-a network = google_compute_network.vpc.name private_ip_google_access = true } resource "google_compute_subnetwork" "subnet_b" { // Private Google Access: OFF name = "subnet-b" ip_cidr_range = var.subnet-cidr-b network = google_compute_network.vpc.name } /* firewall 作成 https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/compute_firewall */ resource "google_compute_firewall" "allow_iap" { // IAP 用 name = "allow-ingress-from-iap" network = google_compute_network.vpc.name direction = "INGRESS" allow { protocol = "tcp" ports = [ "3389" ] } source_ranges = [ "35.235.240.0/20" ] } /* サービスアカウント作成 https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/google_service_account */ resource "google_service_account" "service_account_for_vm" { account_id = "service-account-for-vm" display_name = "VM 用サービスアカウント" } /* サービスアカウント権限付与 https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/google_project_iam */ 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 } " } /* vm 作成 x4 https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/compute_instance */ resource "google_compute_instance" "vm_a_1" { // パターン1 name = "vm-a-1" machine_type = var.instance_type service_account { email = google_service_account.service_account_for_vm.email scopes = [ "cloud-platform" ] } boot_disk { initialize_params { image = var.instance_os } } network_interface { network = google_compute_network.vpc.name subnetwork = google_compute_subnetwork.subnet_a.name network_ip = "10.0.10.2" access_config {} } allow_stopping_for_update = true } resource "google_compute_instance" "vm_a_2" { // パターン2 name = "vm-a-2" machine_type = var.instance_type service_account { email = google_service_account.service_account_for_vm.email scopes = [ "cloud-platform" ] } boot_disk { initialize_params { image = var.instance_os } } network_interface { network = google_compute_network.vpc.name subnetwork = google_compute_subnetwork.subnet_a.name network_ip = "10.0.10.3" } allow_stopping_for_update = true } resource "google_compute_instance" "vm_b_1" { // パターン3 name = "vm-b-1" machine_type = var.instance_type service_account { email = google_service_account.service_account_for_vm.email scopes = [ "cloud-platform" ] } boot_disk { initialize_params { image = var.instance_os } } network_interface { network = google_compute_network.vpc.name subnetwork = google_compute_subnetwork.subnet_b.name network_ip = "10.0.20.2" access_config {} } allow_stopping_for_update = true } resource "google_compute_instance" "vm_b_2" { // パターン4 name = "vm-b-2" machine_type = var.instance_type service_account { email = google_service_account.service_account_for_vm.email scopes = [ "cloud-platform" ] } boot_disk { initialize_params { image = var.instance_os } } network_interface { network = google_compute_network.vpc.name subnetwork = google_compute_subnetwork.subnet_b.name network_ip = "10.0.20.3" } allow_stopping_for_update = true } 参考: TerraformをGoogle Cloudで使ってみた リソースの確認 terraform apply によって作成されたリソースを一部確認します。 リソース(VPC) リソース(GCE) 結果 結果は以下の通りです。Private Google Access が On の場合は、外部 IP アドレスの有無に関わらずライセンス認証が可能です。対して、Private Google Access が Off の場合は、外部 IP アドレスがないとライセンス認証はできません。 パターン名 詳細 ライセンス認証の結果 パターン1 Private Google Access:On 外部 IP:有 可 パターン2 Private Google Access:On 外部 IP:無 可 パターン3 Private Google Access:Off 外部 IP:有 可 パターン4 Private Google Access:Off 外部 IP:無 不可 通信経路としては以下のようになっています。 通信経路図 これ以降はパターンごとに実際の画面と、Windows Server VM から KMS サーバー(35.190.247.13)への通信を Google Cloud の 接続テスト で結果を確認していきます。各 VM へは Identity-Aware Proxy の TCP 転送 を利用して RDP します。 参考: 接続テストを作成して実行する / RDP 接続のトンネリング パターン1 問題なくライセンス認証されています。 結果①(パターン1) 接続テストも問題ありません。 結果②(パターン1) パターン2 問題なくライセンス認証されています。 結果①(パターン2) 接続テストも問題ありません。 結果②(パターン2) パターン3 問題なくライセンス認証されています。 結果①(パターン3) 接続テストも問題ありません。 結果②(パターン3) パターン4 Not activated となっており、ライセンス認証ができていません。 結果①(パターン4) 接続テストの結果、以下のようなエラーが出ています。 パケットはドロップされている可能性があります 内部 IP アドレスのみを持つインスタンスが Google API とサービスにアクセスしようとしています。限定公開の Google アクセスが有効になっていません。 結果②(パターン4) おわりに 以上で、外部 IP アドレスを持っていない Windows Server VM の場合でも、Private Google Access を On にすることでライセンス認証ができることが確認できました。 藤岡 里美 (記事一覧) クラウドソリューション部 接客業からエンジニアへ。2022年9月 G-gen にジョイン。Google Cloud 認定資格は全冠。2023 夏アニメのオススメは、ダークギャザリング。箏を習っています :) Follow @fujioka57621469
アバター
当記事は みずほリサーチ&テクノロジーズ × G-gen エンジニアコラボレーション企画 で執筆されたものです。 G-gen の片岩です。Google Cloud の Cloud Spanner について徹底解説します。Cloud Spanner は強整合性を持ちながらグローバルな負荷分散が可能なサービスです。高い可用性、データの完全性が求められる金融分野での活用が見込まれるデータベースです。 Cloud Spanner の概要 Cloud Spanner とは 強整合性と結果整合性 水平スケーリング インターフェイス SQL API クライアント ユースケース Cloud Spanner が適したケース 代表的な事例 類似サービスの使い分け Cloud SQL AlloyDB Cloud Spanner 料金 インスタンスと構成 インスタンス リージョン構成、マルチリージョン構成 コンピューティング容量 読み取り方式 バックアップ バックアップと復元 インポート/エクスポート ポイントインタイムリカバリ 運用 データ処理の仕組み シャーディング インターリーブ 強整合性とスケーラビリティの両立 3 種類のレプリカ 書き込み(commit)の仕組み 読み取りの仕組み 強力な読み取り (最新データを読み取る必要がある場合) ステイル読み取り リージョン構成 / マルチリージョン構成それぞれにおけるレプリカの内訳 データブースト Cloud Spanner の概要 Cloud Spanner とは Cloud Spanner は 強整合性 を保証する RDB(リレーショナルデータベース)の特徴と グローバルに水平スケーリング できる NoSQL データベースの特徴を併せ持つデータベースです。そしてダウンタイムが最小限であり運用負荷の少ない フルマネージド のデータベースでもあります。 Cloud Spanner は強整合性 (RDB の強み) と、スケーラビリティ (NoSQL の強み) のいいとこどりのアーキテクチャを兼ね備えており、他クラウドにも類似サービスのない、ユニークなデータストアサービスです。 参考 : Cloud Spanner 強整合性と結果整合性 強整合性 とは、読み取り処理で必ず最新の書き込み情報を読み取れる性質です。例えばECサイトでは必ず最新の在庫状況を取得できなければ、在庫がないにもかかわらずお客さまから商品の注文を受け付けてしまいます。勘定、決済などを取り扱う場合にも強整合性が必要なことは想像に難くないと思います。 対照的に、最新情報の読み込みを保証しない性質が 結果整合性 です。NoSQL データベースは、スケーラビリティとパフォーマンスが高い(後述)反面、結果整合性を持ちます。例えば自分の投稿にお気に入りをした人の数など、結果整合性で十分なデータでは NoSQL データベースの特性を十二分に活用できます。 水平スケーリング NoSQL データベースの内部ではデータを分割して保存するため、簡単に水平スケーリングすることができます。 一方、RDB ではデータベースを分割しないため水平スケーリングは難しく、垂直スケーリングしか取れないため、性能上限に抵触しやすいです。 Cloud Spanner は強整合性という RDB の特徴を持ちながら、NoSQL の特徴である水平スケーリングも実現する、ユニークなデータベースサービスです。 インターフェイス SQL Cloud Spanner のデータは SQL で操作することができます。SQL の方言として GoogleSQL と PostgreSQL のいずれかから選択することができます。選択はデータベース作成時に行います。 GoogleSQL と PostgreSQL は完全互換ではありませんが、ほとんど同様に利用できます。開発チームが PostgreSQL に慣れていたり、あるいはクライアントとして psql などの PostgreSQL エコシステムのツールを利用したい場合は PostgreSQL を選択することが望ましいです。 参考 : PostgreSQL Interface 参考 : Dialect parity between GoogleSQL and PostgreSQL API SQL の他にも、各プログラミング言語用のクライアントライブラリを使ったり、Rest API / gRPC を使ったデータの読み書きを行うことができます。 参考 : トランザクションについて 参考 : トランザクション外部の読み取り 参考 : ミューテーションを使用してデータを挿入、更新、削除する クライアント Spanner では以下のようなユーザーインターフェイスを通じて SQL を実行したり、 ミューテーション (API コールによるデータの更新)を実行したりすることができます。 Google Cloud コンソール (Spanner Studio) 各言語用の Spanner クライアントライブラリ (Cloud SDK) REST API RPC API PostgreSQL クライアント (PostgreSQL Interface モードの場合) psql などのコマンドラインで Spanner に接続する場合、 PGAdapter というプロキシソフトウェアを経由して接続します。PGAdapter は JVM の動作するスタンドアロンプロセスあるいは Docker イメージとして提供され、クライアントのローカル環境やサイドカーコンテナとして起動されます。psql 等のクライアントからこの PGAdapter に対して接続すると、PGAdapter は IAM 認証を経て、インターネット経由(gRPC プロトコル)で Spanner インスタンスに接続します。 ユースケース Cloud Spanner が適したケース 以下のいずれかにあてはまる場合に Cloud Spanner が有力な選択肢となります。 強整合性とスケーラビリティがどちらも必要な場合 ダウンタイムを極力減らしたい場合 データベースの運用負荷を軽減したい場合 代表的な事例 Cloud Spanner を利用した、代表的な公開事例をご紹介します (Google Cloud 社の Web サイト)。 みんなの銀行 の勘定系システムで利用されています。東阪両現用を実現するデータストアとして Cloud Spanner を活用しています。 ポケモンGo で利用されています。1 秒あたり 100 万件近いトランザクションを処理することも可能です。 メルペイ で利用されています。信頼性の高い決済処理を数百万のユーザーに 24 時間 365 日 提供しています。 類似サービスの使い分け Google Cloud が提供する RDB のサービスには Cloud Spanner のほかに Cloud SQL と AlloyDB があります。 Cloud SQL Cloud SQL は MySQL、PostgreSQL、SQLServer を扱える RDB です。利用規模が小〜中規模な場合、また非常に大きなスループットを要しない場合は、コスト面で優位な Cloud SQL で十分と言えます。 blog.g-gen.co.jp AlloyDB AlloyDB は 99.99% の可用性(1年間における停止時間の合計が約53分以内)を保証する、パフォーマンスに優れたデータベースで、AWS の Amazon Aurora に相当するサービスです。 PostgreSQL 完全互換のため利用可能なライブラリが充実しています。利用規模が中〜大規模で、高パフォーマンスが要求され、PostgreSQL の豊富なライブラリを利用したい場合は AlloyDB が望ましいと言えます。 blog.g-gen.co.jp Cloud Spanner Cloud Spanner ではマルチリージョン構成の場合に 99.999 % の可用性(1年間における停止時間の合計が約5分以内)を保証します。 スケーラビリティにも優れているため、グローバルにサービスを展開したい場合や、サービスをほぼ無停止で展開したい場合に Cloud Spanner の利用を検討します。 料金 Cloud Spanner は割り当てたコンピューティング容量と使用しているストレージ量に対して課金されます。 例として 100 処理ユニット、ストレージ使用量を 100GB で 1 ヶ月間フル稼働すると、約 16,795 円 / 月です(2023年2月時点、東京リージョン、135円/ドル)。 同一のコンピューティング容量であっても、シングルリージョンとマルチリージョン、さらにアメリカとアジアとヨーロッパをまたいだマルチリージョンで単価が異なります。 最新の利用料金は以下の公式ページでご確認ください。また、公式の見積もりツールで料金が試算できます。 参考 : Cloud Spanner の料金 参考 : Google Cloud Pricing Calculator また、Cloud Spanner には 確約利用割引 の仕組みがあります。1 年間または 3 年間の利用を確約することで最大 40% の割引を受けることができます。詳細は以下のドキュメントをご参照ください。 参考 : Spanner CUD の料金 インスタンスと構成 インスタンス Cloud Spanner は インスタンス の単位で管理します。 Cloud Spanner におけるインスタンスは、データベースの基盤となる仮想サーバー群のことです。インスタンスごとに構成(リージョン構成またはマルチリージョン構成)とコンピューティング容量を選択します。 リージョン構成、マルチリージョン構成 リージョン構成 では、指定したリージョン内の複数のゾーンにクラスタが展開されます。マルチリージョン構成に比べて、コストパフォーマンスに優れています。 マルチリージョン構成 では、指定した複数のリージョンにまたがってクラスタが構成されます。リージョン構成に比べて、可用性や、複数の国や地域からアクセスされる際のレイテンシに優れています。 リージョン障害への対応が必要な場合や、東阪両現用のように複数のリージョンにまたがって高いパフォーマンスが要求される場合はマルチリージョン構成を選択します。 また、SLA にも違いがあります。リージョン構成では99.99%、マルチリージョン構成では99.999%の可用性 SLA が設定されています。 参考 : Cloud Spanner Service Level Agreement (SLA) 各構成においては、選択する地域によって、どのリージョンにレプリカ(サーバー)が配置されるかが決まります。詳細は後述の「リージョン構成 / マルチリージョン構成それぞれにおけるレプリカの内訳」の項をご参照ください。 なおインスタンス構成は、運用開始後でもダウンタイム無く変更できるため、最初はリージョン構成から開始し、利用規模が大きくなってからマルチリージョン構成に変更することも可能です。 参考 : インスタンスを移動する コンピューティング容量 インスタンスのコンピューティング容量は、 処理ユニット (処理単位、processing units)の割り当てで決定します。処理ユニットの値によって、データのスループットや秒間クエリ数、ストレージの上限が決まります。 また、テーブル数の上限も処理ユニットによって決定されます。10ユニット毎に1テーブルの上限があります。 処理ユニットの下限値は 100 で、100 処理ユニット単位でコンピューティング容量を指定できます。また 1,000 処理ユニットを 1 ノードと呼び、1,000 処理ユニット以上を指定したい場合はノード単位でコンピューティング容量を指定することになります。 参考 : コンピューティング容量、ノード、処理単位 読み取り方式 Cloud Spanner では 強力な読み取り と ステイル読み取り 、そして トランザクション という3種類の読み取り方法があります。 デフォルトの読み取り方式は強力な読み取りです。強力な読み取りでは強整合性を持った読み取りが可能な反面、読み取りの都度最新のデータであるかチェックするため、内部通信が発生しレイテンシが大きくなります。 必ずしも強整合成が必要のない読み取りの場合はステイル読み取りを使用します。stale は新鮮ではない、というニュアンスの言葉です。 ステイル読み取りではレプリカ自身が持つ最新のデータであるかのチェックをスキップするため、より高速な読み取りが可能となります。 参考 : トランザクション外部の読み取り 最後に、トランザクションは、ロックに依存する書き込みと読み込みを同時に行う場合や、複数の読み取りで一貫性を持たせたい場合に用います。以下のドキュメントを参照してください。 参考 : トランザクションの概要 バックアップ Cloud Spanner のバックアップでは、 バックアップと復元 機能、もしくは インポート/エクスポート 機能が使用できます。 バックアップと復元 バックアップと復元(back up and restore)機能では、インスタンスと同じ地理的ロケーションにバックアップを保管することができます。 また、バックアップはトランザクションの処理には直接関係しないため、インスタンスのパフォーマンスには影響しません。 バックアップ機能は手軽に利用できる反面、以下のような制約があります。 バックアップの保存期間は最長 1 年間 インスタンス内のリソースになるため、同じ構成のインスタンスを作成する場合を除いてデータの持ち出し不可 基本的にはバックアップと復元 機能の利用を検討し、上記の制約が許容できない場合はインポート/エクスポートを使用します。 参考 : バックアップの概要 インポート/エクスポート インポート/エクスポートでは、Cloud Storage の CSV ファイルや Avro ファイルの読み取り/書き込みが可能です。 バックアップはファイルとして出力されるので上記の形式に対応した外部のデータベースにデータを持ち出したり、ファイル自身のライフサイクルを管理して1年以上保管することができます。 「バックアップと復元」機能と「インポートとエクスポート」機能の比較については、以下のドキュメントもご参照ください。 参考 : Spanner のインポートとエクスポートの概要 ポイントインタイムリカバリ マスタデータを誤って削除した場合など、一部データのみ復元したい場合には、 ポイントインタイム リカバリ (PITR)を利用できます。 最長7日間の任意の時刻のスキーマとデータを復元することができます。 参考 : ポイントインタイム リカバリ(PITR)の概要 運用 従来のデータベースであれば、データ量が膨大になり 1 つのデータベースに収まりきらなくなると、以下のように複数のデータベースにデータを分割するといった作業が発生しました。 ユーザー ID が a 〜 m から始まる場合はデータベース 1 を利用 ユーザー ID が n 〜 z から始まる場合はデータベース 2 を利用 Cloud Spanner では全自動でデータの分割処理が実施されるため、上記のような作業は発生しません。必要に応じて以下の作業を行うだけとなります。 CPU 使用率をモニタリングする ノードの追加や削除を実施する バックアップ / リストアを実施する このように運用負荷が少ないことも Cloud Spanner の強みです。 データ処理の仕組み ここからは Cloud Spanner の内部的な仕組みをご紹介します。まずは大量のデータを高速に処理する仕組みです。 シャーディング Cloud Spanner では PK のレンジに沿ってデータがいくつかに分割 (シャーディング) されてストレージに保存されます。この分割されたデータをスプリットと呼びます。 各ノードは、それぞれ割り当てられたスプリットの読み込み処理や書き込み処理を担当します。ノードを増やすと 1 ノードあたりのデータ量が減り、それだけ負荷分散できることになります。 3 ノード構成の場合の内部構成 各データが均等にアクセスされる場合は、各スプリットのサイズも概ね均等になります。 各データへのアクセス頻度が均等な場合のスプリット 各データが均等にアクセスされない場合、たとえば上記の図で y や z から始まるデータのアクセス量が多い場合は、各ノードの負荷が均等になるように、以下のように自動的にリシャーディングされます。 y や z から始まるデータのアクセス量が多い場合のスプリット 一時的なリシャーディングは問題ありませんが、継続的なリシャーディングが発生するとデータベースに無駄な負荷がかかってしまいます。たとえば上記の図で z から開始するデータだけが増え続けてしまうと、ノード 3 のみに負荷が集中することになり、継続的なリシャーディングが発生してしまいます。 特定のノードに負荷が集中して継続的なリシャーディングが発生しないよう、データベースのスキーマ設計時はご留意ください。 インターリーブ 詳細は後述しますが、単一リージョン構成の場合はデータが 3 つのゾーンに 3 重に冗長化されています。以下は 2 つのテーブル①とテーブル②が 3 つのゾーンで冗長化されるイメージです。 テーブル①とテーブル②が 3 つのゾーンで冗長化される それら 3 のゾーンのうち読み書きを実行するリーダーがスプリットごとに選ばれます。特段の指定をしない場合は、テーブル①とテーブル②はそれぞれ独立してスプリットに分割されます。 スプリットごとにリーダーが選ばれる たとえば上の図の PK が b からはじまるデータに対してテーブル①とテーブル②に更新処理を実行すると、更新処理はリーダーを通して実行されるため、テーブル①ではゾーン a で処理し、テーブル②ではゾーン b で処理するといった事象が発生します。 このようにJOIN したいテーブルのノードが異なると、ノード間の通信が必要になり、オーバーヘッドが発生してしまいます。 このオーバーヘッドを回避するために、関連するテーブルのデータを物理的に連続した同じ位置に配置することができる仕組みをインターリーブと呼びます。インターリーブを使用することにより、JOIN 時に異なるノード間の通信が発生することを抑えることができます。 インターリーブにより PK の先頭列が同じデータを同じスプリットに含められる 強整合性とスケーラビリティの両立 次にグローバルに強整合性を担保できるユニークな仕組みを見ていきましょう。 3 種類のレプリカ Cloud Spanner のインスタンスは、読み書きレプリカ、読み取り専用レプリカ、ウィットネス レプリカの 3 種類のレプリカから構成されます。 読み書きレプリカ は読み書きすることができます。唯一書き込みが処理できます。 読み取り専用レプリカ も読んで字のごとく読み取りのみすることができます。 ウィットネス レプリカ は読み取りしないで、書き込みの commit を決める投票(後述)のみ参加するレプリカです。 読み書きレプリカは複数存在し、そのうちの 1 つが リーダーレプリカ となります。 詳細は後述しますが、リーダーレプリカは書き込み処理において中心的な役割を果たします。 レプリカの種類 リーダー可否 書き込み 読み込み 投票 読み書き ◯ ◯ ◯ ◯ 読み取り専用 x x ◯ x ウィットネス x x x ◯ 書き込み(commit)の仕組み Cloud Spannerは commit の仕組みに特徴があります。 すべての書き込みリクエストがリーダーレプリカに送信されます。 リーダーレプリカは書き込みリクエストがあったことをログに記録します。 リーダーレプリカは読み書きレプリカとウィットネスレプリカに書き込みリクエストを転送します。 書き込みリクエストを受け取ったレプリカは書き込みを実行後、リーダーレプリカに投票を返却します。 投票が過半数のレプリカから返却されるとリーダーレプリカはcommit 完了とみなします。 リーダーレプリカがcommitを完了とみなした後も、書き込みリクエストを受け取ったレプリカは書き込みを継続し完了します。 画像は 公式ブログ から引用 分散データベースで利用される 2 フェーズコミットでは投票可能なレプリカの すべて が書き込みの commit に同意した時点で、書き込みが commit されます。 対して Cloud Spanner では投票可能なレプリカの 過半数 が書き込みの commit に同意した時点で、書き込みが commit される点が特徴です。 なお、リーダーレプリカは 10 秒間隔で読み取り専用レプリカを更新します。ステイル読み取りでは何秒前のデータを読み取るかが指定できますが、15 秒とすることが推奨されています。これは前述の 10 秒間隔の更新を想定したものです。 参考 : Cloud Spanner のマルチリージョン構成について理解する 読み取りの仕組み 強力な読み取り (最新データを読み取る必要がある場合) レプリカは自身が持つデータが最新であるか判断するために、以下の処理を行います。 レプリカは読み取りリクエストを受信します。 レプリカはリーダーに読み取りリクエストを送信します。 リーダーは、対象行の最新トランザクションのタイムスタンプを応答します。 レプリカは自身が持つデータが最新であるか判断します。 行が最新の場合、レプリカは結果を返すことができます。 最新でない場合は、リーダーからの更新情報を受け取ってから応答します。 画像は 公式ブログ から引用 なお、読み書きトランザクションに含まれる読み取りはこの流れではありません。書き込みが必要なトランザクションでは直接リーダーレプリカがトランザクションを処理するためです。 ステイル読み取り 前述のステイル読み取りではリーダーレプリカに最新のデータかどうか問い合わせる処理をスキップすることで、強整合性を犠牲にレイテンシーを低減することができます。 たとえば 最新時刻から見て 15 秒前のデータで問題なければ、15 秒前のデータをリクエストします。レプリカはリーダーに最新情報を問い合わせる必要なく低レイテンシでデータを返すことができます。 画像は 公式ブログ から引用 リージョン構成 / マルチリージョン構成それぞれにおけるレプリカの内訳 リージョン構成の場合は以下の構成になります。 1 つのリージョンの 3 つの異なるゾーンそれぞれに読み書きレプリカ マルチリージョン構成、たとえば asia1(東京 / 大阪 / ソウル)の場合は以下の構成になります。 東京に 2 個の 読み書きレプリカ 大阪に 2 個の 読み書きレプリカ ソウルに 1 個のウィットネスレプリカ 大陸間をまたぐマルチリージョン構成、たとえば nam-eur-asia1(北アメリカ / ヨーロッパ / アジア)の場合は以下の構成になります。 アイオワに 2 個の 読み書きレプリカ オクラホマに 2 個の 読み書きレプリカ ベルギーに 2 個の読み取り専用レプリカ 台湾に 2 個の読み取り専用レプリカ サウスカロライナに 1 個のウィットネスレプリカ データブースト Cloud Spanner の データブースト (Data Boost)機能は、読み取り専用のコンピュートリソースを既存クラスタとは別で割り当てることで、分析用途などの読み取りワークロードを分離することができる仕組みです。 この機能を用いることで、通常のアプリケーションが使っている Spanner のリソースに影響を与えることなく、分析用のクエリを発行することなどが可能です。この機能を有効化すると、必要に応じて読み取り専用のコンピュートリソースが臨時で割り当てられ、Spanner のストレージにアクセスできます。 利用例として「BigQuery から Cloud Spanner への連携クエリ(Federated query)のために利用する」「レポート作成やデータエクスポートのための定期ジョブに利用する」などが挙げられます。 当機能を利用すると、一時的に割り当てられた処理ユニットに対して料金が発生することにご注意ください。 参考 : データブーストの概要 片岩 裕貴 (記事一覧) クラウドソリューション部 クラウドディベロッパー課 2022年5月にG-genにジョインした和歌山県在住のエンジニア。興味分野はAI/ML。2024年にGoogle Cloud認定資格全冠達成。最近は子供と鈴鹿サーキットや名古屋のレゴランドに行ってきました。
アバター
クラウドサービスの普及に伴い、オンプレミスからクラウドへの移行を検討する企業が増加しています。しかし、いざクラウド移行を進めるとなると考慮する点も多いものです。クラウド移行にはメリット・デメリットがあり、すべての企業にとって最善の選択肢とは限りません。この記事では、クラウド移行のメリット・デメリットを解説します。具体的な移行手順も紹介しているので、ぜひ参考にしてください。 クラウドとオンプレミス クラウドとは オンプレミスとは クラウドとオンプレミスの違い クラウド移行のメリット スモールスタートできる スピーディに導入できる 導入コストを抑えられる 運用負荷を軽減できる エンジニア採用に有利に働く クラウド移行のデメリット 既存システムとの互換性 レイテンシ スキルセットの違い クラウド移行の判断ポイント クラウドがおすすめな企業の特徴 オンプレミスがおすすめな企業の特徴 クラウド移行の具体的な手順 (1) 既存資産の可視化 (2) 移行計画の作成 (3) 設計・構築・移行 (移行後) 運用・保守 クラウド移行の失敗事例 クラウド移行で重視すべきポイント 運用体制 クラウド利用料を考慮する ネットワーク要件 まとめ クラウドとオンプレミス クラウドとは クラウドとは、クラウド事業者が提供する環境やサービスを、ネットワーク経由で利用するシステム利用形態のことです。 自社でサーバを用意する必要がない点が特徴で、IT分野で急速に普及しつつあります。たとえば、現在主流のメールサービスの多くは、メールサーバを社内に構築する必要がありません。サービスの Web サイトからアカウントを発行するだけで、メールを利用できます。これもクラウドサービスの1つです。 大別すると IaaS、PaaS、SaaS の3種類に分けることが可能です。当記事では特に、企業のITインフラのクラウド化に用いられる IaaS (Infrastructure as a Service) に着目してご紹介します。 オンプレミスとは オンプレミスとは、自社で物理的なサーバを保有するシステム利用形態のことです。 自社でサーバやネットワーク、ソフトウェアを構築し、管理・運用も社内で行います。クラウドサービスが普及する前は、こうした自社運用システムが一般的でした。 クラウドとオンプレミスの違い クラウドとオンプレミスの特徴をまとめると、以下の通りです。 クラウド オンプレミス コスト ・初期費用がかからない ・従量課金制が主流で、必要なときに、必要な分のコストを支払えばよい ・サーバ機器などの初期費用がかかる ・拡張が必要となる度、新たなコストが発生する 導入スピード ・スピーディに利用できる ・サーバ機器などの選定から、導入まで1か月程度かかる セキュリティ ・高い ・自社で構築する必要あり カスタマイズ性 ・サービスによる ・高い 障害対応 ・クラウド事業者が対応するため、業務負担が軽減される ・自社が復旧作業にあたる バックアップ ・サービスによるが、機能として用意されていることが多い ・自社で構築する必要あり クラウド移行のメリット ここからは、クラウド移行のメリットについて詳しく解説します。なお、ここでいうクラウド移行とは、自社のサーバ資産を IaaS へ移行することを指します。 スモールスタートできる クラウドサービスはオンプレミスと比べて拡張性が高く、ストレージやCPU、メモリなどのリソースを柔軟に増やすことができます。自社の利用状況に応じた、迅速な性能拡張が可能です。 新規事業など、成否や成長速度の予測をしづらいビジネスの IT 基盤にはクラウドが最適です。クラウドにより基盤をスモールスタートさせ、ビジネスの成長と共に拡大していけばよいのです。 スピーディに導入できる 自社でイチからサーバを構築する場合や、システムを開発する場合と比べて、導入までのスピード感が速い点もメリットです。利用登録後すぐにサーバを構築できるため、スピーディな導入を実現できるでしょう。 またシステムの利用ボリュームや頻度の変化に応じて、柔軟にサーバ台数やスペックの変更がスピーディに可能なため、コストが最適化されます。 導入コストを抑えられる 自社でサーバを保有する必要がないため、サーバ機器の購入・設置にかかる初期設備投資が不要です。 また、多くのクラウドサービスは利用量に応じて料金が発生する従量課金制を採用しています。必要なときに、必要な分だけの費用を支払えばよいため、コストの無駄がありません。 運用負荷を軽減できる サーバの運用・管理はクラウド事業者が行うため、自社の人員が最低限で済みます。メンテナンスや障害対応もクラウド事業者に任せられるので、運用負荷を軽減できます。 エンジニア採用に有利に働く 募集要項に「クラウドを利用している=社内でクラウドを扱える」ことを記載しておくと、技術的に好奇心旺盛なエンジニアが募集してくる可能性が高まります。その結果、優秀なエンジニアを確保しやすくなり、自社のIT活用力を強化できるでしょう。 クラウド移行のデメリット クラウド移行にはさまざまなメリットがある一方、いくつかのデメリットも存在します。 既存システムとの互換性 既存システム (パッケージ) によっては、パブリッククラウドサービスをサポートしておらず、クラウド上に移行すると製品サポートが受けられなかったり、他の既存システムとの連携や移行がうまくいかない場合もあります。 引き続き同じパッケージやソフトウェアを利用する場合には、互換性をしっかり確認しておくことが大切です。 レイテンシ クラウドサービスはネットワーク経由でクラウド事業者のコンピュートリソースを利用することが最大の特徴です。地理的に・ネットワーク的に離れている場所にあるリソースを利用するので、ミリセカンドのレベルのパフォーマンス要件が求められるシステムでは、レイテンシが問題になることがあります。 とはいえこれが問題になるのはレスポンスが重要となるような IoT、制御系システムやアクションゲームのサーバなどであり、通常の社内システムや Web サービスではこのようなレイテンシが問題となることは稀です。 スキルセットの違い クラウドサービスは日進月歩であり、管理・運用を行うエンジニアにも日々、スキルのアップデートが求められます。 クラウドでは物理レイヤの保守・運用が一切不要になる代わりに、より上位レイヤ (アプリケーションやデータに近いレイヤ) の知見が求められます。これまでの社内のインフラエンジニアやアプリケーションエンジニアには、新しいスキルセットが求められることになりますので、それが戸惑いを呼んだり、新しいエンジニア育成や採用の戦略が必要になるかもしれません。 クラウド移行の判断ポイント クラウドがおすすめな企業、オンプレミスがおすすめな企業の特徴をそれぞれ記載します。オンプレミスを続行するのか、クラウド移行するのか、自社にとって最善の選択肢を選ぶための判断材料として活用してください。 クラウドがおすすめな企業の特徴 課題 メリット 運用・保守に人員を割く余裕がない ・サーバの運用・管理はクラウド事業者に任せられる ・業務負担の軽減や、人件費の削減につながる 内製でデジタル化、DX化を進めたい ・アジリティが高く、スモールスタートがしやすい ・新たなビジネスを素早く、小さくはじめて試すということができる スタートアップ企業・新規事業 同上 オンプレミスがおすすめな企業の特徴 課題 メリット ミリセカンドレベルのレイテンシが問題である ・制御系 ・ゲームサーバ ・その他レスポンス要件がシビアなシステム 互換性に問題がある ・既存システムをそのまま移行したいがパッケージシステム等がクラウドに対応していない クラウド移行の具体的な手順 ここからは、クラウド移行の具体的な手順を説明します。ここでは特に自社のサーバ資産を AWS、Azure、Google Cloud 等の IaaS に移行する場合の、計画から運用開始までをイメージして解説します。 (1) 既存資産の可視化 自社サーバ資産がインベントリ化されていない場合、これを機にリストアップします。全物理サーバのスペック、用途、OSとそのバージョン、含まれている仮想サーバ台数、インストール済みソフトウェアやそのバージョンを精査します。 またそれらサーバの監視の有無、方法なども整理します。 (2) 移行計画の作成 既存資産の可視化に基づいて、クラウド (IaaS) への移行計画を策定します。 まずは、移行対象サーバの分類を行います。サーバを「そのまま IaaS へ移行する」「PaaS や SaaS に置き換える」「廃棄する」などの選択肢があります。必ずしも全てのサーバを移行する必要はなく、これを機に不要なサーバ資産の棚卸しを行うことが重要です。 また、サーバ移行の優先順位を付け、それに基づいたスケジュール策定も行います。 移行に当たってクラウドパートナー (インテグレーター) の支援を仰ぐ場合は、各ベンダへの見積もり依頼や役割分担の確認、自社体制の確立を行います。 (3) 設計・構築・移行 クラウド環境の仮想ネットワーク、仮想サーバ、セキュリティ設定に関する設計と構築を行います。 適切な設計を行うことで、クラウドサービスをセキュアに、かつコスト効率良く利用できます。クラウドサービスでは物理的な構築に一切時間がかからず、サーバをボタン一つで起動できるという利便性がありますが、インフラ設計はオンプレミスと変わらず重要です。 構築が完了して移行の準備ができたら、データ移行を行います。データ移行の方法は複雑で多岐にわたり、適切な計画が必要です。 以下は、データ移行とカットオーバー (リリース) の計画の一例です。 既存システムの利用を停止 (データベースへの書込が停止) データベースからダンプを出力 ダンプファイルをクラウド上の新データベースに転送 クラウド上の新データベースでデータを取込 新アプリケーション・新データベースの動作確認 ユーザ側の向き先をクラウド上の新アプリケーションに変更 (DNS 変更等) 新アプリケーションの打鍵確認 上記のようにファイルを用いた伝統的な移行方法とは別に、各クラウドベンダが提供するオンライン移行ツールを用いて、最小限のダウンタイムでデータを移行することも検討できます。 (移行後) 運用・保守 移行手順ではありませんが、必ずついて回るものとして、運用・保守のフェイズも意識することが必要です。IaaS では物理機器の運用・保守は不要ですが、OS、ミドルウェア、アプリケーションの運用・保守は引き続き必要になります。 運用コストを最適化するために、IaaS だけでなく、PaaS や SaaS との適切な組み合わせが望ましいと言えます。 クラウドベンダーのパートナーによっては、クラウド環境の運用・保守をサービスとして提供している場合があります。 クラウド移行の失敗事例 クラウド移行には失敗事例もつきものです。失敗してもすぐリカバーできるのがクラウドの魅力でもありますが、いくつか先例をご紹介します。 例 事象 利用料金が想定よりも大きくなった ・利用サービスの適切な選定や、設定値の適切な設計が無かったため、運用をしていく中で想定よりも速いペースで利用料金が増大した(例: ストレージ料金) 運用コストが想定よりも大きくなった ・運用メンバーのスキルアップが思うように進まず、クラウド環境の運用に想定より大きな工数が発生 ・セキュリティリスクとその対処が適切にできず (判断できず) オンプレミスの運用をクラウドでもそのまま続けていることで工数が削減できない ライセンスやサポート料金の発生 ・クラウドサービスの利用にあたり、必要なライセンスやサポートを見落としてしまうケース クラウド移行で重視すべきポイント 最後に、クラウド移行で重視すべきポイントをまとめます。 運用体制 前述の通り、IaaS では物理機器の運用・保守は不要ですが、OS、ミドルウェア、アプリケーションの運用・保守は引き続き必要です。 高い優先度で運用体制を検討する必要があります。クラウドの最も大きい特徴である「アジリティ (敏捷性)」を活かすために、自社での運用内製化を検討することが重視されます。 クラウド利用料を考慮する 利用しようとしているクラウドサービスを正しく理解し、実際にかかるクラウド利用料を計算します。 クラウドへの移行は、金銭的なコストだけを考慮するとオンプレミスよりも高価になる場合も多いです。前述の運用コストと合わせて、総合的に判断する必要があります。運用にかかる人的コストも含めた総合的なコストを指して Total Cost of Ownership (TCO) という語を用いることもあります。 ネットワーク要件 社内システムをクラウド (IaaS) に配置する場合、インターネット経由でのアクセスとするか、あるいは Internet VPN / 専用線の確保をするか、選択することになります。 接続方法と想定される通信量に合わせて、十分なインターネット回線や専用線の帯域を確保する必要もあります。 まとめ クラウド移行やオンプレミス続行か、自社にとって最適な選択を見定めるためには、クラウドサービスのメリット・デメリットや考慮点をしっかりと把握することが大切です。 自社の利益につながるようなシステムの構築を目指しましょう。 クラウド移行を検討中なら、ぜひG-genにご相談ください。お客様のGoogle Cloud環境に対する運用代行・伴走支援を行い、豊富な実績にもとづく技術サポートを安価な利用環境とセットでご提供します。 お問い合わせはこちら G-gen 編集部 (記事一覧) 株式会社G-genは、サーバーワークスグループとして「クラウドで、世界を、もっと、はたらきやすく」をビジョンに掲げ、2021年よりクラウドの導入から最適化までを支援しているGoogle Cloud専業のクラウドインテグレーターです。
アバター
当記事は みずほリサーチ&テクノロジーズ × G-gen エンジニアコラボレーション企画 で執筆されたものです。 みずほリサーチ&テクノロジーズ株式会社の浅香です。 G-gen さんとのコラボ企画のもと、弊社エンジニアによる Google Cloud の記事を執筆する機会を頂戴しました。 他の方々が先行して Google Cloud に関する各種記事を投稿していますので、本記事では少し趣向を変えてみて Google Cloud と関わりが深い Terraform について、その中でも特に Terraform Cloud Business をご紹介させていただきます。 当ブログは G-gen × みずほRT によるコラボ記事です Terraformについて Terraform とは Terraform の特徴 Terraform の種類 Terraform Cloud Business とは Terraform OSS の利用イメージ Terraform OSS を利用している場合の課題 Terraform Cloud Business の利用イメージと課題への対応 人的なRV依頼 / 実施 への解決アプローチ コード / シークレットの管理(バージョン管理や自動化) への解決アプローチ 操作ログや監査、変更の通知など への解決アプローチ CLI による操作/管理 への解決アプローチ ステートファイルの管理 への解決アプローチ Terraform外の変更管理 への解決アプローチ Terraform Cloud Business と プロビジョニング先の通信 まとめ Terraformについて Terraform とは Terraform とは、 IaC ( Infrastructure as Code ) ツールの一種です。 HashiCorp 社によって開発され、オープンソースソフトウェアとして開発され続けています。 Terraform の特徴 Terraform は、各種パブリッククラウド / SaaS 製品に対応しています。 どのプラットフォームであっても統一された構文で IaC 化を実現することが可能で、各プラットフォームごとの IaC ツール / サービスを使うための学習コストを削減することができます。 ご参考までに、メジャーなパブリッククラウドでは、それぞれの IaC ツール / サービスが提供されていますが、このいずれのパブリッククラウドも Terraform は対応しています。 プラットフォーム IaC ツール / サービス AWS AWS CloudFormation 、 AWS Cloud Development Kit (AWS CDK) など Microsoft Azure Azure Resource Manager (ARM) など Google Cloud Cloud Development Manager Terraform の種類 Terraform は大きく分けて 3 種類あります。 Terraform OSS 名前の通り、 OSS として提供されている形態です。 Terraform Enterprise 商用版の Terraform で、自ホストにインストールして利用する形態です。 Terraform Cloud 名前から想像できるかもしれませんが、 Cloud として提供されている形態です。 HashiCorp 社 が運用する Terraform のマネージドサービスです。 Terraform Cloud Business とは 上述した Terraform Cloud の中にもいくつかのプランがあります。 プランごとに料金はもちろん、利用できる機能やサポートが異なります。(詳細は リンク先 をご参照ださい。) Terraform Cloud Business はその中の 1 つのプランです。 後述していますが、様々な機能が利用可能となっています。 本記事では、この Terraform Cloud Business について、Terraform OSS との比較を交えてご紹介していきます。 Terraform OSS の利用イメージ まずは Terraform OSS をそのまま利用した場合の利用イメージを共有したいと思います。 Terrafrom OSS の利用フロー 担当者Aさんが、Terraform のコードを書いて、必要な変数やシークレットを用意して、Plan を実行し確認して… 次に承認を得るために、コードと Plan 結果を添えて、管理者に承認依頼をして… 管理者さんは、メールをもとに内容を確認しますが、機械的ではなく人間の目によるチェックを実施し… 問題があれば差戻をし、問題なければ添付ファイルもとに Apply を CLI で実行し… 比較のためにもあえて、シンプルな利用イメージとしてますが、IaC としてコード化しているにも関わらず、実行に至るまでのプロセスは手動となっており、その分ヒューマンエラーの可能性も大きくなってしまいます。 また、今回は登場人物は 2 人だけ、かつ、やりとりするコードも少数のイメージになっていますが、実際はこれが複数かつ並列に走っていくことになり、より複雑になっていきます。 Terraform OSS を利用している場合の課題 Terraform OSS を利用している場合の課題 上述のフローをもとにした際に生じる課題を挙げてみます。 人的なRV依頼 / 実施 メールを介したレビュー依頼や、人の目によるチェックなどは、どうしてもヒューマンエラーが生じてしまいます。 コード / シークレットの管理(バージョン管理や自動化) バージョン管理をしないと事故のもとになります。ただ一方で、シークレットは平文管理は望ましくないです。 操作ログや監査、変更の通知など 有事に備える意味でも、いつ、誰が、何をしたのかは把握が必要です。 CLI による操作/管理 CLI でも操作は可能ですが、視覚的には見づらくなってしまいます。GUI で直観的に情報に辿りつける方が優しいです。 ステートファイルの管理 Terraform を使う以上、ステートファイル ( tfstate ) の管理は必須です。不用意に変更できないように管理する必要があります。 Terraform外の変更管理 急ぎの対応などは、リソースを直接修正してしまいがちですが、そうすると Terraform との差分が出てきてしまいます。 忘れることなくて追い付きの Terraform 修正を抜け漏れなく対応できればいいですが… Terraform Cloud Business の利用イメージと課題への対応 上述の課題を踏まえて、Terraform Cloud Business を利用した場合に置き換えてみます。 文章での説明の前に、課題の解決案を示したイメージを先に載せてみます。 Terrafrom Cloud の利用フロー 構成については、先程の OSS 利用時はコンピューターにしていた箇所を VCS ( Version Control System ) に置き換えていますが、OSS で生じていた課題に対して、それぞれ解決策となるTerraform Cloud Businessの機能を示しています。 Terraform Cloud Businessの機能の紹介を兼ねて、先程の課題への解決アプローチを説明していきます。 人的なRV依頼 / 実施 への解決アプローチ 画像は Run Tasks Integration より引用 Plan と Apply の間に、レビューにあたる各種設定を組み込むことが可能になります。 画像の中にも載っていますが、具体的には Cost Estimation 、 Sentinel Policy ( Policy as Code ) 、 Run Tasks を設定することができ、それぞれで各種レビューの役割を果たすことができます。 Cost Estimation 主にIaaSを中心に、Applyを実行して作成されるリソースの月額コストの予想見積を出すことが可能になります。 数が多い、サイズが大きいリソースの作成を意図せず組み込んでしまった際に、実行前に検知することができます。 Sentinel Policy ( Policy as Code ) 標準的なセキュリティや社内ルールなどを Policy という形で定義 ( Policy as Code ) することで、それらに準拠しているかを機械的にチェックすることが可能になります。 そのチェック結果に応じて、後続のオペレーションをどうするか(実行中止や警告出力した上で続行など)を設定することもできます。 Policy as Code を実現する手段として、 Hashicorp 社が提供している Sentinel というツールや、Open Policy Agent (OPA) などを適用することができます。 Sentinel であれば、Cost Estimation のあとに実行されるため、コスト次第では実行させないなどの制御も可能です。 Run Tasks サードパーティーツールとの連携に加え、自前のツールによるチェックを組み込むことが可能になります。 上記の機能で補完できない部分を自作して使う方法などが考えられます。 コード / シークレットの管理(バージョン管理や自動化) への解決アプローチ 各種 VCS と連携することで、VCS 側のイベントを契機に Terraform を実行することが可能になります。 Pull Request を契機に Plan の自動実行、merge を契機に Apply を自動実行などができます。 コードの作成 -> Pull Request -> ( Plan ) -> レビュー -> merge -> ( Apply ) を一連で行うことができ、コードの変更履歴やレビュー指摘などを VCS 側に記録できます。後述しますが、Terraform での実行履歴を GUI を介して確認することも可能です。 なお、連携できるVCSは リンク先 をご確認ください。 また、必要なクレデンシャル情報や環境変数などの Variable を Terraform Cloud 内に保存することができ、コードとして管理する必要がなくなります。誤ってクレデンシャル情報を VCS に平文で格納してしまうなどの事故を防止します。 なお、保存された情報は Terraform Cloud 内で暗号化されるため、セキュリティも確保されています。 暗号化対象や暗号化方法は リンク先 をご確認ください。 操作ログや監査、変更の通知など への解決アプローチ 各種APIを用いて、ログや監査証跡を取得することが可能になります。 Audit Trails API を用いると、監査証跡として、いつ・誰が・何をしたかなどの情報を取得することができます。 監査証跡は Splunk と統合することもでき、定期的に Splunk に取り込まれ、可視化できます。 また、Terraform の各種イベントを契機にして、通知を発信することもできます。 Email / Slack / Microsoft Teams などは設定のみで通知可能となっていますし、Webhook にも対応しているため、それら以外の柔軟な対応も可能です。 CLI による操作/管理 への解決アプローチ Web UI が提供されており、各種設定や情報の参照が視覚的にわかりやすくなります。 ワークスペースごとの Plan や Apply などの Terraform の実行履歴なども一覧で見ることができます。 例えば、実行状態の確認をしたい場合を考えると、CLI だとコマンドを確認する必要がありますが、GUI であればクリックしてアクセスするのみになります。 Terraform の CLI に慣れている人であれば、大きな違いはないかもしれませんが、慣れていない人や初学者にとっては、技術的にも心理的にもハードルが大きく下がります。 画像は Change Infrastructure より引用 また、ユーザー認証を SSO を利用して行うことができます。対応している連携先は、MS Azure AD / Okta / SAML があり、既存で利用しているものがあればそれを流用し ID 管理やガバナンスを実現できます。 それに加え、Teams 管理機能を用いて権限管理(認可)を設定できます。ユーザーは Team に所属する必要があり、その Team 単位に実行権限やアクセスできるワークスペースの設定などができ、適切なアクセス管理を実現できます。 ステートファイルの管理 への解決アプローチ 文字通りになりますが、ステートファイル ( tfstate ) の管理をマネージド化し、 Terraform Cloud 上での管理にできます。 これによりローカル環境に保存していた場合の共有管理や排他制御の必要性がなくなります。 (必要性自体は変わっていませんが、その部分の管理を利用者側でする必要がなくなります) ステートファイルには、コードによっては DB の管理パスワードなど機密情報が含まれる場合があり、暗号化やアクセス制御も重要になります。 Terraform Cloud上での管理にすれば、暗号化の実施やアクセス制限、監査証跡から追跡することが可能になり、より厳格な管理が可能になります。 Terraform外の変更管理 への解決アプローチ ドリフト検出機能 ( Drift Detection ) を利用することで、Terraform 外からの変更を監視 / 検出することが可能になります。 利用者側でスケジュール定義はできませんが、24時間ごとに実行され継続的なドリフト検出ができ、ドリフト検出をした際には、上述の通知機能により通知することも可能です。 実際にドリフトを検出をした場合には、そのドリフトへの対応が必要になりますが、以下の 2 つの選択肢があります。 ドリフト検出の変更結果を受け入れる 変更内容(実リソースの内容)を正として、ステートファイルを更新します。 Refresh State にて実行することでステートファイルを、実リソースに合わせることができます。 ただし、実際のコード ( tfファイル ) までは修正されないため、そこは手動で修正する必要があります。 ドリフト検出の変更結果を受け入れない 実際のコード ( tfファイル ) を正とします。 既存のコードを用いて改めてApplyすることで、実リソースが更新され、あるべき姿の状態になります。 Terraform Cloud Business と プロビジョニング先の通信 上述の通り Terraform Cloud Business を用いると、Terraform OSS で抱えていた課題を解決してくことができます。 一方で、Terraform を Cloud 環境に移行することにより、Terraform OSS にはなかった通信経路も必要になります。 Plan や Apply 、ドリフト検出等により、Terraform Cloud からプロビジョニング先に対してアクセスが発生します。 その際のアクセスイメージは以下の通りです。 デフォルトのアクセスイメージ プロビジョニング先から見ると、Terraform Cloud 側の IP アドレスから各種APIを実行されていることになります。 プロビジョニング先の設定によりソース IP アドレス制限などをしている場合は、Terraform Cloud 側の IP アドレスを許可しておく必要があります。 なお、Terraform Cloud 側の IP アドレスは可変となっておりますが、 IP Range API を通して取得することができます。 頻繁な更新があるわけではないとドキュメントにも記載はありますが、変更される可能性はあるため考慮する必要があります。 しかし、環境によってはこれらの要件が受け入れがたい場合もあると思います。 その場合は、Terraform Cloud Business で利用可能な Terraform Cloud Agent という選択肢があります。 その際のアクセスイメージは以下の通りです。 Terraform Cloud Agentのアクセスイメージ Terraform Cloud Agent を利用すると、Terraform Cloud からのインバウンドアクセスはなくなり、アウトバウンド通信のみにすることができます。 プロビジョニング先から見ると、Terraform Cloud Agent の配置先が各種APIの実行元となります。 図のように、プロビジョニング先 = Terraform Cloud Agent の配置先とすれば、内部から API を発行していることになります。 これにより IP 制限がかけられている環境であっても、必要最低限の対応で利用することが可能になります。 ただし、Terraform Cloud Agent は VCS とは直接通信することはできません。 VCS 連携する場合は、VCS に対してTerraform Cloud 側の IP アドレスからのアクセスを許可しておく必要があります。 特に、VCS をプロビジョニング先やオンプレなどに構築している場合は、VCS に限り Terraform Cloud からのインバウンドアクセスの許可が必須となるのでご注意ください。 VCS 連携時のアクセスイメージ まとめ 改めてにはなりますが、本記事では Terraform Cloud Business についてご紹介させて頂きました。 今回紹介できた内容は一部に過ぎず、その他にも多種多様な機能が提供されていますし、続々とアップデートされています。 直近で発表のあった以下のアップデートなども、個人的には管理面やセキュリティ面で大きなアップデートだと思っています。 Terraform Cloud Adds Dynamic Provider Credentials for Vault and Official Cloud Providers Terraform Cloud Adds ‘Projects’ to Organize Workspaces at Scale これらのアップデートがいち早く享受できることも Cloud 利用の大きなメリットの 1 つですね。今後も注目して行きたいと思います。 最後までご覧いただきありがとうございました。本記事がどなたかの一助となれれば幸いです。 浅香 樹 みずほリサーチ&テクノロジーズ 先端技術研究部に所属。2019年から、CCoEとしてAWSの社内利活用の推進に従事。 Google Cloudは2022年より利用開始し、それを機にTerraformにも取り組んでいます。
アバター
G-gen の藤岡です。当記事と2回に分けて Google Cloud(旧称 GCP)の Compute Engine(以下 GCE)において、Windows Server のオンデマンドライセンスを使用した場合のライセンス認証の概要と設定について紹介します。当記事は「概要」についてです。 はじめに サービスの概要 GCE とは OS イメージ Windows Server VM における Microsoft ライセンス 認証方法 ライセンスの更新と有効期限 確認方法 サーバーマネージャー 設定 コマンドプロンプト PowerShell ログから見る Windows Server VM から KMS サーバーへの通信 前提 ファイアウォールルールロギング VPC フローログ KMS サーバーへアクセスできない時に認証強制をした際の挙動 はじめに GCE で Windows Server VM を使う際は、Microsoft ライセンスの認証をする必要があります。ライセンス認証がされていない場合、以下の記事のようなエラーが出力されます。 blog.g-gen.co.jp 当記事では、上記の記事を踏まえライセンス認証の概要や確認方法について紹介します。今後の「設定編」では、 外部 IP アドレスを持たない Windows Server VM でライセンス認証を行う方法 について紹介します。 サービスの概要 GCE とは GCE は、 Google Cloud が提供する仮想サーバーのサービスです。 GCE について詳しく知りたい方は以下の記事をご参照ください。 blog.g-gen.co.jp blog.g-gen.co.jp OS イメージ GCE では、Debian 系や Red Hat 系の Linux ディストリビューションから Windows Server 等の OS イメージが提供されています。 イメージには無料で使えるものと、プレミアムイメージとして使用料が発生するものがあります。 当記事で扱う Windows Server はプレミアムイメージであり、プレミアムイメージの場合、 Spot VM (および プリエンプティブル VM )によってイメージのコストが削減はされません。また、 無料トライアルは適用されません 。 参考: オペレーティング システムの詳細 / ディスクとイメージの料金 Windows Server VM における Microsoft ライセンス 認証方法 前述の通り、Windows Server VM を使う際は、Microsoft ライセンスの認証をする必要があります。 ライセンスオプションの詳細は ドキュメント に記載の通りで、当記事ではその中から Google から Windows Server のオンデマンド ライセンスを購入した場合 のライセンス認証について紹介します。 Microsoft ライセンス認証は、Windows Server VM が Windows Key Management Service(KMS)サーバーと通信することでライセンス認証および更新することができます。 Google Cloud の KMS サーバーの情報は以下の通りです。 項目 値 エンドポイント名 kms.windows.googlecloud.com IP アドレス 35.190.247.13/32 ポート 1688 プロトコル TCP 参考: Windows Server インスタンスの作成 / ライセンスについて ライセンスの更新と有効期限 Windows Server VM は、KMS サーバーにアクセスをして、 7日ごとにライセンスを更新 (下部キャプチャの 更新間隔 項目)します。ライセンスの有効期限は、180日です。Windows Server VM(Windows Server 2022 Datacenter) を作成直後にライセンスの認証状況を確認すると以下のようになっています(表示言語は 日本語 に変更)。 認証状況(GCE) 以下、一部抜粋します。 項目( 英語表記の場合) 値(英語表記の場合) ライセンスの状態 (License Status) ライセンスされています (Licensed) ボリュームライセンス認証の有効期限 (Volume activation expiration) 180日 (180 day(s)) 登録 KMS コンピューター名 (Registered KMS machine name) kms.windows.googlecloud.com:1688 KMS コンピューターの IP アドレス (KMS machine IP address) 35.190.247.13 ライセンス認証の間隔 (Activation interval) 120 分 (120 minutes) 更新間隔 (Renewal Interval) 10080 分 (10080 minutes) 以上の有効期限や更新間隔等の結果は、Microsoft の ドキュメント に記載の通りです。 例として AWS EC2 の Windows Server VM(Windows Server 2022 Base)の場合のライセンス認証状況は以下の通りです(表示言語は 日本語 に変更)。 GCE で確認した、登録 KMS コンピューター名 と KMS コンピューターの IP アドレス が異なることが確認できます。 認証状況(EC2) 参考: Windows Serverを完全日本語化してみた 確認方法 Windows Server VM のライセンスの認証状況の確認方法を4種類紹介します。 全てライセンス認証されている状態の表記 です。 サーバーマネージャー サーバーマネージャーで プロダクト ID から確認できます。 サーバーマネージャーで確認 設定 スタートメニューで 設定 > 更新とセキュリティ > ライセンス認証 から確認できます。 設定から確認 コマンドプロンプト コマンドプロンプトで slmgr /dlv を実行すると Windows Script Host が起動し、 ライセンスの状態 から確認できます。 コマンドプロンプトで確認 PowerShell PowerShell で Get-CimInstance -ClassName SoftwareLicensingProduct | where {$_.PartialProductKey} | select LicenseStatus を実行することで確認できます。 PowerShell で確認 (実行例) PS C:\Users\fujioka> Get-CimInstance -ClassName SoftwareLicensingProduct | where {$_.PartialProductKey} | select LicenseStatus LicenseStatus ------------- 1 PS C:\Users\fujioka> LicenseStatus の説明は ドキュメント をご参照ください。 ログから見る Windows Server VM から KMS サーバーへの通信 前提 1. KMS サーバー(35.190.247.13/32)へ 1688/tcp の下り通信を明示的に許可、 ファイアウォールルールロギング を有効化したファイアウォールルール(ルール名: allow-kms-egress )を作成 (※ 以降一部 xxxxx として伏せ字) fujioka @ cloudshell :~ ( xxxxx )$ gcloud compute firewall - rules list -- filter =" name = 'kms' " -- format = json [ { " allowed ": [ { " IPProtocol ": " tcp ", " ports ": [ " 1688 " ] } ] , " creationTimestamp ": " 2023-02-25T23:17:58.678-08:00 ", " description ": "", " destinationRanges ": [ " 35.190.247.13/32 " ] , " direction ": " EGRESS ", " disabled ": false , " id ": " xxxxx ", " kind ": " compute#firewall ", " logConfig ": { " enable ": true , " metadata ": " INCLUDE_ALL_METADATA " } , " name ": " allow-kms-egress ", " network ": " https://www.googleapis.com/compute/v1/projects/xxxxx/global/networks/default ", " priority ": 1000 , " selfLink ": " https://www.googleapis.com/compute/v1/projects/xxxxx/global/firewalls/allow-kms-egress " } ] fujioka @ cloudshell :~ ( xxxxx )$ 参考: gcloud compute firewall-rules list 2. 対象サブネットで VPC フローログ を有効化 logConfig が "enable": true になっています。サンプリングレートは 1.0(100%)としていますが、取得できるログは平均で 3% 程度のためご注意ください。 fujioka @ cloudshell :~ ( xxxxx )$ gcloud compute networks subnets list -- filter =" name = 'default' " -- format = json | jq - r '.[] | select(.region == "https://www.googleapis.com/compute/v1/projects/${PROJECT_ID}/regions/asia-northeast1")' { " creationTimestamp ": " 2022-09-06T02:45:41.176-07:00 ", " enableFlowLogs ": true , " fingerprint ": " xxxxx ", " gatewayAddress ": " 10.146.0.1 ", " id ": " xxxxx ", " ipCidrRange ": " 10.146.0.0/20 ", " kind ": " compute#subnetwork ", " logConfig ": { " aggregationInterval ": " INTERVAL_5_SEC ", " enable ": true , " flowSampling ": 1 , " metadata ": " INCLUDE_ALL_METADATA " } , " name ": " default ", " network ": " https://www.googleapis.com/compute/v1/projects/xxxxx/global/networks/default ", " privateIpGoogleAccess ": true , " privateIpv6GoogleAccess ": " DISABLE_GOOGLE_ACCESS ", " purpose ": " PRIVATE ", " region ": " https://www.googleapis.com/compute/v1/projects/xxxxx/regions/asia-northeast1 ", " selfLink ": " https://www.googleapis.com/compute/v1/projects/xxxxx/regions/asia-northeast1/subnetworks/default ", " stackType ": " IPV4_ONLY " } fujioka @ cloudshell :~ ( xxxxx )$ 参考: gcloud compute networks subnets list / ログのサンプリングと処理 3. Windows Server VM の情報 項目 値 VM 名 windows-server 内部 IP アドレス 10.146.15.210 外部 IP アドレス 34.146.103.xx VPC default(デフォルトで作成されている VPC) 次の項で Cloud Logging の出力結果を記載します。 ファイアウォールルールロギング KMS サーバー(35.190.247.13)へアクセスしていることがわかります。 { " insertId ": " xxxxx ", " jsonPayload ": { " disposition ": " ALLOWED ", " connection ": { " dest_port ": 1688 , " src_port ": 49696 , " protocol ": 6 , " dest_ip ": " 35.190.247.13 ", " src_ip ": " 10.146.15.210 " } , " rule_details ": { " reference ": " network:default/firewall:allow-kms-egress ", " destination_range ": [ " 35.190.247.13/32 " ] , " action ": " ALLOW ", " ip_port_info ": [ { " ip_protocol ": " TCP ", " port_range ": [ " 1688 " ] } ] , " direction ": " EGRESS ", " priority ": 1000 } , " remote_location ": { " continent ": " America ", " country ": " usa " } , " instance ": { " project_id ": " xxxxx ", " region ": " asia-northeast1 ", " vm_name ": " windows-server ", " zone ": " asia-northeast1-b " } , " vpc ": { " subnetwork_name ": " default ", " project_id ": " xxxxx ", " vpc_name ": " default " } } , " resource ": { " type ": " gce_subnetwork ", " labels ": { " project_id ": " xxxxx ", " subnetwork_name ": " default ", " location ": " asia-northeast1-b ", " subnetwork_id ": " xxxxx " } } , " timestamp ": " 2023-02-26T10:47:49.672982838Z ", " logName ": " projects/xxxxx/logs/compute.googleapis.com%2Ffirewall ", " receiveTimestamp ": " 2023-02-26T10:47:55.869479250Z " } VPC フローログ 同様に、KMS サーバー(35.190.247.13)へアクセスしていることがわかります。 { " insertId ": " xxxxx ", " jsonPayload ": { " start_time ": " 2023-02-26T10:47:49.687694347Z ", " end_time ": " 2023-02-26T10:47:50.430404649Z ", " src_instance ": { " region ": " asia-northeast1 ", " project_id ": " xxxxx ", " zone ": " asia-northeast1-b ", " vm_name ": " windows-server " } , " connection ": { " src_ip ": " 10.146.15.210 ", " dest_ip ": " 35.190.247.13 ", " protocol ": 6 , " dest_port ": 1688 , " src_port ": 49696 } , " dest_location ": { " continent ": " America ", " asn ": 15169 , " country ": " usa " } , " packets_sent ": " 8 ", " reporter ": " SRC ", " src_vpc ": { " vpc_name ": " default ", " subnetwork_name ": " default ", " project_id ": " xxxxx " } , " bytes_sent ": " 0 " } , " resource ": { " type ": " gce_subnetwork ", " labels ": { " location ": " asia-northeast1-b ", " project_id ": " xxxxx ", " subnetwork_id ": " xxxxx ", " subnetwork_name ": " default " } } , " timestamp ": " 2023-02-26T10:48:03.213215298Z ", " logName ": " projects/xxxxx/logs/compute.googleapis.com%2Fvpc_flows ", " receiveTimestamp ": " 2023-02-26T10:48:03.213215298Z " } KMS サーバーへアクセスできない時に認証強制をした際の挙動 一時的に KMS サーバーへ下り 1688/tcp を拒否(deny)するファイアウォールールを作成し、ライセンス認証を強制した際の挙動を確認します。以下のようなエラーが出力された場合は、KMS サーバーへアクセスができていないため、ファイアウォールルールやルートを確認してください。 TcpTestSucceeded : False となる。 # (事前確認)PowerShell で疎通確認 PS C:\Users\fujioka> Test-NetConnection 35.190.247.13 -Port 1688 WARNING: TCP connect to (35.190.247.13 : 1688) failed ComputerName : 35.190.247.13 RemoteAddress : 35.190.247.13 RemotePort : 1688 InterfaceAlias : Ethernet SourceAddress : 10.146.15.210 PingSucceeded : True PingReplyDetails (RTT) : 1 ms TcpTestSucceeded : False Error: 0x80070005 Access denied: the requested action requires elevated privileges となる。 # KMS のサーバー IP アドレスを設定 PS C:\Users\fujioka> cscript \windows\system32\slmgr.vbs /skms 35.190.247.13:1688 Microsoft (R) Windows Script Host Version 5.812 Copyright (C) Microsoft Corporation. All rights reserved. Error: 0x80070005 Access denied: the requested action requires elevated privileges PS C:\Users\fujioka> Error: 0xC004F074 The Software Licensing Service reported that the computer could not be activated. No Key Management Service (KMS) could be contacted. Please see the Application Event Log for additional information. となる。 # ライセンス認証を強制 PS C:\Users\fujioka> cscript \windows\system32\slmgr.vbs /ato Microsoft (R) Windows Script Host Version 5.812 Copyright (C) Microsoft Corporation. All rights reserved. Activating Windows(R), ServerDatacenter edition (ef6cfc9f-8c5d-44ac-9aad-de6a2ea0ae03) ... Error: 0xC004F074 The Software Licensing Service reported that the computer could not be activated. No Key Management Service (KMS) could be contacted. Please see the Application Event Log for additional information. PS C:\Users\fujioka> 参考: ライセンスについて 藤岡 里美 (記事一覧) クラウドソリューション部 接客業からエンジニアへ。2022年9月 G-gen にジョイン。Google Cloud 認定資格は全冠。2023 夏アニメのオススメは、ダークギャザリング。箏を習っています :) Follow @fujioka57621469
アバター
G-gen の藤岡です。当記事では、 Google Cloud(旧称 GCP)のノーコードツールである Google AppSheet(以下 AppSheet)の概要からはじめ方について紹介します。 概要 AppSheet とは データソース 4つのプラン AppSheet と Google Workspace 注意点 ユースケース はじめ方 概要 AppSheet とは AppSheet は、プログラミングの知識のない人や開発経験のない人でもマウス操作のみで簡単にアプリケーションを開発できるノーコードツールのサービスです。IT 部門ではなく、実際の業務を遂行する現場の社員がアプリケーションを作る、という「市民開発」を掲げてサービスが開始されました。 2020年に Google が AppSheet 社を買収し、Google のサービスに加わりました。従来使われていた Google のノーコードツールのサービスである App Maker は 2021年1月にサービスが終了 し、現在は AppSheet が使われています。 開発したアプリは、ブラウザやスマートフォン、タブレットで使うことができます。以下のようなステップでアプリを開発します。 開発の流れ また、 AppSheet Automation 機能を使うことで、アプリ内の一連のフローの中でメールや SMS 配信、データ変更等のイベントをトリガーに Google Apps Script を実行することもでき、業務改善により役立ちます。 以下のようにテンプレートも用意されているため、ゼロから自分たちで作る必要はありません。 www.appsheet.com データソース データソースには、Google のサービスである Google Sheets や BigQuery はもちろん、Microsoft Excel(Office 365 および SharePoint)や Amazon DynamoDB、Salesforce など多様なデータソースを選択することができます。 対応しているデータソースの詳細は以下のドキュメントをご参照ください。 support.google.com 4つのプラン AppSheet には以下の4つのプランがあります。プランによって使える機能が異なります。例として Apigee や BigQuery のコネクタを使うためには Enterprise Standard 以上のプランが必要です。また、アプリケーションのパフォーマンスのためのリソースの割り当てもプランによって異なるため、用途に合わせて選択してください。 Starter Core Enterprise Standard Enterprise Plus 各プランの料金の詳細は以下のドキュメントをご参照ください。 about.appsheet.com なお、アプリを公開する場合は利用機能に応じたプランに登録する必要がありますが、そこまでの アプリの開発とテストは無料 です。加入プランを問わず、全ての機能を無料で開発、テストすることができます。また、教育機関および非営利団体には割引があります。 参考: How to choose a subscription plan / AppSheet for non-profits AppSheet と Google Workspace Google Workspace (旧称 G Suite)の Enterprise Plus と Education Plus のプランには、前述の Core プランのライセンスが含まれています。そのため追加料金なしで、Core プランの機能を使うことができます。Enterprise Standard 以上の機能を使う場合には追加でライセンスを購入する必要があります。 参考: Google Workspace の各エディションの比較 注意点 2023年2月時点で、AppSheet は日本語に対応していません。そのため、公式ドキュメントや操作画面は英語表記です。 ユースケース AppSheet を使うことで、業務の効率化やデータの見える化を実現できるため、例えば以下のような場面で活用できます。 在庫管理 / 棚卸し 顧客情報管理 日報 予約管理 また、前述の AppSheet Automation を使うことで承認フローの自動化等も可能です。 はじめ方 URL にアクセスします。以降の UI は変わる場合があります。 Get started をクリックします。 始め方-2 サインインするアカウントを選択します。Google の他にも Microsoft や Apple アカウントも使用できます。今回は Google をクリックします。 始め方-3 対象の Google アカウントをクリックします。 始め方-4 AppSheet が Google アカウントへアクセスすることを許可します。 許可 をクリックします。 始め方-5 サインイン完了です。ここからアプリを作成することができます。 始め方-6 プランを確認します。My account > Billing から現在のプランを確認できます。 始め方-7 今回は、AppSheet の概要について紹介しました。今後、実際にアプリを作る方法についても紹介していきます。 藤岡 里美 (記事一覧) クラウドソリューション部 接客業からエンジニアへ。2022年9月 G-gen にジョイン。Google Cloud 認定資格は全冠。2023 夏アニメのオススメは、ダークギャザリング。箏を習っています :) Follow @fujioka57621469
アバター
当記事は みずほリサーチ&テクノロジーズ × G-gen エンジニアコラボレーション企画 で執筆されたものです。 G-gen の又吉です。当記事では、Cloud Functions を用いて Google Workspace もしくは Cloud Identity (以下、Google Workspace 等) で管理している Google アカウント / グループの棚卸し を日次で行う方法を紹介します。 なお、Google Cloud 側の設定は Terraform を使用します。 概要 背景 今回やること 準備 フォルダ階層 Python ソースコード main.py requirements.txt 説明 Terraform コード main.tf 実行 Terraform 実行 Google Workspace 管理コンソールでの設定 カスタムロールの作成 ロールの付与 確認 概要 背景 Google Cloud を利用する際、企業の監査要件等で Google アカウント (ID) / グループの管理・棚卸しを定期的に行わければいけない時はないでしょうか? 同じパブリッククラウドの AWS では、 ID は IAM User 、グループは IAM Group と呼ばれ AWS アカウント (テナント) の中で管理されているため、AWS のインベントリサービスである AWS Config を用いることで、ID / グループ のスナップショットも AWS Config ひとつで取得できます。 しかし、Google Cloud の場合、Google アカウント / グループは Google Cloud からは分離 されており、Google Workspace 等で管理されております。 したがって、Google Cloud のインベントリサービスである Cloud Asset Inventory を用いても、Google Cloud の組織配下のリソースやポリシーのメタデータは取得できるものの、 組織の Google アカウント / グループは取得できません 。 Google Cloud の ID や IAM についての概念を、AWS との違いと合わせて詳しく知りたい方は以下の記事をご参照下さい。 blog.g-gen.co.jp 今回やること 今回は、Cloud Functions を用いて Directory API を実行し、Google Workspace 等で管理されている Google アカウント / グループの棚卸しを日次で行う方法をご紹介します。 構成図 準備 フォルダ階層 フォルダ階層は以下の通りです。 cloud_identity_inventory ├── terraform │ └── main.tf └── python_source_code ├── main.py └── requirements.txt cloud_identity_inventory というフォルダ配下に、 Terraform を実行する terraform フォルダと、Cloud Functions で使うソースファイルを格納している python_source_code フォルダを作ります。 Cloud Functions を Terraform からデプロイする際、関数のソースコードの場所を Cloud Storage もしくは Cloud Source Repository から指定でき、今回は Cloud Storage から取得するように構成しています。 Terraform を実行すると、以下の流れで処理が進みます。 python_source_code 配下のファイルを圧縮した ZIP ファイルを main.tf と同階層に作成 ソースコード格納用 Cloud Storage バケットを作成し 1. で作成した ZIP ファイルを格納 2 でバケットに格納されたソースコードを指定して Cloud Functions をデプロイ Python ソースコード Python ソースコードは以下の通りです。 main.py import os from datetime import datetime, timedelta import pandas as pd import functions_framework from googleapiclient.discovery import build from google.cloud import storage CUSTOMER_ID = os.environ[ "CUSTOMER_ID" ] UPLOAD_BUKET_NAME = os.environ[ "UPLOAD_BUKET_NAME" ] # storage クライアントのインスタンス化 storage_client = storage.Client() def gcs_upload (kind, file_name, df): # 本日の日付を取得し、["YYYY", "MM", "DD"] のリストで格納 now = (datetime.utcnow() + timedelta(hours= 9 )).strftime( "%Y-%m-%d" ) now_li = now.split( "-" ) # file_name を作成 kind = kind file_name = f "{kind}/{now_li[0]}/{now_li[1]}/{now_li[2]}/{file_name}.csv" # csv に変換し GCS にアップロード content_type = "text/csv" bucket = storage_client.get_bucket(UPLOAD_BUKET_NAME) blob = bucket.blob(file_name) blob.upload_from_string( df.to_csv(index= False , header= True , sep= "," ), content_type=content_type ) @ functions_framework.cloud_event def main (cloud_event): # service オブジェクトの作成 service = build( "admin" , "directory_v1" ) # ユーザー一覧を取得 users_list_result = service.users().list(customer=CUSTOMER_ID).execute() users = users_list_result.get( "users" , []) # ユーザー数が 0 のときは処理を終了 if len (users) == 0 : return "User does not exist." users_df = pd.DataFrame.from_records(users) gcs_upload(kind= "users_list" , file_name= "users_list" , df=users_df) # グループ一覧を取得 groups_list_result = service.groups().list(customer=CUSTOMER_ID).execute() groups = groups_list_result.get( "groups" , []) # グループ数が 0 のときは処理を終了 if len (groups) == 0 : return "Group does not exist." groups_df = pd.DataFrame.from_records(groups) gcs_upload(kind= "groups_list" , file_name= "groups_list" , df=groups_df) # グループ配下のメンバーを取得 for i in range ( len (groups_df)): groupKey = groups_df.at[i, "id" ] groupName = groups_df.at[i, "name" ] members_list_result = service.members().list(groupKey=groupKey).execute() members = members_list_result.get( "members" , []) members_df = pd.DataFrame.from_records(members) gcs_upload(kind= "members_list" , file_name=groupName, df=members_df) return "ok" requirements.txt DateTime==5.0 pandas==1.5.2 functions-framework==3.3.0 google-api-python-client==2.72.0 google-auth==2.16.0 google-cloud-storage==2.7.0 説明 今回、Python クライアントライブラリを用いて以下の 3 つの Directory API を実行しています。 No Method 名 説明 1 users.list 削除されたユーザーまたはドメイン内のすべてのユーザーのリストを取得します。 2 groups.list ドメインまたはユーザーのすべてのグループのリストを取得します。 3 members.list グループ内のすべてのメンバーのリストを取得します。 groups.list のレスポンスからはグループの一覧は取得できますが、グループに所属しているユーザー情報が取得できないため、groups.list で取得したグループ数だけ members.list の処理を回すようにしています。 そうすることで、グループに所属しているメンバー一覧も取得できます。 データの形成については、後々分析しやすいようデータは csv 形式に変換して Cloud Storage へアップロードしています。 また、生データをそのままの形式で蓄積しつつ、分析しやすいようにデータ形成も行いたい場合は以下のアーキテクチャも検討できます。 今回はサンプルのため実装しておりませんが、 Directory API を実行する時の注意点として、1 回のリクエストで大量のレスポンスデータを取得することが保証されていない点です。 本番運用では、 nextPageToken/pageToken を考慮し、大量のレスポンスデータは複数回に分けて取得する必要があります。 Terraform コード main.tf # ローカル変数の定義 locals { project_id = “<プロジェクト ID>” customer_id = “<顧客 ID>” } # terraform / providers の設定 terraform { required_providers { google = { source = "hashicorp/google" version = ">= 4.0.0" } } required_version = ">= 1.3.0" backend "gcs" { bucket = "<state 管理バケット名>" prefix = "<任意の prefix>" } } ## プロジェクトの作成 resource "google_project" "poc" { name = local.project_id project_id = local.project_id folder_id = "<folder_id>" billing_account = "<billing_account>" } ## API 有効化 module "audit_project_services" { source = "terraform-google-modules/project-factory/google//modules/project_services" version = " 14.1.0 " project_id = google_project.poc.project_id enable_apis = true activate_apis = [ "run.googleapis.com" , "cloudfunctions.googleapis.com" , "eventarc.googleapis.com" , "cloudscheduler.googleapis.com" , "admin.googleapis.com" , "artifactregistry.googleapis.com" , "pubsub.googleapis.com" , "cloudbuild.googleapis.com" , "storage.googleapis.com" , ] disable_services_on_destroy = false } ## ソースコード格納用バケットの作成 # バケットの作成 resource "google_storage_bucket" "source" { project = local.project_id location = "ASIA-NORTHEAST1" name = "ggen-matayuuu-admin-sdk-api-gcf-source-002" force_destroy = true } # Cloud Functions で使うソースコードを ZIP 化 data "archive_file" "source_code" { type = "zip" source_dir = "../python_source_code" output_path = "./source_code.zip" } # ZIP 化したソースコードをバケットに追加 resource "google_storage_bucket_object" "source_code" { name = "code/function-run-1.${data.archive_file.source_code.output_md5}.zip" bucket = google_storage_bucket.source.name source = data.archive_file.source_code.output_path } ## API で取得したデータ格納用バケットの作成 # バケットの作成 resource "google_storage_bucket" "cloud_identity_inventory" { project = local.project_id location = "ASIA-NORTHEAST1" name = "ggen-cloud-identity-inventory" force_destroy = true } ## Cloud Functions と Eventarc に付与するサービスアカウントを作成 # サービスアカウント作成 resource "google_service_account" "sa_gcf" { project = local.project_id account_id = "sa-gcf" display_name = "Cloud Functions & Eventarc 用サービスアカウント" } # cloud function 実行権限を付与 resource "google_project_iam_member" "invoke_gcf" { project = local.project_id role = "roles/run.invoker" member = "serviceAccount:${google_service_account.sa_gcf.email}" } # データ格納用バケットのストレージ管理者権限を付与 resource "google_storage_bucket_iam_member" "member" { bucket = google_storage_bucket.cloud_identity_inventory.name role = "roles/storage.admin" member = "serviceAccount:${google_service_account.sa_gcf.email}" } ## Cloud Functions の作成 # pub/subトピックの作成 resource "google_pubsub_topic" "topic" { project = local.project_id name = "cloud-scheduler" } # cloud schedulerの作成 resource "google_cloud_scheduler_job" "job" { project = local.project_id region = "asia-northeast1" name = "invoke-gcf" schedule = "0 10 * * *" pubsub_target { topic_name = google_pubsub_topic.topic.id data = base64encode("run") } } # cloud functionsの作成 resource "google_cloudfunctions2_function" "execute_admin_sdk_api" { project = local.project_id location = "asia-northeast1" name = "execute-admin-sdk-api" build_config { runtime = "python310" entry_point = "main" source { storage_source { bucket = google_storage_bucket.source.name object = google_storage_bucket_object.source_code.name } } } service_config { max_instance_count = 3 min_instance_count = 1 available_memory = "256M" timeout_seconds = 60 ingress_settings = "ALLOW_ALL" all_traffic_on_latest_revision = true service_account_email = google_service_account.sa_gcf.email environment_variables = { CUSTOMER_ID = local.customer_id UPLOAD_BUKET_NAME = google_storage_bucket.cloud_identity_inventory.name } } event_trigger { trigger_region = "asia-northeast1" event_type = "google.cloud.pubsub.topic.v1.messagePublished" retry_policy = "RETRY_POLICY_DO_NOT_RETRY" pubsub_topic = google_pubsub_topic.topic.id service_account_email = google_service_account.sa_gcf.email } } 当記事では Terraform の概要等について触れないため、コマンドや tfstate ファイル等については以下の記事をご参照ください。 blog.g-gen.co.jp blog.g-gen.co.jp 実行 Terraform 実行 main.tf ファイルのある階層に移動した後、 terraform init で初期化し、 terraform plan でドライランを行い問題がなければ、 terraform apply で実環境に適用します。 Apply complete! Resources: 20 added, 0 changed, 0 destroyed. と表示されれば成功です。 matayuuu@penguin:~/cloud_identity_inventory/terraform$ terraform apply data.archive_file.source_code: Reading... 〜省略〜 Plan: 20 to add, 0 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 〜省略〜 Apply complete! Resources: 20 added, 0 changed, 0 destroyed. Google Workspace 管理コンソールでの設定 今回使用する Directory API は、 Google Workspace 等の ユーザー読み取り権限 と グループ読み取り権限 が必要です。 したがって、必要最小限の権限を持ったカスタムロールを作成し、Cloud Functions のサービスアカウントに付与します。 カスタムロールの作成 Google Workspace ( Cloud Identity ) 管理コンソール > [ アカウント ] > [ 管理者ロール ] > [ 新しいロールを作成 ] をクリック 任意の[ 名前 ]と[ 説明 ]を入力し、[ 続行 ]をクリック [ ユーザー / 読み取り ]と[ グループ / 読み取り ]を選択し、[ 続行 ]をクリック [ ロールを作成 ] をクリック ロールの付与 Google Workspace ( Cloud Identity ) 管理コンソール > [ アカウント ] > [ 管理者ロール ] > [ 先程作成したカスタムロール ] をクリック [ ロールを割り当て ] をクリック [ サービスアカウントへの割り当て ]をクリック [ <Cloud Functions のサービスアカウント> ]を入力し、[ 追加 ]を選択 [ ロールを割り当て ]をクリック ここまでで設定は完了です。 確認 日次で Cloud Scheduler が実行されるのですが、強制的に即時実行させてみます。 Google Cloud コンソール > [ Cloud Scheduler ] > [ 操作 ] の「︙」をクリック [ ジョブを強制実行する ] をクリック すると Cloud Scheduler から Pub / Sub を経由し Cloud Functions が実行されます。 Cloud Storage バケットを確認すると、 3 つのフォルダ ができてました。 また、それぞれのフォルダ配下には、 年 / 月 / 日 / csv ファイル が存在しました。 このように、組織で管理する Google アカウント / グループ の棚卸しを自動化することができます。 又吉 佑樹 (記事一覧) クラウドソリューション部 はいさーい!沖縄出身のクラウドエンジニアです!! 前職は SIer テクニカルセールス。Google Cloud の魅力に惚れ、技術を磨きたくセールスからエンジニアへ転身。Google Cloud 認定資格は全 11 資格保有。最近は AI/ML 分野に興味あり。 Follow @matayuuuu
アバター
G-gen の佐々木です。当記事では Google Cloud ( 旧称 GCP ) の代表的サービスである Google Kubernetes Engine ( GKE ) の限定公開クラスタを、 Terraform を使用して作成していきます。 前提知識 Google Kubernetes Engine とは Terraform とは なぜ 検証用の GKE クラスタを Terraform で作成するのか 構成図 Terraform のディレクトリ構成 Terraform 詳細 environments/test/main.tf environments/test/variables.tf および modules/variables.tf environments/test/terraform.tfvars modules/vpc.tf modules/gke.tf modules/bastion.tf modules/nat.tf modules/outputs.tf Terraform の実行 前提知識 Google Kubernetes Engine とは Google Kubernetes Engine (以下、GKE ) は、Google Cloud のインフラストラクチャ上に構築された マネージドな Kubernetes クラスタ を利用することができるサービスです。 サービスの詳細は以下の記事で解説しています。 blog.g-gen.co.jp Terraform とは Terraform は Infrastructure as Code (IaC) を実現するオープンソースのツールです。 Terraform の概要は以下の記事で解説しています。 blog.g-gen.co.jp なぜ 検証用の GKE クラスタを Terraform で作成するのか GKE クラスタを作成する際、設定できるパラメータの項目数は非常に多く、検証のたびに 同じ構成でクラスタを作成するのは非常に煩雑な作業 になります。 クラスタのパラメータ以外にも、クラスタを配置する VPC とサブネットの作成が必要となり、限定公開クラスタを使用したい場合は、以下のリソースも追加で必要となる場合があります。 コントロールプレーンにプライベート IP で接続するための 踏み台 VM クラスタからインターネットにアクセスするための Cloud NAT このように GKE クラスタ以外のリソースが増えてくると、環境の構築手順が煩雑になり、またクラスタが不要になった際に、 リソースの削除漏れ が起こりやすくなります。 リソースの管理に Terraform を使用することによって、コマンド 1 つで すべてのリソースを一括で作成/削除する ことができます。 また、GKE では、Standard モードのクラスタであれば、ノードとして起動される Commpute Engine インスタンスの料金が常に発生します。 そして Standard モード、Autopilot モードのどちらであっても、 クラスタ管理手数料 として $74.40 / 月 の料金が発生します (最初の 1 クラスタのみ条件を満たせば無料枠あり)。 簡単な検証目的の利用であれば、料金節約のためにこまめにクラスタを削除することが望ましいでしょう。 構成図 当記事では、限定公開の GKE クラスタを Autopilot モードで作成していきます。 GKE ノードが配置される VPC に踏み台 VM を作成し、踏み台から Kubernetes の管理操作を行えるようにします。 また、クラスタからインターネットにアクセスできるように、Cloud NAT を作成します。 構成図 Terraform のディレクトリ構成 ファイルを environments/ ディレクトリと modules/ ディレクトリに分けています。 root/ ├ environments/ │ └ test/ │   ├ main.tf │   ├ terraform.tfvars │   └ variables.tf └ modules/ ├ bastion.tf ├ gke.tf ├ nat.tf ├ outputs.tf ├ variables.tf └ vpc.tf 各 Google Cloud リソースを定義するモジュールを modules/ ディレクトリに集約し、 environments/ ディレクトリ配下にある test/main.tf から各モジュールを呼び出します。 同一構成の環境を異なる設定値で作成したい場合は、 environments/ 配下の test/ ディレクトリを複製し、 terraform.tfvars に記述されている設定値を書き換えます。 Terraform 詳細 environments/test/main.tf terraform.tfvars から変数を受け取り、 modules/ 配下にあるモジュールを呼び出して各リソースを作成します。 リソースの作成後、 modules/outputs.tf から受け取った出力を元に、作成した GKE クラスタに接続するためのコマンドをターミナルに出力します。 terraform { required_providers { google = { source = "hashicorp/google" version = ">= 4.0.0" } } required_version = ">= 1.3.0" backend "gcs" { # tfstate ファイルの保存先となる GCS バケット bucket = "tfstate" prefix = "gke-test" } } provider "google" {} module "modules" { source = "../../modules" # 変数を modules に渡す common = var.common vpc = var.vpc gke = var.gke bastion = var.bastion } # 全リソース作成後、GKE クラスタの接続コマンドを出力 output "command_to_connect_cluster" { value = "\n$ gcloud container clusters get-credentials $ { module.modules.cluster_name } --region $ { module.modules.cluster_location } --project $ { module.modules.cluster_project } \n" } environments/test/variables.tf および modules/variables.tf 変数を宣言するファイルで、変数の受け渡しのために environments/ 側と modules/ 側で同一のファイルを配置します。 変数をオブジェクト型で設定することで、リソースの種類ごとにパラメータをグループ分けすることができ、どのリソースに関するパラメータなのかが明瞭になります。 variable "common" { type = object ( { prefix = string # 固有のプレフィクス (任意の文字列) env = string # 環境名 ( dev、prod など) project = string # プロジェクト region = string # リージョン zone = string # ゾーン } ) description = "リソース共通の設定値" } variable "vpc" { type = object ( { subnet_cidr = string # サブネットの CIDR 範囲 ( GKE ノード、踏み台ホストが使用する IP 範囲) } ) description = "VPC の設定値" } variable "gke" { type = object ( { cluster_cidr = string # GKE Pod が使用する IP 範囲 service_cidr = string # GKE Service が使用する IP 範囲 master_cidr = string # コントロールプレーンが使用する IP 範囲 } ) description = "GKE の設定値" } variable "bastion" { type = object ( { machine_type = string # 踏み台 VM のマシンタイプ ssh_sourcerange = string # 踏み台 VM に SSH アクセスできるソース IP 範囲 } ) description = "踏み台 VM の設定値" } environments/test/terraform.tfvars variables.tf で宣言した変数にパラメータを代入します。 同一の構成で別のクラスタを作成したい場合は、 test/ ディレクトリを複製し、このファイルのパラメータを修正するだけで実現できるようにします。 common = { prefix = "ggen" env = "test" project = "myproject" region = "asia-northeast1" zone = "asia-northeast1-b" } vpc = { subnet_cidr = "192.168.100.0/24" } gke = { cluster_cidr = "172.16.0.0/16" service_cidr = "172.31.0.0/16" master_cidr = "192.168.200.0/28" } bastion = { machine_type = "e2-small" ssh_sourcerange = "35.235.240.0/20" # IAP の IP 範囲 } modules/vpc.tf GKE クラスタを配置する VPC リソースを作成します。 # GKE クラスタを作成する VPC resource "google_compute_network" "vpc_network" { project = var.common.project name = "vpc-$ { var.common.prefix } -$ { var.common.env } " auto_create_subnetworks = false } # GKE クラスタを作成するサブネット resource "google_compute_subnetwork" "subnet_asia_ne1" { project = var.common.project name = "subnet-$ { var.common.prefix } -$ { var.common.env } " ip_cidr_range = var.vpc.subnet_cidr region = var.common.region network = google_compute_network.vpc_network.id private_ip_google_access = true } modules/gke.tf 限定公開の GKE クラスタを Autopilot モードで作成します。 resource "google_container_cluster" "primary" { project = var.common.project name = "cluster-$ { var.common.prefix } -$ { var.common.env } -autopilot" enable_autopilot = true # Autopilot モードでクラスタを作成 location = var.common.region network = google_compute_network.vpc_network.id subnetwork = google_compute_subnetwork.subnet_asia_ne1.id ip_allocation_policy { cluster_ipv4_cidr_block = var.gke.cluster_cidr services_ipv4_cidr_block = var.gke.service_cidr } # 限定公開クラスタの設定 private_cluster_config { enable_private_nodes = true enable_private_endpoint = true master_ipv4_cidr_block = var.gke.master_cidr } master_authorized_networks_config { # コントロールプレーンへのアクセスを許可する IP 範囲 cidr_blocks { cidr_block = var.vpc.subnet_cidr # ノードと踏み台が作られるサブネットからのアクセスを許可 } } } modules/bastion.tf コントロールプレーンに接続する踏み台 VM を作成します。 起動スクリプトを使用して、Kubernetes の管理操作に必要なコマンドと GKE のプラグインをインストールしています。 # 踏み台 VM が使用するサービスアカウント resource "google_service_account" "bastion" { project = var.common.project account_id = "sa-$ { var.common.prefix } -$ { var.common.env } " display_name = "Service Account for bastion-$ { var.common.prefix } -$ { var.common.env } " } # サービスアカウントにロールを付与 resource "google_project_iam_member" "bastion" { project = var.common.project role = "roles/container.developer" # Kubernetes Engine デベロッパー ロール member = "serviceAccount:$ { google_service_account.bastion.email } " } # 踏み台 VM resource "google_compute_instance" "bastion" { project = var.common.project name = "bastion-$ { var.common.prefix } -$ { var.common.env } " machine_type = var.bastion.machine_type zone = var.common.zone tags = [ "ssh" ] # ネットワークタグ boot_disk { initialize_params { image = "debian-cloud/debian-11" # OS イメージ } } network_interface { subnetwork_project = var.common.project network = google_compute_network.vpc_network.name # VPC subnetwork = google_compute_subnetwork.subnet_asia_ne1.name # サブネット access_config {} # パブリック IP を付与 } metadata = { enable-oslogin = "true" # OS Login を有効化 } # 起動スクリプトで kubectl と GKE のプラグインをインストール metadata_startup_script = <<EOF #!/bin/bash sudo apt update sudo apt install kubectl sudo apt install google-cloud-sdk-gke-gcloud-auth-plugin EOF service_account { email = google_service_account.bastion.email scopes = [ "cloud-platform" ] } } resource "google_compute_firewall" "ssh" { project = var.common.project name = "vpc-$ { var.common.prefix } -$ { var.common.env } -ssh-allow" network = google_compute_network.vpc_network.name allow { protocol = "tcp" ports = [ "22" ] } source_ranges = [ var.bastion.ssh_sourcerange ] target_tags = [ "ssh" ] } modules/nat.tf クラスタからインターネットアクセスするための Cloud NAT を作成します。 Cloud NAT を使用するためには Cloud Router が必要になるため、このモジュールで一緒に作成します、 # Cloud Router resource "google_compute_router" "router" { project = var.common.project name = "router-$ { var.common.prefix } -$ { var.common.env } " region = var.common.region network = google_compute_network.vpc_network.id } # Cloud NAT resource "google_compute_router_nat" "nat" { project = var.common.project name = "nat-$ { var.common.prefix } -$ { var.common.env } " router = google_compute_router.router.name region = google_compute_router.router.region nat_ip_allocate_option = "AUTO_ONLY" source_subnetwork_ip_ranges_to_nat = "ALL_SUBNETWORKS_ALL_IP_RANGES" log_config { enable = true filter = "ERRORS_ONLY" } } modules/outputs.tf GKE クラスタの接続コマンドを出力するために必要なリソース情報を main.tf に返します。 output "cluster_name" { value = google_container_cluster.primary.name } output "cluster_location" { value = google_container_cluster.primary.location } output "cluster_project" { value = google_container_cluster.primary.project } Terraform の実行 terraform apply コマンドは environments/test/ ディレクトリで実行します。 実行が完了すると、ターミナルに以下のような文字列が出力されます。 Apply complete! Resources: 9 added, 0 changed, 0 destroyed. Outputs: command_to_connect_cluster = <<EOT $ gcloud container clusters get-credentials cluster-ggen-test-autopilot --region asia-northeast1 --project myproject EOT ここで出力された gcloud container clusters get-credentials コマンドを踏み台 VM で実行することで、作成した GKE クラスタに接続することができます。 佐々木 駿太 (記事一覧) G-gen 最北端、北海道在住のクラウドソリューション部エンジニア。 2022 年 6 月に G-gen にジョイン。Google Cloud All Certifications Engineer。 好きな Google Cloud プロダクトは Cloud Run。最近は Dataflow を勉強中。 Follow @sasashun0805
アバター
こんにちは!G-genの片岡です。本記事では Cloud Monitoring で利用可能な、機械学習を使った Forecast condition (予測条件) を実際に検証してみました。 はじめに Cloud Monitoring 振り返り Forecast condition とは 検証内容 VM のセットアップ アラート設定 設定する内容 条件設定 動作検証 ダミーファイルを定期生成 アラート確認 VM 確認 Incident metrics 確認 はじめに Cloud Monitoring 振り返り Cloud Monitoring では、アラートポリシーを用いて、アラートを通知する条件と手法を定義することができます。 例えば GCE の運用において、VMの負荷や稼働状況を収集し、指定した条件に応じてメールやSlack 通知を行うことで、事象に素早く気付くことが可能となります。 Cloud Monitoring の概要及び基本機能については、以下の記事で解説しています。 blog.g-gen.co.jp Forecast condition とは Forecast condition (予測条件) は、 ディスク容量 、 メモリ使用率 、 Google Cloud クォータ などの指標(メトリクス)の値が、指定したウインドウ内にしきい値を超えると予測される場合に、アラートを発報できる機能です。 参考: アラート ポリシーの種類  |  Cloud Monitoring  |  Google Cloud なお、本機能は 2023 年 2 月現在 Preview 中です。Preview 段階のサービスは本番環境での利用は推奨されませんのでご注意ください。以下の記事で Preview 版について触れています。 blog.g-gen.co.jp 検証内容 今回は Google Compute Engine (以下GCE) の VM の、永続ディスクの使用済み容量の増加傾向を学習させて、アラート通知をしてみます。 VM のセットアップ 検証用 VMは、 Debian GNU/Linux 11 (bullseye) とします。 Cloud Monitoring はデフォルトで標準的な指標(Google Cloud の指標)を取得することができますが、今回取得したい ディスク使用率 は、「Ops エージェント」を VM にインストールしないと取得できない指標です。 Ops エージェントの概要とセットアップ方法については、以下の記事で触れています。 blog.g-gen.co.jp アラート設定 設定する内容 今回は Forecast condition(予測条件)を利用し、メトリックの予測値に関するアラートを設定していきます。 Forecast condition を作成することで、予測ウィンドウ(予測期間)内において、しきい値に違反することが予測されたときに通知を受け取ることができます。 ディスク容量 、 メモリ使用率 、 Google Cloud クォータ などに利用可能ですが、予測を始めるために、予測ウィンドウの 2倍 の初期学習時間が必要です。 予測ウィンドウは、1時間 〜 7日で指定可能ですが、例えば予測ウィンドウを 1 時間とした場合、2 時間の初期学習時間が必要です。初期学習のあとも、最大で予測ウインドウの 6倍の時間の直近データが学習されていきます。 条件設定 こちら の手順を参考に、ディスク使用率が予測ウィンドウ内にしきい値を超える と予測された場合に、通知を行うポリシーを作成します。 アラート条件を指標から選択します。 VM Instance > Disk > Disk utilization を選択します。 Monitoring の対象とするデバイスのフィルタ設定を行います。 条件タイプとして Forecast を選択、予測期間(予測ウィンドウ)は 1時間 、しきい値は 90% より上 とします。 通知チャンネル と アラートポリシー名 を設定し、ポリシーを作成します。 動作検証 ダミーファイルを定期生成 VM上でディスク使用率を増加させるために、500MBのダミーファイルを、10分に1個作成するスクリプトを実行し、アラート状況を確認します。 # 500MB のダミーファイルを10分毎に作成(21:00から起動するようセット) $ while true; do dd if=/dev/zero of=`date "+%Y_%m%d_%H%M"`.dummy bs=1M count=500; sleep 600; done 今回は予測ウィンドウを1時間に設定しているため、ポリシー設定完了後から2時間の初期学習時間を経て、しきい値を超える1時間前にアラートが発報されるだろう という仮説を立て、その時を待ちます。 アラート確認 結果として期待通り、しきい値超過のタイミングが予測され、その約1時間前にアラートが発報されました。 VM 確認 VMのダミーファイルは以下の通り作成され、ファイル作成開始から約4時間経過後の 2023_0212_1601.dummy が作成された後にアラートが発報されていました。 # ダミーファイル 作成状況の確認 ls -lh |grep 500M -rw-rw-r--. 1 yoshinari yoshinari 500M Feb 12 12:00 2023_0212_1200.dummy -rw-rw-r--. 1 yoshinari yoshinari 500M Feb 12 12:10 2023_0212_1210.dummy -rw-rw-r--. 1 yoshinari yoshinari 500M Feb 12 12:20 2023_0212_1220.dummy -rw-rw-r--. 1 yoshinari yoshinari 500M Feb 12 12:30 2023_0212_1230.dummy -rw-rw-r--. 1 yoshinari yoshinari 500M Feb 12 12:40 2023_0212_1240.dummy -rw-rw-r--. 1 yoshinari yoshinari 500M Feb 12 12:50 2023_0212_1250.dummy -rw-rw-r--. 1 yoshinari yoshinari 500M Feb 12 13:00 2023_0212_1300.dummy -rw-rw-r--. 1 yoshinari yoshinari 500M Feb 12 13:10 2023_0212_1310.dummy -rw-rw-r--. 1 yoshinari yoshinari 500M Feb 12 13:20 2023_0212_1320.dummy -rw-rw-r--. 1 yoshinari yoshinari 500M Feb 12 13:30 2023_0212_1330.dummy -rw-rw-r--. 1 yoshinari yoshinari 500M Feb 12 13:40 2023_0212_1340.dummy -rw-rw-r--. 1 yoshinari yoshinari 500M Feb 12 13:50 2023_0212_1350.dummy -rw-rw-r--. 1 yoshinari yoshinari 500M Feb 12 14:00 2023_0212_1400.dummy -rw-rw-r--. 1 yoshinari yoshinari 500M Feb 12 14:10 2023_0212_1410.dummy -rw-rw-r--. 1 yoshinari yoshinari 500M Feb 12 14:20 2023_0212_1420.dummy -rw-rw-r--. 1 yoshinari yoshinari 500M Feb 12 14:30 2023_0212_1430.dummy -rw-rw-r--. 1 yoshinari yoshinari 500M Feb 12 14:40 2023_0212_1440.dummy -rw-rw-r--. 1 yoshinari yoshinari 500M Feb 12 14:50 2023_0212_1450.dummy -rw-rw-r--. 1 yoshinari yoshinari 500M Feb 12 15:00 2023_0212_1500.dummy -rw-rw-r--. 1 yoshinari yoshinari 500M Feb 12 15:11 2023_0212_1510.dummy -rw-rw-r--. 1 yoshinari yoshinari 500M Feb 12 15:21 2023_0212_1521.dummy -rw-rw-r--. 1 yoshinari yoshinari 500M Feb 12 15:31 2023_0212_1531.dummy -rw-rw-r--. 1 yoshinari yoshinari 500M Feb 12 15:41 2023_0212_1541.dummy -rw-rw-r--. 1 yoshinari yoshinari 500M Feb 12 15:51 2023_0212_1551.dummy -rw-rw-r--. 1 yoshinari yoshinari 500M Feb 12 16:01 2023_0212_1601.dummy ★ -rw-rw-r--. 1 yoshinari yoshinari 500M Feb 12 16:11 2023_0212_1611.dummy -rw-rw-r--. 1 yoshinari yoshinari 500M Feb 12 16:21 2023_0212_1621.dummy -rw-rw-r--. 1 yoshinari yoshinari 500M Feb 12 16:31 2023_0212_1631.dummy -rw-rw-r--. 1 yoshinari yoshinari 500M Feb 12 16:41 2023_0212_1641.dummy -rw-rw-r--. 1 yoshinari yoshinari 500M Feb 12 16:51 2023_0212_1651.dummy -rw-rw-r--. 1 yoshinari yoshinari 500M Feb 12 17:01 2023_0212_1701.dummy -rw-rw-r--. 1 yoshinari yoshinari 500M Feb 12 17:11 2023_0212_1711.dummy -rw-rw-r--. 1 yoshinari yoshinari 500M Feb 12 17:21 2023_0212_1721.dummy -rw-rw-r--. 1 yoshinari yoshinari 500M Feb 12 17:31 2023_0212_1731.dummy Incident metrics 確認 事前学習したディスク増加傾向を元に、このまま推移すると1時間以内(予測ウィンドウ)にしきい値を超えるということが予測され、アラートを通知することができました。 機械学習により未来が予測されることで、先回りした対応が可能になるなど、多くのメリットを享受できそうです。 様々な分野で機械学習が取り入れられており、今後もますます目が離せないですね! 片岡 義就 (記事一覧) クラウドソリューション部 2022年7月 G-genにジョイン。 前職ではAWSをはじめインフラ全般の設計構築、販促企画に従事。お客様にとって最適なクラウドジャーニーを伴走支援すべく、スキルUPに奔走中。
アバター
G-gen の神谷です。本記事では、Looker Studio で BigQuery データソースを使う際のコスト最適化について説明します。 はじめに Looker Studio とは 料金体系 料金削減の基本方針 レポートごとの BigQuery コストを調べる Looker Studio レポート側の基本方針 BigQuery 側の方針 Looker Studio のキャッシュ機能 レポートのキャッシュ 抽出されたデータソース BigQuery のキャッシュ機能 クエリ結果のキャッシュ マテリアライズドビュー BI Engine キャッシュ以外の手法 BigQuery Editions パーティショニングとクラスタリング 事前集計とカラム設計 Looker Studio から BigQuery のパーティションは有効なのか API 上限設定 その他の参考記事 はじめに Looker Studio とは Looker Studio とは、Google Cloud が提供する BI ツールであり、様々なデータソースに接続し、簡単にダッシュボードを作ることができます。 もともとは Google Workspace 内の製品でありデータポータル(もしくは Data Studio)と呼ばれていましたが、リブランドが行われた結果、Looker Studio という名称になりました。 なお、Looker と Looker Studio は別の製品となります。詳細については、以下の記事をを参照してください。 blog.g-gen.co.jp 料金体系 Looker Studio は無料版と Pro 版の二種類があります。Looker Studio Pro は、無料版にガバナンス機能が追加されたものであり、ユーザー単位でのサブスクリプションとして申込可能です。 Looker Studio Pro の詳細な解説は、以下の記事をご参照ください。 blog.g-gen.co.jp Looker Studio の無償版は、利用に料金はかかりません。ただし、データソースとして BigQuery を利用している場合、ダッシュボードが読み込まれた際にクエリが実行され、BigQuery からのデータ読み出し料金が発生します。 当記事では、BigQuery をデータソースとする Looker Studio レポートで、コストを削減する手法について紹介します。 料金削減の基本方針 レポートごとの BigQuery コストを調べる まずは、どのレポートでコストが多く発生しているかを把握することが重要です。 特定の Looker Studio レポートで、どのくらいの BigQuery コストがかかっているかは、以下の記事の方法で調べることができます。 blog.g-gen.co.jp Looker Studio レポート側の基本方針 Looker Studio レポート側では、以下の基本方針に従って、コスト削減を図ります。 データの 更新頻度 を減らす 参考 : データの更新頻度を管理する レポートの編集者権限を持っている人は、データを手動で更新できますが、閲覧者権限の人は手動更新ができません 閲覧者権限の人にとって、Looker Studio レポートのデータは、データソースに設定した更新頻度で更新されるのを待つことになります データが更新されるまでは、同じクエリに対してはレポートのキャッシュが適用されます 抽出されたデータソース を使用する 参考 : パフォーマンス向上のためにデータを抽出する BigQuery 側の方針 BigQuery 側では、以下の基本方針に従って、コスト削減を図ります。 BI Engine を使用して BigQuery データソースを高速化する 参考 : BI Engine とは パーティショニング や クラスタリング を設定する 参考 : BigQueryのパーティションとクラスタリングについての解説 BigQuery Editions を導入して Baseline や Max 値を設定する 参考 : BigQuery Editionsを徹底解説 事前集計した マートテーブル を作成する マテリアライズドビュー を使用する 参考 : マテリアライズド ビューの概要 BigQuery のキャッシュを利用する 同じ Google アカウントから発行された同じクエリには、キャッシュが適用されます Looker Studio のキャッシュ機能 レポートのキャッシュ Looker Studio のキャッシュは、レポートによって生成されたクエリとその結果を保持します。本記事では「 レポートのキャッシュ 」と表現しますが、ドキュメントでは「一時的にメモリに保存する」と表現されています。 Looker Studio では、レポートのページを読み込んだときや、フィルタを変更したり、プルダウンから項目を選択したりした際に、データソースへのクエリ(SQL)が生成されます。このときに生成されたクエリが 以前に実行されたことのあるクエリと完全に同一 であり、かつデータソースに設定された 「データの更新頻度」の時間内 であれば、キャッシュにヒットする可能性があります。 Looker Studio のデータソースには データ更新頻度 の設定項目があり、この時間を超えた場合は、クエリが過去のものと同一でもデータソースへクエリが再発行され、キャッシュがリフレッシュされます。 また、キャッシュはデータソースへの認証情報ごとに保持されるため、 オーナーの認証情報によって認証するようデータソースを設定する と、最もキャッシュ効率が良くなります。ただし、データソースへの認証情報に何を設定するかは、コスト面だけでなく、データへのアクセス権限の要件をよく検討したうえで決定してください。 キャッシュが最後に更新された日時は、 レポートの左下に表示 されています。レポートを読み込んだり、フィルタを変更した際に、左下の「最終更新日」が変更された場合、キャッシュヒットせずに、データソースへクエリが発行されたことがわかります。 なお BigQuery コネクタの場合、更新頻度は「分単位: 1~50 分」「時間単位: 1~12 時間」から選択可能です。 参考 : データの更新頻度を管理する 抽出されたデータソース レポートのキャッシュとは別に、 抽出されたデータソース というデータのスナップショットを保持する機能があります。 データソースを選択して、特定のフィールドを指定すると、そのフィールドだけを含む「抽出済みデータソース」を作成することができます。 抽出されたデータソースは、100 MB を上限として、データを Looker Studio に保持しておくことができます。データを自動的に更新したい場合は、自動更新を有効化し、更新頻度を設定します。 参考 : パフォーマンス向上のためにデータを抽出する BigQuery のキャッシュ機能 クエリ結果のキャッシュ BigQuery の クエリ結果のキャッシュ は、デフォルトで提供される機能です。過去に実行されたクエリと同一のクエリが同じ Google アカウントから実行された場合、キャッシュが使用されます。 なお、BigQuery Editions のうち Enterprise または Enterprise Plus エディションを使っている場合、別の Google アカウントのキャッシュも利用できます。 参考 : キャッシュに保存されているクエリ結果を使用する 参考 : BigQueryを徹底解説!(基本編) - クエリ結果のキャッシュ マテリアライズドビュー マテリアライズドビュー は、データの実体を保持するビューです。マテリアライズドビューを SQL で定義すると、定期的にデータが更新され、また古くなったデータをクエリするとその部分だけが自動的に更新されるため、常に最新の情報を取得できます。 通常のビューとは異なりいくつかの制約がありますが、要件に応じて導入を検討してください。 参考 : マテリアライズド ビューの概要 BI Engine BI Engine は、BI ツールのために BigQuery のデータをキャッシュしておくためのメモリです。パーティショニングやクラスタリングと組み合わせることで、特に効果を発揮します。 参考 : BI Engine とは 参考 : BI Engine の容量を予約する 参考 : 最適化される SQL 関数と演算子 BI Engine には、以下のような特徴があります。 BI Engine に保存されたデータから結果を取得するクエリを実行する場合、データの読み取りが課金されない BigQuery Editions で購入したスロット数に応じて無料の BI Engine 容量が付帯する 例として100スロットを購入すると、5 GiB の BI Engine 容量が無料で付帯する 参考 : BI Engine の料金 BI Engine に有効なクエリ演算子が決まっており、このリスト以外のクエリでは効果が薄れる 参考 : 最適化される SQL 関数と演算子 BI Engine は結合が多いクエリだと効果が弱くなる 参考 : 結合を最小化する 優先テーブル設定により、BI Engine が適用されるテーブルを限定できる 参考 : 優先テーブル キャッシュ以外の手法 BigQuery Editions BigQuery Editions を使うと、Autoscaler の効果により、デフォルト設定であるオンデマンドよりも料金が安くなる可能性があります。 必要に応じてスロットの1年または3年コミットメントを購入すると、さらに安価に利用可能になります。 blog.g-gen.co.jp パーティショニングとクラスタリング パーティション分割テーブルやクラスタリングを利用することで BigQuery テーブルに対するフルスキャンが回避され、コスト削減に繋がります。 blog.g-gen.co.jp 事前集計とカラム設計 動的集計が必要なケース(例 : 日付の期間指定によるユニークカウント(distinct)。小計の合計が総計にならない)以外は、raw データを扱うのではなく、なるべく事前集計をします。 日付やフィルタ項目のパターン分、あらかじめ集計しておくことが有用です。以下は、その例です。 SELECT date_, title, contributor_ip, COUNT (title) AS revision_count FROM `jkamiya.Looker Studio_test.wikipedia`, UNNEST ([date_, " 9999-12-31 " ]) AS date_, UNNEST ([title, " ALL " ]) AS title, UNNEST ([contributor_ip, " ALL " ]) AS contributor_ip GROUP BY date_, title, contributor_ip ORDER BY revision_count desc 「9999-12-31」は全期間、「ALL」は全項目での集計という意味です。 BigQuery での GROUP BY CUBE の実装方法 Looker Studio のフィルタ項目を「UNNEST」と対応させ、BigQuery のパーティショニングやクラスタリングのカラムとして設定します。 パーティションは1つ、クラスタリングは4つまで設定可能で、併用もできます。クラスタリングカラムは裏側でデータをソートし整理しておくことでスキャン量を減らすことができます。 設定順が重要なため、フィルタ項目でよく使うものから設定すると良いでしょう。 Looker Studio から BigQuery のパーティションは有効なのか Looker Studio から BigQuery のパーティションは、有効に働きます。Looker Studio から BigQuery にアクセスする際には、以下のように Looker Studio 側で自動生成された SQL が BigQuery にクエリされます。 Looker Studio で自動生成された BigQuery の SQL 一見、日付パーティションである「date_」カラムで絞り込む前に「 jkamiya.Looker Studio_test.wikipedia t0」テーブルにアクセスしているため、フルスキャンをしているのではと心配になります。 しかし、BigQuery はこういった書き方でもパーティションによるスキャンが効果を発揮します。 試しに、パーティションフィルタを有効化した上で、対象箇所をコメントアウトすると、以下のようにエラーが出ます。 パーティション未指定エラー コメントアウトを戻すと、以下のように「924.62MB」となり、エラーも起きず、対象パーティションで絞り込まれていることがわかります。 日付パーティションによるスキャン パーティションは GROUP BY したものに対して WHERE をかけても有効です。 GROUP BY したあとでもパーティションは有効 API 上限設定 オンデマンドモードで BigQuery を利用している場合、BigQuery API に上限を設定することで、ある一定以上は課金されないようにフタをすることが可能です。以下の記事を参考にしてください。 blog.g-gen.co.jp その他の参考記事 blog.g-gen.co.jp blog.g-gen.co.jp 神谷 乗治 (記事一覧) クラウドソリューション部 クラウドエンジニア。2017 年頃から Google Cloud を用いたデータ基盤構築や ML エンジニアリング、データ分析に従事。クラウドエース株式会社システム開発部マネージャーを経て現職。Google Cloud Partner Top Engineer 2023,2024、Google Cloud Champion Innovators(Database)、 著書:「GCPの教科書III【Cloud AIプロダクト編】」  
アバター
G-gen の藤岡です。当記事では、 Google Cloud(旧称 GCP)の Filestore を Compute Engine(以下 GCE)の Windows Server にマウントする方法を紹介します。 サービスの概要 Filestore とは ネットワーク サービスティア クライアントへの接続 実施内容 構成図 前提条件 構築 事前作業 プロジェクトの作成と請求先アカウントの紐づけ デフォルトプロジェクトのセット API の有効化 ネットワーク構築 VPC とサブネットの作成 ファイアウォールルールの作成 HA Cloud VPN ゲートウェイの作成 Cloud Router の作成 VPN トンネルの作成 BGPセッションの構成 GCE 構築 GCE(Windows Server)の作成 GCE に RDP 疎通テスト Filestore 構築 Filestore の作成 マウント(nfs-client-a) NFS クライアントの設定 マウント ルートのアドバタイズ マウント(nfs-client-b) テスト 事後作業 プロジェクトの削除 注意点 サービスの概要 Filestore とは Filestore は、 GCE や GKE クラスタ、オンプレミスマシンに接続できるフルマネージドのファイルサーバーです。 プロトコルは NFSv3 で NFSv3 互換のクライアントがサポートされています。 Cloud VPN または Cloud Interconnect を使用して、異なる VPC のGCE やオンプレミスマシン等のリモートネットワーク上のクライアントに接続することもできます。 Linux や Windows にマウントしてファイルサーバーとして利用出来るため、オンプレミスマシンの急な容量不足にも対応可能です。 ネットワーク Filestore では、ネットワークに 標準 VPC または 共有 VPC を選択できます。クライアントが Filestore にアクセスするには Filestore と同じネットワーク上にある必要があり、作成後にネットワークを変更することはできません。 Filestore のインスタンスはサービスプロデューサーのネットワークと呼ばれる Google が管理する専用ネットワークに配置されます。 Cloud SQL や Cloud Build 等と同様の扱い になります。 Filestore インスタンスに使われる IP アドレスは内部 IP アドレス( 10.0.0.0/8 , 172.16.0.0/12 , 192.168.0.0/16 )の範囲である必要があり、後述するサービスティアによって /29 や /26 のように必要となるレンジが異なります。 なお、推移的ピアリングをサポートしていません。そのため、当記事で紹介する構成で、 vpc-a と vpc-b を Cloud VPN ではなく、 VPC ネットワークピアリング で接続していた場合、 vpc-b 内のインスタンスは Filestore へアクセスできません。 参考: ネットワーキング / 予約済み IP アドレスの範囲を構成する サービスティア サービスティアとは、 Filestore インスタンスのインスタンスタイプとストレージタイプを組み合わせたものです。以下の4種類があり、インスタンスの作成後にサービスティアを変更することはできません。 基本 HDD 基本 SSD 高スケール SSD エンタープライズ パフォーマンスはもちろん、プロビジョニング可能な容量もサービスティアによって異なりますので、ユースケースに合わせて選択してください。 参考: サービスティア クライアントへの接続 前述の通り、 GCE や GKE クラスタ、オンプレミスマシンにマウントすることが出来ますが、最適なパフォーマンスを得るには、マシンタイプファミリーが n1-standard-8 以上のスペックのクライアントにマウントすることが推奨されています。 参考: Compute Engine クライアントでのファイル共有のマウント 実施内容 構成図 今回の作成する構成は以下の通りです。 構成図 前提条件 当記事では、 gcloud CLI を使用してリソースを作成するため、 CUI 操作に慣れていない方は以下の記事で Cloud VPN をコンソールから作成する方法について紹介していますのでご参照ください。 blog.g-gen.co.jp 各コマンドは Cloud Shell から実行 します。参考までに、実行時の環境は以下の通りです。 fujioka@cloudshell:~ (fujioka-xxxx)$ gcloud -v Google Cloud SDK 416.0.0 alpha 2023.01.30 app-engine-go 1.9.72 app-engine-java 2.0.10 app-engine-python 1.9.101 app-engine-python-extras 1.9.97 beta 2023.01.30 bigtable bq 2.0.84 bundled-python3-unix 3.9.16 cbt 0.13.0 cloud-datastore-emulator 2.3.0 cloud-run-proxy 0.3.0 core 2023.01.30 gcloud-crc32c 1.0.0 gke-gcloud-auth-plugin 0.4.0 gsutil 5.19 kpt 1.0.0-beta.24 local-extract 1.5.7 minikube 1.29.0 pubsub-emulator 0.7.4 skaffold 2.1.0 fujioka@cloudshell:~ (fujioka-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 projects create / gcloud beta billing projects link デフォルトプロジェクトのセット 作成したプロジェクトをデフォルトプロジェクトとしてセットし、結果を確認します。 $ gcloud config set project ${PROJECT_ID} && \ gcloud config list project 参考: gcloud config set / gcloud config list API の有効化 これ以降は、実行したコマンドを出力結果とともに記載します。コマンドのリファレンスを参考に載せているので、試す際にはリソース名やオプション等は環境に合わせて適宜置き換えてください。 今回有効化する API は以下の2つです。 Compute Engine API Cloud Filestore API 環境によりますが、 API の有効化には数分程度かかる場合があります。 fujioka@cloudshell:~ (fujioka-230212)$ gcloud services enable compute.googleapis.com && \ gcloud services enable file.googleapis.com Operation "operations/acf.p2-381487557941-71a52ef2-f5c9-49de-9a68-fbd6f88beafa" finished successfully. Operation "operations/acf.p2-381487557941-81c07e57-3e55-4386-ac3a-33767bbef368" finished successfully. fujioka@cloudshell:~ (fujioka-230212)$ 参考: gcloud services enable ネットワーク構築 VPC とサブネットの作成 2つの VPC とサブネットを作成します。 fujioka@cloudshell:~ (fujioka-230212)$ gcloud compute networks create vpc-a --subnet-mode custom && \ gcloud compute networks subnets create subnet-a --network vpc-a --region asia-northeast1 --range 10.0.0.0/24 && \ gcloud compute networks create vpc-b --subnet-mode custom && \ gcloud compute networks subnets create subnet-b --network vpc-b --region asia-northeast1 --range 172.16.0.0/24 ~省略~ Created [https://www.googleapis.com/compute/v1/projects/fujioka-230212/regions/asia-northeast1/subnetworks/subnet-b]. NAME: subnet-b REGION: asia-northeast1 NETWORK: vpc-b RANGE: 172.16.0.0/24 STACK_TYPE: IPV4_ONLY IPV6_ACCESS_TYPE: INTERNAL_IPV6_PREFIX: EXTERNAL_IPV6_PREFIX: fujioka@cloudshell:~ (fujioka-230212)$ 作成済みの VPC とサブネットを確認します。今回はデフォルトで作成される default 以外を出力させます。 fujioka@cloudshell:~ (fujioka-230212)$ gcloud compute networks list --filter="name!='default'" && \ gcloud compute networks subnets list --filter="name!='default'" NAME: vpc-a SUBNET_MODE: CUSTOM BGP_ROUTING_MODE: REGIONAL IPV4_RANGE: GATEWAY_IPV4: NAME: vpc-b SUBNET_MODE: CUSTOM BGP_ROUTING_MODE: REGIONAL IPV4_RANGE: GATEWAY_IPV4: NAME: subnet-a REGION: asia-northeast1 NETWORK: vpc-a RANGE: 10.0.0.0/24 STACK_TYPE: IPV4_ONLY IPV6_ACCESS_TYPE: INTERNAL_IPV6_PREFIX: EXTERNAL_IPV6_PREFIX: NAME: subnet-b REGION: asia-northeast1 NETWORK: vpc-b RANGE: 172.16.0.0/24 STACK_TYPE: IPV4_ONLY IPV6_ACCESS_TYPE: INTERNAL_IPV6_PREFIX: EXTERNAL_IPV6_PREFIX: fujioka@cloudshell:~ (fujioka-230212)$ 参考: gcloud compute networks create / gcloud compute networks list ファイアウォールルールの作成 各 VPC に2つのファイアウォールルールを作成します。 icmp は GCE 同士の疎通確認用です。 tcp:3389 は RDP 用のため、 --source-ranges ${YOUR_GROBAL_IP_ADDRESS} は自身のグローバル IP アドレスに置き換えてください。 fujioka@cloudshell:~ (fujioka-230212)$ gcloud compute firewall-rules create allow-rdp-from-home-a \ --network vpc-a \ --direction ingress \ --action allow \ --source-ranges ${YOUR_GROBAL_IP_ADDRESS} \ --rules=tcp:3389 && \ gcloud compute firewall-rules create allow-icmp-from-subnet-b-a \ --network vpc-a \ --direction ingress \ --action allow \ --source-ranges 172.16.0.0/24 \ --rules=icmp && \ gcloud compute firewall-rules create allow-rdp-from-home-b \ --network vpc-b \ --direction ingress \ --action allow \ --source-ranges ${YOUR_GROBAL_IP_ADDRESS} \ --rules=tcp:3389 && \ gcloud compute firewall-rules create allow-icmp-from-subnet-a-b \ --network vpc-b \ --direction ingress \ --action allow \ --source-ranges 10.0.0.0/24 \ --rules=icmp ~省略~ Creating firewall...working..Created [https://www.googleapis.com/compute/v1/projects/fujioka-230212/global/firewalls/allow-icmp-from-subnet-a-b]. Creating firewall...done. NAME: allow-icmp-from-subnet-a-b NETWORK: vpc-b DIRECTION: INGRESS PRIORITY: 1000 ALLOW: icmp DENY: DISABLED: False fujioka@cloudshell:~ (fujioka-230212)$ 作成済みのファイアウォールルールを確認します。デフォルトで作成されるルール以外を出力させます。 fujioka@cloudshell:~ (fujioka-230212)$ gcloud compute firewall-rules list --filter="name!='default'" NAME: allow-icmp-from-subnet-a-b NETWORK: vpc-b DIRECTION: INGRESS PRIORITY: 1000 ALLOW: icmp DENY: DISABLED: False NAME: allow-icmp-from-subnet-b-a NETWORK: vpc-a DIRECTION: INGRESS PRIORITY: 1000 ALLOW: icmp DENY: DISABLED: False NAME: allow-rdp-from-home-a NETWORK: vpc-a DIRECTION: INGRESS PRIORITY: 1000 ALLOW: tcp:3389 DENY: DISABLED: False NAME: allow-rdp-from-home-b NETWORK: vpc-b DIRECTION: INGRESS PRIORITY: 1000 ALLOW: tcp:3389 DENY: DISABLED: False To show all fields of the firewall, please show in JSON format: --format=json To show all fields in table format, please see the examples in --help. fujioka@cloudshell:~ (fujioka-230212)$ 参考: gcloud compute firewall-rules create / gcloud compute firewall-rules list HA Cloud VPN ゲートウェイの作成 今回は 高可用性(HA)Cloud VPN を使用します。はじめに、vpc-a と vpc-b に HA Cloud VPN ゲートウェイを作成します。 fujioka@cloudshell:~ (fujioka-230212)$ gcloud compute vpn-gateways create vpn-gateway-a \ --region=asia-northeast1 --network=vpc-a && \ gcloud compute vpn-gateways create vpn-gateway-b \ --region=asia-northeast1 --network=vpc-b ~省略~ Creating VPN Gateway...done. NAME: vpn-gateway-b INTERFACE0: 34.157.79.62 INTERFACE1: 34.157.206.255 NETWORK: vpc-b REGION: asia-northeast1 fujioka@cloudshell:~ (fujioka-230212)$ 作成されていることを確認します。 fujioka@cloudshell:~ (fujioka-230212)$ gcloud compute vpn-gateways list NAME: vpn-gateway-a INTERFACE0: 34.157.66.147 INTERFACE1: 34.157.195.105 NETWORK: vpc-a REGION: asia-northeast1 NAME: vpn-gateway-b INTERFACE0: 34.157.79.62 INTERFACE1: 34.157.206.255 NETWORK: vpc-b REGION: asia-northeast1 fujioka@cloudshell:~ (fujioka-230212)$ 参考: gcloud compute vpn-gateways create / gcloud compute vpn-gateways list Cloud Router の作成 次に、vpc-a と vpc-b に Cloud Router を作成します。 fujioka@cloudshell:~ (fujioka-230212)$ gcloud compute routers create cloud-router-a \ --region=asia-northeast1 --network=vpc-a \ --asn=65001 && \ gcloud compute routers create cloud-router-b \ --region=asia-northeast1 --network=vpc-b \ --asn=65002 ~省略~ Creating router [cloud-router-b]...done. NAME: cloud-router-b REGION: asia-northeast1 NETWORK: vpc-b fujioka@cloudshell:~ (fujioka-230212)$ 作成されていることを確認します。 fujioka@cloudshell:~ (fujioka-230212)$ gcloud compute routers list NAME: cloud-router-a REGION: asia-northeast1 NETWORK: vpc-a NAME: cloud-router-b REGION: asia-northeast1 NETWORK: vpc-b fujioka@cloudshell:~ (fujioka-230212)$ 参考: gcloud compute routers create / gcloud compute routers list VPN トンネルの作成 HA Cloud VPN のため、トンネルは計4本作ります。 fujioka@cloudshell:~ (fujioka-230212)$ gcloud compute vpn-tunnels create vpn-tunnel-a-1 \ --peer-gcp-gateway=vpn-gateway-b \ --region=asia-northeast1 \ --ike-version=2 \ --shared-secret=sdijennji29duj \ --router=cloud-router-a \ --vpn-gateway=vpn-gateway-a \ --interface=0 && \ gcloud compute vpn-tunnels create vpn-tunnel-b-1 \ --peer-gcp-gateway=vpn-gateway-a \ --region=asia-northeast1 \ --ike-version=2 \ --shared-secret=sdijennji29duj \ --router=cloud-router-b \ --vpn-gateway=vpn-gateway-b \ --interface=0 && \ gcloud compute vpn-tunnels create vpn-tunnel-a-2 \ --peer-gcp-gateway=vpn-gateway-b \ --region=asia-northeast1 \ --ike-version=2 \ --shared-secret=sdijennji29duj65 \ --router=cloud-router-a \ --vpn-gateway=vpn-gateway-a \ --interface=1 && \ gcloud compute vpn-tunnels create vpn-tunnel-b-2 \ --peer-gcp-gateway=vpn-gateway-a \ --region=asia-northeast1 \ --ike-version=2 \ --shared-secret=sdijennji29duj65 \ --router=cloud-router-b \ --vpn-gateway=vpn-gateway-b \ --interface=1 ~省略~ Creating VPN tunnel...done. NAME: vpn-tunnel-b-2 REGION: asia-northeast1 GATEWAY: vpn-gateway-b VPN_INTERFACE: 1 PEER_ADDRESS: 34.157.195.105 fujioka@cloudshell:~ (fujioka-230212)$ 作成されていることを確認します。 fujioka@cloudshell:~ (fujioka-230212)$ gcloud compute vpn-tunnels list NAME: vpn-tunnel-a-1 REGION: asia-northeast1 GATEWAY: vpn-gateway-a PEER_ADDRESS: 34.157.79.62 NAME: vpn-tunnel-a-2 REGION: asia-northeast1 GATEWAY: vpn-gateway-a PEER_ADDRESS: 34.157.206.255 NAME: vpn-tunnel-b-1 REGION: asia-northeast1 GATEWAY: vpn-gateway-b PEER_ADDRESS: 34.157.66.147 NAME: vpn-tunnel-b-2 REGION: asia-northeast1 GATEWAY: vpn-gateway-b PEER_ADDRESS: 34.157.195.105 fujioka@cloudshell:~ (fujioka-230212)$ 参考: gcloud compute vpn-tunnels create / gcloud compute vpn-tunnels list 現時点をコンソールから確認をすると、トンネルは確立されていますが BGP セッションが構成されていません。 BGP セッション構成前 BGPセッションの構成 BGP セッションを構成します。 fujioka@cloudshell:~ (fujioka-230212)$ gcloud compute routers add-interface cloud-router-a \ --interface-name=bgp-a-1 \ --ip-address=169.254.0.1 \ --mask-length=30 \ --vpn-tunnel=vpn-tunnel-a-1 \ --region=asia-northeast1 && \ gcloud compute routers add-bgp-peer cloud-router-a \ --peer-name=bgp-b-1 \ --interface=bgp-a-1 \ --peer-ip-address=169.254.0.2 \ --peer-asn=65002 \ --region=asia-northeast1 && \ gcloud compute routers add-interface cloud-router-b \ --interface-name=bgp-b-1 \ --ip-address=169.254.0.2 \ --mask-length=30 \ --vpn-tunnel=vpn-tunnel-b-1 \ --region=asia-northeast1 && \ gcloud compute routers add-bgp-peer cloud-router-b \ --peer-name=bgp-a-1 \ --interface=bgp-b-1 \ --peer-ip-address=169.254.0.1 \ --peer-asn=65001 \ --region=asia-northeast1 && \ gcloud compute routers add-interface cloud-router-a \ --interface-name=bgp-a-2 \ --ip-address=169.254.0.5 \ --mask-length=30 \ --vpn-tunnel=vpn-tunnel-a-2 \ --region=asia-northeast1 && \ gcloud compute routers add-bgp-peer cloud-router-a \ --peer-name=bgp-b-2 \ --interface=bgp-a-2 \ --peer-ip-address=169.254.0.6 \ --peer-asn=65002 \ --region=asia-northeast1 && \ gcloud compute routers add-interface cloud-router-b \ --interface-name=bgp-b-2 \ --ip-address=169.254.0.6 \ --mask-length=30 \ --vpn-tunnel=vpn-tunnel-b-2 \ --region=asia-northeast1 && \ gcloud compute routers add-bgp-peer cloud-router-b \ --peer-name=bgp-a-2 \ --interface=bgp-b-2 \ --peer-ip-address=169.254.0.5 \ --peer-asn=65001 \ --region=asia-northeast1 ~省略~ Creating peer [bgp-a-2] in router [cloud-router-b]...done. fujioka@cloudshell:~ (fujioka-230212)$ 参考: gcloud compute routers add-interface / gcloud compute routers add-bgp-peer 以上でネットワークの構築は完了です。先程構成されていなかった BGP セッションも構成されています。 BGP セッション構成後 GCE 構築 GCE(Windows Server)の作成 次に Filestore をマウントする用の GCE を2つ作成します。OS は Windows Server 2022 を使用します。 fujioka@cloudshell:~ (fujioka-230212)$ gcloud compute instances create nfs-client-a \ --zone asia-northeast1-a \ --boot-disk-auto-delete \ --image-project windows-cloud \ --image-family windows-2022 \ --machine-type n1-standard-8 \ --network=vpc-a \ --subnet=subnet-a \ --private-network-ip=10.0.0.2 && \ gcloud compute instances create nfs-client-b \ --zone asia-northeast1-a \ --boot-disk-auto-delete \ --image-project windows-cloud \ --image-family windows-2022 \ --machine-type n1-standard-8 \ --network=vpc-b \ --subnet=subnet-b \ --private-network-ip=172.16.0.2 ~省略~ Created [https://www.googleapis.com/compute/v1/projects/fujioka-230212/zones/asia-northeast1-a/instances/nfs-client-b]. NAME: nfs-client-b ZONE: asia-northeast1-a MACHINE_TYPE: n1-standard-8 PREEMPTIBLE: INTERNAL_IP: 172.16.0.2 EXTERNAL_IP: 34.84.161.249 STATUS: RUNNING fujioka@cloudshell:~ (fujioka-230212)$ 作成されていることを確認します。 fujioka@cloudshell:~ (fujioka-230212)$ gcloud compute instances list NAME: nfs-client-a ZONE: asia-northeast1-a MACHINE_TYPE: n1-standard-8 PREEMPTIBLE: INTERNAL_IP: 10.0.0.2 EXTERNAL_IP: 35.187.213.228 STATUS: RUNNING NAME: nfs-client-b ZONE: asia-northeast1-a MACHINE_TYPE: n1-standard-8 PREEMPTIBLE: INTERNAL_IP: 172.16.0.2 EXTERNAL_IP: 34.84.161.249 STATUS: RUNNING fujioka@cloudshell:~ (fujioka-230212)$ 参考: gcloud compute instances create / gcloud compute instances list GCE に RDP Windows Server に初回ログイン時は Windows パスワードを再設定する必要があります。2台分の GCE のパスワードを再設定します。出力される password を使用して各 GCE へ RDP します。 fujioka@cloudshell:~ (fujioka-230212)$ gcloud beta compute reset-windows-password "nfs-client-a" --zone "asia-northeast1-a" && \ gcloud beta compute reset-windows-password "nfs-client-b" --zone "asia-northeast1-a" ~省略~ Would you like to set or reset the password for [fujioka] (Y/n)? Y Resetting and retrieving password for [fujioka] on [nfs-client-a] Updated [https://www.googleapis.com/compute/beta/projects/fujioka-230212/zones/asia-northeast1-a/instances/nfs-client-a]. ip_address: 35.187.213.228 password: [ao9+Tf4TitXWyh username: fujioka ~省略~ Would you like to set or reset the password for [fujioka] (Y/n)? Y Resetting and retrieving password for [fujioka] on [nfs-client-b] Updated [https://www.googleapis.com/compute/beta/projects/fujioka-230212/zones/asia-northeast1-a/instances/nfs-client-b]. ip_address: 34.84.161.249 password: .fD_brM2U~k$yeR username: fujioka fujioka@cloudshell:~ (fujioka-230212)$ コンソールから [RDP] をクリックし、再設定したパスワードを入力し、ログインします。 コンソールから RDP 参考: RDP を使用して Windows VM に接続する / gcloud beta compute reset-windows-password 疎通テスト 異なる VPC に属する GCE 同士が内部 IP アドレスで疎通出来ることを確認します。左が nfs-client-a、右が nfs-client-b です。 問題なく疎通出来ています。 疎通テスト Filestore 構築 Filestore の作成 Filestore を作成します。サービスティアや環境によって異なりますが、数分〜数十分程かかる場合があります。 fujioka@cloudshell:~ (fujioka-230212)$ gcloud filestore instances create nfs-server \ --zone=asia-northeast1-a \ --tier=basic-hdd \ --file-share=name="vol",capacity=1TB \ --network=name="vpc-a",reserved-ip-range="10.0.1.0/29" Waiting for [operation-1676217502204-5f482ccc90135-fd023b8f-aac4ad51] to finish...done. fujioka@cloudshell:~ (fujioka-230212)$ 参考: gcloud filestore instances create マウント(nfs-client-a) NFS クライアントの設定 nfs-client-a のインスタンスで ドキュメント に従い、 管理者として PowerShell を開き 以下を順に実行します。 NFS クライアントをインストールする Install-WindowsFeature -Name NFS-Client インスタンスを再起動する NFS クライアントが使用するユーザー ID を構成する New-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\ClientForNFS\CurrentVersion\Default" ` -Name "AnonymousUid" -Value "0" -PropertyType DWORD New-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\ClientForNFS\CurrentVersion\Default" ` -Name "AnonymousGid" -Value "0" -PropertyType DWORD NFS クライアントサービスを再起動する nfsadmin client stop nfsadmin client start PowerShell を終了する exit マウント マウント前は C ドライブのみです。 マウント前(nfs-client-a) Command Prompt で Filestore 上のファイル共有名とドライブ文字をマッピングし、マウントします。 mount -o mtype=hard 10.0.1.2:/vol Z: マウント後(nfs-client-a) 参考 Compute Engine VM インスタンスにファイル共有をマウントする ルートのアドバタイズ nfs-client-b に Filestore をマウントする前に、Filestore のサービス用サブネット( 10.0.1.0/29 )をアドバタイズする必要があります。アドバタイズしない場合に発生するエラーについては後述の 注意点 の通りです。 vpc-a のルートでは 10.0.1.0/29 へのルートがあります。 vpc-a のルート それに対し、vpc-b には 10.0.1.0/29 へのルートがありません。 vpc-b のルート cloud-router-a で 10.0.1.0/29 のルートをアドバタイズします。 fujioka@cloudshell:~ (fujioka-230212)$ gcloud compute routers update cloud-router-a \ --advertisement-mode=CUSTOM \ --region=asia-northeast1 \ --set-advertisement-groups=ALL_SUBNETS \ --set-advertisement-ranges 10.0.1.0/29=filestore-range Updating router [cloud-router-a]...done. fujioka@cloudshell:~ (fujioka-230212)$ vpc-b に Filestore のサービス用サブネット( 10.0.1.0/29 )がアドバタイズされました。 アドバタイズ後の vpc-b のルート 参考: カスタム IP 範囲のアドバタイズ マウント(nfs-client-b) 上記の マウント(nfs-client-a) と同様の手順を nfs-client-b にも行い、Filestore をマウントをさせます。 マウント後(nfs-client-b) テスト nfs-client-a の Z ドライブ(Filestore)で任意のファイルを作成し、nfs-client-b の Z ドライブにも反映されていることが確認できます。左が nfs-client-a、右が nfs-client-b です。 テスト 事後作業 プロジェクトの削除 fujioka@cloudshell:~ (fujioka-230212)$ gcloud projects delete fujioka-230212 Your project will be deleted. Do you want to continue (Y/n)? Y Deleted [https://cloudresourcemanager.googleapis.com/v1/projects/fujioka-230212]. You can undo this operation for a limited period by running the command below. $ gcloud projects undelete fujioka-230212 See https://cloud.google.com/resource-manager/docs/creating-managing-projects for information on shutting down projects. fujioka@cloudshell:~ (fujioka-xxxx)$ 参考: gcloud projects delete 注意点 nfs-client-b に Filestore をマウントする前に、Filestore のサービス用サブネット( 10.0.1.0/29 )をアドバタイズせずにマウントをしようとすると、以下のようなエラーが発生します。エラー発生時には、ルートが原因の場合がありますので、確認してください。 C:\Users\fujioka>hostname nfs-client-b C:\Users\fujioka> C:\Users\fujioka>mount -o mtype=hard 10.0.1.2:/vol Z: Network Error - 53 Type 'NET HELPMSG 53' for more information. C:\Users\fujioka> 藤岡 里美 (記事一覧) クラウドソリューション部 接客業からエンジニアへ。2022年9月 G-gen にジョイン。Google Cloud 認定資格は全冠。2023 夏アニメのオススメは、ダークギャザリング。箏を習っています :) Follow @fujioka57621469
アバター
当記事は みずほリサーチ&テクノロジーズ × G-gen エンジニアコラボレーション企画 で執筆されたものです。 みずほリサーチ&テクノロジーズ株式会社の舘山と申します。 本日は、Google Cloudの監査ログの長期保管とアクセス監査について、調査した内容を共有します。 参考資料として、Google Cloudのログ取得サービス、監査ログの全体像は下記のブログ、Google Cloudドキュメントにまとめられています。 G-genブログ Cloud Loggingの概念と仕組みをしっかり解説 Cloud Audit Logsを解説。Google Cloud(GCP)の証跡管理 Google Cloudドキュメント Cloud Audit Logs の概要 Cloud 監査ログのベスト プラクティス Google Cloud でのセキュリティ ログ分析 当ブログは G-gen × みずほRT によるコラボ記事です 1.当記事が前提とする要件 2.デフォルトの監査ログ設定と要件のギャップ 3.要件を満たす対応の実施 (1)データアクセス監査ログの有効化 (2)Cloud Loggingバケットの保存先を東京リージョンに変更 (3)Cloud Loggingバケットの暗号化に顧客管理の暗号鍵(CMEK)を利用 (4)組織の監査ログを集約して、長期保管 A.集約シンクの設定 B.配信先ストレージの選択 C.Cloud Storageの保持ポリシーによるログの保護 D.組織、フォルダーに紐づくリソースの設定変更監視について 4.アクセス監査資料の作成 (1)ログフォーマットの把握 (2)BigQuery外部テーブルの作成 (3)BigQueryから読み込むデータ量(=分析料金)の最適化 集約シンクから直接エクスポートしたBigQueryテーブルの場合 (4)アクセス監査のためのクエリを考える (5)処理の自動化を考える A. Looker Studioによるレポート自動配信 B. CloudSchedularからPub/Sub経由でCloudFunctions起動 (a)Cloud Functions サーバレスVPCアクセスコネクタインスタンスはCMEKでの暗号化に非対応 (b)Cloud Functions(第二世代)が自動生成するCloud Storage バケット、Artifact Registry リポジトリはCMEKでの暗号化に非対応 5.まとめ 1.当記事が前提とする要件 当記事が前提とする要件を記載します。 組織全体の監査ログを本番/開発別の監査用プロジェクトに集約し2年間保管する。 組織全体で日次で開発者がアクセスした本番プロジェクト(組織、フォルダ)の監査を行う。 リソースの更新(ADMIN_WRITE)だけでなく、リソースの参照(ADMIN_READ)、データの参照、更新(DATA_READ、DATA_WRITE)の監査ログも保管する。 サービス制約がない限り、データは国内リージョンに保有する。 (組織的な統制には、 組織ポリシーconstraints/gcp.resourceLocations が利用可能です。) サービス制約がない限り、暗号化には 顧客管理の暗号鍵(CMEK) を利用する。 (組織的な統制には、 組織ポリシーconstraints/gcp.restrictNonCmekServices が利用可能です。) 実際のシステム構築では、以下のトピックについても考慮が必要ですが、当記事では基本的に扱いません。 VPCフローログ 、 ファイアウォールルールログ 等のネットワークのログ 業務アプリケーションやミドルウェアのログ GKEのKubernetes API(k8s.io)の監査ログ Cloud Identity(ユーザーアカウント管理) 、請求先アカウントの監査ログ アクセスの透明性 のログ(Googleのエンジニアによるアクセスの監査ログ) リアルタイムの監査ログ監視 ログに対する細かいアクセス制御 Cloud Asset Inventoryによるリソースの スナップショット 、 変更履歴 の集約 Security Command Center の検出結果の集約 2.デフォルトの監査ログ設定と要件のギャップ デフォルトの監査ログ設定での、今回の要件の順守状況を整理すると下の表のようになります。 要件 順守状況 全ての監査ログ取得 ×:BigQuery以外のデータアクセス監査ログは取得対象外 国内リージョンの利用 ×:Cloud Loggingバケットの保存先はグローバル(海外含む) 暗号化に顧客管理の暗号鍵(CMEK)の利用 ×:Cloud LoggingバケットはGoogle管理の鍵で暗号化 組織全体の監査ログ集約、長期保管 ×:各プロジェクトの監査ログは、各プロジェクトのCloud Loggingバケットにのみ保存    Requiredバケットの保存期間は400日固定    Defaultバケットの保存期間の初期設定は30日 3.要件を満たす対応の実施 (1)データアクセス監査ログの有効化 各プロジェクト単位ではなく、組織レベルでデータアクセス監査ログの有効化設定が可能です。 設定手順についてはCloud Loggingのガイドを参照してください。 データアクセス監査ログを構成する 各サービスのデータアクセス監査ログを有効にした場合、Cloud Loggingのデフォルト設定では、API実行対象の組織、フォルダ、プロジェクトの_Defaultバケットに全てのログが取り込まれ、追加の料金が発生します。 GoogleCloud のオペレーション スイートの料金 大量のログを取り込む場合のCloud Loggingの費用感としては、 Cloud Bigtableの例 が参考になります。(加えて、ログ集約先のストレージコスト、監査のためのクエリのコストにも影響があります。) 要件に対して不要なログを削減するため、 特定のプリンシパルのロギングを制限する ことが可能です。 (2)Cloud Loggingバケットの保存先を東京リージョンに変更 こちらも組織レベルで保存先リージョンを設定することが可能です。​​ ​​​​​ 設定手順についてはCloud Loggingのガイドを参照してください。 Cloud Logging のデータ リージョン なお、Loggingバケットのリージョン指定は、既存のバケットには遡及しないため、フォルダ、プロジェクトを作成するより前に設定を行う必要があります。 組織作成時に作成された、組織自体に対する操作の監査ログが格納される _Requiredバケットについては、保存先をリージョンに変更することができません。 また、現状ではLoggingバケットの保存先をグローバルからリージョンに変更した場合、 Error Reporting 機能が利用できない制約 があります。 (3)Cloud Loggingバケットの暗号化に顧客管理の暗号鍵(CMEK)を利用 こちらも組織レベルでCMEKの利用を設定することが可能です。 設定手順についてはCloud Loggingのガイドを参照してください。 Logging のストレージ データを保護する鍵を管理する ガイドの手順に従い、組織に関連付けられたサービスアカウント( cmek-o<組織ID>@gcp-sa-logging.iam.gserviceaccount.com )に暗号鍵の暗号化 / 復号の役割を割り当てる際に、追加で以下の対応が必要でした。 暗号鍵がVPC Service Controlsの境界内にある場合、組織のサービスアカウントを アクセスレベル に追加して、境界への アクセス許可を設定 する。 組織ポリシーconstraints/iam.allowedPolicyMemberDomains で組織外部への権限付与を制限している場合、組織のサービスアカウントは外部IDと判定されポリシー違反となった。暗号鍵の利用権付与する際、鍵を保有するプロジェクトで一時的に組織ポリシーを上書きしてエラーを回避する。 保存先がグローバルのバケットの暗号化にはCMEKを利用できない制約があります。そのため、(保存先をグローバルから変更できない)組織自体の _RequiredバケットをCMEKで暗号化することはできません。 (4)組織の監査ログを集約して、長期保管 A.集約シンクの設定 Cloud Loggingの集約シンクを利用することで、組織の全プロジェクトの監査ログを監査プロジェクトへ配信することができます。 設定手順についてはCloud Loggingのガイドを参照してください。 集約シンクを構成する 全ての監査ログを転送する場合、包含フィルタ条件には  logName:cloudaudit.googleapis.com を指定します。 本番/開発別の監査プロジェクトへ集約は、(本番と開発でプロジェクトを配置するフォルダを分けていることを前提に)フォルダに対して集約シンクを適用することで実現できます。 また組織自体に対する操作は、組織のCloud Loggingに記録されるため、下位のフォルダの集約シンクでは拾われません。組織自体にも(非集約)シンクを作成して、本番用の監査プロジェクトへログを転送します。 ​​​​​​​​​Cloud Identityの監査ログも集約対象とする場合、事前に 監査ログの共有設定 が必要です。 シンク設定前のログは転送されませんが、 ログのコピー機能 がプレビュー版で提供されています。 組織の監査ログの集約構成例 今回検証していませんが、別案として組織直下に集約シンクを2つ作成し、フィルタ条件で本番/開発プロジェクトのログを選別するという構成も可能だと思います。 また、後述のBigQueryによる監査クエリのコストを最適化する観点で、監査が人(Googleアカウント)による操作だけを対象とする場合には、人以外(サービスアカウント)のAPI実行の監査ログ保管先を分けるという構成も考えられます。 B.配信先ストレージの選択 シンクからログを配信する先のストレージと対応するユースケースは以下の表のようになります。 配信先 ユースケース BigQuery 高度な分析 Cloud Storage コンプライアンス、長期保管 Pub/Sub Splunk等外部サービスとの統合 ログバケット ログベースの指標 、 アラート 参考サイト: BigQuery の監査ログ パイプラインを活用した使用状況分析 今回はコンプライアンスと長期保管を主目的として、Cloud Storageを選択します。 Cloud Storageバケットのロケーション、CMEKの利用についてはCloud Storageのガイドを参照してください。 バケットの保存場所 顧客管理の暗号鍵の使用 C.Cloud Storageの保持ポリシーによるログの保護 Cloud Storageの保持ポリシー機能を利用することで、保存期間内の監査ログの更新、削除が不可になります。​​​​​​​ 設定手順についてはCloud Storageのガイドを参照してください。 保持ポリシーと保持ポリシーのロック なお、保持ポリシーでバケット内のログは保護されますが、集約シンクが削除されると新規ログの配信がストップするため、集約シンク自体は別途保護(IAMポリシーを適切に設定する、変更を監視する等)する必要があります。 また、Cloud Storageのライフライクル管理機能により保存期間を超過したログを自動削除することも可能です。監査等によるアクセス頻度によっては、一定期間経過後の ストレージクラス の変更もコスト削減に寄与します。 設定手順についてはCloud Storageのガイドを参照してください。 オブジェクトのライフサイクル管理 D.組織、フォルダーに紐づくリソースの設定変更監視について リソースの設定変更監視は、 Cloud Security Centerのモニタリング脆弱性チェック のように、監査ログに対してログベースの指標に基づくアラートを作成する方法があります。(現在はログベースのアラートもGAになっています。) 一方で、組織、フォルダ階層の監査ログをソースとする場合 ログベースの指標、アラートは 組織、フォルダの階層では定義不可能。 組織、フォルダからプロジェクトのログバケットへ転送されたログに対する、ログベースの指標は 、2023年1月時点でGA前。 組織、フォルダー階層の監視用ログシンクからPub/Subトピックへの配信を利用する場合、組み込みのメール送信が未サポート。 という制限事項があります。 また、監視に Cloud Asset Inventoryの変更フィード を利用する場合、組織レベル、フォルダーレベルの変更フィードからVPC Service Controls境界内のPub/Subトピックへの配信不可など、 VPC Service Controls関連の制限事項 があります。 4.アクセス監査資料の作成 前掲の監査ログ保管ストレージの選択肢の表から、高度な分析にはBigQueryが適しています。 監査資料作成のためのクエリをBigQueryで実行するため、監査ログをBigQueryにも重複配信(BigQuery側での保存期間は監査要件に応じて短く設定)するという構成も可能ですが、今回は監査ログを保管しているCloud StorageバケットをBigQueryから外部テーブルとして参照する方法を紹介します。 (1)ログフォーマットの把握 Cloud Loggingのログレコードは LogEntryという構造 になっています。 監査ログの場合、LogEntryのprotoPayload要素が、監査ログを表す AuditLogという構造 になっています。 このレコード定義と実ログのサンプルから監査資料で必要そうな項目がどこにあるかを把握していきます。 下の画像はBigQueryテーブルのメタデータ更新が権限不足エラーとなった場合の監査ログを一部加工したものです。 監査ログレコードのサンプル 日次のアクセス監査ではプロジェクト(組織、フォルダー)の粒度でアクセス先を識別できればよいものとして、必要な項目の格納場所を整理します。 項目 LogEntry内の格納場所 操作日時 timestamp が使えそうです。 注意点としてBigQueryのデータ読み取り量を最適化するため、WHERE句で日付を限定する条件はこの項目ではなく、_FILE_NAME疑似列に適用します。(詳細は後述) 操作ユーザー protoPayload.authenticationInfo.principalEmail が該当します。 システム自動処理や、Google Cloudの内部処理によるAPI実行も監査ログに記録されます。 また、権限不足エラーとなった操作など 操作ユーザーが匿名化されるケース があります。 アクセスしたプロジェクト(組織、フォルダー) logName が監査ログの出元です。 ​​​​​logNameの例: ​​​​​"projects/[PROJECT_ID]/logs/[LOG_ID]" ​​​​​"organizations/[ORGANIZATION_ID]/logs/[LOG_ID]" ​​​​​"billingAccounts/[BILLING_ACCOUNT_ID]/logs/[LOG_ID]" ​​​​​"folders/[FOLDER_ID]/logs/[LOG_ID]" ​​​​​logNameの二階層目の文字列までを抽出することで、アクセスしたプロジェクト(組織、フォルダー)が特定できそうです。 監査ログの種類(リソースの更新操作か) logName 末尾の[LOG_ID]部分を見ればわかりそうです。 cloudaudit.googleapis.com%2Factivity がリソース更新の監査ログです。 注意点として操作日時同様、BiqQueryクエリのWHERE句では、_FILE_NAME疑似列に条件を適用します。 データアクセス監査ログのサブタイプ(ADMIN_READ、DATA_READ、DATA_WRITE)については直接識別する属性がなく、protoPayload.serviceNameとprotoPayload.methodNameの組から個別に判断するしかなさそうです。 同様にポリシー拒否監査ログの対象操作がADMIN_WRITE、ADMIN_READ、DATA_READ、DATA_WRITE、のどれに該当するかも直接識別できません。 権限不足エラーだったか 権限エラーとなった操作の監査ログを確認すると protoPayload.status.code の値が 7 になっています。 Java Cloud Client Libraries のEnumの定義を見ると、PERMISSION_DENIEDのエラーコードが7のようです。 なお、VPC Service Controlsのポリシー違反も同じエラーコードになるようです。 区別が必要な場合は、外部テーブルの_FILE_NAME疑似列の値にcloudaudit.googleapis.com/policyが含まれるものが、ポリシー違反のログになります。 接続元IPアドレス protoPayload.requestMetadata.callerIp に格納されています。 Google Cloudサービス経由の呼び出しなど、 IPアドレスが秘匿化されるケース も存在します。 (2)BigQuery外部テーブルの作成 監査ログを保管したCloud Storageバケットを参照する外部テーブルを作成します。 作業手順についてはBigQueryのガイドを参照してください。 Cloud Storage データのクエリ シンクの転送先に直接BigQueryを指定した場合には 特別な命名規則を使用するスキーマ が自動生成されます。 一方で外部テーブルとして監査ログを保管したCloud Storageバケットを参照する場合、スキーマの自動認識に特別な配慮はないようです。@typeなどのフィールド名がBigQueryの命名規則に合致しないため、スキーマの自動認識はエラーとなりました。 インターネットで検索しましたが流用できるスキーマ定義が見つからなかったため、以下のスキーマ定義を手動で作成しました。 LogEntry の第一階層までを定義し、ネストされた構造化データのフィールドはRECORD型ではなく、JSON型としています。 監査ログ格納バケットを参照する外部テーブルのスキーマ定義 (3)BigQueryから読み込むデータ量(=分析料金)の最適化 BigQueryではクエリで読み込んだデータ量に応じても従量課金されます。( オンデマンド料金モデル の場合) そのためBigQueryのテーブルはクエリの条件となる項目(日付等)でパーテーション分割して、アクセスするデータ範囲を削減することが定石となっています。 Cloud Storageの場合、Hiveパーティション分割レイアウトに沿ったフォルダ構成 例: gs://[バケット名]/myTable/dt=2019-10-31/lang=en/foo になっていれば、BigQueryからパーテーション分割された外部テーブルとして扱われます。 Cloud Storage の外部パーティション分割データに対してクエリを実行する 監査ログを 集約シンクでCloud Storageへ配信した場合のファイルパス は下記の様になります。 gs://[バケット名]/cloudaudit.googleapis.com/[activity|data_access|policy]/2022/12/28/03:00:00_03:59:59_S0.json これは2022/12/28 3時台のログファイルの例です。なお、タイムゾーンは日本時間ではなく、UTCです。 パスに日付と時間が含まれているものの、Hiveパーティション分割レイアウトに沿っていないため、BigQueryからはパーテーション分割されたデータとは認識されません。 一方で、ファイルのパスは  クエリから _FILE_NAMEという疑似列で参照することができ、 BigQueryガイド Cloud Storage でパーティション分割されていないデータをクエリする には クエリに _FILE_NAME 疑似列に対するフィルタ述語がある場合、BigQuery は、フィルタに一致しないファイルの読み取りをスキップしようとします。 と記載されています。 この特性から1日分のアクセス監査資料を作成したい場合、監査ログ内のタイムスタンプではなく、 日時を含む_FILE_NAME疑似列のパスに対して条件を適用することで、アクセスするデータ範囲を削減することが可能になります。 _FILE_NAME LIKE ' %2022/12/30% ' という条件では日本時間ではなく、UTCでの1日分の監査ログが対象になります。 日本時間での1日分の監査ログの抽出が必要な場合、日付を求める手順としては、 ・正規表現でファイルパスから年/月/日/時間を抽出し ・タイムスタンプ型にパースした上で日本時間での日付に変換する という方法が考えられます。 DATE (PARSE_TIMESTAMP( ' %Y/%m/%d/%H ' , REGEXP_EXTRACT(_FILE_NAME,  ' [0-9]+/[0-9]+/[0-9]+/[0-9]+ ' )) , ' Asia/Tokyo ' ) =  ' 2022-12-30 ' _FILE_NAME 疑似列への条件の記載方法によっては、実データへのアクセスがスキップされないケース があります。 実際に実データへのアクセスがスキップされるかは、先日付を指定してクエリ結果の「課金されるバイト数」が0 Bとなることで裏取りします。 _FILE_NAME疑似列条件による実データへのアクセススキップ なお、 組織ポリシーconstraints/gcp.restrictNonCmekServices でBigQueryのCMEK暗号化利用を必須としている場合、コンソールからクエリを実行する前に、 クエリ結果を格納する一時テーブルの暗号化に利用する鍵を設定する 必要があります。 (クエリ画面の「その他」→「クエリの設定」→「詳細オプション」→「顧客管理の暗号化」に鍵のリソースIDを入力します。) 集約シンクから直接エクスポートしたBigQueryテーブルの場合 ログシンクからBigQueryへ直接エクスポートする場合、 自動生成されるテーブルはtimestampのUTC日付でパーテーション分割 (クラスタリング列は未指定)されます。私が調べた限り、シンクからのエクスポート時にパーテーションのタイムゾーンを調整する設定は見つかりませんでした。 外部テーブルとの違いとして、日本時間での1日分のログの参照条件の記載は DATE ( timestamp , " Asia/Tokyo " ) = " 2022-12-30 " という記載では、全パーテーションが走査対象となってしまうため、パーテーション列側を加工するのを避け timestamp >= TIMESTAMP ( ' 2022-12-29 15:00:00 ' ) AND timestamp < TIMESTAMP ( ' 2022-12-30 15:00:00 ' ) という記載にする必要がありました。 自然体だと日本時間での1日分のログを監査するには、UTC2日分のパーテーションの走査が必要となります。 クエリ最適化対策として、オンデマンド配車および配達ソリューションのログ解析のガイドでは、 自動生成されたテーブルに後からtimestampをクラスタリング列に追加する方法 が紹介されていました。 for TABLE in `bq ls --format=csv -project_id =< プロジェクトID > < ログエクスポート先のデータセットID > | cut -d, -f2 | tail + 2 ` \ ; do \ bq update --clustering_fields=timestamp ${TABLE} \ ; done (4)アクセス監査のためのクエリを考える 一例として、本番プロジェクトへのアクセスには事前申請が必要であり、無申請のアクセスがないかを監査するシナリオを想定します。 ユーザーはコンソールからユーザーアカウント(Cloud Identityで管理するユーザー)でアクセスすることを前提として、各ユーザーが1日にアクセスしたプロジェクトの情報を出力します。 Cloud Identityへのログインは、特定のプロジェクトへのアクセスを示すものではないため、ログインの監査ログだけではアクセス先は不明です。 AWSでIAMユーザーでログイン後に各アカウントのIAMロールにスイッチロールする運用の場合には、スイッチロールを各アカウントへのログイン操作とみなす作戦が取れますが、 Google Cloudには当てはまりません。 代替手段として力技になりますが、なんらかの監査ログが記録されていれば当該プロジェクトへログインしたとみなし、各ユーザーアカウント/アクセス先プロジェクト毎に ユーザーアカウント アクセスしたプロジェクト(組織、フォルダ) (権限エラーのぞく)最初の操作時間 (権限エラーのぞく)最後の操作時間 (権限エラーの)最初の操作時間 (権限エラーの)最後の操作時間 接続元IPアドレス(複数ある場合カンマで連結) アクセス先横断で当該ユーザーの当日の全操作が権限エラーの場合〇を表示 を権限エラーの明細行を分けて出力します。​​​​​​​改善の余地ありますが、以下のようなSQLが考えられます。 WITH audit_logs AS ( SELECT JSON_VALUE(protoPayload.authenticationInfo.principalEmail) AS principalEmail, REGEXP_EXTRACT(logName, ' [^/]+/[^/]+ ' ) AS accessedProject, /* 正規表現で[organizations/xxxx][folders/xxxxx][projects/xxxx]部分を抽出 */ DATETIME ( timestamp , ' Asia/Tokyo ' ) AS operationTimestamp, /*パフォーマンス最重視する場合、tz補正は最後のSELECT句で行う*/ JSON_VALUE(protoPayload.requestMetadata.callerIp) AS callerIp, CASE WHEN JSON_VALUE(protoPayload.status.code) = ' 7 ' THEN true ELSE false END AS permissionDenied FROM `poc01-hn- audit -■■■■■■■■■■. audit .external_audit_log` WHERE DATE (PARSE_TIMESTAMP( ' %Y/%m/%d/%H ' , REGEXP_EXTRACT(_FILE_NAME, ' [0-9]+/[0-9]+/[0-9]+/[0-9]+ ' )) , ' Asia/Tokyo ' ) = ' 2022-12-29 ' /* 日付はパラメタ化*/ /* 読み取りデータ量削減のため、実データではなく_FILE_NAME疑似列で日付、ログ種別を絞る。_FILE_NAMEはUTCの日時 */ AND ( _FILE_NAME LIKE ' %cloudaudit.googleapis.com/activity% ' OR _FILE_NAME LIKE ' %cloudaudit.googleapis.com/data_access% ' OR _FILE_NAME LIKE ' %cloudaudit.googleapis.com/policy% ' ) /* 監査対象はユーザーアカウントに絞る*/ AND JSON_VALUE(protoPayload.authenticationInfo.principalEmail) LIKE ' %@% ' AND JSON_VALUE(protoPayload.authenticationInfo.principalEmail) NOT LIKE ' %gserviceaccount.com ' ) SELECT principalEmail, accessedProject , MIN ( CASE WHEN permissionDenied THEN null ELSE operationTimestamp END ) AS firstOperationTimestamp, MAX ( CASE WHEN permissionDenied THEN null ELSE operationTimestamp END ) AS lastOperationTimestamp, MIN ( CASE WHEN permissionDenied THEN operationTimestamp ELSE null END ) AS firstAuthorizationErrorTimestamp, MAX ( CASE WHEN permissionDenied THEN operationTimestamp ELSE null END ) AS lastAuthorizationErrorTimestamp, STRING_AGG( DISTINCT callerIp ORDER BY callerIp) AS callerIp , CASE WHEN    COUNTIF( NOT permissionDenied ) OVER( PARTITION BY principalEmail ) > 0 THEN '' ELSE ' 〇 ' END AS permissionDeniedOnlyUser FROM audit_logs GROUP BY principalEmail,accessedProject,permissionDenied order by 1 , 2 , 3 , 5 アクセス監査クエリ結果 監査用クエリで利用している下記の要素 WITH句、 共通テーブル式 SELECT句内での 条件分岐 Window関数 (クエリ中のCOUNTIF関数は、集計関数ではなくWindow関数として利用) は、SQLの入門書では扱われる事が少なく、馴染みがない人もいたと思います。 共通テーブル式と、SELECT句内でのCASEの活用については以下の記事が参考になります。 SQL緊急救命室 第5回 時代錯誤症候群~進化し続けるSQLに取り残されるな! SQL緊急救命室 第2回 冗長性症候群~条件分岐をUNIONで表現するなかれ Window関数(OLAP関数)については、以下の記事が参考になります。 SQLアタマアカデミー 最終回 OLAP関数で強力な統計処理を実現!―手続き型から理解するSQL (2)OLAP関数の基本構文 両方の記事の著者であるDBエンジニアのミック氏のサイトには、有益な記事が多数掲載されています。 リレーショナル・データベースの世界 一般的なDBエンジンと異なり、BigQueryでは非再帰の共通テーブル式がSQL文中で複数回参照される場合でも実体化されず、毎回SELECT処理が実行される点に注意が必要です。 そのため、監査クエリの後半部分をSELECT句内のCASEにより分岐ではなく、共通テーブル式を参照する2つのSELECT文をUNION ALLするようなSQLで記載すると、データ処理料金が2倍になってしまいます。 BigQueryでインラインビューではなく非再帰の共通テーブル式を使う主なメリットは、性能面にはなく、SQLの可読性になります。 SQL文がBigQueryのパフォーマンスに与える影響については、以下のブログ記事が具体例が豊富で参考になります。 14 Best Practices to Tune BigQuery SQL Performance (5)処理の自動化を考える A. Looker Studioによるレポート自動配信 Googleが提供するBIツールの Looker Studio には、 レポートのメール配信スケジュールを設定する機能 があります。 この機能を利用して、日次でBigQueryをデータソースに監査資料を作成し、PDFを監査担当者にメール配信できないかと期待しましたが、私が確認した限り 明細行数の多い表を、レポートを改ページして全行表示することができない。(明細行数が多い場合、レポート"ページ内部で"スクロールかページングする構成) PDFにはレポートページで見える範囲の明細行しか出力できない という仕様のため、全ての明細行をPDFに出力することが不可能でした。 B. CloudSchedularからPub/Sub経由でCloudFunctions起動 代替策として Cloud Scheduler から日次で  Pub/Subトピック経由で Cloud Functions を起動 し、BigQueryのクエリ結果から作成した監査資料をCloud Storageへアップロードする構成が考えられます。(最大実行時間等の クォータ には注意が必要です) その際CMEKの利用について、以下の制約、ワークアラウンドがあります。 (a) Cloud Functions サーバレスVPCアクセスコネクタインスタンスはCMEKでの暗号化に非対応 ネットワーク統制要件として、Cloud Functions関数の全通信をサーバレスVPCアクセスコネクタ経由にする要件がある場合に問題となります。 CMEKの利用を組織レベルで強制するために、 組織ポリシーconstraints/gcp.restrictNonCmekServices でConpute EngineのCMEK利用を必須としていると、コネクタインスタンスの起動に失敗します。 コネクタインスタンスはオートスケールの対象のため、設定時のみポリシーを無効化するワークアラウンドは採用できません。 サーバレスVPCアクセスコネクタを利用する際は、Compute Engineについては、組織ポリシーconstraints/gcp.restrictNonCmekServicesの適用対象外とし、別途発見的統制でCMEK利用の統制を行う必要があります。 (b)Cloud Functions(第二世代)が自動生成するCloud Storage バケット、Artifact Registry リポジトリはCMEKでの暗号化に非対応 ワークアラウンドとして、最初のCloudFunctions関数を作成するより前に同名のリソース Cloud Storageバケット gcf-v2-uploads-<プロジェクト番号>-<リージョン> Cloud Storageバケット gcf-v2-sources-<プロジェクト番号>-<リージョン> Artifact Registryリポジトリ gcf-artifacts をCMEKを利用する設定で作成しておくことで、CMEKの利用が可能になります。 なお、コンソールからは確認、設定できませんが、自動生成されるCloud StorageバケットにはCORS設定がされています。先回りして作成するバケットにも同じCORS設定をしないと、コンソールから関数のソースコードの参照、更新が不可になります。 CloudFunctionsが自動生成するGCSバケットのCORS設定 5.まとめ 本日の内容をまとめると以下になります。 デフォルトでは(BigQuery以外の)データアクセス監査ログは記録されない。全ての監査ログを記録するには追加の設定が必要。 リージョン指定、CMEK利用の要件がある場合は、追加の設定が必要。 集約シンクを利用することで組織全体の監査ログを集約して保管できる。 Cloud Storageの保持ポリシーにより、監査ログを変更、削除から保護できる。 BigQueryからCloud Storage上の監査ログの分析ができる。 舘山 浩之 みずほリサーチ&テクノロジーズ 先端技術研究部に所属。個人のキャリアではAWSの利用経験が長く、Google Cloudは2022年より利用開始。
アバター
G-gen の武井です。 当記事では Google Cloud (旧称 GCP) のリソース情報を gcloud コマンドと jq コマンドで 取得する方法を紹介します。 概要 gcloud コマンド jq コマンド 実行方法 コマンドツールの準備 使い方 失敗例 成功例 実例 組織 組織名 (ドメイン名) を抽出 組織 ID を抽出 条件に合致する値を抽出 フォルダ フォルダ名を抽出 フォルダ ID を抽出 条件に合致する値を抽出 プロジェクト プロジェクト名を抽出 プロジェクト ID を抽出 プロジェクト番号 を抽出 条件に合致する値を抽出 概要 gcloud コマンド gcloud コマンド ( gcloud CLI ) は、Google Cloud リソースの管理を行うコマンドツールです。 Cloud コンソール ( GUI ) と同様、Google Cloud のリソース作成からその後の管理を含め、一連の操作をコマンドラインを介して行えます。 jq コマンド jq コマンドは JSON データの加工や整形 ( 抽出 / 変換 / 集計 ) をするツールです。Linux でいう grep / sed / awk コマンドに相当します。 実行方法 それぞれのコマンドを組み合わせて実行するには、 gcloud コマンドの取得したデータを | (パイプ) コマンドで jq コマンドに渡します。 gcloud コマンドで取得したデータは JSON 形式して渡す必要があるため、 --format を使ってデータを変換します。 コマンドツールの準備 Cloud Shell では gcloud / jq コマンドのどちらもインストール済みですが、それ以外の環境ではインストールが必要です。 ※ 当記事では実行例を中心に紹介するため、インストール方法等の説明は割愛します。 使い方 gcloud organizations list コマンド ( 組織を一覧表示するコマンド ) を例に使い方を紹介します。 失敗例 前述の通り、 jq コマンドに渡すデータは JSON 形式にする必要があります。 gcloud コマンドの出力 (デフォルト) はテキスト形式なので、データ形式を明示的に指定しない場合はエラーになります。 $ gcloud organizations list | jq parse error : Invalid numeric literal at line 1 , column 13 成功例 --format=json でデータ形式を JSON に変換して jq コマンドに渡すことでコマンドラインが成功します。 まずは jq コマンドには渡さず、JSON 形式で出力した場合の例です。 $ gcloud organizations list -- format = json [ { " creationTime ": " 2023-01-19T23:02:33.677Z ", " displayName ": " example001.co.jp ", " lifecycleState ": " ACTIVE ", " name ": " organizations/11111111 ", " owner ": { " directoryCustomerId ": " AAAAAAAA " } } , { " creationTime ": " 2022-11-02T00:35:22.696Z ", " displayName ": " example002.co.jp ", " lifecycleState ": " ACTIVE ", " name ": " organizations/22222222 ", " owner ": { " directoryCustomerId ": " BBBBBBBB " } } , { " creationTime ": " 2021-11-11T03:22:50.021Z ", " displayName ": " example003.co.jp ", " lifecycleState ": " ACTIVE ", " name ": " organizations/33333333 ", " owner ": { " directoryCustomerId ": " CCCCCCCC " } } , { " creationTime ": " 2021-10-27T03:09:00.929Z ", " displayName ": " example004.co.jp ", " lifecycleState ": " ACTIVE ", " name ": " organizations/44444444 ", " owner ": { " directoryCustomerId ": " DDDDDDDD " } } , { " creationTime ": " 2021-08-12T08:08:27.729Z ", " displayName ": " example005.co.jp ", " lifecycleState ": " ACTIVE ", " name ": " organizations/55555555 ", " owner ": { " directoryCustomerId ": " EEEEEEEE " } } ] 次に jq コマンドに渡した場合です。オプション等を使用せず、単に jq に渡しただけでは前述の出力と変わりありません。 jq コマンドによる加工や整形については次の章でご紹介します。 $ gcloud organizations list -- format = json | jq [ { " creationTime ": " 2023-01-19T23:02:33.677Z ", " displayName ": " example001.co.jp ", " lifecycleState ": " ACTIVE ", " name ": " organizations/11111111 ", " owner ": { " directoryCustomerId ": " AAAAAAAA " } } , { " creationTime ": " 2022-11-02T00:35:22.696Z ", " displayName ": " example002.co.jp ", " lifecycleState ": " ACTIVE ", " name ": " organizations/22222222 ", " owner ": { " directoryCustomerId ": " BBBBBBBB " } } , { " creationTime ": " 2021-11-11T03:22:50.021Z ", " displayName ": " example003.co.jp ", " lifecycleState ": " ACTIVE ", " name ": " organizations/33333333 ", " owner ": { " directoryCustomerId ": " CCCCCCCC " } } , { " creationTime ": " 2021-10-27T03:09:00.929Z ", " displayName ": " example004.co.jp ", " lifecycleState ": " ACTIVE ", " name ": " organizations/44444444 ", " owner ": { " directoryCustomerId ": " DDDDDDDD " } } , { " creationTime ": " 2021-08-12T08:08:27.729Z ", " displayName ": " example005.co.jp ", " lifecycleState ": " ACTIVE ", " name ": " organizations/55555555 ", " owner ": { " directoryCustomerId ": " EEEEEEEE " } } ] 実例 組織 組織に関する情報の取得には、 gcloud organizations list コマンドを使用しますが、その中から特定の値を抽出する方法をいくつかご紹介します。 組織名 (ドメイン名) を抽出 組織名の key は displayName ですので、以下のようにコマンドを実行すると組織名を抽出できます。 $ gcloud organizations list -- format json | jq - r '.[].displayName' example001 . co . jp example002 . co . jp example003 . co . jp example004 . co . jp example005 . co . jp 組織 ID を抽出 組織 ID の key は name ですので、以下のようにコマンドを実行すると組織 ID を抽出できます。 $ gcloud organizations list -- format json | jq - r '.[].name' organizations /11111111 organizations /22222222 organizations /33333333 organizations /44444444 organizations /55555555 ID 部分だけを抽出したい場合、"organizations/" は不要となるので除外します。 ( どちらも結果は同じ ) # gsub に渡して置換するパターン $ gcloud organizations list -- format json | jq - r '.[].name | gsub("organizations/";"")' 11111111 22222222 33333333 44444444 55555555 # sed に渡して置換するパターン $ gcloud organizations list -- format json | jq - r '.[].name' | sed - e " s | organizations /||" 11111111 22222222 33333333 44444444 55555555 条件に合致する値を抽出 select を使用して条件 (組織名) に合致する値 (組織 ID) を抽出します。 # gsub に渡して置換するパターン $ gcloud organizations list -- format json | jq - r '.[] | select(.displayName == "example001.co.jp") | .name | gsub("organizations/";"")' 11111111 # sed に渡して置換するパターン $ gcloud organizations list -- format json | jq - r '.[] | select(.displayName == "example001.co.jp") | .name' | sed - e " s | organizations /||" 11111111 key を , (カンマ) で区切れば複数の値 (組織 ID と 顧客 ID) も抽出可能です。 # gsub に渡して置換するパターン $ gcloud organizations list -- format json | jq - r '.[] | select(.displayName == "example001.co.jp") | .displayName,.name,.owner.directoryCustomerId | gsub("organizations/";"")' example001 . co . jp 11111111 AAAAAAAA # sed に渡して置換するパターン gcloud organizations list -- format json | jq - r '.[] | select(.displayName == "example001.co.jp") | .displayName,.name,.owner.directoryCustomerId' | sed - e " s | organizations /||" example001 . co . jp 11111111 AAAAAAAA フォルダ フォルダに関する情報の取得には、 gcloud resource-manager folders list コマンドを使用しますが、その中から特定の値を抽出する方法をいくつかご紹介します。 出力例は以下のとおりです。 $ gcloud resource - manager folders list -- organization =$ { ORG_ID } -- format json [ { " createTime ": " 2021-12-26T15:07:59.662Z ", " displayName ": " dev ", " lifecycleState ": " ACTIVE ", " name ": " folders/11111111 ", " parent ": " organizations/11111111 " } , { " createTime ": " 2021-10-27T03:32:42.669Z ", " displayName ": " stg ", " lifecycleState ": " ACTIVE ", " name ": " folders/22222222 ", " parent ": " organizations/11111111 " } , { " createTime ": " 2022-11-11T11:26:24.972Z ", " displayName ": " prod ", " lifecycleState ": " ACTIVE ", " name ": " folders/33333333 ", " parent ": " organizations/11111111 " } ] フォルダ名を抽出 フォルダ名の key は displayName ですので、以下のようにコマンドを実行するとフォルダ名だけを抽出できます。 $ gcloud resource - manager folders list -- organization =$ { ORG_ID } -- format json | jq - r '.[].displayName' dev stg prod フォルダ ID を抽出 フォルダ ID の key は name ですので、以下のようにコマンドを実行するとフォルダ ID だけを抽出できます。 $ gcloud resource - manager folders list -- organization =$ { ORG_ID } -- format json | jq - r '.[].name' folders /11111111 folders /22222222 folders /33333333 ID 部分だけを抽出したい場合、"folders/" は不要となるので除外します。 ( どちらも結果は同じ ) # gsub に渡して置換するパターン $ gcloud resource - manager folders list -- organization =$ { ORG_ID } -- format json | jq - r '.[].name | gsub("folders/";"")' 11111111 22222222 33333333 # sed に渡して置換するパターン $ gcloud resource - manager folders list -- organization =$ { ORG_ID } -- format json | jq - r '.[].name' | sed - e " s | folders /||" 11111111 22222222 33333333 条件に合致する値を抽出 select を使用して条件 (フォルダ名) に合致する値 (フォルダ ID) だけを抽出します。 # gsub に渡して置換するパターン $ gcloud resource - manager folders list -- organization =$ { ORG_ID } -- format json | jq - r '.[] | select(.displayName == "dev") | .name | gsub("folders/";"")' 11111111 # sed に渡して置換するパターン $ gcloud resource - manager folders list -- organization =$ { ORG_ID } -- format json | jq - r '.[] | select(.displayName == "dev") | .name' | sed - e " s | folders /||" 11111111 プロジェクト プロジェクトに関する情報の取得には、 gcloud projects list コマンドを使用しますが、その中から特定の値を抽出する方法をいくつかご紹介します。 今回の例ではフォルダ配下のプロジェクトをリストするため、 ${FLDR_ID} で特定のフォルダ ID を指定します。 また、組織直下に作成されたプロジェクトリソースをリストする場合は組織 ID を指定します。 $ gcloud projects list -- filter =" parent . id : $ { FLDR_ID } " -- format json [ { " createTime ": " 2023-02-07T04:52:43.403Z ", " lifecycleState ": " ACTIVE ", " name ": " alpha ", " parent ": { " id ": " 11111111 ", " type ": " folder " } , " projectId ": " alpha ", " projectNumber ": " 11111111 " } , { " createTime ": " 2023-01-06T03:57:43.872Z ", " lifecycleState ": " ACTIVE ", " name ": " bravo ", " parent ": { " id ": " 11111111 ", " type ": " folder " } , " projectId ": " bravo ", " projectNumber ": " 22222222 " } , { " createTime ": " 2022-12-20T05:53:14.193Z ", " lifecycleState ": " ACTIVE ", " name ": " charlie ", " parent ": { " id ": " 11111111 ", " type ": " folder " } , " projectId ": " charlie ", " projectNumber ": " 33333333 " } ] プロジェクト名を抽出 プロジェクト名の key は name ですので、以下のようにコマンドを実行するとプロジェクト名だけを抽出できます。 $ gcloud projects list -- filter =" parent . id : $ { FLDR_ID } " -- format json | jq - r '.[].name' alpha bravo charlie プロジェクト ID を抽出 プロジェクト ID の key は projectId' ですので、以下のようにコマンドを実行するとフォルダ ID だけを抽出できます。 $ gcloud projects list -- filter =" parent . id : $ { FLDR_ID } " -- format json | jq - r '.[].projectId' alpha bravo charlie プロジェクト番号 を抽出 プロジェクト ID の key は projectNumber' ですので、以下のようにコマンドを実行するとフォルダ ID だけを抽出できます。 $ gcloud projects list -- filter =" parent . id : $ { FLDR_ID } " -- format json | jq - r '.[].projectNumber' 11111111 22222222 33333333 条件に合致する値を抽出 select を使用して条件 (プロジェクト名) に合致する値 (プロジェクト ID と番号) だけを抽出します。 $ gcloud projects list -- filter =" parent . id : $ { FLDR_ID } " -- format json | jq - r '.[] | select(.name == "alpha") | .projectId, .projectNumber' alpha 11111111 武井 祐介 (記事一覧) 2022年4月入社 / クラウドソリューション部 / 技術2課所属 趣味はゴルフにロードバイク。IaC や CI/CD 周りのサービスやプロダクトが興味分野です。 Google Cloud 認定全冠達成!(2023年6月)
アバター
G-gen の杉村です。Google Cloud(旧称 GCP)の VPC では、備え付きのファイアウォール機能である Cloud Next Generation Firewall (Cloud NGFW)が利用できます。そのうち ファイアウォールポリシー (Firewall policies)で利用可能な Cloud NGFW Standard の通信制御機能について解説します。 概要 Cloud NGFW Standard とは 利用可能な制御機能 ルールの適用 料金 FQDN オブジェクト 概要 ユースケース 仕組み その他の仕様 名前解決順序 利用できないドメイン 位置情報オブジェクト 概要 ユースケース 仕組み 脅威インテリジェンス(Google Cloud Threat Intelligence) 概要 ユースケース 仕組み 概要 Cloud NGFW Standard とは Cloud NGFW Standard とは、Google Cloud の VPC ネットワークで利用可能なファイアウォールである、Cloud Next Generation Firewall(以下、Cloud NGFW)の有償機能です。 Cloud NGFW には無償の Essentials と、有償の Standard 、および Enterprise の3ティアが存在します。Essentials は IP アドレスやプロトコル、ポート番号などの L3/L4 レベルの制御のみを提供し、Standard では FQDN ベースの制御や地理情報に基づいた制御など、高度なルールが利用可能です。Enterprise は、IPS(侵入防止サービス)を提供します。 参考 : Cloud NGFW の概要 Cloud NGFW Standard や Essentials、Enterprise を含めた全体の体系については、以下の記事をご参照ください。 blog.g-gen.co.jp 利用可能な制御機能 Cloud NGFW Standard では、以下の制御オブジェクトが利用可能です。 参考 : ファイアウォール ポリシールール No 名称 概要 1 FQDN オブジェクト FQDN (fully qualified domain name) を基にフィルタリング 2 位置情報オブジェクト 接続元/先の地理的情報をもとにフィルタリング 3 脅威インテリジェンス(Google Cloud Threat Intelligence) Google が持つ脅威インテリジェンスをもとにフィルタリング ルールの適用 Cloud NGFW Standard のルールは、 ファイアウォールポリシー でのみ利用可能です。 ファイアウォールポリシーとは、通信制御ルールをまとめるためのグルーピング機能であり、複数のプロジェクトや複数の VPC ネットワークに適用可能です。ファイアウォールポリシーには、階層型ファイアウォールポリシー、グローバルネットワークファイアウォールポリシー、リージョンネットワークファイアウォールポリシー」の3種類があります。 Google Cloud の VPC ネットワークにおいては、ファイアウォールポリシーの他に、単一 VPC ネットワークでのみ利用可能な VPC ファイアウォールルール も存在していますが、こちらでは Cloud NGFW Standard の機能は利用できず、無償で L3/L4 レイヤの制御を提供する Cloud NGFW Essentials のみが利用できます。 ファイアウォールポリシーや VPC ファイアウォールルールの解説は、以下の記事も参照してください。 blog.g-gen.co.jp 料金 Cloud NGFW Standard ルールを使用したファイアウォールポリシーがインターネットと VM 間で発生したトラフィックを評価すると、$0.018/GB の料金が発生します(2025年4月現在)。課金対象は以下のトラフィックです。 インターネットから VM への通信 VM からインターネットへの通信 反対に、以下のトラフィックは課金対象になりません。 インターネットと VM 間の通信のうち、プロキシ型の Cloud Load Balancing で中継される通信 Google Cloud 内に閉じる通信 詳細は、以下の公式料金表を参照してください。 参考 : Cloud Next Generation Firewall の料金 FQDN オブジェクト 概要 FQDN オブジェクト を使うと、アクセス先やアクセス先として FQDN(ドメイン名)を指定できます。 FQDN とは Fully Qualified Domain Name の略であり、 www.google.com や g-gen.co.jp などのいわゆる「ドメイン名」と呼ばれるものを指します。厳密な定義では、 www というホスト名と google.com というドメイン名をつなげて、末尾に . をつけるのが、FQDN の正式なフォーマットです。 FQDN オブジェクトを使うことで、VM から指定の FQDN へのアクセス、または指定の FQDN から VM へのアクセスを禁止したり、許可したりできます。 参考 : ファイアウォール ポリシールール - FQDN オブジェクト ユースケース 当機能を使うことで、例えば「 g-gen.co.jp へのアクセスを禁止する」や、「 update.microsoft.com などいくつかのドメインへのアクセスを許可し、それ以外への外向きアクセスは全て禁止する」などのルールが作成可能です。 仕組み FQDN オブジェクト機能は、パケットのどの部分を見て FQDN を判断しているかという点で注意が必要です。 一般的なファイアウォール製品で URL フィルタリングを行う場合、HTTP ヘッダの HOST ヘッダや、CONNECT メソッドの URI などを読み取って、これをもとにフィルタリングを行います。つまり、L7(アプリケーション層)の情報をもとにしてフィルタリングをするのが一般的です。 一方で VPC ファイアウォールポリシーの FQDN オブジェクトでは、以下のような方法で FQDN を IP アドレスに変換したうえで、 IP アドレスベースで フィルタリングを行います。 FQDN を Cloud DNS により IP アドレスへ名前解決 その IP アドレスをファイアウォールが保持(定期的に更新) 保持した IP アドレスをもとにフィルタリング つまり、HTTP リクエスト等に含まれる URI の情報を見ているわけではなく、あくまで IP ヘッダに含まれる IP アドレスをもとにフィルタリングをしていることになります。 このことから、以下のようなことも起こります。 例えば 1.1.1.1 という IP アドレスに名前解決される hoge.example.com があるとします。しかしこの IP アドレスには別名があり、 fuga.example.com も 1.1.1.1 に名前解決されるとします。 この場合、 hoge.example.com だけをフィルタリング対象としてファイアウォールポリシーに登録した場合でも、 fuga.example.com にはアクセスできなくなります。IP アドレスが同じだからです。また 1.1.1.1 という IP アドレスを直接指定してアクセスしようとしても、同様にアクセスは拒否されます。 その他の仕様 名前解決順序 Cloud DNS による名前解決は、そのファイアウォールポリシーが適用されている VPC の名前解決順序に依存します。 参考 : 名前解決順序 たとえば VPC A に Cloud DNS の限定公開ゾーン(Private Zone) example.com が紐付けられており、この Zone では www.example.com が 10.0.0.10 という A レコードとして登録されているとします。この場合、ファイアウォールポリシーでフィルタリングされる IP アドレスは 10.0.0.10 になります。 このとき仮に、 www.example.com がパブリックで名前解決できる FQDN だとしても、VPC の名前解決順序の仕様として限定公開ゾーンが優先されるため、こちらがフィルタリング対象の IP アドレスとなります。 利用できないドメイン *.example.com などアスタリスクを含むドメイン、すなわちワイルドカードドメインは利用できません。 また .org などトップレベルドメインも指定できません。 位置情報オブジェクト 概要 位置情報オブジェクト では、アクセス元やアクセス先の、地理的情報に基づいてフィルタリングを行うルールを作成できます。 位置情報オブジェクトは、Google が管理するデータベースに基づいて、地域情報と IP アドレスのマッピングから地理的情報を取得します。 参考 : ファイアウォール ポリシールール - 位置情報オブジェクト ルール内で地域を指定するには、US、FR、JP のような、ISO 3166-1 alpha-2 コードを使います。 参考 : Wikipedia - ISO 3166-1 alpha-2 ユースケース 位置情報オブジェクトは、ファイアウォールポリシーのルール内で、アクセス元としてもアクセス先としても利用できるため、特定地域から VM に入ってくるアクセスを拒否(許可)したり、VM から特定地域へ向けて出ていくアクセスを拒否(許可)したりできます。 ただし、あくまで IP アドレスの登録情報により国を判別しているので、必ずしも100%の精度で地域を特定できるわけでは無いことに留意してください。 仕組み 地理情報の判断は、IP アドレスと国コードのマッピングデータベースに基づきます。データベースは Google によって管理されています。 IP アドレスの所有権が移動した場合などに、Google のデータベースへの反映が遅れる場合もあるため、IP アドレスが常に正しい地理情報と紐付いていることは保証されないことに注意が必要です。 脅威インテリジェンス(Google Cloud Threat Intelligence) 概要 脅威インテリジェンス (Google Cloud Threat Intelligence)では、Google が持つ脅威インテリジェンス情報に基づいて、パケットをフィルタリングすることができます。 Google の持つ情報をもとに、以下のような IP アドレスをフィルタリングして拒否したり、許可することができます。以下から、利用したい項目のみを選択的に使用できます。 No 名称 説明 1 iplist-tor-exit-nodes Tor の出口ノード(exit node)として知られている IP アドレス 2 iplist-known-malicious-ips 既知の悪意ある IP アドレス 3 iplist-search-engines-crawlers サーチエンジンのクローラー(bot)の IP アドレス 4 iplist-public-clouds-aws / iplist-public-clouds-azure / iplist-public-clouds-gcp / iplist-public-clouds-google-services Amazon Web Services (AWS) / Microsoft Azure / Google Cloud の IP アドレス / Google サービスの IP アドレス 5 iplist-public-clouds No. 5 のすべてを包含 6 iplist-vpn-providers 評価の低い VPN プロバイダに属する IP アドレス 7 iplist-anon-proxies 公開の匿名プロキシに属する IP アドレス 8 iplist-crypto-miners 暗号通貨マイニングサイトに属する IP アドレス 参考 : ファイアウォール ポリシールール - ファイアウォール ポリシールールの Google 脅威インテリジェンス ユースケース 例として、以下のような設定が可能です。 悪用されている既知の IP アドレスから、VM への上り(Ingress)通信を拒否 VM から、悪用されている既知の IP アドレスへの下り(Egress)通信を拒否 Tor から VM への上り(Ingress)通信を拒否 仕組み 脅威インテリジェンスの IP アドレスリストは、Google によって管理されているため、ブラックボックスです。対象となっている IP アドレス範囲は、IPv4、IPv6、またはその両方の場合もあります。 また当機能は、パケットの振る舞いを見てブロックするわけではなく、あくまで IP アドレス情報に基づいて パケットをフィルタリングする機能です。当機能を使ったとしても、未知の IP アドレスからの攻撃を防げるわけではない点に留意が必要です。あくまで、既知の攻撃元からの侵害の可能性を下げるものであることを、理解して利用してください。 杉村 勇馬 (記事一覧) 執行役員 CTO / クラウドソリューション部 部長 元警察官という経歴を持つ現 IT エンジニア。クラウド管理・運用やネットワークに知見。AWS 12資格、Google Cloud認定資格11資格。X (旧 Twitter) では Google Cloud や AWS のアップデート情報をつぶやいています。 Follow @y_sugi_it
アバター