TECH PLAY

株式会社G-gen

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

744

G-gen の杉村です。2023年8月29日〜31日 (現地時間)、Google Cloud Next '23 が米国・サンフランシスコで開催されました。 前回の記事 では1日目の発表を扱いましたので、今回の記事ではそれ以外の発表等をご紹介します。 はじめに 開発の効率化 Jump Start Solutions GitLab との提携 Application Integration の GA インフラ C3A / C3D VM Titanium BigQuery と AI/ML BigQuery ML での生成 AI 利用 Feature Store の BigQuery 対応 BigQuery でのベクトルインデックス構築 BigQuery とデータ分析 Data clean rooms (Preview) BigQuery to Bigtable export BigQuery Omni の機能強化 新しい Google 製 LLM "Gemini" Vertex AI / Generative AI Vertex AI Extensions (Vertex AI Data Connectors) その他 旧来の Dataform は廃止 Serverless Composer 全体で 161 個の発表 次回の Google Cloud Next (2024) はじめに 当記事では Google Cloud Next '23 の2&3日目で行われた発表を分かりやすくお伝えします。なお当記事で扱う話題の中には1日目に既に発表されていたものもありますが 前回記事 (1日目) で扱いきれなかった内容も含まれます。 なお Google Cloud Next '23 の2&3日目の発表内容は、以下の公式記事もご参照ください。 参考 : Next 23 recap Day 2 | Google Cloud Blog 一日目の発表内容については、以下の当社記事もあわせてご参照ください。 blog.g-gen.co.jp 開発の効率化 Jump Start Solutions 定形のアプリケーションインフラを簡単にデプロイできる Jump Start Solutions が紹介されました。 「3層ウェブアプリケーション」「CI/CD パイプライン」「BigQuery を使ったデータウェアハウス」など、Google が用意したテンプレートからワンクリックで環境をデプロイすることができます。 Jump Start Solutions GitLab との提携 Google Cloud と GitLab の パートナーシップが発表 されました。 DevSecOps の領域で、Google Cloud と GitLab の親和性がこれまで以上に高まっていきます。 Application Integration の GA Preview 公開の位置づけだった Application Integration の GA (一般公開) が 発表されました 。 Application Integration は、BigQuery や AlloyDB などの Google Cloud サービスを始め、Salesforce、Zendesk、ServiceNow などサードパーティ製品など同士のデータ連携をノーコードで実現できるツールです。Integration Platform as a Service (iPaaS) と表現されています。 インフラ C3A / C3D VM C3A VM と C3D VM が 発表されました 。 C3A は AmpereOne 社の ARM ベースの CPU を積んだマシンタイプで、2023年9月に Preview 公開予定です。従来のインテルベースのマシンからコスト効率が40%改善したとされています。 C3D は AMD 社のインテル互換の CPU を搭載した VM で、同じく2023年9月に Preview 公開予定です。前世代の N2D よりパフォーマンスが45%改善されたとしています。 またこれとあわせ従来より存在する C3 VM では Hyperdisk (Compute Engine VM 用のハイパフォーマンスな永続化ディスク) への対応が発表され、最大 500K IOPS が実現できます。これはハイパースケーラー (いわゆるメガクラウド) の中でも最高クラスです。 Titanium Titanium と呼ばれる技術が公表されました。 これは Google Cloud の新しいプロダクトというわけではなく、C3 VM などの裏側で既に使われている技術です。CPU からストレージ管理等のタスクをオフロードすることにより、VM に割り当てられたコンピュートリソースとストレージ管理用リソースを切り離して疎結合にすることにより、高 IOPS を実現する Hyperdisk の扱いを可能にしました。従来はより高い IOPS を得るためにはより大きいサイズのインスタンスを選択する必要がありましたが、Titanium によりこの密結合を切り離すことができるようになりました。 BigQuery と AI/ML BigQuery ML での生成 AI 利用 BigQuery から SQL を使って ( ML.GENERATE_TEXT ) PaLM API を呼び出す機能は、以前から Preview 公開されていましたが GA になりました 。 また ML.GENERATE_TEXT_EMBEDDING を使いテキストのエンベディング (ベクトル化) を行う機能も Preview 公開されました。 BigQuery 内のテキストを使ったエンベディングやテキスト生成が SQL によって可能になるほか、一日目で発表になった BigQuery Studio との組み合わせも可能になります。BigQuery Studio (Colab Enterprise のノートブック) 上で Python によってスクリプトを書き、データを整備して、BigQuery ML により PaLM リモートモデルを呼び出してテキストの生成などを行うなど、データの前処理と推論をより柔軟に実施することができるようになりました。 Feature Store の BigQuery 対応 Vertex AI Feature Store はこれまで独自のレポジトリに AI/ML 用の Feature (特徴量) を保管していましたが、BigQuery のテーブルを feature store として利用できるようになります。2023年9月末に Preview 公開が予定されています。 これまでとは異なり BigQuery のデータを低レイテンシで読み出すことができ、オンラインでの利用が想定されています。従来は BigQuery のデータをリアルタイム推論に利用するのは、レイテンシや BigQuery 特有の課金体系等の理由から現実的ではありませんでしたが、今回のアップデートによりその様子が変わります。 BigQuery でのベクトルインデックス構築 こちらも現時点ではロードマップですが、BigQuery で CREATE VECTOR INDEX によりベクトルインデックスの構築とベクトル検索ができるようになります。 SQL によるバッチ処理として検索することに加えて、先述の Feature Store との統合により、API 経由でオンラインでの検索にも対応するものと想定されます。 BigQuery とデータ分析 Data clean rooms (Preview) BigQuery で Data clean rooms が Preview 公開されました。 Data clean rooms は、組織間でデータを共有するための仕組みである Analytics Hub の機能の一部です。Analytics Hub は従来から存在していましたが、今回発表された Data clean rooms はこれを拡張し、データ保護とセキュリティを提供する機能です。 Data clean rooms を使うことで、データ利用者はデータ提供者のデータをコピーすることなく利用できるという Analytics Hub の利点はそのままに、データ提供側は自社データを統合したり匿名化する等してセキュアにデータを提供することができます。 参考 : Secure and privacy-centric sharing with data clean rooms in BigQuery 参考 : Use data clean rooms BigQuery to Bigtable export EXPORT DATA 文を使い、BigQuery から Bigtable (Google Cloud のフルマネージドな NoSQL データベース) へデータを直接エクスポートできるようになりました (Preview)。 BigQuery Omni の機能強化 BigQuery Omni は以前よりある機能で、Amazon S3 や Azure Blob Storage 上の構造化・半構造化データに対して BigQuery から外部テーブル定義を行うことができる機能です。今回これが機能強化され Cross-Cloud Join や Cross-Cloud Materialized View が使えるようになります。 また Salesforce Data Cloud とのデータ連携や、対応リージョンの拡大予定も発表されています。 新しい Google 製 LLM "Gemini" 新しい Google 製 LLM である Gemini が発表されました。今後の Google の旗艦 AI モデル (flagship AI model) と位置づけられており、2023年12月に正式リリースの予定です。 詳細なドキュメントは公開されていませんが、GPT-4 の直接的な競合になるとされています。65兆のトークンを用いてトレーニングされており、テキストや画像を生成できることが公表されています。トレーニングには YouTube の動画も利用されています。 科学的用途、クリエイティブ用途、専門知識領域の用途などが想定されています。 Gemini のトレーニングに用いられているデータは法務チームにより監査されており、著作権/版権に配慮されています。 Vertex AI / Generative AI Vertex AI Extensions (Vertex AI Data Connectors) グラウンディング (生成 AI が生成する文章の正確性を高める) の目的で、Vertex AI で扱う生成 AI 基盤モデルから外部データに接続するための拡張機能 を利用可能にする Vertex AI Extensions が Private Preview になりました。社内に蓄積されたドキュメントをもとに、生成 AI チャットボットがより正確な回答を生成するように調整できます。自社開発した Extension を外部公開することも可能です。 Vertex AI Data Connectors は Google Cloud 公式の Extensions で、企業データや Salesforce、Confluence、JIRA などを用いたグラウンディングを助けるものです。こちらも Preview となっています。 その他 旧来の Dataform は廃止 Dataform は主に BigQuery 用の ELT ツールで、SQL ライクな構文でデータ変換をワークフロー管理できるツールです。もともと Google Cloud とは別開発のツールでしたが、2023年に Google Cloud に統合されました。 Google Cloud 統合前の旧 Dataform は、2024/02/26 に 廃止されます 。 Serverless Composer 詳細は未発表ですが、Cloud Composer (Apache Airflow のマネージドサービス) のサーバーレス版が発表されました。利用可能時期等は未発表です。 全体で 161 個の発表 当社記事で紹介したものも含めて、今回の Next では大小を含めて161個の発表がありました。 その全ては以下の公式記事にまとめられています。 参考 : Google Cloud Next 2023 wrap up | Google Cloud Blog その全てに目を通すのは大変ですが、中には役に立つ新機能もあるかもしれません。 次回の Google Cloud Next (2024) 2024年の Google Cloud Next は 2024年4月 (4月9日〜11日) にラスベガスで実施されることが発表されました。 次回の Next まで半年あまりという短期間で実施されることになります。 杉村 勇馬 (記事一覧) 執行役員 CTO / クラウドソリューション部 部長 元警察官という経歴を持つ現 IT エンジニア。クラウド管理・運用やネットワークに知見。AWS 12資格、Google Cloud認定資格11資格。Twitter では Google Cloud や AWS のアップデート情報をつぶやいています。 Follow @y_sugi_it
アバター
G-gen の杉村です。生成 AI を使って Google Workspace における業務をサポートする Duet AI for Google Workspace をプレビューしてみましたので、その機能の一部をご紹介します。今回は Gmail 編です。 はじめに Duet AI for Google Workspace とは 当記事の注意点 文章の自動生成 フォーマルにする (Formalize) 文章を短くする (Shorten) 文章を長くする (Elaborate) I'm feeling lucky その他の記事 はじめに Duet AI for Google Workspace とは Duet AI for Google Workspace は、Google のコラボレーションソリューションである Google Workspace において 生成 AI (Generative AI) を使って各種業務をサポートする機能です。以下のようなことが実現できるようになります。 Google Docs (ドキュメント) : 文章の自動生成・フォーマル化・要約・精緻化・言い換え Gmail (メール) : E メールの自動生成・推敲 Google Slides (スライド) : 自然言語を与えると画像を自動生成 Google Sheets (スプレッドシート) : データの分析、ラベル割り当て、タスク計画の生成 Google Meet (Web 会議) : バーチャル背景の生成 2023年8月現在では日本語版は提供されておらず、英語版のみです。 当記事の注意点 当記事では Duet AI for Google Workspace の先行テスタープログラム中に利用した機能をご紹介しています。以下の点にご留意ください。 当記事で紹介する Duet AI for Google Workspace の機能は先行テスタープログラム当時のものであり、今後変更になる可能性があります G-gen 社は検証目的でのみ Duet AI for Google Workspace を用いており、顧客業務には利用していません(検証当時) 文章の自動生成 Gmail で Duet AI を使った文章の自動生成を試してみます。 新規メール作成画面の下部メニューのアイコン一覧に、青い鉛筆マークが表示されています。クリックすると Help me write というボタンが表示されます。 Duet AI の鉛筆アイコン クリックすると、以下のように文章生成を指示するプロンプトが入力できます。 プロンプトを入力する 以下のようなプロンプトを入力し、Create ボタンを押下しました。 Invitation to an online meeting to discuss project details. Some date proposals. (プロジェクトの詳細を議論するためのオンラインミーティングへの招待。いくつかの日程候補も。) Create ボタンを押すと、以下のように生成が始まります。 生成中 数秒待つと、以下のように文章が生成されました。 生成された文章 しっかりとEメールの体裁で、オンライン会議へ招待している旨と、候補日が生成されました (ただし日付候補日は2月や3月であり、検証時は2023年8月でしたので、そこは汲んでくれないようです)。 Insert ボタンを押すと、メールに文章が挿入されます。 挿入する前に Refine (推敲) ボタンを押すと、Formalize (フォーマル化)、Elaborate (引き伸ばす)、Shorten (短くする)、I'm Feeling Lucky (???。後述) の選択肢が表示されました。生成された文章をさらにアレンジすることができます。 4つの Refine フォーマルにする (Formalize) 先程の文章をフォーマライズ、すなわちさらに丁寧にしてみます。先程の4つのメニューから、Formalize を選択すると、しばらくして新しい文章が表示されました。 フォーマル化された文章 もともとの文章よりも言い回しが丁寧になっています。 もっとわかり易い例を試してみます。以下のような少しぞんざいな文章を書いて、これに対して Formalize を使ってみます。 もともとの文章 すると、以下のように随分と丁寧になりました。自動生成した文章に対して Refine するだけでなく、このように自分で書いた文章に対する Refine も可能です。また、しっかりと E メールの体裁になっていることにもご注目ください。 Formalize 後の文章 文章を短くする (Shorten) Shorten (短くする) を試してみます。先程 Formalize で生成された丁寧な長い E メールを、短縮してみます。 Shorten 後の文章 文章を長くする (Elaborate) 次は文章を長くする (Elaborate) ことを試してみます。 以下の文章を Elaborate してみます。 もともとの文章 文章を引き伸ばしてくれたうえに、E メールの体裁に整えてくれました。 Elaborate 後の文章 ただしご注意いただきたいのは、生成 AI 全般に言えることですが、必ずしも正しい情報に基づいて文章を生成してくれるとは限りません。Google Cloud Next 23 の正しい開催地は東京ビッグサイトですし、日付は2023年11月15日から16日です。あくまで Duet AI の機能は下書きや大枠の作成にのみ使用し、人間がその正しさを見極めて使いこなす必要があります。 I'm feeling lucky さて、文章に対する Refine アクションとして I'm feeling lucky というものがありました。 先程の Google Cloud Next へのお誘いの文章に対して I'm feeling lucky を使ってみました。 I'm feeling lucky ラップのリリックらしきものが生成されました。韻を踏もうと頑張っているらしきことが分かります (あまり上手くいっていないようですが)。 その後も何回か I'm feeling lucky を試してみました。詩的な言い回しであったり、ユーモアを含んだ文章が生成されます。どうやら、少し遊び心のある文章を生成する機能のようです。 I'm feeling lucky は Google 検索の機能名でもおなじみです。検索結果一覧を表示せず、結果の一番目に出る Web ページへ直接アクセスする機能ですね。 その他の記事 Duet AI for Google Workspace のその他の機能を、以下の記事でもご紹介しています。 blog.g-gen.co.jp blog.g-gen.co.jp 杉村 勇馬 (記事一覧) 執行役員 CTO / クラウドソリューション部 部長 元警察官という経歴を持つ現 IT エンジニア。クラウド管理・運用やネットワークに知見。AWS 12資格、Google Cloud認定資格11資格。X (旧 Twitter) では Google Cloud や AWS のアップデート情報をつぶやいています。 Follow @y_sugi_it
アバター
G-gen の杉村です。2023年8月29日〜31日 (現地時間)、Google Cloud Next '23 が米国・サンフランシスコで開催されました。当日は多くの Google Cloud / Google Workspace 関連の新機能や新サービスが発表されました。その全てではありませんが、重要な発表のいくつかをお伝えします。当記事では一日目、すなわち8月29日 (火) の発表を取り上げます。 はじめに 総評 参考リンク 二日目・三日目 生成 AI Duet AI in Google Workspace Duet AI in Google Cloud Vertex AI の生成 AI 関連機能強化 責任ある AI (Responsible) 機械学習向けコンピュートリソース (A3 VM / Cloud TPU v5e) 業界特化の PaLM データ分析とデータベース BigQuery Studio & BigQuery DataFrames AlloyDB AI Database Migration Service (DMS) の生成 AI サポート インフラストラクチャ GKE Enterprise Google Distributed Cloud のポートフォリオ強化 Cross-Cloud Network セキュリティ Cloud NGFW Mandiant Hunt for Chronicle Security Command Center のエージェントレス脆弱性スキャン Assured Workloads の日本リージョン対応 はじめに 総評 Google Cloud Next '23 は、圧倒的に 生成 AI (Generative AI) をフューチャーしたものとなりました。 一日目の Keynote (基調講演) の内容から分かるように、生成 AI が Google Cloud や Google Workspace のすべての機能にこれから深く関わっていくことが示唆され、これまで一種「おもちゃ」のように扱われてきた生成 AI が、我々の実業務に深く関わっていく未来を予感させるものになりました。 BigQuery に自然言語で指示をすると、クエリ (SQL) が自動生成され、そのビジュアライズまで自動的に行われる。そしてスライドも自動的に生成される。人間はそれをつかってビジネス判断に集中できる。そのような未来が近いことを実感させられました。 その他にもインフラ系・セキュリティ系の新機能も発表されましたが、いずれも機械学習をより高パフォーマンスに行うためのものであったり、やはり Google が AI/ML に強い関心を払っていることを裏付ける内容でもありました。 参考リンク 以下の公式ブログで、Next '23 の一日目の recap (おさらい) を確認することができます。ただし英語のみであること、またある程度抽象的な表現に留まっています。当記事では以下の記事をベースに、一日目で行われた発表を分かりやすくお伝えします。 参考 : Welcome to Google Cloud Next ’23 参考 : Day 1 at Next ‘23: AI everywhere, data galore, and more また、セッション動画などは以下の公式サイトで公開されています。 参考 : Google Cloud Next '23 二日目・三日目 二日目・三日目の発表や、一日目の記事で扱いきれなかった発表は以下の記事にまとめています。 blog.g-gen.co.jp 生成 AI Duet AI in Google Workspace 数ヶ月前から一部の顧客に対する先行テストプログラムがスタートしていた Duet AI in Google Workspace の GA (一般公開) が発表されました。 Duet AI in Google Workspace は、生成 AI を用いて人間の作業を補助する機能です。例えば Gmail や Google Docs においては文章の自動生成や要約、Slides では画像の自動生成、Sheets では表の自動生成などを、自然言語の指示に基づいて生成 AI が行ってくれます。発表時点では英語版のみの対応です。 Google Workspace のライセンスのアドオンとして販売されますが、Business Starter など一部エディションは対象外ですので、Google Cloud もしくはパートナーの営業担当者までご確認ください。 なお 申し込みフォーム からトライアルの申込みが可能です。パートナー経由で Workspace ライセンスをご購入のお客様は、パートナーの営業担当者にもご確認ください。 なお GA 前は Duet AI for Google Workspace という名称で発表されていましたが、Next '23 以降は in と for の両方の表記が見られます。 以下の当社記事で Duet AI in Google Workspace の機能をプレビューした様子を紹介していますので、ご参照ください。 blog.g-gen.co.jp blog.g-gen.co.jp blog.g-gen.co.jp Duet AI in Google Cloud 以前より公表されていた Duet AI in Google Cloud が Preview 公開されました。以下のようなサービスで、生成 AI による支援を受けることができるようになります。 BigQuery Looker Cloud Spanner Database Migration Services (DMS) Google Kubernetes Engine (GKE) Security Command Center Mandiant Threat Intelligence Chronicle Security Operations 例えば BigQuery においては、自然言語で指示することで SQL が自動生成されたり、表やグラフを含む Looker Studio ダッシュボードが自動生成される様子がデモされました。またその結果を Google Slides のスライドに反映させることも可能です。Looker でも同様に、自然言語からの自動的な分析ダッシュボード生成のデモがセッションで発表されました。 また Google Cloud コンソール上では、Duet AI がチャット形式で Google Cloud サービスに関する質問に回答したり、公式ガイドへのリンクを表示させたりなど、Google Cloud 利用者のスキルを補助する強力なツールになりそうです。 Preview するには サインアップ が必要です。 なおこちらも、従来は前置詞「for」が使われていましたが、発表後は「in」の表記になっているようです。 Vertex AI の生成 AI 関連機能強化 Vertex AI が生成 AI 関連の機能をさらに強化しました。Meta 社の生成 AI 基盤モデルである Llama 2 が Model Garden (Vertex AI から利用可能な機械学習モデルのカタログ集) から利用可能になったり、従来から利用可能だった Google の PaLM 2、Codey、Imagen (画像生成モデル) に対する機能強化や精度向上が 発表 されました。 Vertex AI 経由で利用できる PaLM API では text-bison-32k が使えるようになり、従来は 8k が最大だった入力トークンが 32k まで拡張されています。また textembedding-gecko-multilingual モデルが 利用可能 になり、日本語テキストのエンベディングができるようになりました。 またチャットボットやエンタープライズサーチ (組織内向け検索エンジン) の構築補助ツールである Generative AI App Builder (Gen App Builder) は Vertex AI Search and Conversation と名前を変え、GA になりました。 責任ある AI (Responsible) Imagen (画像を生成する生成 AI モデル) により自動生成された画像にはデジタル・ウォーターマーキング (Digital Watermarking) と呼ばれる電子透かし (人の目には見えないが、特定の方法で識別可能になる透かし) を入れることで、AI によって生成された画像を識別できるようにするなど、責任ある AI に対する姿勢も従来どおり明確にしています。この技法は Google DeepMind SynthID と呼ばれる技術をベースとしています。 また生成 AI がチャットボット等で返答するテキストは hallucination、すなわち本当らしく見えるが事実と異なる文章になり得ます。これへの対策として、情報源のリンクを表示させるなど、正当性の担保 (Grounding) を行う手法が強調されていました。 機械学習向けコンピュートリソース (A3 VM / Cloud TPU v5e) 柔軟に VM にアタッチ可能な TPU である Cloud TPU v5e がリリースされました (Preview)。TPU とは Tensor Processing Unit の略称であり、Google が開発した機械学習向けプロセッサです。従来からある Cloud TPU v4 に比較して、生成 AI や LLM でのコスト対効果が向上しています。 NVIDA との提携により、NVIDIA 製 GPU を搭載した A3 VM も発表されました (2023年9月に GA 予定)。こちらも 生成 AI / LLM 用途が想定されています。 業界特化の PaLM Google の LLM である PaLM の派生として、セキュリティに特化した Sec-PaLM、医療業界に特化した Med-PaLM が紹介されました。 データ分析とデータベース BigQuery Studio & BigQuery DataFrames BigQuery に対する新しいユーザーインターフェイスである BigQuery Studio が Preview 公開されました。 従来どおりの Google SQL の編集を補助 (コード補完等) できることに加え Colab Enterprise (こちらも今回 Preview 発表。Python 向けノートブック) と統合された UI で Python により BigQuery のデータを操作できるようになりました。 BigQuery DataFrames に対応しており、pandas 互換なインターフェイスで BigQuery にアクセスできます。 BigQuery DataFrames では処理が BigQuery にプッシュダウン (= BigQuery のコンピュートリソースを利用する) されるので、pandas の操作感で高速・高効率なデータ処理が可能です。 AlloyDB AI AlloyDB AI が Preview 公開されました。AlloyDB AI では、データベース内のデータを SQL 操作で容易・高速にエンベディング (データをベクトル化し、機械学習等に利用できるように変換すること) することができます。 AlloyDB AI は2023年8月現在で AlloyDB Omni (AlloyDB のダウンロード版) でのみ利用可能な Preview 版となっています。 Database Migration Service (DMS) の生成 AI サポート Database Migration Service (DMS) でも生成 AI を活用した機能が発表されました。 DMS では例えば Oracle から PostgreSQL への移行などに対する異種 DB 間移行の支援ツールがあります。セッションでは、ある Oracle のプロシージャの SQL の一つの関数を PostgreSQL 向けに書き換えると、同様の変換を他のプロシージャにもまとめて実行するような自動化を、生成 AI が支援する様子がデモされました。 インフラストラクチャ GKE Enterprise GKE Enterprise が2023年9月に Preview 公開予定です。 GKE Enterprise は、多数の Google Kubernetes Engine (GKE) や Anthos のクラスタを fleet と呼ばれる単位で管理することで、管理工数の削減やセキュア化、管理の移譲などを実現する機能です。Google Cloud 上のみならず、他プラットフォームで動くコンテナ群をも管理対象とし、大規模なコンテナ運用を実現できます。 Google Distributed Cloud のポートフォリオ強化 Google Distributed Cloud (GDC) における、AI 関連の ポートフォリオ強化が発表 されました。GDC は Google Cloud のオンプレミス版とも言えるサービスで、自前のデータセンター等に専用ハードウェアを設置することで、Google Cloud と同様のインターフェイスで環境を利用できるサービスです。 今回の発表ではハードウェア強化に加え、Vertex AI や AlloyDB Omni、Dataproc Spark との統合が発表されました。 Cross-Cloud Network Cross-Cloud Network の概念が発表になりました。これは従来から存在するサービスや今回発表されたサービス群を、一連のアセットとして命名したものです。Open, Secure, Optimized をキーワードとし、プログラマブルな形でクラウドを横断するネットワークを構築するサービス群となっています。 Cross-Cloud Network は、 Cross-Cloud Interconnect 、 Private Service Connect などの既存サービス群に加えて、Next '23 で Preview 公開となった Cloud NGFW などが含まれます。 Cross-Cloud Network はこれらのサービス群を用いたリファレンス・アーキテクチャと捉えることもでき、複数のバックエンドを持つ分散アプリケーションやインターネット公開のアプリケーションの効率化、またセキュリティ強化に用いられます。 セキュリティ Cloud NGFW Cloud NGFW は Cloud Firewall の新しいティアとして提供されます。NGFW は Next Generation Firewall の略称であり、アプリケーション層のデータも見て脅威と成り得るトラフィックを検知・ブロックする機能です。 従来、Cloud Firewall は Essential と Standard の2つのティアが存在していましたが、これに Plus ティアが追加になり、NGFW 機能が追加されました。ドキュメントでは Intrusion Prevention System (IPS) とも呼ばれています。 Mandiant 社と Palo Alto Networks 社により開発された脅威シグネチャに基づいて、インラインで侵入行為やマルウェアの通信、C&C サーバへの通信などを検知・防御します。 参考 : Cloud Firewall 参考 : Intrusion prevention service overview Mandiant Hunt for Chronicle Mandiant Hunt for Chronicle が Preview 公開されました。Chronicle は Google 製の SIEM (Security Information and Event Management、セキュリティログの解析ツール) です。Mandiant Hunt for Chronicle は、Mandiant 社 (2022年に Google が買収したセキュリティ会社) のエキスパートが Chronicle を使ったセキュリティ解析を行い、セキュリティ侵害行為を検知するマネージドサービスです。 Security Command Center のエージェントレス脆弱性スキャン Security Command Center で、エージェントなしで VM の脆弱性スキャンを行う機能が発表されました。Tenable 社の技術をもとに、OS やアプリケーションの脆弱性を検知します。 Assured Workloads の日本リージョン対応 Assured Workloads は少ない工数で Google Cloud 環境の状態を特定の規制下に維持するための機能です。 Google Cloud 上のデータの所在、アクセス制御、暗号化などをある程度ラップして実装し、準拠状況のモニタリングも行うことができます。 杉村 勇馬 (記事一覧) 執行役員 CTO / クラウドソリューション部 部長 元警察官という経歴を持つ現 IT エンジニア。クラウド管理・運用やネットワークに知見。AWS 12資格、Google Cloud認定資格11資格。Twitter では Google Cloud や AWS のアップデート情報をつぶやいています。 Follow @y_sugi_it
アバター
G-gen又吉です。当記事では、Googleの生成AI、PaLM 2(言語基盤モデル)のモデルチューニングについて解説します。 構成図 はじめに Generative AI support on Vertex AI 基盤モデル モデルチューニングとは ユースケース 仕組み トレーニングデータセット サンプル数 トレーニングデータセット形式 注意点 モデルチューニングジョブの実行 概要 サンプルコード サポートされてるリージョン トレインステップ 料金 チューニングされたモデルの呼び出し トラブルシューティング はじめに Generative AI support on Vertex AI 先日 Vertex AI でも Generative AI がサポートされました。Generative AI モデル (生成 AI モデル、または基盤モデル) の裏側は PaLM 2 が利用されており、多言語、推論、コーディング機能が強化された最先端の大規模言語モデル (LLM) です。 Vertex AI の Generative AI サポートについての詳細は以下の記事をご参照下さい。 blog.g-gen.co.jp 基盤モデル Vertex AI では、デフォルトでいくつかの基盤モデルが提供されています。これらの基盤モデルは、一般的なユースケースに対応するような設計になっております。 2023 年 8 月現在、以下の基盤モデルが提供されています。 No モデル名 説明 モデルチューニングサポート 1 text-bison 自然言語の指示に従うような微調整された基盤モデルです。要約 / 分類 / 感情分析 / エンティティ抽出 / アイデア出し など汎用的な用途で利用できます。 ◯ 2 textembedding-gecko テキスト入力のベクトル化、つまりエンべディングを返します。 - 3 chat-bison 会話のユースケース用にファインチューニング (微調整) された基盤モデルです。 - 4 code-bison 自然言語に基づいたコード生成用に微調整された基盤モデルです。 ◯ 5 codechat-bison コード関連の質問に役立つチャットボット用に微調整された基盤モデル ◯ 6 code-gecko 記述されたコードのコンテキストに基づいてコード補完を提案するように微調整された基盤モデルです。 - 参考: Available models in Generative AI Studio モデルチューニングとは ユースケース Vertex AI の基盤モデルは一般的なユースケースに合わせて微調整されておりますが、さらに特定のユースケースに対応させたい場合、基盤モデルに対しモデルチューニングを行いパフォーマンスを向上させることができます。 特定のユースケースとして、以下のような例が考えられます。 特定のフォーマットに整形して出力させたい場合 企業独自の製品カテゴリに分類したい場合 個人を特定できる情報 (PII) を削除したい場合 参考: Scenarios to use model tuning 仕組み モデルチューニングは、タスクの例 (入力と予期される出力のペア) を含むトレーニングデータセットを基盤モデルに提供する必要があります。 モデルチューニングを実行すると、モデルは目的の動作を行うための追加のパラメーターを学習します。 そしてモデルチューニングの出力としては、新しく学習したパラメーターと元のモデルの組み合わせでできた新しいモデルです。 2023 年 8 月現在、モデルチューニングをサポートしている基盤モデルは以下となります。 text-bison code-bison codechat-bison 参考: Tune language foundation models トレーニングデータセット サンプル数 モデルトレーニングに使用されるトレーニングデータセットには、モデルに実行させたいタスクに合わせた入出力のサンプルが含まれています。 トレーニングデータセットには最低 10 個のサンプルが必要であり、良い結果を得るには少なくとも 100 個のサンプルを含めることを推奨してます。 一般的に、より多くのサンプルを与えるほど、より良い結果が得られるとされています。 参考: Prepare your model tuning dataset トレーニングデータセット形式 モデル調整データセットは、 JSON Lines (JSONL) 形式である必要があります。 各行は、 input_text でモデルへのプロンプトを含む入力フィールドと、 output_text で予想するレスポンスの例を含む出力フィールドで構成されます。 以下はトレーニングデータセット例となります。 {"input_text": "質問: 北京には何人住んでいますか? 本文: 2,100 万人以上の住民を抱える北京は、世界で最も人口の多い首都であり、上海に次ぐ中国第 2 の都市です。中国北部に位置し、北京は、南東に隣接する天津を除き、大部分が河北省に囲まれており、これら 3 つの部門を合わせて京津市を形成しています。 巨大都市と中国の首都圏。", "output_text": "2,100 万人以上"} {"input_text": "質問: ルイジアナ州には教区がいくつありますか? 本文: 米国ルイジアナ州は、米国の他の 48 州が郡に分割されているのと同じように、64 の教区に分割されています。 アラスカはいくつかの自治区に分かれています。", "output_text": "64"} 参考: Dataset format 注意点 モデルチューニングには、以下 2 つの注意点がございます。 1 つ目に、基盤モデルをチューニングしたからと言って、一般的な知識が向上する保証はありません。 2 つ目に、チューニングされた基盤モデルは必ずしもコンテキストを考慮するとは限りません。したがって、関連するコンテキストがある場合は、明示的にプロンプト コンテキストを入力するようにしましょう。例えば、「あなたは小学校の教員です」などのペルソナを設定する等。 参考: Design chat prompts - Context (optional) モデルチューニングジョブの実行 概要 トレーニングデータセットが準備できたら、Cloud Storage バケットへアップロードします。ジョブの実行時に新規バケットを作成することもできますし、既存バケットに予めファイルを保存し参照することもできます。 モデルチューニングが完了したら、チューニングされたモデルは Vertex AI エンドポイントに自動的にデプロイ されます。また、Generative AI Studio で操作することもできます。 サンプルコード モデルチューニングジョブの作成は、Google Cloud コンソール、API、または Vertex AI SDK for Python を使用して作成できます。 以下は REST API の pipelineJobs メソッドを使用した例です。 TUNING_LOCATION={モデルチューニングが行われるリージョン} PROJECT_ID={プロジェクト ID} DISPLAY_NAME={PipelineJob の表示名} GCS_OUTPUT_DIRECTORY={チューニングされたモデルを出力するバケットの URI} MODEL_DISPLAY_NAME={PipelineJob によって作成されたモデルの表示名} DATASET_URI={モデルチューニングデータセットを格納しているバケットの URI} TRAIN_STEP={モデル調整のために実行するステップの数} LEANING_RATE={学習率の値 (推奨は 1.0)} curl \ -X POST \ -H "Authorization: Bearer $(gcloud auth print-access-token)" \ -H "Content-Type: application/json; charset=utf-8" \ https://${TUNING_LOCATION}-aiplatform.googleapis.com/v1/projects/${PROJECT_ID}/locations/${TUNING_LOCATION}/pipelineJobs?pipelineJobId=tune-large-model-$(date +"%Y%m%d%H%M%S") -d \ $'{ "displayName": "'${DISPLAY_NAME}'", "runtimeConfig": { "gcsOutputDirectory": "'${GCS_OUTPUT_DIRECTORY}'", "parameterValues": { "location": "us-central1", "project": "'${PROJECT_ID}'", "large_model_reference": "text-bison@001", "model_display_name": "'${MODEL_DISPLAY_NAME}'", "train_steps": '${TRAIN_STEP}', "dataset_uri": "'${DATASET_URI}'", "learning_rate": '${LEANING_RATE}' } }, "templateUri": "https://us-kfp.pkg.dev/ml-pipeline/large-language-model-pipelines/tune-large-model/v3.0.0" }' サポートされてるリージョン 2023 年 8 月現在、以下のリージョンがサポートされています。 No リージョン 使用リソース バッチサイズ 1 us-central1 [Restricted image training Nvidia A100 80GB GPUs per region] × [8 の倍数] 8 2 europe-west4 [Restricted image training TPU V3 pod cores per region] × [64 の倍数] 24 また、利用するリージョンによって、使用リソースが異なります。 特に、 GPU や TPU が含まれるため、事前に割り当てが十分あることを確認する必要があります。十分な割り当てがない場合は、クォータ制限の引き上げをリクエストしてください。 参考: Request a higher quota limit 参考: Tune language foundation models - Quota トレインステップ トレインステップを計算する前に、エポック ( epoch ) という概念を知っておく必要があります。 エポックとは、トレーニングデータセット全体を 1 回処理することをいいます。 基盤モデルのような大規模言語モデル (LLM) は、ディープラーニングモデルの一種です。 ディープラーニングモデルは、パラメータ数が多く、トレーニング時にエポック数を増やし学習させることで精度を向上させます。しかしエポック数が多すぎると過学習になる恐れがあるので、 多すぎず少なすぎないエポック数を指定する必要があります。 train_steps オプションでは、トレーニングデータセットのサンプル数と、モデルチューニングが行われるリージョンのバッチサイズによってエポック数を定義できます。 例えば、トレーニングデータセットに 240 個のサンプルがある場合、 us-central1 ではバッチサイズが 8 なのでデータセット全体を 1 回処理 (1 エポック) するのに 240 / 8 = 30 ステップ かかります。一方で、 europe-west4 ではバッチサイズが 24 なのでデータセット全体を 1 回処理 (1 エポック) するのに 240 / 24 = 10 ステップ かかります。 参考: Create a model tuning job 参考: GitHub - llm-pipeline-examples 料金 モデルチューニングの料金は、モデルチューニングで使用した Vertex AI のカスタムトレーニングで使用したリソース料金に依存します。 例えば、us-central1 で、 Nvidia A100 80GB GPUs を 8 つ使用し、トレーニング時間に 1 時間費やしたと仮定すると以下のような計算式になります。 4.517292 [USD/hour] × 8 × 1 [hour] ≒ 36 [USD] トレーニングにかかる時間は、トレーニングデータセットの量とエポック数によって変動します。 参考: Vertex AI pricing - Custom-trained models チューニングされたモデルの呼び出し チューニングされたモデルは Vertex AI エンドポイントデプロイされており、Generative AI Studio で選択することもできます。 以下は、Vertex AI SDK for Python の TextGenerationModel を使用してチューニングされたモデルを読み込んでいます。 import vertexai from vertexai.preview.language_models import TextGenerationModel TUNED_MODEL_NAME = {チューニングされたモデル名 : [projects/PROJECT_ID/locations/LOCATION/models/MODEL_ID] 形式} model = TextGenerationModel.get_tuned_model(TUNED_MODEL_NAME) 調整されたモデルの MODEL_ID は、 Vertex AI Model Registry 内で確認できます。 トラブルシューティング モデルチューニングジョブを実行すると、以下のエラーのように 500 のエラーコードと Internal error encountered. のエラーメッセージが返される場合の対処法を紹介します。 { "error": { "code": 500, "message": "Internal error encountered.", "status": "INTERNAL" } } 以下の cURL コマンドを実行して、空の Vertex AI データセットを作成することで先述のエラーは回避できます。 DATASET_NAME={Vertex AI データセットの表示名} curl \ -X POST \ -H "Authorization: Bearer $(gcloud auth print-access-token)" \ -H "Content-Type: application/json" \ https://${TUNING_LOCATION}-aiplatform.googleapis.com/ui/projects/${PROJECT_ID}/locations/${TUNING_LOCATION}/datasets \ -d '{ "display_name": "'${DATASET_NAME}'", "metadata_schema_uri": "gs://google-cloud-aiplatform/schema/dataset/metadata/image_1.0.0.yaml", "saved_queries": [{"display_name": "saved_query_name", "problem_type": "IMAGE_CLASSIFICATION_MULTI_LABEL"}] }' コマンドが完了したら、約 5 分置いてモデルチューニングを再実行します。 参考: Troubleshooting 又吉 佑樹 (記事一覧) クラウドソリューション部 はいさい、沖縄出身のクラウドエンジニア! セールスからエンジニアへ転身。Google Cloud 全 11 資格保有。Google Cloud Partner Top Engineer 2024。Google Cloud 公式ユーザー会 Jagu'e'r でエバンジェリストとして活動中。好きな分野は AI/ML。 Follow @matayuuuu
アバター
G-gen の佐々木です。当記事では Google Cloud(旧称 GCP)の認定資格の一つである、 Professional Machine Learning Engineer 試験の対策や出題傾向について解説します。 基本的な情報 Professional Machine Learning Engineer とは 難易度 試験対策 機械学習の一般的な知識 代表的な機械学習アルゴリズム 評価指標 回帰問題における評価指標 分類問題における評価指標 ヒューリスティック 機械学習モデルの開発、運用における課題の解決 データの前処理 欠損値の処理 カテゴリカル変数の扱い 不均衡データの対策 過学習の対策 正則化 早期停止 トレーニングの改善 ハイパーパラメータの調整 トレーニング時間の改善 交差検証 モデルのモニタリングと改善 スキューとドリフト モデルの軽量化手法 Google Cloud の機械学習サービス Vertex AI Vertex AI Training Vertex AI Model Registry Vertex AI Endpoints Vertex AI Batch Prediction Vertex Explainable AI Vertex ML Metadata BigQuery ML 事前トレーニング済み API サービス データ系サービス Google Cloud における機械学習モデルの開発 機械学習パイプラインの構築 インフラストラクチャの選定 GPU、TPUの利用 preemptible VM を使用したコスト節約 データエンジニアリング TensorFlow におけるパイプライン最適化 Professional Machine Learning Engineer 基本的な情報 Professional Machine Learning Engineer とは Professional Machine Learning Engineer 試験は、Google Cloud における AI/ML サービス全般の知識に加え、機械学習モデルの開発、運用に関する一般的な知識、およびそれを Google Cloud 上でどのように実現するかについての専門的な知識が問われる試験です。 Google Cloud 認定資格の中でも Google が提供するサービスに関する出題が比較的少なく、それに対して機械学習モデルの開発、運用に関する出題が非常に多いことが特徴的な試験となっています。 2023年 8月現在で 英語版のみ の提供となっています。 試験費用は $200 で試験時間は 2時間、問題数は 50~60問の多項選択式となっています。 難易度 Professional Machine Learning Engineer 試験の難易度は、Google Cloud 認定資格の中でも特に高いと言えます。 その理由として、この試験では Google Cloud の AI/ML サービスに関する知識だけではなく、機械学習モデルの開発に関する一般的な知識が問われる点、また現在は試験が日本語に対応していないため、機械学習に関する専門的な用語を英語で理解していないと問題文を読み解くことができない点が挙げられます。逆に言えば、普段から業務などでこれらに触れる機会がある方であれば、他の試験よりも楽に合格できるでしょう。 試験対策 以下の勉強方法はあくまで一例であり、最適な方法は、受験者の予備知識や経験によって異なるものとご了承ください。 Professional Data Engineer 試験 を学習し、合格することで Google Cloud におけるデータ系サービスの理解を深める。 公式の 試験ガイド で試験範囲を理解する developers.google.com の機械学習コース を読む。 Vertex AI、BigQuery ML、各種 API 系サービスといった Google Cloud の AI/ML 系サービスを学習する 公式の 模擬試験 を受験し感覚を掴む 当記事の出題傾向を読み、足りない知識領域をカバーする学習を行う 3番の developers.google.com の機械学習コースは試験範囲と密接に関わる資料ですが、初学者には難しいと思われるかもしれません。その場合、まずは機械学習に関する基礎的な知識を入門書で学ぶことをおすすめします。 そのほか、日本ディープラーニング協会が提供する G 検定 のシラバスなどを参考にするのもよいでしょう。このシラバスのうち「機械学習の具体的手法」「ディープラーニングの概要」「ディープラーニングの手法」については Professional Data Engineer 試験にも出てくる用語が多く見られます。 機械学習の一般的な知識 代表的な機械学習アルゴリズム 機械学習で使用される代表的なアルゴリズムについて、基本的な理解を深めます。 計算式などの詳細を理解する必要はありませんが、どのアルゴリズムをどのような場面で使用すればよいかといった点や、アルゴリズムごとの強み(精度やトレーニング時間、説明可能性など)を理解しておくと良いでしょう。 以下は代表的なアルゴリズムの例です。 用途 アルゴリズム アルゴリズムの英語表記 回帰モデル (regression model) 線形回帰 linear regression 決定木ベース(Random Forest、XGBoost) decision tree 分類モデル (classification model) ロジスティック回帰 logistic regression サポートベクターマシン Support Vector Machine, SVM 決定木ベース(Random Forest、XGBoost) decision tree ニューラルネットワーク (neural network) 言語処理系 RNN - LSTM - word2vec - ニューラルネットワーク (neural network) 画像処理系 CNN - ResNet - ニューラルネットワーク (neural network) 時系列データ RNN - LSTM - その他 レコメンデーション recommendation 主成分分析 principal component analysis, PCA クラスタリング (k-means) clustering 強化学習 reinforcement learning 評価指標 機械学習における評価指標について理解を深めます。 評価指標とは、機械学習モデルの性能を評価するための指標であり、モデルの予測精度や予測の偏りなどを測定するために使用します。 回帰問題における評価指標 売上予測などの回帰問題において使用される評価指標を理解します。 決定係数(R2) 、 二乗平均平方根誤差(RMSE) 、 平均絶対誤差(MSE) のような代表的な指標の特徴を把握しておきましょう。 参考: 回帰モデルの性能評価および評価指標の解説 分類問題における評価指標 メールのスパム判定や EC サイトでの購入有無の予測のような分類問題に対して使用する評価指標を理解します。 正解率(Accuracy) 、 適合率(Precision) 、 再現率(Recall) 、 F値(F-value) といった基本的な評価指標は頻出です。ローンの審査分析や健康診断での疾病発見の分析など、問題に応じてどの評価指標を利用すべきか把握しておきましょう。 それら評価指標を算出するために必要となる 真陽性(True Positive) 、 偽陽性(False Positive) 、 真陰性(True Negative) 、 偽陰性(False Negative) の概念についても理解しておきましょう。 参考: 機械学習の評価指標 分類編:適合率や再現率、AUC(ROC曲線、PR曲線)を解説 ヒューリスティック 優れた機械学習モデルの開発には大量のデータが必要であり、データが不十分な場合は ヒューリスティック(Heuristic) を使用した予測に劣る精度しか実現できないこともあります。 機械学習による解決を急がず、まずはヒューリスティックを使用して問題に対処しながらデータを貯めていくことも重要であることを覚えておきましょう。 参考1: ML エンジニアリングのベストプラクティス 参考2: ヒューリスティックな知識 機械学習モデルの開発、運用における課題の解決 データの前処理 データの前処理は機械学習モデルの開発工程における大部分を占めると言われており、当試験でも基本的な前処理の手法を問われます。 欠損値の処理 欠損値のあるデータの場合、そのカラムが重要でなければ削除したり、データのばらつきが小さければ平均値等で補填したりするなど、カラムの重要度に応じた対処方法を押さえておきましょう。 参考: 欠損値の対処法 カテゴリカル変数の扱い トレーニング時のカテゴリカル変数の扱い方を学んでおきましょう。 学習手法によってはカテゴリカルな変数はそのまま使用することができず、変数を数値化する必要があります。特に、ダミー変数用いて数値化を行う処理は One-Hot エンコーディング といいます。 参考: ダミー変数(One-Hotエンコーディング)とは?実装コードを交えて徹底解説 不均衡データの対策 トレーニングに使用するデータに偏りがあると、完成したモデルが目的に沿った働きをしなくなる可能性があります。例えば異常検知のような課題では、異常データが発生する頻度がそもそも少ないと、全て「正常」と判断するようなモデルでも正解率がかなり高くなってしまいます。 この場合、 オーバーサンプリング や アンダーサンプリング によってデータの数を調整することがあります。どちらを行うかは収集したデータの量を考慮する必要があります。 また、トレーニング時に少数派のデータに 重み(重要性) をつけることで、少数派データの予測精度を重視してトレーニングを行う方法もあります。不均衡データへの対処方法として、特にこの 3 つを押さえておきましょう。 参考: 不均衡データ(Imbalanced Data)とは? 過学習の対策 機械学習モデルがトレーニング時のデータに過剰に適合してしまう 過学習(Overfitting) について理解を深めます。 過学習の原因としては、一般的に以下のようなものが挙げられます。 学習の反復(エポック)が多すぎる 使用するパラメータが多すぎてモデルが複雑になっている トレーニングデータが少なすぎる リーケージ(leakage) が起きている。 それぞれの原因に対する対策をしっかり押さえておきましょう。 参考1: 〜AutoMLで実践する〜 ビジネスユーザーのための機械学習入門シリーズ 【第 4 回】AutoML のための ML デザイン 参考2: 機械学習で入ってはいけないデータが混入する「リーケージ」とその対策 正則化 過学習の対策としての 正則化(regularization) の手法を理解します。正則化ではモデルの各特徴の重みに対してペナルティを課すことでモデルの過学習を抑制します。 L1 正則化 と L2 正則化 をしっかり区別し、特徴に対してどのような処理を行い、どのような効果が期待できるかを押さえておきましょう。 L1 正則化ではあまり重要ではない特徴に対して重みを 0 にする(もしくは 0 に近づける)ことで、L2 正則化では各特徴の重みの大きさに応じて大きなペナルティを課すことで、モデルの汎化性能を高めます。L1 正則化では不要な特徴を排除する次元圧縮の効果があるのに対し、L2 正則化では影響力の強い特徴を弱めつつ、影響力の弱い特徴を活用できるようにします。 また、正則化とは異なる概念ですが、標準化(standardization, Z-score normalization)、正規化(normalization, Min-Max normalization)という英語でも日本語でも混同しやすい単語に注意します。それぞれ何をするものなのか、しっかり整理しておきましょう。 参考1: 機械学習の正則化とは?L1正則化とL2正則化やPythonでの実装までカンタン解説! 参考2: 【統計・機械学習】標準化(Standardization)と正規化(Normalization)とは?初心者向けにわかりやすく解説 早期停止 早期停止(early stopping) とは、モデルのトレーニングの際に、トレーニングデータに対する過学習が起こる前に学習を終了させる手法です。トレーニングデータとテストデータのそれぞれに対する予測の誤差(訓練誤差、テスト誤差)のうち、後者が大きくなる前に学習を打ち切ります。 参考: 早期終了 トレーニングの改善 機械学習モデルの開発ではトレーニングをただ実行するだけではなく、モデルの精度をより高めるための工夫が求められます。 また、使用するデータの量や質により、膨大な計算時間を費やすこともあります。 ハイパーパラメータの調整 ハイパーパラメータの調整(hyperparameter tuning)について理解を深めます。 たとえば、モデルがなかなか収束しない場合に 学習率(learning late) を調整したり、 エポック数 を調整して過学習が起こらない程度でトレーニングを終えたり(early stopping)といった調整をすることがあります。 参考: 学習率 – 【AI・機械学習用語集】 トレーニング時間の改善 試験では、「モデルの精度をできる限り落とさずに」「コストを節約しながら」などの条件つきで、トレーニング時間を改善する方法が問われます。このような制約がある場合、シンプルに CPU、GPU などの計算資源を増やすといった力技で解決することができない場合があります。 こうした制約の下では、たとえば浮動小数点数に bfloat16 形式を使用することで、モデルの精度の低下を抑えつつ、計算量を減らすことでトレーニング時間を短縮する方法があります。 参考: bfloat16 の数値形式 交差検証 機械学習モデルの開発では、データセットをトレーニングデータとテストデータに分け、トレーニングデータで学習を行ったあと、テストデータに対する予測を行うことでモデルを評価します。このとき、評価を一度しか行わないと、テストデータに対する予測精度がたまたま良くなる可能性があります。 交差検証(Cross-Validation) では、トレーニングデータとテストデータを交差させ、それぞれの分割パターンに対する評価を平均することで最終的な評価値とします。なぜ交差検証を行う必要があるのかをしっかり覚えておきましょう。 参考: 交差検証(Python実装)を徹底解説!図解・サンプル実装コードあり モデルのモニタリングと改善 機械学習モデルが完成して実運用が始まった後でも安心できません。モデルの精度を維持したり、予測のパフォーマンスを改善したりなど、さらなる努力が必要となる場合があります。 スキューとドリフト 一度モデルを開発したあとでも、時間の経過にしたがって現実世界の状況が変化し、データの分布が変わってしまうことでモデルの精度は劣化します( 予測ドリフト )。また、データの収集方法や前処理の問題で、トレーニング時と実際の予測時のデータの分布がそもそも異なる場合もあります( Training-serving skew )。 これらは適切な方法でモニタリングし、検知した場合はモデルを再トレーニングするなどして改善する必要があります。機械学習モデルの運用において非常に重要な要素のため、しっかり覚えておきましょう。 参考 : 概念ドリフト(Concept drift)/データドリフト(Data drift)とは? Google Cloud では Vertex AI Model Monitoring という機能でこれらのモニタリングを行うことができます。 モデルのトレーニングに利用したデータを利用して スキュー検出 を有効にすることで、モデルの トレーニング/サービングスキュー (training-serving skew)をモニタリングできます。トレーニングデータを使えない場合や、トレーニングデータではなく本番環境での経時的な変化を捉えたい場合、 ドリフト検知 (drift detection)を使います。 参考 : Vertex AI Model Monitoring の概要 モデルの軽量化手法 精度が高いモデルが完成しても、その複雑さなどによりメモリ使用量や計算量が膨大になり、リソース不足によるパフォーマンスの低下が懸念されます。特に IoT などのエッジデバイスにモデルを乗せる場合に考慮しなければならない問題です。 以下のような、代表的なモデルの軽量化手法の概要を把握しておきましょう。 枝刈り(Pruning) 量子化(Quantize) 蒸留(Distillation) 参考: ディープラーニングを軽量化する「モデル圧縮」3手法 Google Cloud の機械学習サービス Vertex AI Google Cloud の代表的な AI/ML サービスである Vertex AI は頻出となっています。 Vertex AI は Google Cloud 上で機械学習ワークロードを構築するためのツール群です。どのような場面でどのツールが使用できるかをしっかり理解しておきましょう。 Vertex AI の各ツールの概要については以下のブログをご一読ください。 blog.g-gen.co.jp Vertex AI Training Vertex AI Training では、Google Cloud のマネージドなインフラストラクチャを使用して機械学習モデルのトレーニングを行うことができます。トレーニングには AutoML と カスタムトレーニング の 2種類があります。それぞれ、どのような場面で使用すれば良いのかを整理しておくことが重要です。 AutoML では、トレーニングに使用するデータを Google Cloud にアップロードするだけで、データの前処理からハイパーパラメータ調整、トレーニングの実行までを全て自動で行うことができます。機械学習に精通したエンジニアがいない場合や、機械学習モデルの開発に時間や人的コストがかけられない場合に非常に有用なツールです。 カスタムトレーニングでは、トレーニングを実行するコードをユーザ側で用意し、Vertex AI の ビルド済みのコンテナ 上で実行します。ビルド済みのコンテナでは、Vertex AI でサポートされている機械学習フレームワークやライブラリしか使用できません。 Vertex AI でサポートされていないフレームワーク、ライブラリを使用したい場合は、それらを含めた カスタムコンテナイメージ をユーザ側で作成して使用できます。 参考1: AutoML 初心者向けガイド 参考2: トレーニング コードの要件 Vertex AI Model Registry Google Cloud では、機械学習モデルを Vertex AI Model Registry に格納し、モデルを一括で管理することができます。モデルによる予測を行うためには、エンドポイントにモデルをデプロイする(Vertex AI Endpoints)か、Model Registry で管理されているモデルを直接使用します(Vertex AI Batch Prediction)。 また、モデルをエクスポートすることで、オンプレミスや別のクラウドプロバイダを含む任意の環境でモデルをホストすることもできます。 重要な機能としては、Model Registry で管理する機械学習モデルはバージョン管理することができ、モデルに対するどの変更が影響を及ぼしたかをバージョン間で比較することができます。 参考: Vertex AI Model Registry を使用したモデルのバージョニング Vertex AI Endpoints Vertex AI Endpoints では、モデルをフルマネージドなコンピューティングノードにデプロイすることで、予測リクエストを同期的に処理(オンライン予測)するエンドポイントを生成することができます。 Endpoints の重要な機能として、 1つのエンドポイントに対して複数のモデル をデプロイすることができ、モデルごとにトラフィックの割合を調整することができます。これを利用することで、モデルの新しいバージョンを カナリアリリース することが可能です。 参考: オンライン予測と説明を取得する Vertex AI Batch Prediction Vertex AI Batch Prediction では、Cloud Storage オブジェクト(CSV)もしくは BigQuery テーブルをデータソースとして、1 回のリクエストで大量のデータに対する予測を行うことができます(バッチ予測)。 モデルを何かしらのエンドポイントにデプロイすることなくサーバーレスで予測を行うことができるため、モデルによる予測をリアルタイムで行う必要がない場合はこちらを使用します。Endpoints とのユースケースの違いはしっかり覚えておきましょう。 参考: バッチ予測と説明の取得 Vertex Explainable AI Vertex AI では Vertex Explainable AI により、機械学習モデルが予測を行う際に「各特徴が予測にどの程度影響を及ぼしているか」を可視化することができます(特徴アトリビューション)。これにより、モデルの出力を人間が理解できるような形で説明し、モデルが期待通りに動作できているかどうかを検証できます。 特徴アトリビューションには Shapley 値サンプリング方式 (Sampled Shapley)や 統合勾配方式 (Integrated gradients)、 XRAI (eXplanation with Ranked Area Integrals)といった手法が利用できます。モデルの種類に応じてどの特徴アトリビューション手法が使用できるかを整理しておきましょう。例えば、ディープラーニングで建築物の部品の異常検知を行う場合で、その画像が陽性になったことを説明するには、どのような方式を使えばよいか、回答できるようにしておきましょう。 参考: Vertex Explainable AI の概要 Vertex ML Metadata 機械学習ワークフローで生成されたメタデータは Vertex ML Metadata で一元管理することができます。 メタデータを使用することで、アーティファクト(機械学習ワークフローで使用・生成されたデータやモデル)やコンテキスト(アーティファクトと機械学習ワークフロー実行に関する情報の組)をクエリにより追跡・分析することができます。 参考: ML メタデータの分析 BigQuery ML BigQuery ML を使用することで、BigQuery 上で Google SQL クエリを使用して機械学習モデルを構築、実行することができます。BigQuery を利用するがゆえの利点と制約があるため、Vertex AI との使い分けなど、ユースケースをしっかり把握しておきましょう。 たとえば、Google Cloud で表形式データを使用して機械学習モデルを開発する場合、まず選択肢として挙がるのは Auto ML ですが、AutoML に使用できる データ構造の要件 (行数の制限など)によりそれが達成できない場合や、SQL のみを使用して開発を行いたい場合などは BigQuery ML が候補に挙がります。 また、データが全て BigQuery に格納されているのであれば、BigQuery 上にモデルをインポートして予測を行うことで、コストや予測のパフォーマンス、管理の容易さといったメリットを享受できます。 BigQuery ML については以下の記事で解説しています。是非ご一読ください。 blog.g-gen.co.jp 事前トレーニング済み API サービス 機械学習モデルの開発を支援するサービスだけではなく、Google によって事前にトレーニングされた様々な機械学習モデルを API を通して使用できるサービス群があります。これらのサービスは、機械学習を使用して解決したい課題に特有のデータがない(専用のモデルを開発する必要性が低い)場合や、機械学習によるソリューションを手早く構築したい場合に役立ちます。 以下のような事前トレーニング済み API が提供されています。それぞれサービス名と用途を対応付けて覚えておきましょう。 サービス名 説明 Vision AI 画像内のオブジェクトを検出する。また画像や PDF 中の文字を抽出する OCR 機能などが利用可能。 Video AI 動画内のオブジェクト、場所、アクションを分析する。 Translation AI 高速かつ動的な機械翻訳を利用できる。 Natural Language AI テキストの感情分析、エンティティ分析 (固有名詞の抽出)などが利用可能。 Speech-to-Text 音声を文字に変換することができる。 Text-to-Speech 文字から人間の自然なイントネーションの音声を生成できる。 参考: AI と機械学習のプロダクト データ系サービス データに関する分野の試験ということで、Professional Data Enginner の出題範囲と重複するサービスの理解も問われます。 Dataflow、Dataproc、Dataprep、Pub/Sub、Composer、Data Loss Prevention などのデータ系サービスのユースケースをしっかり把握しておきましょう。 blog.g-gen.co.jp Google Cloud における機械学習モデルの開発 機械学習パイプラインの構築 機械学習モデルの継続的な改善のために、トレーニング、検証、デプロイの各ステップをオーケストレートするパイプラインを構築します。 パイプラインの構築には、 TensorFlow Extended (TFX) や Kubeflow Pipelines といったオープンソースフレームワークを使用します。Google Cloud では、 VertexAI Pipelines を使用することで TFX や Kubeflow Pipelines SDK を使用したサーバーレスな機械学習パイプラインを利用することができます。 Vertex AI Pipelines については以下の記事で解説しています。是非ご一読ください。 blog.g-gen.co.jp 参考1: TensorFlow Extended、Vertex AI Pipelines、Cloud Build を使用した MLOps のアーキテクチャ 参考2: パイプラインの構築 インフラストラクチャの選定 Google Cloud で機械学習パイプラインを構築する際のインフラストラクチャ選定に関する理解を深めます。 GPU、TPUの利用 モデルのトレーニングや予測のパフォーマンス改善のため、 GPU や TPU を使用する場合があります。これらのリソースはリージョン/ゾーンによっては使用できないケースがあることを覚えておきましょう。 トレーニングの際にはデータを ミニバッチ というサブセットに分割して処理を行うことがあります。バッチサイズを大きくするケース、小さくするケースのそれぞれを押さえておきましょう。 たとえば、使用する GPU や TPU の数が増やす場合、バッチサイズを大きくすることでリソースを効率的に使用し、より高速な計算を行うことができるようになります。逆に、これらのリソースをあまり確保できない場合は、バッチサイズを小さくして計算負荷を減らすようにします。 参考1: GPU について 参考2: Cloud TPU の概要 参考3: 複数の GPU に対する深層学習トレーニングをスケーリングするためのハイパーパラメーターの調整の重要性 preemptible VM を使用したコスト節約 GPU や TPU は高価なリソースであり、パフォーマンスとコストがトレードオフとなります。 トレーニングにチェックポイントを設けることができる場合などは、 プリエンプティブル VM(Preemptible VM) を使用してコストを節約することができます。「preemptible」と「checkpoints」はセットで覚えておきましょう。 参考1: プリエンプティブル VM インスタンス 参考2: プリエンプティブル TPU データエンジニアリング 機械学習に使用するデータは Cloud Storage や BigQuery に格納します。 Professional Data Enginner の出題範囲と重なっていますが、Google Cloud におけるデータの ETL / ELT パイプラインのパターンはしっかり理解しておきましょう。 機械学習ワークロードにおける例としては、Dataflow から事前トレーニング済み API サービスや Vertex AI Endpoints に対して予測リクエストを送信し、結果を BigQuery に書き込むようなパターンがあります。 参考1: ETL とは 参考2: データパイプライン・ELT とは - BigQueryを徹底解説!(基本編) TensorFlow におけるパイプライン最適化 TensorFlow を使用して機械学習モデルを開発する場合、 TFRecord 形式のデータを使用することで、通常ではメモリに収まらない大規模なデータセットであっても効率的にトレーニングすることができます。 また TendorFlow では、 tf.data API を使用することで、TFRecord 形式を含む様々な形式のデータを効率的に処理するための複雑なパイプラインを構築することができます。 参考1: 人工知能フレームワーク入門(第4回):TensorFlowのデータフォーマット「TFRecord」を使う 参考2: tf.data: TensorFlow 入力パイプラインの構築 佐々木 駿太 (記事一覧) G-gen最北端、北海道在住のクラウドソリューション部エンジニア 2022年6月にG-genにジョイン。Google Cloud Partner Top Engineer 2025 Fellowに選出。好きなGoogle CloudプロダクトはCloud Run。 趣味はコーヒー、小説(SF、ミステリ)、カラオケなど。 Follow @sasashun0805
アバター
G-gen の藤岡です。当記事では、Google Cloud(旧称 GCP)の Private Service Connect から Google Cloud APIs へのアクセスが Private Service Connect Endpoint 経由でプライベート接続できているか確認する方法を紹介します。 Private Service Connect とは 検証の背景 インターネットを経由してないか確認 実施内容 確認方法 前提 構成図 構築 プロジェクトの作成と請求先アカウントの紐づけ デフォルトプロジェクトのセット API の有効化 VPC とサブネットの作成 ファイアウォールルールの作成 Compute Engine の作成 Private Service Connect の作成 Private Service Connect の確認 経路の確認 バケットの作成 storage.googleapis.com curl コマンド tcpdump コマンド VPC フローログ storage-pscendpoint.p.googleapis.com curl コマンド tcpdump コマンド VPC フローログ デフォルトルートの削除 結果 storage.googleapis.com storage-pscendpoint.p.googleapis.com その他の確認方法 Private Service Connect とは Private Service Connect とは、 外部 IP を持たない VM やオンプレミスのクライアントから内部ネットワーク経由で Google Cloud APIs や、Google Cloud でホストする独自サービスへアクセスできるようにするための仕組みです。 VPC 内に IP アドレスを持つエンドポイントが作られ、このエンドポイント経由で Google Cloud APIs や独自サービスにアクセスできるようになります。 類似機能として 限定公開の Google アクセス (Private Google Access)があります。この 2 つの違いについては当記事では触れませんので、以下の記事をご参照ください。 blog.g-gen.co.jp blog.g-gen.co.jp 検証の背景 インターネットを経由してないか確認 上述の通り、Private Service Connect を使うことで VM やオンプレミスのクライアントから内部ネットワーク経由で Google Cloud APIs へアクセスできます。しかし、設定に誤りがあった場合や意図しない設定により、Private Service Connect を経由せず、デフォルトルートからインターネットを経由していた、等のケースも考えられます。 そこで、当記事では Private Service Connect を経由して Google Cloud APIs へアクセスしているか確認する方法を紹介します。但し、ここで確認できるのはあくまで VM から Private Service Connect Endpoint までの通信 です。Google Cloud が管理するネットワーク(サービスプロデューサー)はユーザー側で確認はできません。 実施内容 確認方法 Private Service Connect の設定をした上で、VM 上で curl コマンドを実行します。その時の送信元と送信先までのアクセス経路を以下の 2 つの方法で確認します。 tcpdump コマンド VPC フローログ 前提 Private Service Connect のエンドポイント p.googleapis.com の DNS 名を使用 実行環境 Cloud Shell から各リソースを作成 # gcloud CLI のバージョン fujioka@cloudshell:~ ( xxxx ) $ gcloud version | grep ' Google Cloud SDK ' Google Cloud SDK 441 . 0 . 0 fujioka@cloudshell:~ ( xxxx ) $ VM への接続 VM への接続は  Cloud IAP  を使用 当記事で扱わないこと 各リソースの作成に必要な権限 構成図 以下の 2 つの経路で確認します。 Default Internet Gateway からインターネットを経由する場合(赤線)、インターネットに公開されている Cloud Storage のエンドポイント( storage.googleapis.com )へアクセスします。 Private Service Connect を使う場合(青線)、内部ネットワークを経由し、 Cloud Storage のエンドポイント( storage-pscendpoint.p.googleapis.com )へアクセスします。 構成図 構築 プロジェクトの作成と請求先アカウントの紐づけ プロジェクトを作成し、作成したプロジェクトに請求先アカウントを紐づけます。 $ 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 API の有効化 必要な API を有効化します。 Compute Engine API Service Directory API Cloud DNS API $ gcloud services enable compute.googleapis.com servicedirectory.googleapis.com dns.googleapis.com 参考 gcloud services VPC とサブネットの作成 VPC とサブネットを作成します。後述の経路の確認で VPC フローログ を使うため有効化します。サンプルレートを 1.0(100%、すべてのログエントリを保持)としていますが、実際に処理されるパケットは平均で約 3% です。 gcloud compute networks create customer-vpc \ --subnet-mode=custom && gcloud compute networks subnets create customer-subnet \ --network=customer-vpc \ --range=10.0.0.0/24 \ --region=asia-northeast1 \ --enable-flow-logs \ --logging-flow-sampling=1.0 \ --enable-private-ip-google-access 参考 ログのサンプリングと処理 gcloud compute networks gcloud compute networks subnets コンソールから作成する場合は以下の記事をご参照ください。 blog.g-gen.co.jp ファイアウォールルールの作成 Cloud IAP 経由で VM へ SSH 接続するため、 35.235.240.0/20 を許可するルールを作成します。 $ gcloud compute firewall-rules create allow-ssh-from-iap \ --network customer-vpc \ --direction ingress \ --action allow \ --source-ranges 35 . 235 . 240 . 0 / 20 \ --rules=tcp:22 参考 gcloud compute firewall-rules Compute Engine の作成 VM を作成します。一時的に外部 IP は付与しています。 $ gcloud compute instances create vm \ --image=debian-10-buster-v20230711 \ --image-project debian-cloud \ --machine-type e2-micro \ --network=customer-vpc \ --subnet=customer-subnet \ --zone asia-northeast1-b VM に dnsutils をインストール後、外部 IP は外します。 # パッケージのアップデート fujioka@vm:~$ sudo apt update # dnsutils のインストール fujioka@vm:~$ sudo apt install -y dnsutils 参考 gcloud compute instances この状態では Cloud Storage エンドポイントはインターネット上のアドレス( storage.googleapis.com )が返ってきます。 fujioka@vm:~$ dig storage.googleapis.com ; <<>> DiG 9.11.5-P4-5.1+deb10u9-Debian <<>> storage.googleapis.com ;; global options: +cmd ;; Got answer: ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 37777 ;; flags: qr rd ra; QUERY: 1, ANSWER: 16, AUTHORITY: 0, ADDITIONAL: 1 ;; OPT PSEUDOSECTION: ; EDNS: version: 0, flags:; udp: 512 ;; QUESTION SECTION: ;storage.googleapis.com. IN A ;; ANSWER SECTION: storage.googleapis.com. 300 IN A 142.250.207.48 storage.googleapis.com. 300 IN A 142.250.196.112 storage.googleapis.com. 300 IN A 142.250.196.144 storage.googleapis.com. 300 IN A 172.217.175.80 storage.googleapis.com. 300 IN A 216.58.220.144 storage.googleapis.com. 300 IN A 142.250.199.112 storage.googleapis.com. 300 IN A 172.217.175.112 storage.googleapis.com. 300 IN A 142.251.222.16 storage.googleapis.com. 300 IN A 142.251.42.144 storage.googleapis.com. 300 IN A 142.251.42.176 storage.googleapis.com. 300 IN A 142.251.42.208 storage.googleapis.com. 300 IN A 142.251.222.48 storage.googleapis.com. 300 IN A 172.217.26.240 storage.googleapis.com. 300 IN A 172.217.31.144 storage.googleapis.com. 300 IN A 172.217.161.80 storage.googleapis.com. 300 IN A 142.250.198.16 ;; Query time: 4 msec ;; SERVER: 169.254.169.254#53(169.254.169.254) ;; WHEN: Sun Aug 06 22:55:49 UTC 2023 ;; MSG SIZE rcvd: 307 fujioka@vm:~$ Private Service Connect の作成 エンドポイントに割り振る内部 IP アドレスを予約します。 gcloud compute addresses create psc-address \ --global \ --purpose=PRIVATE_SERVICE_CONNECT \ --addresses=10.0.20.1 \ --network=customer-vpc 転送ルールを作成します。 gcloud compute forwarding-rules create pscendpoint \ --global \ --network=customer-vpc \ --address=psc-address \ --target-google-apis-bundle=all-apis 参考 エンドポイントを作成する gcloud compute addresses gcloud compute forwarding-rules Private Service Connect の確認 エンドポイントが機能していることを確認します。エンドポイントが機能している場合、以下のように HTTP 204 レスポンス コードが返されます。エンドポイントは、ping(ICMP)に応答しないため以下のように確認します。 fujioka@vm:~$ curl -v 10 . 0 . 20 . 1 /generate_204 * Expire in 0 ms for 6 ( transfer 0x5636b4fb80f0 ) * Trying 10 . 0 . 20 . 1 ... * TCP_NODELAY set * Expire in 200 ms for 4 ( transfer 0x5636b4fb80f0 ) * Connected to 10 . 0 . 20 . 1 ( 10 . 0 . 20 . 1 ) port 80 ( #0) > GET /generate_204 HTTP/ 1 . 1 > Host: 10 . 0 . 20 . 1 > User-Agent: curl/ 7 . 64 . 0 > Accept: */* > < HTTP/ 1 . 1 204 No Content < Content-Length: 0 < Cross-Origin-Resource-Policy: cross-origin < Date: Sun, 06 Aug 2023 22:56:48 GMT < * Connection #0 to host 10.0.20.1 left intact fujioka@vm:~$ 443 ポートでも成功します。 fujioka@vm:~$ curl -v 10 . 0 . 20 .1:443/generate_204 * Expire in 0 ms for 6 ( transfer 0x5591c9b4c0f0 ) * Trying 10 . 0 . 20 . 1 ... * TCP_NODELAY set * Expire in 200 ms for 4 ( transfer 0x5591c9b4c0f0 ) * Connected to 10 . 0 . 20 . 1 ( 10 . 0 . 20 . 1 ) port 443 ( #0) > GET /generate_204 HTTP/ 1 . 1 > Host: 10 . 0 . 20 .1:443 > User-Agent: curl/ 7 . 64 . 0 > Accept: */* > * Empty reply from server * Connection #0 to host 10.0.20.1 left intact curl: ( 52 ) Empty reply from server fujioka@vm:~$ 参考 エンドポイントが機能していることを確認する エンドポイントを作成すると、そのエンドポイントを使用して利用可能 API とサービスの DNS レコードが Service Directory によって作成されます。 Private Service Connect によって作られたゾーン Service Directory によって作成されたレコードにより、Cloud Storage のエンドポイント( storage-pscendpoint.p.googleapis.com )へ内部ネットワークでアクセスできています。 fujioka@vm:~$ dig storage-pscendpoint.p.googleapis.com ; <<>> DiG 9.11.5-P4-5.1+deb10u9-Debian <<>> storage-pscendpoint.p.googleapis.com ;; global options: +cmd ;; Got answer: ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 25971 ;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1 ;; OPT PSEUDOSECTION: ; EDNS: version: 0, flags:; udp: 512 ;; QUESTION SECTION: ;storage-pscendpoint.p.googleapis.com. IN A ;; ANSWER SECTION: storage-pscendpoint.p.googleapis.com. 60 IN A 10.0.20.1 ;; Query time: 9 msec ;; SERVER: 169.254.169.254#53(169.254.169.254) ;; WHEN: Sun Aug 06 23:03:30 UTC 2023 ;; MSG SIZE rcvd: 81 fujioka@vm:~$ 参考 p.googleapis.com DNS 名を使用する storage.googleapis.com はエンドポイント作成前と変わらずインターネット上に公開されているアドレスが返ってきます。 fujioka@vm:~$ dig storage.googleapis.com ; <<>> DiG 9.11.5-P4-5.1+deb10u9-Debian <<>> storage.googleapis.com ;; global options: +cmd ;; Got answer: ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 29405 ;; flags: qr rd ra; QUERY: 1, ANSWER: 16, AUTHORITY: 0, ADDITIONAL: 1 ;; OPT PSEUDOSECTION: ; EDNS: version: 0, flags:; udp: 512 ;; QUESTION SECTION: ;storage.googleapis.com. IN A ;; ANSWER SECTION: storage.googleapis.com. 300 IN A 142.250.196.112 storage.googleapis.com. 300 IN A 142.250.196.144 storage.googleapis.com. 300 IN A 172.217.175.80 storage.googleapis.com. 300 IN A 216.58.220.144 storage.googleapis.com. 300 IN A 142.250.199.112 storage.googleapis.com. 300 IN A 142.251.222.16 storage.googleapis.com. 300 IN A 142.251.42.144 storage.googleapis.com. 300 IN A 142.251.42.176 storage.googleapis.com. 300 IN A 142.251.42.208 storage.googleapis.com. 300 IN A 142.251.222.48 storage.googleapis.com. 300 IN A 172.217.26.240 storage.googleapis.com. 300 IN A 172.217.31.144 storage.googleapis.com. 300 IN A 142.250.198.16 storage.googleapis.com. 300 IN A 172.217.31.176 storage.googleapis.com. 300 IN A 172.217.161.48 storage.googleapis.com. 300 IN A 142.250.207.16 ;; Query time: 6 msec ;; SERVER: 169.254.169.254#53(169.254.169.254) ;; WHEN: Sun Aug 06 23:03:54 UTC 2023 ;; MSG SIZE rcvd: 307 fujioka@vm:~$ ここまでで以下の構成となっています。 再掲:構成図 これ以降は、Cloud Storage のエンドポイントをインターネット経由のエンドポイント( storage.googleapis.com )と Private Service Connect 経由のエンドポイント( storage-pscendpoint.p.googleapis.com )のどちらにアクセスしているか確認する方法を紹介します。 経路の確認 バケットの作成 確認用にバケットを作成します。 $ gcloud storage buckets create gs://test-bucket-20230807 -l asia-northeast1 --uniform-bucket-level-access storage.googleapis.com Default Internet Gateway 経由で Cloud Storage のエンドポイント( storage.googleapis.com )へアクセスしている時の結果を確認します。 curl コマンド curl コマンドで確認します。問題なく結果が返ってきます。 fujioka@vm:~$ curl -X GET -H "Authorization: Bearer $(gcloud auth print-access-token)" "https://storage.googleapis.com/storage/v1/b?project=$(gcloud config get-value project)" { "kind": "storage#buckets", "items": [ { "kind": "storage#bucket", "selfLink": "https://www.googleapis.com/storage/v1/b/test-bucket-20230807", "id": "test-bucket-20230807", "name": "test-bucket-20230807", "projectNumber": "012345", "metageneration": "1", "location": "ASIA-NORTHEAST1", "storageClass": "STANDARD", "etag": "CAE=", "timeCreated": "2023-08-06T12:53:47.056Z", "updated": "2023-08-06T12:53:47.056Z", "iamConfiguration": { "bucketPolicyOnly": { "enabled": true, "lockedTime": "2023-11-04T12:53:47.056Z" }, "uniformBucketLevelAccess": { "enabled": true, "lockedTime": "2023-11-04T12:53:47.056Z" }, "publicAccessPrevention": "inherited" }, "locationType": "region" } ] } fujioka@vm:~$ 参考 リクエスト エンドポイント tcpdump コマンド 上記の curl 実行時に、別ターミナルから tcpdump でアクセス先を確認します。 この 216.58.220.112 は先程 dig で返ってきた storage.googleapis.com のアドレスです。 fujioka@vm:~$ sudo tcpdump -nn -tttt dst port 443 tcpdump: verbose output suppressed, use -v or -vv for full protocol decode listening on ens4, link-type EN10MB (Ethernet), capture size 262144 bytes 2023-08-06 23:07:25.122857 IP 10.0.0.2.50926 > 216.58.220.112.443: Flags [S], seq 289325051, win 65320, options [mss 1420,sackOK,TS val 2811924983 ecr 0,nop,wscale 7], length 0 2023-08-06 23:07:25.123628 IP 10.0.0.2.50926 > 216.58.220.112.443: Flags [.], ack 3659639751, win 511, options [nop,nop,TS val 2811924983 ecr 1285463732], length 0 2023-08-06 23:07:25.124890 IP 10.0.0.2.50926 > 216.58.220.112.443: Flags [P.], seq 0:517, ack 1, win 511, options [nop,nop,TS val 2811924985 ecr 1285463732], length 517 2023-08-06 23:07:25.125667 IP 10.0.0.2.50926 > 216.58.220.112.443: Flags [.], ack 4321, win 491, options [nop,nop,TS val 2811924985 ecr 1285463734], length 0 2023-08-06 23:07:25.126753 IP 10.0.0.2.50926 > 216.58.220.112.443: Flags [P.], seq 517:597, ack 4321, win 501, options [nop,nop,TS val 2811924986 ecr 1285463734], length 80 2023-08-06 23:07:25.126993 IP 10.0.0.2.50926 > 216.58.220.112.443: Flags [P.], seq 597:643, ack 4383, win 501, options [nop,nop,TS val 2811924987 ecr 1285463735], length 46 2023-08-06 23:07:25.127043 IP 10.0.0.2.50926 > 216.58.220.112.443: Flags [P.], seq 643:692, ack 4383, win 501, options [nop,nop,TS val 2811924987 ecr 1285463735], length 49 2023-08-06 23:07:25.127145 IP 10.0.0.2.50926 > 216.58.220.112.443: Flags [P.], seq 692:727, ack 4383, win 501, options [nop,nop,TS val 2811924987 ecr 1285463735], length 35 2023-08-06 23:07:25.127292 IP 10.0.0.2.50926 > 216.58.220.112.443: Flags [P.], seq 727:1640, ack 4414, win 501, options [nop,nop,TS val 2811924987 ecr 1285463735], length 913 2023-08-06 23:07:25.127412 IP 10.0.0.2.50926 > 216.58.220.112.443: Flags [P.], seq 1640:1671, ack 4414, win 501, options [nop,nop,TS val 2811924987 ecr 1285463735], length 31 2023-08-06 23:07:25.308584 IP 10.0.0.2.50926 > 216.58.220.112.443: Flags [P.], seq 1671:1695, ack 5654, win 501, options [nop,nop,TS val 2811925168 ecr 1285463916], length 24 2023-08-06 23:07:25.308882 IP 10.0.0.2.50926 > 216.58.220.112.443: Flags [R.], seq 1695, ack 5655, win 501, options [nop,nop,TS val 2811925169 ecr 1285463917], length 0 ^C 12 packets captured 12 packets received by filter 0 packets dropped by kernel fujioka@vm:~$ VPC フローログ VM( 10.0.0.2 )から Cloud Storage エンドポイントアドレス( 216.58.220.112 )へのフローログです。 { "insertId": "1w9cwmwfsezc3l", "jsonPayload": { "bytes_sent": "4504", "reporter": "SRC", "start_time": "2023-08-06T23:07:25.123378275Z", "packets_sent": "16", "connection": { "dest_ip": "216.58.220.112", "src_port": 50926, "protocol": 6, "dest_port": 443, "src_ip": "10.0.0.2" }, "end_time": "2023-08-06T23:07:25.308756331Z" }, "resource": { "type": "gce_subnetwork", "labels": { "subnetwork_id": "5059267080321417437", "project_id": "xxxxx", "location": "asia-northeast1-b", "subnetwork_name": "customer-subnet" } }, "timestamp": "2023-08-06T23:07:36.397651875Z", "logName": "projects/xxxx/logs/compute.googleapis.com%2Fvpc_flows", "receiveTimestamp": "2023-08-06T23:07:36.397651875Z" } Cloud Storage エンドポイントアドレス( 216.58.220.112 )から VM( 10.0.0.2 )へのフローログです。 { " insertId " : " 1w9cwmwfsezc3m " , " jsonPayload " : { " end_time " : " 2023-08-06T23:07:25.308756331Z " , " connection " : { " protocol " : 6 , " dest_ip " : " 10.0.0.2 " , " src_ip " : " 216.58.220.112 " , " dest_port " : 50926 , " src_port " : 443 } , " reporter " : " DEST " , " bytes_sent " : " 248 " , " start_time " : " 2023-08-06T23:07:25.123378275Z " , " packets_sent " : " 16 " } , " resource " : { " type " : " gce_subnetwork " , " labels " : { " location " : " asia-northeast1-b " , " subnetwork_id " : " 5059267080321417437 " , " subnetwork_name " : " customer-subnet " , " project_id " : " xxxx " } } , " timestamp " : " 2023-08-06T23:07:36.397651875Z " , " logName " : " projects/xxxx/logs/compute.googleapis.com%2Fvpc_flows " , " receiveTimestamp " : " 2023-08-06T23:07:36.397651875Z " } storage-pscendpoint.p.googleapis.com 内部ネットワークを経由し、 Cloud Storage のエンドポイント( storage-pscendpoint.p.googleapis.com )へアクセスしている時の結果を確認します。 curl コマンド curl コマンドで確認します。問題なく結果が返ってきます。 fujioka@vm:~$ curl -X GET -H "Authorization: Bearer $(gcloud auth print-access-token)" "https://storage-pscendpoint.p.googleapis.com/storage/v1/b?project=$(gcloud config get-value project)" { "kind": "storage#buckets", "items": [ { "kind": "storage#bucket", "selfLink": "https://www.googleapis.com/storage/v1/b/test-bucket-20230807", "id": "test-bucket-20230807", "name": "test-bucket-20230807", "projectNumber": "012345", "metageneration": "1", "location": "ASIA-NORTHEAST1", "storageClass": "STANDARD", "etag": "CAE=", "timeCreated": "2023-08-06T12:53:47.056Z", "updated": "2023-08-06T12:53:47.056Z", "iamConfiguration": { "bucketPolicyOnly": { "enabled": true, "lockedTime": "2023-11-04T12:53:47.056Z" }, "uniformBucketLevelAccess": { "enabled": true, "lockedTime": "2023-11-04T12:53:47.056Z" }, "publicAccessPrevention": "inherited" }, "locationType": "region" } ] } fujioka@vm:~$ tcpdump コマンド 上記の curl 実行時に、別ターミナルから tcpdump でアクセス先を確認します。 この 10.0.20.1 は Private Service Connect Endpoint のアドレスです。 fujioka@vm:~$ sudo tcpdump -nn -tttt dst port 443 tcpdump: verbose output suppressed, use -v or -vv for full protocol decode listening on ens4, link-type EN10MB (Ethernet), capture size 262144 bytes 2023-08-06 23:11:40.027044 IP 10.0.0.2.52156 > 10.0.20.1.443: Flags [S], seq 2145849587, win 65320, options [mss 1420,sackOK,TS val 3419769925 ecr 0,nop,wscale 7], length 0 2023-08-06 23:11:40.027954 IP 10.0.0.2.52156 > 10.0.20.1.443: Flags [.], ack 1219629433, win 511, options [nop,nop,TS val 3419769926 ecr 250771621], length 0 2023-08-06 23:11:40.029234 IP 10.0.0.2.52156 > 10.0.20.1.443: Flags [P.], seq 0:517, ack 1, win 511, options [nop,nop,TS val 3419769927 ecr 250771621], length 517 2023-08-06 23:11:40.062691 IP 10.0.0.2.52156 > 10.0.20.1.443: Flags [.], ack 7041, win 479, options [nop,nop,TS val 3419769960 ecr 250771656], length 0 2023-08-06 23:11:40.062707 IP 10.0.0.2.52156 > 10.0.20.1.443: Flags [.], ack 9835, win 467, options [nop,nop,TS val 3419769960 ecr 250771656], length 0 2023-08-06 23:11:40.064094 IP 10.0.0.2.52156 > 10.0.20.1.443: Flags [P.], seq 517:597, ack 9835, win 501, options [nop,nop,TS val 3419769962 ecr 250771656], length 80 2023-08-06 23:11:40.064468 IP 10.0.0.2.52156 > 10.0.20.1.443: Flags [P.], seq 597:643, ack 9897, win 501, options [nop,nop,TS val 3419769962 ecr 250771658], length 46 2023-08-06 23:11:40.064537 IP 10.0.0.2.52156 > 10.0.20.1.443: Flags [P.], seq 643:692, ack 9897, win 501, options [nop,nop,TS val 3419769962 ecr 250771658], length 49 2023-08-06 23:11:40.064576 IP 10.0.0.2.52156 > 10.0.20.1.443: Flags [P.], seq 692:727, ack 9897, win 501, options [nop,nop,TS val 3419769962 ecr 250771658], length 35 2023-08-06 23:11:40.064691 IP 10.0.0.2.52156 > 10.0.20.1.443: Flags [P.], seq 727:1650, ack 9897, win 501, options [nop,nop,TS val 3419769962 ecr 250771658], length 923 2023-08-06 23:11:40.064777 IP 10.0.0.2.52156 > 10.0.20.1.443: Flags [P.], seq 1650:1681, ack 9928, win 501, options [nop,nop,TS val 3419769962 ecr 250771658], length 31 2023-08-06 23:11:40.240570 IP 10.0.0.2.52156 > 10.0.20.1.443: Flags [.], ack 11099, win 501, options [nop,nop,TS val 3419770138 ecr 250771834], length 0 2023-08-06 23:11:40.240903 IP 10.0.0.2.52156 > 10.0.20.1.443: Flags [P.], seq 1681:1705, ack 11130, win 501, options [nop,nop,TS val 3419770138 ecr 250771834], length 24 2023-08-06 23:11:40.241162 IP 10.0.0.2.52156 > 10.0.20.1.443: Flags [R], seq 2145851293, win 0, length 0 2023-08-06 23:11:40.241170 IP 10.0.0.2.52156 > 10.0.20.1.443: Flags [R.], seq 1705, ack 11169, win 501, options [nop,nop,TS val 3419770139 ecr 250771834], length 0 ^C 15 packets captured 15 packets received by filter 0 packets dropped by kernel fujioka@vm:~$ VPC フローログ VM( 10.0.0.2 )からPrivate Service Connect Endpoint( 10.0.20.1 )へのフローログです。 { "insertId": "rr9iweg19nih87", "jsonPayload": { "start_time": "2023-08-06T23:11:04.052467571Z", "packets_sent": "8", "reporter": "SRC", "connection": { "src_port": 49890, "src_ip": "10.0.0.2", "protocol": 6, "dest_port": 443, "dest_ip": "10.0.20.1" }, "rtt_msec": "0", "bytes_sent": "1944", "end_time": "2023-08-06T23:11:04.261488665Z" }, "resource": { "type": "gce_subnetwork", "labels": { "location": "asia-northeast1-b", "subnetwork_id": "5059267080321417437", "subnetwork_name": "customer-subnet", "project_id": "xxxx" } }, "timestamp": "2023-08-06T23:11:16.402034721Z", "logName": "projects/xxxx/logs/compute.googleapis.com%2Fvpc_flows", "receiveTimestamp": "2023-08-06T23:11:16.402034721Z" } Private Service Connect Endpoint( 10.0.20.1 )から VM( 10.0.0.2 )へのフローログです。 { "insertId": "rr9iweg19nih88", "jsonPayload": { "bytes_sent": "10442", "packets_sent": "20", "end_time": "2023-08-06T23:11:04.261488665Z", "reporter": "DEST", "rtt_msec": "0", "connection": { "dest_port": 49890, "src_ip": "10.0.20.1", "src_port": 443, "dest_ip": "10.0.0.2", "protocol": 6 }, "start_time": "2023-08-06T23:11:04.052467571Z" }, "resource": { "type": "gce_subnetwork", "labels": { "location": "asia-northeast1-b", "subnetwork_id": "5059267080321417437", "subnetwork_name": "customer-subnet", "project_id": "xxxx" } }, "timestamp": "2023-08-06T23:11:16.402034721Z", "logName": "projects/xxxx/logs/compute.googleapis.com%2Fvpc_flows", "receiveTimestamp": "2023-08-06T23:11:16.402034721Z" } デフォルトルートの削除 デフォルトルートを削除します。これによって、Default Internet Gateway へのルートがなくなめ、Cloud Storage のエンドポイントの storage.googleapis.com へアクセスできなくなり、 storage-pscendpoint.p.googleapis.com へアクセスできれば、Private Service Connect を経由していることが証明されます。 デフォルトルート削除時の構成 # デフォルトルートの削除前 fujioka@cloudshell:~ (xxxxj)$ gcloud compute routes list NAME: default-route-cfca000dac79779e NETWORK: customer-vpc DEST_RANGE: 10.0.0.0/24 NEXT_HOP: customer-vpc PRIORITY: 0 NAME: default-route-edbfb93b447ab755 NETWORK: customer-vpc DEST_RANGE: 0.0.0.0/0 NEXT_HOP: default-internet-gateway PRIORITY: 1000 fujioka@cloudshell:~ (xxxxj)$ # デフォルトルートの削除 fujioka@cloudshell:~ (xxxxj)$ gcloud compute routes delete default-route-edbfb93b447ab755 The following routes will be deleted: - [default-route-edbfb93b447ab755] Do you want to continue (Y/n)? Y Deleted [https://www.googleapis.com/compute/v1/projects/xxxx/global/routes/default-route-edbfb93b447ab755]. fujioka@cloudshell:~ (xxxxj)$ # デフォルトルートの削除後 fujioka@cloudshell:~ (xxxxj)$ gcloud compute routes list NAME: default-route-cfca000dac79779e NETWORK: customer-vpc DEST_RANGE: 10.0.0.0/24 NEXT_HOP: customer-vpc PRIORITY: 0 fujioka@cloudshell:~ (xxxxj)$ 参考 gcloud compute routes 結果 storage.googleapis.com storage.googleapis.com へアクセスできなくなりました。 fujioka@vm:~$ curl -X GET -H "Authorization: Bearer $(gcloud auth print-access-token)" "https://storage.googleapis.com/storage/v1/b?project=$(gcloud config get-value project)" curl: (28) Connection timed out after 300001 milliseconds fujioka@vm:~$ ^C storage-pscendpoint.p.googleapis.com storage-pscendpoint.p.googleapis.com へは問題なくアクセスできます。 fujioka@vm:~$ curl -X GET -H "Authorization: Bearer $(gcloud auth print-access-token)" "https://storage-pscendpoint.p.googleapis.com/storage/v1/b?project=$(gcloud config get-value project)" { "kind": "storage#buckets", "items": [ { "kind": "storage#bucket", "selfLink": "https://www.googleapis.com/storage/v1/b/test-bucket-20230807", "id": "test-bucket-20230807", "name": "test-bucket-20230807", "projectNumber": "012345", "metageneration": "1", "location": "ASIA-NORTHEAST1", "storageClass": "STANDARD", "etag": "CAE=", "timeCreated": "2023-08-06T12:53:47.056Z", "updated": "2023-08-06T12:53:47.056Z", "iamConfiguration": { "bucketPolicyOnly": { "enabled": true, "lockedTime": "2023-11-04T12:53:47.056Z" }, "uniformBucketLevelAccess": { "enabled": true, "lockedTime": "2023-11-04T12:53:47.056Z" }, "publicAccessPrevention": "inherited" }, "locationType": "region" } ] } fujioka@vm:~$ 以上から、Private Service Connect が機能しており、内部ネットワークでアクセスできていることがわかります。 その他の確認方法 当記事では、複数の観点から確認をしましたが、 Network Intelligence Center の接続テストでも Private Service Connect を宛先として指定できます。但し、こちらも同様に確認できるのは VM から Private Service Connect Endpoint までの通信です。Google Cloud が管理するネットワーク(サービスプロデューサー)はユーザー側で確認はできません。 参考 サポートされている構成 藤岡 里美 (記事一覧) クラウドソリューション部 接客業からエンジニアへ。2022年9月 G-gen にジョイン。Google Cloud 認定資格は全冠。2023 夏アニメのオススメは、ダークギャザリング。箏を習っています :) Follow @fujioka57621469
アバター
G-gen の武井です。当記事では Config Controller (Config Sync) を用いて GitOps で Google Cloud (旧称 GCP) のリソース管理を試してみましたので紹介します。 Config Controller (Config Sync) はじめに 当記事の概要 前提知識 Anthos Config Management Config Controller Config Sync Config Controller と Terraform の違い 関連記事 アーキテクチャ 設定 実行環境 参考ドキュメント Config Controller API の有効化 Config Controller クラスタの作成 Cloud NAT の作成 IAM Policy の設定 Config Sync 認証情報の作成 認証情報の紐づけ (GitHub) 認証情報の紐づけ (Config Sync) GitHub との連携 マニフェストファイルの準備 動作確認 nomos コマンド Cloud コンソール Reconciliation Loop はじめに 当記事の概要 Config Controller と Config Sync を併用すると、Google Cloud のリソース管理を GitOps で管理できます。 Git リポジトリで管理されるマニフェストファイルを定期的に参照する Google Cloud 環境の現状とマニフェストファイルを比較する マニフェストファイルを正として、環境が本来あるべき姿を維持するようリソースを管理 (作成、変更、削除) する 当記事では上記を実現するための方法をご紹介します。 前提知識 Anthos Config Management Anthos Config Management は、Google Cloud リソースのプロビジョニングとオーケストレーションを行うサービスです。 Config Controller 、 Config Sync 、 Policy Controller という 3つ のコンポーネントから構成されます。 ※ 当記事で Policy Controller の解説は割愛します。 # 機能 概要 1 Config Controller Kubernetes の仕組みを用いて Google Cloud リソースを管理する中核的コンポーネント 2 Config Sync GitHub 等のリポジトリからマニフェストファイルを取得して Google Cloud リソースを管理 (GitOps サービス) 3 Policy Controller Google Cloud リソースのセキュリティとコンプライアンスを維持するためのカスタムポリシーを管理 (ガードレール) Config Controller Config Controller は、 Config Connector と呼ばれる Kubernetes アドオンを利用してGoogle Cloud リソースのプロビジョニングとオーケストレーションを行うサービスです。 簡単に表すと、Kubernetes の仕組みを利用して、Google Cloud リソースの状態を定義したマニフェストファイルを kubectl で管理します。 Config Sync Config Sync は GitOps サービスを提供するコンポーネントで、Git リポジトリと連携し、格納されたマニフェストファイルに従いリソースを動的に管理します。 そのため、Config Controller のみの利用 ( kubectl による手動実行) とは異なり、一元管理された情報源にもとづきリソースが管理されることとなります。 Config Controller と Terraform の違い IaC (Infrastructure as Code) という観点で両者に大きな違いはありませんが、Config Controller は Kubernetes の仕組みを用いているため、 Reconciliation Loop に基づき管理するリソースをあるべき状態を維持しようと作用します。 つまり、マニフェストファイルで定義された内容が各リソースの ”あるべき状態” となるため、例えばリソースに何らかの変更が手動で加えられたとしても、Config Controller が自動的に検知・是正します (あるべき状態に戻します) 。 関連記事 以下の記事でも Config Controller について解説しています。こちらもあわせてご参照ください。 blog.g-gen.co.jp blog.g-gen.co.jp アーキテクチャ Config Controller は前述の通り Config Connector と呼ばれる Kubernetes アドオンを使ったサービスで、実体としては GKE クラスタ が使用されています。 GKE クラスタには Config Controller の他にも Config Sync と Policy Controller がプリインストールされていて、今回の例では Config Sync を有効化して GitHub リポジトリと連携します。 これにより、任意のブランチに格納されたマニフェストファイルに従い Google Cloud リソースを管理します。 アーキテクチャ図 設定 実行環境 本設定では gcloud 、 kubectl 、 nomos ( Config Sync のオプションツール ) などのコマンドを利用するため、これらがプリインストールされた Cloud Shell を利用します。 参考ドキュメント 以下の公式ガイドを参考に Config Controller と Config Sync を設定しています。 参考: Config Controller でリソースを管理する 参考: Config Sync で GitOps を設定する 参考: Git へのアクセス権を付与する Config Controller API の有効化 Config Controller を作成するプロジェクト (今回は prj-cc-example ) で gcloud services enable コマンドを実行し、必要な API を有効にします。 gcloud services enable krmapihosting.googleapis.com \ container.googleapis.com \ cloudresourcemanager.googleapis.com \ serviceusage.googleapis.com \ cloudbilling.googleapis.com Config Controller クラスタの作成 gcloud anthos config controller create コマンドで Config Controller クラスタを作成します。 --location 以外のオプションについては次のとおりです。 # オプション 役割 1 --network GKE クラスタの VPC を指定 (未指定だと default VPC を選択する) 2 --subnet GKE クラスタのサブネットを指定 3 --full-management Autopilot でクラスタを構成 gcloud anthos config controller create cc-example \ --location=asia-northeast1 \ --network=vpc-cc-example \ --subnet=subnet-cc-example \ --full-management クラスタの作成には時間がかかります。完了すると以下の戻り値が表示されます。 Created instance [ cc-example ] . Fetching cluster endpoint and auth data. kubeconfig entry generated for krmapihost-cc-example. gcloud anthos config controller list コマンドでクラスタの作成を確認できます。 gcloud anthos config controller list --location=asia-northeast1 NAME: cc-example LOCATION: asia-northeast1 STATE: RUNNING gcloud anthos config controller get-credentials コマンドで作成したクラスタを kubectl に紐付けます。 gcloud anthos config controller get-credentials cc-example \ --location asia-northeast1 Fetching cluster endpoint and auth data. kubeconfig entry generated for krmapihost-cc-example. Cloud NAT の作成 Config Controller クラスタは 限定公開クラスタ として作成されるため、インターネットに接続できません。 今回 Config Sync を介して GitHub に接続するので Cloud NAT を作成します。 # Cloud NAT ルーターを作成 gcloud compute routers create cc-nat-router \ --network vpc-cc-example \ --region asia-northeast1 # NAT ゲートウェイを作成 gcloud compute routers nats create cc-nat-config \ --router-region asia-northeast1 \ --router cc-nat-router \ --nat-all-subnet-ip-ranges \ --auto-allocate-nat-external-ips 完了すると以下の戻り値が表示されます。 Creating router [ cc-nat-router ] ...done. NAME: cc-nat-router REGION: asia-northeast1 NETWORK: vpc-cc-example Creating NAT [ cc-nat-config ] in router [ cc-nat-router ] ...done. IAM Policy の設定 Google Cloud リソースを管理する権限を Config Controller が使用するサービスアカウントに付与します。 以下のように組織レベルで権限を付与する場合、 gcloud organizations add-iam-policy-binding コマンドを使用します。 PROJECT_NO = { プロジェクト番号を入力 } ORG_ID = { 組織 ID を入力 } SA_EMAIL = " service- ${PROJECT_NO} @gcp-sa-yakima.iam.gserviceaccount.com " # 組織レベルでオーナーロールを付与 gcloud organizations add-iam-policy-binding ${ORG_ID} \ --role=roles/owner \ --member= " serviceAccount: ${SA_EMAIL} " # 組織レベルでプロジェクト作成者ロールを付与 gcloud organizations add-iam-policy-binding ${ORG_ID} \ --role=roles/resourcemanager.projectCreator \ --member= " serviceAccount: ${SA_EMAIL} " Config Sync 認証情報の作成 Config Sync には GitHub に対する読み取り権限が必要となるため、認証情報を作成する必要があります。 Config Sync はいくつかの 認証方式 をサポートしていますが、今回は GitHub が対応している SSH 認証鍵ペア を使って認証方式で設定します。 Config Sync では SSH 認証鍵のパスフレーズをサポートしていない ため、以下のコマンドでパスフレーズなしの SSH 認証鍵ペア (公開鍵と秘密鍵) を作成します。 # ~/.ssh ディレクトリ配下に秘密鍵 (id_rsa) と公開鍵 (id_rsa.pub) をパスフレーズなしで作成 # GIT_REPOSITORY_USERNAME にはConfig Sync がリポジトリへの認証で使用するユーザー名を入力 ssh-keygen -t rsa -b 4096 \ -C " GIT_REPOSITORY_USERNAME " \ -N '' 認証情報の紐づけ (GitHub) 先程作成した SSH 認証鍵ペアのうち、GitHub に公開鍵を紐付けるための設定 ( デプロイキーの設定 ) を行います。 GitHub リポジトリにアクセスします。 Settings > Deploy keys と遷移したら Add deploy key をクリックします。 Settings をクリック Deploy keys から キーを作成 Title に任意のタイトル名を入力、 Key に公開鍵 (id_rsa.pub) の値を入力したら Add key をクリックします。 公開鍵の値を入力してデプロイキーを作成 GitHub ユーザーアカウントに MFA 認証が設定されている場合は認証コードを入力します。 認証コードを入力 (MFA 認証が有効な場合) デプロイキーが作成されたことを確認します。 デプロイキーが作成された 認証情報の紐づけ (Config Sync) 先程作成した SSH 認証鍵ペアのうち、クラスタに秘密鍵 (id_rsa)を紐付けるために以下の設定を行います。 Config Sync の Namespace を config-management-system (固定値) で作成します。 Secret を git-creds (固定値) で作成して秘密鍵の値を登録します。 kubectl create ns config-management-system && \ kubectl create secret generic git-creds \ --namespace=config-management-system \ --from-file=ssh=~/.ssh/id_rsa GitHub との連携 GitHub と連携するため、以下のマニフェストファイルを kubectl で適用します。 # 項目 説明 1 metadata.name リソース名 (任意) 2 metadata.namespace Config Sync の Namespace (固定) 3 spec.git.repo 参照先リポジトリ名 (SSH 形式) 4 spec.git.branch 参照先ブランチ名 5 spec.git.dir 参照先ディレクトリ名 (ルートディレクトリから見た相対パス) # root-sync.yaml apiVersion : configsync.gke.io/v1beta1 kind : RootSync metadata : name : prj-cc-example-root-sync namespace : config-management-system spec : sourceType : git sourceFormat : unstructured git : repo : git@github.com:repo-owner/repo-name.git branch : main dir : sample/ auth : ssh secretRef : name : git-creds ※ spec.git.repo の repo-owner と repo-name は GitHub リポジトリの URL に表示される値を入力します。 GitHub リポジトリの URL # kubectl で適用 kubectl apply -f root-sync.yaml マニフェストファイルの準備 Config Sync の構成にあわせ、Google Cloud のリソースを定義したマニフェストファイルを GitHub リポジトリの main ブランチに用意します。 コードの編集には コードスペース を使用しており、 /workspaces/sample-repo-name がルートディレクトリに相当します。 @username ➜ /workspaces/sample-repo-name ( main ) $ pwd /workspaces/sample-repo-name @username ➜ /workspaces/sample-repo-name ( main ) $ tree . └── sample └── test .yaml 今回準備したマニフェストファイルでは プロジェクト と Storage バケット を定義しています。 # test.yaml # テスト用プロジェクト apiVersion : resourcemanager.cnrm.cloud.google.com/v1beta1 kind : Project metadata : annotations : cnrm.cloud.google.com/auto-create-network : "false" name : cc-test-prj namespace : config-control spec : # name (プロジェクト名、ディスプレイ表示) と resourceID (プロジェクトID) は一致させる name : cc-test-prj resourceID : cc-test-prj folderRef : # フォルダID external : "1111111111111" billingAccountRef : # 請求先アカウントID external : "222222-222222-222222" --- # テスト用 Storage バケット apiVersion : storage.cnrm.cloud.google.com/v1beta1 kind : StorageBucket metadata : annotations : cnrm.cloud.google.com/project-id : cc-test-prj cnrm.cloud.google.com/state-into-spec : absent name : cc-test-prj-demo-bucket namespace : config-control spec : storageClass : STANDARD location : asia-northeast1 uniformBucketLevelAccess : true --- 動作確認 nomos コマンド nomos コマンドは Config Sync のオプションツールで、コマンドラインで Config Sync のステータスを確認できます。 GitHub リポジトリの main ブランチにマニフェストファイルを格納 (Merge) した後、 nomos status コマンドを実行した際の出力は以下のとおりです。 ステータスが Current の場合、リソースの状態が目的の状態と一致することを意味します。 nomos status < 一部省略 > *gke_prj-cc-example_asia-northeast1_krmapihost-cc-example -------------------- < root > :prj-cc-example-root-sync git@github.com:sample-repo-owner/sample-repo-name.git/sample@main SYNCED @ 2023-08-12 23:26:39 + 0900 JST 71888b401ee6b76ec0a0b94c8c012123c8406ed1 Managed resources: NAMESPACE NAME STATUS SOURCEHASH config-control project.resourcemanager.cnrm.cloud.google.com/cc-test-prj Current 71888b4 config-control storagebucket.storage.cnrm.cloud.google.com/cc-test-prj-demo-bucket Current 71888b4 Cloud コンソール Config Sync のステータスは Cloud コンソールからも確認可能です。 Cloud コンソール > Anthos > Config の順に遷移するとダッシュボードが閲覧できます。 ダッシュボード nomos コマンド同様、プロジェクトと Storage バケットがマニフェストファイルで定義した状態と同じ状態にあることがわかります。 目的の状態であることを示す Current が表示 Reconciliation Loop リソースの状態を意図的に変更した場合の動作も確認してみます。 Cloud コンソールから Storage バケットを削除します。 Storage バケットを削除する 削除後の状態 しばらくして Cloud コンソールを確認すると、Storage バケットが再作成されています。 バケットが自動的に再作成されている マニフェストファイルに記載された内容で Storage バケットが存在している状態こそが 本来のあるべき姿 となるため、削除された状態は本来あるべき姿ではありません。 そのため、Reconciliation Loop が働き、結果として削除前と同じ状態で Storage バケットが復元されました。 武井 祐介 (記事一覧) 2022年4月入社 / クラウドソリューション部 / 技術2課所属 趣味はゴルフにロードバイク。IaC や CI/CD 周りのサービスやプロダクトが興味分野です。 Google Cloud 認定全冠達成!(2023年6月)
アバター
G-gen の藤岡です。当記事では、Google Cloud(旧称 GCP)で組織外のユーザーにプロジェクトレベルでオーナー(roles/owner)を付与する際の注意点について紹介します。 はじめに・前提知識 IAM と ID 管理 組織外のユーザー 組織外のユーザーにプロジェクトレベルでオーナーを付与する際の注意点 プロジェクトレベルのみ メール認証が必要 コンソールからのみ付与可能 はじめに・前提知識 IAM と ID 管理 Google Cloud ではリソースへのアクセス制御に Identity and Access Management(略称 IAM もしくは Cloud IAM)を使います。 IAM によって、誰が、どのリソースに対して、どういう条件で、何をできるか、という「認可」を管理します。 IAM について詳しく知りたい方は、以下の記事をご参照ください。 blog.g-gen.co.jp ここで、IAM の「誰が」にあたる部分が ID(アカウント)です。 Google Cloud の ID には、無償の Gmail アカウントや Google Workspace 、 Cloud Identity が使えます。 組織外のユーザー 当記事で説明する「組織外のユーザー」とは、Google Workspace や Cloud Identity ドメインに関連付けられていないユーザーを指します。 例えば、Google Cloud を企業ドメイン(例:@g-gen.co.jp)で利用している場合は、fujioka@g-gen.co.jp ユーザーは「組織 内 のユーザー」と見なされ、個人の Gmail アカウント(例:user@gmail.com)や異なるドメインのアカウント(例:user@example.com)は「組織 外 のユーザー」と見なされます。 組織外のユーザーにプロジェクトレベルでオーナーを付与する際の注意点 プロジェクトレベルのみ これ以降に記載する注意点が該当するのは プロジェクトレベルで組織外のユーザーにオーナーを付与する場合 のみです。 つまり、組織やフォルダレベルでオーナーを付与する場合は後述する制約はありません。 参考 Method: projects.setIamPolicy メール認証が必要 組織外のユーザーにプロジェクトレベルでオーナーを付与する場合、メール認証が必要です。 承諾待ち画面 以下の招待メールが届きます。(件名: Join my project on Google Cloud ) 招待メール この招待メールの送信アドレスは noreply-cloud@google.com ですが、差出人はオーナーを付与したユーザー名です。 招待メール詳細 このように、メール認証が必要なため、組織で Gmail の利用が許可されていない場合、オーナーを付与されたユーザー側で承諾ができません。 参考 Method: projects.setIamPolicy 組織内のユーザーの Gmail へのアクセスを管理する コンソールからのみ付与可能 組織外のユーザーにプロジェクトレベルでオーナーを付与できるのは、 コンソールからのみ です。gcloud CLI や Terraform から付与はできません。 以下のようなエラーとなります。 # プロジェクトレベルで付与 fujioka@cloudshell:~ (xxxx)$ gcloud projects add-iam-policy-binding <プロジェクト ID> --member='user:<組織外のユーザー>' --role='roles/owner' ERROR: Policy modification failed. For a binding with condition, run "gcloud alpha iam policies lint-condition" to identify issues in condition. ERROR: (gcloud.projects.add-iam-policy-binding) INVALID_ARGUMENT: Request contains an invalid argument. - '@type': type.googleapis.com/google.cloudresourcemanager.v1.ProjectIamPolicyError member: user:<組織外のユーザー> role: roles/owner type: ORG_MUST_INVITE_EXTERNAL_OWNERS fujioka@cloudshell:~ (xxxx)$ 前述の通り、組織やフォルダレベルでは付与が可能です。 # フォルダレベルで付与 fujioka@cloudshell:~ (xxxx)$ gcloud resource-manager folders add-iam-policy-binding <フォルダ ID> --member='user:<組織外のユーザー>' --role='roles/owner' ... bindings: - members: - user:<組織外のユーザー> role: roles/owner ... fujioka@cloudshell:~ (xxxx)$ # 組織レベルで付与 fujioka@cloudshell:~ (xxxx)$ gcloud organizations add-iam-policy-binding <組織 ID> --member='user:<組織外のユーザー>' --role='roles/owner' ... bindings: - members: - user:<組織外のユーザー> role: roles/owner ... fujioka@cloudshell:~ (xxxx)$ 参考 単一ロールの付与 藤岡 里美 (記事一覧) クラウドソリューション部 接客業からエンジニアへ。2022年9月 G-gen にジョイン。Google Cloud 認定資格は全冠。2023 夏アニメのオススメは、ダークギャザリング。箏を習っています :) Follow @fujioka57621469
アバター
G-gen の杉村です。生成 AI を使って Google Workspace における業務をサポートする Duet AI for Google Workspace をプレビューしてみましたので、その機能の一部をご紹介します。今回は Google Docs 編です。 はじめに Duet AI for Google Workspace とは 当記事の注意点 文章の自動生成 文章を短くする (Shorten) 文章をフォーマルにする (Formalize) 文章を長くする (Elaborate) 文章を言い換える (Rephrase) その他の記事 はじめに Duet AI for Google Workspace とは Duet AI for Google Workspace は、Google のコラボレーションソリューションである Google Workspace において 生成 AI (Generative AI) を使って各種業務をサポートする機能です。以下のようなことが実現できるようになります。 Google Docs (ドキュメント) : 文章の自動生成・フォーマル化・要約・精緻化・言い換え Gmail (メール) : E メールの自動生成・推敲 Google Slides (スライド) : 自然言語を与えると画像を自動生成 Google Sheets (スプレッドシート) : データの分析、ラベル割り当て、タスク計画の生成 Google Meet (Web 会議) : バーチャル背景の生成 2023年8月現在では日本語版は提供されておらず、英語版のみです。 当記事の注意点 当記事では Duet AI for Google Workspace の先行テスタープログラム中に利用した機能をご紹介しています。以下の点にご留意ください。 当記事で紹介する Duet AI for Google Workspace の機能は先行テスタープログラム当時のものであり、今後変更になる可能性があります G-gen 社は検証目的でのみ Duet AI for Google Workspace を用いており、顧客業務には利用していません(検証当時) 文章の自動生成 Google Docs (Google ドキュメント) で文章の自動生成を試してみます。 文書中の入力カーソルの左側に、鉛筆マークが表示されています。 青い鉛筆マーク これをクリックすると、以下のように文章生成を指示するプロンプト入力の画面が表示されます。 プロンプト入力画面 以下のようなプロンプトを与えてみました。 Write some sentences explaining the difference between behavior at restaurants in the U.S. and that in Asia. (米国とアジアでのレストランにおける振る舞いの違いを説明する文をいくつか書いてください) プロンプトを入力して Create ボタンを押すと、以下のように生成が始まります。 生成中の画面 数秒待機すると、以下のように文章が生成されました。 生成された文章 米国ではチップの概念があること、注文の習慣の違い、食べるときに使う道具や会話の違いなどが挙げられました。 Insert ボタンを押すと、Docs に文章が挿入されます。 文章を短くする (Shorten) Docs 上の文章をマウスで選択すると、左側に鉛筆マークが表示されます。マークを押すと、Formalize (フォーマルにする)・Shorten (短くする)・Elaborate (長くする)・Rephrase (言い換え) などの選択肢が表示されます。 4つの選択肢 Shorten (短くする) を選択してみました。以下のように、より要約された文章が提示されました。 短縮化された文章 文章をフォーマルにする (Formalize) 今度は Formalize (フォーマルにする) を試してみます。 Hi! Did you get my text yesterday? Are you coming to the party today? (やあ!メール見てくれた?今日のパーティーには来る?) Formalize 前の文章 Formalize を押すと、以下のように、かなり丁寧な文が生成されました。E メールでの文章を想定しているようです。 Formalize 後の文章 次に、ウェビナーの冒頭で発言するような言葉を Formalize してみました。原文には書いていないのに、生成された文章ではこれがプレゼンの場での発話であることがイメージされます。文章のシチュエーションに応じた推敲が行われていることが分かります。 Formalize 後の文章 (2) 文章を長くする (Elaborate) 次は文章を長くする (Elaborate) ことを試してみます。 出来の悪い読書感想文のような文章で試してみます。 This book was a story about a wizard boy with a scar on his forehead. The boy met friends at a magic school, grew up, and defeated a bad wizard. (その本は、額に傷のある魔法使いの男の子のお話です。男の子は魔法学校で友達に出会い、成長し、悪い魔法使いを倒しました。) Elaborate 前の文章 すると、某有名シリーズのキャラクターの固有名詞やストーリーのあらすじ、商業的な成功の経緯までが生成されてしまいました。ネット上の多くの情報から学習しているためか、有名作品を彷彿とさせるプロンプトではこのようになります。 Elaborate 後の文章 文章を言い換える (Rephrase) 文章を言い換える (Rephrase) は、試行した2023年8月3日現在では、またテストできませんでした。 Rephrase 前の文章 Rephrase 後の文章 その他の記事 Duet AI for Google Workspace のその他の機能を、以下の記事でもご紹介しています。 blog.g-gen.co.jp blog.g-gen.co.jp 杉村 勇馬 (記事一覧) 執行役員 CTO / クラウドソリューション部 部長 元警察官という経歴を持つ現 IT エンジニア。クラウド管理・運用やネットワークに知見。AWS 12資格、Google Cloud認定資格11資格。X (旧 Twitter) では Google Cloud や AWS のアップデート情報をつぶやいています。 Follow @y_sugi_it
アバター
G-gen の杉村です。BigQuery の可用性を高めるための クロスリージョン・データセットレプリケーション (Cross-region dataset replication) について解説します。 クロスリージョン・データセットレプリケーションとは 仕組み BigQuery の可用性 データのレプリケーション セカンダリ・レプリカの昇格 料金 制限 ロケーションの考慮事項 その他の制限 セカンダリ・レプリカへのクエリ 仕様 スロット 障害時の挙動 利用方法 レプリカの作成 クエリの実行 昇格 レプリカの削除 クロスリージョン・データセットレプリケーションとは クロスリージョン・データセットレプリケーション (Cross-region dataset replication) は、BigQuery のデータセットに読み取り専用のセカンダリ・レプリカを追加することで、別のリージョンにデータを非同期レプリケーションし、データの読み取り可用性を高める機能です。 クロスリージョン・データセットレプリケーションを用いると、指定した別リージョンに読み取り専用のセカンダリ・レプリカが作成され、データが非同期でレプリケーション (複製) されます。これにより元のリージョンの BigQuery サービスに障害があってもデータの可用性を確保することができます。 またこうして作られたセカンダリ・レプリカはプライマリへの昇格が可能なため、当機能をデータセットのリージョン間移行に用いることもできます。 参考 : Cross-region dataset replication なお当機能は2023年8月に Preview リリースされ、2024年12月に GA(一般公開)されました。 なお BigQuery 自体の詳細な解説については、以下の記事も参照してください。 blog.g-gen.co.jp 仕組み BigQuery の可用性 当機能を使わない通常状態のデータセットでも、単一リージョンの中で2つゾーンにデータが複製されています。そのためデータの堅牢性は高いものの、リージョン単位で BigQuery サービスがダウンした際には、一時的にデータを利用することができなくなります。これは US マルチリージョンなどのマルチリージョンを選択しても同様です。マルチリージョンを選択しても、データはそのマルチリージョン内のいずれかのリージョンのうちの2つのゾーンにのみ複製されます。マルチリージョンを選択するメリットは可用性ではなく、より大きい割り当て (Quota) が得られることにあります。 一方でクロスリージョン・データセットレプリケーションを用いると、メインのリージョンでサービスがダウンしても、複製先のセカンダリ・レプリカに対して読み取りジョブを実行することが可能です。 参考 : ロケーションとリージョン 参考 : 可用性と耐久性 データのレプリケーション イメージ図 当機能を有効化すると、指定したリージョンに セカンダリ・レプリカ が作成されます。セカンダリ・レプリカのあるリージョンを セカンダリ・リージョン と呼びます。セカンダリ・レプリカは 読み取り専用 です。 反対に、もともとのデータセットは プライマリ・レプリカ 、それが存在するリージョンを プライマリ・リージョン と呼びます。 プライマリからセカンダリへのデータの複製は非同期で行われます。すなわち、プライマリでデータの INSERT や UPDATE が完了しても、その変更がセカンダリに反映されるまでにはラグがあります。ラグはデータの量や通信状況、リージョン間の距離にも依存するため一概には言えませんが、数分のオーダーと考えられます。 セカンダリ・リージョンでは、プライマリと同じく、データは2つのゾーンに複製されて保存されます。 なお、セカンダリ・リージョンは複数作成することができます。つまり東京リージョンのデータセットを「ソウル」「大阪」「シンガポール」のように複数リージョンへレプリケーションできます。 参考 : Dataset replication セカンダリ・レプリカの昇格 セカンダリ・レプリカをプライマリ・レプリカに昇格させることができます。 ただし、昇格を実行できるのは プライマリ・レプリカが利用可能なときだけ です。すなわち、プライマリ・リージョン (レプリカ) が障害でダウンしているときは、昇格を行うことができません。 あくまでプライマリの障害時は、セカンダリに読み取りのみが可能です。すなわち 当機能で確保可能なのはデータの読み取り可用性のみ であることになります。 参考 : Promote the secondary replica 料金 データサイズに応じた BigQuery ストレージ料金が、セカンダリ・レプリカにも発生します。また当然、セカンダリ・レプリカに対して読み取りクエリを実行すれば、その分の BigQuery コンピュート料金が発生します。 また、リージョン間のデータ複製に伴い、GiB あたりのネットワーク転送料金が発生します。単価は複製元・先のリージョンが存在する大陸によって異なりますので、詳細は以下のドキュメントをご参照ください。 参考 : Data replication pricing 制限 ロケーションの考慮事項 ビュー、マテリアライズド・ビュー等はレプリケーションされてもテーブル定義が複製されるだけです。セカンダリ・リージョンにあるビュー等にクエリしても、ベーステーブルのロケーションが異なればクエリは失敗します。 また外部テーブルや BigLake テーブルなどは Cloud Storage などのデータソースとテーブルが同じリージョンに存在している必要があるという制限があり、リージョンが異なればクエリは失敗します。 その他に、US マルチリージョン と EU マルチリージョンには、特定のリージョンにレプリカを作成できない制限があります。 リソースごとに様々な制限がありますので、詳細は以下のドキュメントをご参照ください。 参考 : Location considerations 参考 : Resource behavior その他の制限 その他にもいくつかの制限があります。 Storage Write API で書き込まれるデータはコミットされてからレプリケーションが始まる。またラグが大きい場合がある ポリシータグ (列レベルのセキュリティや動的データマスキングで使われる) はレプリケーションされない セカンダリ・レプリカではポリシータグを参照する列へのクエリは失敗する。昇格しても使えるようにはならない 帯域はベストエフォートで 3 GB/秒 (プロジェクトあたり・大陸間ペアあたり) 上記は特に影響が大きそうなもののみピックアップしています。データセットの要件にあわせ、詳細は以下の公式ドキュメントを参照してください。 参考 : Limitations セカンダリ・レプリカへのクエリ 仕様 セカンダリ・レプリカは読み取り専用です。 後述の 利用方法 の章をご覧頂ければ分かるように、セカンダリ・レプリカを作成してもユーザからはデータセットは1個に見えます。セカンダリ・レプリカへのクエリを実行するには、クエリ実行時に実行ロケーションを明示的に指定します。 コンソールであればクエリエディタの歯車マークから、bq コマンドや SDK であればオプションで指定することができます。 スロット セカンダリ・リージョンでクエリを実行すると、セカンダリ・リージョンのスロットを消費します。当該リージョンでオンデマンドクエリを実行するか、プライマリとは別で Editions を購入しておく必要があります。 参考 : Compute capacity in the secondary region 障害時の挙動 プライマリ・リージョンが障害でダウンしても、セカンダリ・リージョンへの読み取りクエリを実行することができます。 セカンダリは読み取り専用のため、書き込みは実行できません。また前述の通りプライマリのダウン中はセカンダリを昇格させることもできません。 セカンダリ・リージョンが障害でダウンした場合、プライマリへの影響はありません。 参考 : Outage scenarios 利用方法 レプリカの作成 データセットに対してセカンダリ・レプリカを作成するには ALTER SCHEMA 文を実行します。 ALTER SCHEMA my_dataset ADD REPLICA `replica- in -seoul` OPTIONS(location= ' asia-northeast3 ' ); 上記は asia-northeast1 (東京) に存在するデータセット my_dataset に対して replica-in-seoul という名称のセカンダリ・レプリカを asia-northeast3 (ソウル) に作成する DDL です。 なおこのようにしてデータセットに対してセカンダリ・レプリカを作成しても、コンソール画面や bq コマンドではデータセットは1個に見えます。ロケーションもプライマリ・リージョンしか表示されません。 データセットは一つにしか見えない レプリカ作成後は、以下のクエリで INFORMATION_SCHEMA を参照することで、レプリカの存在を確認することができます。 SELECT * FROM `region-asia-northeast1`.INFORMATION_SCHEMA.SCHEMATA_REPLICAS; 以下のような結果が表示されます。 +----------------+-------------+------------------+-----------------+--------------------------+-------------------------------------+---------------------+-------------------+-----------+------------------+--------------------+ | catalog_name | schema_name | replica_name | location | replica_primary_assigned | replica_primary_assignment_complete | creation_time | creation_complete | auxiliary | replication_time | placement_location | +----------------+-------------+------------------+-----------------+--------------------------+-------------------------------------+---------------------+-------------------+-----------+------------------+--------------------+ | my-project-id | my_dataset | replica-in-seoul | asia-northeast3 | false | false | 2023-08-19 00:51:44 | true | false | NULL | NULL | | my-project-id | my_dataset | asia-northeast1 | asia-northeast1 | true | true | 2023-08-19 00:49:10 | true | false | NULL | asia-northeast1 | +----------------+-------------+------------------+-----------------+--------------------------+-------------------------------------+---------------------+-------------------+-----------+------------------+--------------------+ クエリの実行 セカンダリ・レプリカにクエリを実行する際は、ロケーションを明示的に指定します。プライマリに実行するときと SQL 文は全く同じですが、実行時にロケーションを明示的に指定します。 Web コンソールでは以下のような手順になります。 コンソールでの設定 (1) コンソールでの設定 (2) コンソールでの設定 (3) my_dataset は asia-northeast1 (東京) にあるので、通常ではエラーになるはずですが、上記のケースではレプリカを asia-northeast3 (ソウル) に作成済みでしたので、クエリを実行することができました。 bq コマンドでは以下のように、オプションでロケーションを指定するだけです。 bq query --nouse_legacy_sql --location = asia-northeast3 \ ' select * from `my-project-id.my_dataset.my_table`; ' 昇格 セカンダリ・レプリカをプライマリへ昇格させるには、以下の DDL を実行します。なお DDL 実行時は、ジョブ実行ロケーションをセカンダリ・リージョン側に設定する必要があります。 ALTER SCHEMA my_dataset SET OPTIONS(primary_replica = ' replica-in-seoul ' ) なお昇格後は以下のようになります。 コンソールや bq コマンドでデータセットを describe したときのロケーション表記は、昇格後のリージョンになる もともとプライマリだったリージョンはセカンダリになり、読み取り専用になる レプリカの削除 セカンダリ・レプリカを削除させるには、以下の DDL を実行します。なお DDL 実行時は、ジョブ実行ロケーションをプライマリ・リージョン側に設定する必要があります。 ALTER SCHEMA my_dataset DROP REPLICA IF EXISTS `replica- in -seoul`; 杉村 勇馬 (記事一覧) 執行役員 CTO / クラウドソリューション部 部長 元警察官という経歴を持つ現 IT エンジニア。クラウド管理・運用やネットワークに知見。AWS 12資格、Google Cloud認定資格11資格。X (旧 Twitter) では Google Cloud や AWS のアップデート情報をつぶやいています。 Follow @y_sugi_it
アバター
G-gen の堂原です。本記事では Config Connector を Config Controller で構築した際に、複数の Kubernetes Namespace を使ってリソース管理を行う方法を紹介します。 本記事について 前提知識 Namespace モード Config Controller のデフォルト状態 実施内容 概要 1. Config Controller インスタンス作成 2. サービスアカウント作成 3. サービスアカウントにロール付与 4. Workload Identity 設定 5. Namespace 及び ConfigConnectorContext オブジェクト作成 6. プロジェクト作成 本記事について Config Connector は、 Kubernetes を使用して Google Cloud のリソースを管理できる Kubernetes のアドオンです。 Google Cloud においては、 Config Controller という Config Connector のマネージドサービスが提供されています。 Config Connector や Config Controller の基本的な解説や使い方は以下の記事で紹介しています。 blog.g-gen.co.jp ※ 本記事では、上記記事で紹介されているような、以下の内容を理解されている前提で話を進めます。 Config Controller を用いて、 Config Connector 用の Google Kubernetes Engine (GKE) クラスタを構築する方法 Config Controller インスタンスが存在する Google Cloud プロジェクト (以後、「プロジェクト」と記載) 上に、Config Connector を用いてリソースを作成する方法 前提知識 Namespace モード Config Connector には Cluster モードと Namespace モードという 2 つのモードが存在します。 Cluster モード : 単一のサービスアカウント で全てのリソースを管理する設定 Namespace モード : 複数のサービスアカウント でリソースを管理する設定で、Kubernetes Namespace (以後、「Namespace」と記載) 毎にサービスアカウントを割り当てる Namespace モードは、複数プロジェクトを跨る大量のリソースを管理するのに向いていると言えます。 なお、Config Connector を GKE クラスタにインストールする場合は、いずれのモードも Workload Identity での実装が基本となります。 Config Controller も GKE クラスタを用いているため、Workload Identity で実装されています。 GKE における Workload Identity については、以下の記事で紹介しています。 blog.g-gen.co.jp Config Controller のデフォルト状態 Config Controller で構築された Config Connector においては、Namespace モードがデフォルトとなっています。 参考 : Config Connector の Namespace また、デフォルトで使用可能な Namespace は config-control です。 config-control 以外の Namespace に Config Connector リソースを作成した場合は、該当の Config Connector リソースのステータスが Unamanged となり、Google Cloud 上に対応するリソースがデプロイされることはありません。 実施内容 概要 下図のような環境にて、フォルダ apple と orange の直下に 2 つずつプロジェクトを作成します。 ※ 実施後に各リソースは削除済みのため ID をマスクせずに掲示しています 実施前のフォルダ構成 実施内容は以下の通りです。 プロジェクト test-cc に Config Controller インスタンスを作成 専用のサービスアカウント test-cc-apple と test-cc-orange を作成 各サービスアカウントに以下のロールを付与 test-cc-apple : フォルダ apple に対する「プロジェクト作成者」ロール test-cc-orange : フォルダ orange に対する「プロジェクト作成者」ロール Workload Identity を設定 Kubernetes クラスタ内に Namespace apple と orange 及び ConfigConnectorContext オブジェクト 作成 ConfigConnectorContext オブジェクト : Config Connector が該当の Namespace を監視するために必要なオブジェクトで、各 Namespace 毎に作成する必要があります。 各プロジェクト作成 完成形を図示すると下図となります。 完成形のイメージ 1. Config Controller インスタンス作成 以下のコマンドを実行して、Config Controller インスタンスを作成します。 Config Controller インスタンスは GKE Standard クラスタと Autopilot クラスタの両方で作成可能ですが、ここでは Autopilot クラスタで作成しています。 gcloud anthos config controller create test-cc --location=asia-northeast1 --full-management コマンド完了後、Kubernetes クラスタの認証情報を取得します。 gcloud anthos config controller get-credentials test-cc --location=asia-northeast1 Config Controller インスタンス作成直後だと、ConfigConnectorContext オブジェクトは Namespace config-control にのみ存在します $ kubectl get ConfigConnectorContext --all-namespaces NAMESPACE NAME AGE HEALTHY config-control configconnectorcontext.core.cnrm.cloud.google.com 9m11s true 2. サービスアカウント作成 一旦 kubectl から離れ、サービスアカウント周りの設定を行っていきます。 まずは今回使用するサービスアカウント test-cc-apple 及び test-cc-orange を作成します。 gcloud iam service-accounts create test-cc-apple --project test-cc-395016 gcloud iam service-accounts create test-cc-orange --project test-cc-395016 3. サービスアカウントにロール付与 作成したサービスアカウントに対してプロジェクト作成に必要なロールを付与します。 個々の Namespace にサービスアカウントを紐付けることが出来るゆえに、必要以上のスコープでロールを付与する必要がなくなります。 gcloud resource-manager folders add-iam-policy-binding 391788755828 \ --member="serviceAccount:test-cc-apple@test-cc-395016.iam.gserviceaccount.com" \ --role="roles/resourcemanager.projectCreator" gcloud resource-manager folders add-iam-policy-binding 1027926769724 \ --member="serviceAccount:test-cc-orange@test-cc-395016.iam.gserviceaccount.com" \ --role="roles/resourcemanager.projectCreator" 4. Workload Identity 設定 Google Cloud のサービスアカウント test-cc-apple と test-cc-orange を、Kubernetes の Service Account に紐づけます。 gcloud iam service-accounts add-iam-policy-binding \ test-cc-apple@test-cc-395016.iam.gserviceaccount.com \ --member="serviceAccount:test-cc-395016.svc.id.goog[cnrm-system/cnrm-controller-manager-apple]" \ --role="roles/iam.workloadIdentityUser" \ --project test-cc-395016 gcloud iam service-accounts add-iam-policy-binding \ test-cc-orange@test-cc-395016.iam.gserviceaccount.com \ --member="serviceAccount:test-cc-395016.svc.id.goog[cnrm-system/cnrm-controller-manager-orange]" \ --role="roles/iam.workloadIdentityUser" \ --project test-cc-395016 この時点では Kubernetes クラスタ上に該当の Service Account は存在せず、ConfigConnectorContext オブジェクト作成時に作成されます。 以上、Google Cloud 側の作業は完了です。 5. Namespace 及び ConfigConnectorContext オブジェクト作成 以下のマニュフェストファイルを適応させることで、Namespace 及び ConfigConnectorContext オブジェクトを作成します。 apiVersion : v1 kind : Namespace metadata : name : apple --- apiVersion : core.cnrm.cloud.google.com/v1beta1 kind : ConfigConnectorContext metadata : name : configconnectorcontext.core.cnrm.cloud.google.com namespace : apple spec : googleServiceAccount : test-cc-apple@test-cc-395016.iam.gserviceaccount.com --- apiVersion : v1 kind : Namespace metadata : name : orange --- apiVersion : core.cnrm.cloud.google.com/v1beta1 kind : ConfigConnectorContext metadata : name : configconnectorcontext.core.cnrm.cloud.google.com namespace : orange spec : googleServiceAccount : test-cc-orange@test-cc-395016.iam.gserviceaccount.com 以下、ConfigConnectorContext オブジェクトのマニフェストファイルのポイントです。 metadata:name の値は configconnectorcontext.core.cnrm.cloud.google.com のみ認められており、これ以外の値だとエラーとなります。 error during reconciliation: the only allowed name for ConfigConnectorContext object is 'configconnectorcontext.core.cnrm.cloud.google.com'. The name restriction is required to ensure that there is only one ConfigConnectorContext instance in your namespace metadata:namespace で、ConfigConnectorContext オブジェクトを作成したい Namespace を指定します。 spec:googleServiceAccount で、Workload Identity で紐づけたサービスアカウントを指定します。 このタイミングで、手順 4. で指定していた Kubernetes Service Account が作成されます。 $ kubectl get ServiceAccount -n cnrm-system NAME SECRETS AGE cnrm-controller-manager-apple 0 26m cnrm-controller-manager-config-control 0 41m cnrm-controller-manager-orange 0 26m cnrm-deletiondefender 0 40m cnrm-resource-stats-recorder 0 40m cnrm-unmanaged-detector 0 40m cnrm-webhook-manager 0 40m default 0 41m 6. プロジェクト作成 最後に metadata:namespace を apple または orange とした上で、 プロジェクト用のマニフェストファイル を適応させます。 以下はフォルダ apple 配下に作成するプロジェクト用のマニフェストファイルです。 apiVersion: resourcemanager.cnrm.cloud.google.com/v1beta1 kind: Project metadata: annotations: cnrm.cloud.google.com/auto-create-network: "false" name: prod-apple namespace: apple spec: name: Prod Apple resourceID: prod-apple-dfe folderRef: external: "391788755828" --- apiVersion: resourcemanager.cnrm.cloud.google.com/v1beta1 kind: Project metadata: annotations: cnrm.cloud.google.com/auto-create-network: "false" name: dev-apple namespace: apple spec: name: Dev Apple resourceID: dev-apple-dfe folderRef: external: "391788755828" 同様に orange も適応させることで、以下のようにプロジェクトを作成することが出来ます。 実施後のフォルダ構成 堂原 竜希 (記事一覧) クラウドソリューション部。2023年4月より、G-genにジョイン。 Google Cloud Partner Top Engineer 2023に選出。休みの日はだいたいゲームをしているか、時々自転車で遠出をしています。 Follow @matayuuuu
アバター
G-gen の杉村です。Google Cloud のマネージドなファイルサーバである Filestore を徹底解説します。 はじめに Filestore とは ユースケース 料金 ディスク容量 バックアップ ネットワーク コンポーネント イメージ図 インスタンス サービスティア サービスティア概要 基本 HDD 基本 SSD ゾーン リージョン 共有 ネットワーク インスタンスとネットワーク ネットワークレベルでのアクセス制御 概要 ファイアウォール (Cloud Firewall) IP ベースのアクセス制御 接続 Compute Engine/オンプレミスマシンから Google Kubernetes Engine (GKE) から Cloud Run から バックアップとリストア バックアップ バックアップの自動取得 スナップショット 運用と監視 モニタリング 割り当てと上限 (Quotas and Limits) データ移行 ファイルのアクセス権限管理 暗号化 通信の暗号化 保存時の暗号化 監査ログ はじめに Filestore とは Filestore は、Google Cloud のマネージドなファイルサーバのサービスです。 Filestore は インスタンス という単位で管理されます。事前に必要なサイズのストレージをプロビジョン (割り当て) したうえで、Compute Engine VM、Google Kubernetes Engine (GKE) クラスタ、オンプレミスマシン等から接続して利用できます。ストレージは、ダウンタイム無しで容量の拡張が可能です。 ファイルシステム・接続プロトコルは NFSv3 です。NFS (Network File System) は主に UNIX/Linux 系システムで利用される仕組みですが、Windows 7/Windows Server 2008 以降には NFSv2/v3 のクライアントが利用可能です。 参考 : Filestore の概要 ユースケース Filestore は以下のようなユースケースで利用されます。 ファイルサーバ用途 Compute Engine や GKE のアプリケーションのファイル入出力先として利用 (複数サービスからのアクセス) Google Cloud VMware Engine の外部ストレージとして利用 Compute Engine や GKE から利用されるストレージには他に、ブロックストレージである 永続ディスク やオブジェクトストレージである Cloud Storage が存在します。Filestore は「ファイルシステムとしてマウントできる」「複数マシンからの読み書きが想定されている」かつ「ディスクをホストするマシン自体の管理が不要」という特徴があります。 参考 : ストレージ オプションを確認する 料金 ディスク容量 Filestore の料金は、割り当てたディスク容量 (GiB 単位) × インスタンスが存在した時間 (1秒単位) で発生します。ディスクが割り当てられていると、実際に使用されていなくても課金されます。 単価は、サービスティア (ディスクの種類。後述) によって異なります。 2024年5月現在の東京リージョンの単価では基本 HDD ティアで $0.00026 / GiB / hour です。作成可能な最小サイズである 1024 GiB のインスタンスを30日間使用すると、0.00026 × 1024 × 24 × 30 で 約 $191.6928 / 月となります。 参考 : Filestore の料金 バックアップ Filestore のバックアップ機能を使うと、GiB あたりの保管料が発生します。 2024年5月現在の東京リージョンの単価では $0.1/GiB/月です。 ネットワーク Filestore への Ingress (上り) のトラフィックには課金されません。また、同一ゾーン内の Filestore・VM 間のトラフィックにも課金されません。 Filestore とクライアントが別々のゾーンに存在したり、インターネットや IPSec VPN 経由で接続された場合、Egress (下り)トラフィックに対して課金が発生します。これは Filestore というより、Google Cloud のネットワークの課金の仕組みです。単価や計算方法は、以下のドキュメントをご参照ください。 参考 : ネットワーキングのすべての料金体系 2024年5月現在の単価例をいくつか記載します。 同一リージョン内の別ゾーンの VM への下り : $0.01/GiB Google Cloud 外 (インターネット経由または IPSec 経由) への下り : $0.12/GiB (東京・プレミアムティア) コンポーネント イメージ図 イメージ図 (コンポーネントとネットワーク) インスタンス インスタンス は、ファイルシステムをホストする仮想サーバです。 インスタンスごとに サービスティア を選択します。 サービスティア サービスティア概要 インスタンス作成時には サービスティア を選択します。サービスティアによって容量の最小・最大値やパフォーマンス、冗長性などに違いがあります。 ティア名 ディスク容量 スケール単位 パフォーマンス 冗長性 基本 HDD 1〜63.9 TiB 1 GiB単位で拡張。縮小は不可 固定(標準) ゾーン 基本 SSD 2.5〜63.9 TiB 1 GiB単位で拡張。縮小は不可 固定(プレミアム) ゾーン ゾーン・低容量 1〜9.75 TiB 256 GiB単位で拡張・縮小 容量に応じて変化 ゾーン ゾーン・高容量 10〜100 TiB 2.5 TiB単位で拡張・縮小 容量に応じて変化 ゾーン リージョン・低容量 1〜9.75 TiB 256 GiB単位で拡張・縮小 容量に応じて変化 リージョン リージョン・高容量 10〜100 TiB 2.5 TiB単位で拡張・縮小 容量に応じて変化 リージョン GiB あたりの料金単価は、上記の表で下に行くほど高くなります ( 料金表 参照)。 参考 : サービスティア 基本 HDD 基本 HDD はハードディスクドライブを使った Filestore インスタンスです。汎用的な用途に使えますが、全てのティアの中で最も安価・低性能です。 最小 1 TiB からインスタンスを作成できますが、10 TiB を超えるとスループット/IOPS が高くなります。最大サイズは 63.9 TiB です。 基本ティアの制約として 172.17.0.0/16 の範囲内にいるクライアントからは使用することができません。Filestore の内部コンポーネントでこの IP アドレスレンジが使用されていることに起因します。同じ理由から、基本ティアの Filestore インスタンスをこの IP レンジ範囲に作成することはできません ( 参考 )。 同時接続クライアント数は 500 が推奨されています。 項目名 値 読み取りスループット ・容量が 1~10 TiB: 100 MiB/秒 ・容量が 10 TiB〜: 180 MiB/秒 書き込みスループット ・容量が 1~10 TiB: 100 MiB/秒 ・容量が 10 TiB〜: 120 MiB/秒 読み取り IOPS ・容量が 1~10 TiB: 600 ・容量が 10 TiB〜: 1,000 書き込み IOPS ・容量が 1~10 TiB: 1,000 ・容量が 10 TiB〜: 5,000 参考 : 基本 HDD ティアと基本 SSD ティア 基本 SSD 基本 SSD は SSD を使った Filestore インスタンスです。基本 HDD と同じく汎用的な用途が想定されていますが、HDD よりも高性能です。 最小サイズは 2.5 TiB、最大サイズは 63.9 TiB で、容量に関わらず性能は固定です。 172.17.0.0/16 の IP アドレスレンジに関する制約は、基本 HDD ティアの説明に書いたものと同様です。 同時接続クライアント数は 500 が推奨されています。 項目名 値 読み取りスループット 1,200 MiB/秒 書き込みスループット 350 MiB/秒 読み取り IOPS 60,000 書き込み IOPS 25,000 参考 : 基本 HDD ティアと基本 SSD ティア ゾーン ゾーン サービスティア(Zonal service tiers)は SSD を使った Filestore インスタンスです。低容量版と大容量版があり、それぞれディスク容量の最小・最大値のレンジや、性能のレンジが異なります。 低容量版は最小サイズ 1 TiB、最大サイズ 9.75 TiB で、大容量版は最小サイズ 10 TiB、最大サイズ 100 TiB です。いずれも割り当てられたサイズに応じて最大性能 (スループット、IOPS、クライアント接続数) が向上していきます。 ゾーンサービスティアでは、ディスクサイズの拡張が可能なことに加え、基本ティアでは不可能なディスク縮小が可能です。ただし低容量版か大容量版かを一度選択すると変更することはできませんので、事前にある程度のサイジングは必要です。 参考 : ゾーン リージョン リージョン サービスティア(Regional service tiers)も同様に SSD を使った Filestore インスタンスです。エンタープライズ向けの NAS として最適化されています。 ここまで紹介してきた他のサービスティアと異なり、リージョンレベルでの高可用性を確保するため、データがゾーン間でレプリケーションされます。そのため片方のゾーンで障害が発生したとしても、もう片方のゾーンで業務の継続が可能です。これを リージョンレベルの可用性 と呼びます。 ゾーンサービスティアと同様、ディスクサイズが 1〜9.75 TiB の低容量版と、10〜100 TiB の大容量版があり、性能のレンジが異なります。 こちらもディスクサイズの拡張だけでなく、基本ティアでは不可能なディスクサイズの縮小が可能です。低容量版か大容量版かを一度選択すると変更することはできないという点も、ゾーンサービスティアと共通です。 詳細な性能は、以下の公式ドキュメントをご参照ください。 参考 : リージョン 共有 共有 (share) は Filestore インスタンス内に作成するストレージ領域で、マウント用のアクセスポイントを持ちます。 基本的にはインスタンス内に、共有を1個だけ作成できます。例外は「 GKE 向け Filestore マルチシェア 」機能を利用するときだけです(詳細は省略します)。 共有名はインスタンス作成時に指定します。例として my_share_01 という共有でインスタンスの IP アドレスが 10.100.0.11 の場合 Linux でのマウント時のコマンドは以下のようになります。 sudo mount -o rw,intr 10 . 100 . 0 .11:/my_share_01 /mnt/myshare ネットワーク インスタンスとネットワーク Filestore インスタンスは、前掲の構成図の通り、ユーザの VPC とは別の、Google が管理する VPC ネットワーク上に作成されます。 インスタンス作成時に、Filestore の専有する IP アドレス範囲を指定します。このとき、ユーザーのネットワークと IP アドレス範囲が重複してはいけません。「基本 HDD」「基本 SSD」ティアでは /29 が、「ゾーン」「リージョン」では /26 のアドレス範囲が必要です。 また 172.17.0.0/16 は「基本 HDD」「基本 SSD」ティアにおいて内部コンポーネント用に予約されていますので、 利用することができない ことに注意してください。この範囲にいるクライアントは基本ティアの Filestore に接続できません。 Filestore インスタンス用ネットワークとユーザの VPC ネットワークを接続する方法は二通りあります。 VPC ネットワークピアリング と プライベートサービスアクセス です。 どちらも Filestore 用ネットワークとユーザーのネットワークを接続するための方法ですが、以下のような違いがあります。 プライベートサービスアクセスではサービスプロジェクト側 (子の側) から 共有 VPC ネットワーク にインスタンスを作成可能 プライベートサービスアクセスでは他の Google Cloud のマネージドサービス (Cloud SQL や Memorystore など) と一括で IP アドレス範囲を管理可能 詳細は以下のドキュメントをご参照ください。 参考 : ネットワーク構成と IP リソースの要件 ネットワークレベルでのアクセス制御 概要 Filestore におけるネットワークレベルでのアクセス制御には、ファイアウォール (Cloud Firewall) と IP ベースのアクセス制御の2つがあります。これらは別々に管理され、両方の制御が AND で適用されます。 ファイアウォール (Cloud Firewall) ここで言及するファイアウォールとは、VPC ファイアウォールまたはファイアウォールポリシーのことであり、Google Cloud 上のファイアウォールサービス (Cloud Firewall) のことです。しかしながら Cloud VPN (IPSec) や Cloud Interconnect (専用線) 経由で Filestore を利用する場合は同様の通信要件がオンプレミス側のファイアウォール機器等で必要です。 NFS プロトコルにおいては、ユーザのネットワークから Filestore への方向の Egress (下り) ルールで TCP ポート 111、2046、2049、2050、4045 が許可されている必要があります。VPC ではデフォルトで全ての Egress が許可される暗黙ルールが存在しますが、これをブロックするルールがある場合、これらのポートを許可する必要があります。 また NFS でのファイルロックが適切に動作するには、Filestore インスタンスからクライアントネットワークへの方向の通信で特定ポートを許可するため、ユーザのファイアウォールに Ingress (上り) ルールを設定する必要があります。NFS ファイルロックは TCP ポート 111 番の許可が必要なほか、クライアントが Linux の場合は statd と nlockmgr デーモンが使うポートを許可する必要があります (Windows の場合は必要ありません)。 参考 : ファイアウォール ルールの構成 IP ベースのアクセス制御 Filestore では IP ベースのアクセス制御 が利用できます。この機能ではクライアントの IP アドレスに応じて、異なったアクセス権を与えることができます。 アクセスレベル 説明 管理者 root として全ファイルの閲覧・変更が可能 管理閲覧者 root として全ファイルの閲覧が可能 編集者 自分の uid/gid に基づいて表示と変更が可能 閲覧者 自分の uid/gid に基づいて表示のみ可能 また Filestore では RFC 1918 の IP アドレス (すなわち 10.0.0.0/8、172.16.0.0/12、192.168.0.0/16) 以外のクライアントからも利用できますが、その場合は明示的にアクセス権を付与する必要があります。 接続 Compute Engine/オンプレミスマシンから Filestore の共有をマウントする方法は、Compute Engine VM からでも、オンプレミスマシン (サーバ/PC) からでも同じです。 Filestore インターネットは Private IP しか持てないこと、また NFSv3 の通信は暗号化されないことから、オンプレミスから Filestore に接続する場合は、Google Cloud との間で Cloud VPN (IPSec) や Cloud Interconnect (専用線) でプライベート接続が確立されている必要があります。 通常の NFS マウントと同様に、Linux では mount コマンドを利用したり /etc/fstab に設定を記載することでマウントできます。 Windows では、NFS クライアントをインストールしたあと、 mount コマンドでマウントします。 詳細な手順は以下のドキュメントを参照してください。 参考 : Compute Engine クライアントでのファイル共有のマウント また、当社の以下の記事では、Compute Engine VM (Windows Server) から Filestore 共有をマウントする手順を詳細に解説しています。 blog.g-gen.co.jp Google Kubernetes Engine (GKE) から Google Kubernetes Engine (GKE) クラスタからは、GKE クラスタでFilestore CSI ドライバを有効化することで Filestore を利用できます。 詳細は、以下をご参照ください。 参考 : Filestore CSI ドライバを使用して Filestore インスタンスにアクセスする Cloud Run から サーバーレスのコンテナプラットフォームサービスである Cloud Run からも Filestore を利用することが可能です。 Filestore が利用可能なのは Cloud Run サービスの第2世代です。コンテナからサーバーレス VPC アクセスコネクタを通じて、VPC 上の Filestore インスタンスへ接続します。 詳細は以下のガイドをご参照ください。 参考 : チュートリアル: Cloud Run での Filestore の使用 バックアップとリストア バックアップ Filestore では標準で バックアップ 機能が利用可能です。全てのファイルのデータとメタデータが保存されます。 バックアップは作成時に保存するリージョンを指定します。データ冗長化のため元のインスタンスと違うリージョンを指定することもできますが、ネットワーク Egress 料金が発生することには注意が必要です。 バックアップは増分で取得され、自動的に圧縮されます。保存費用は圧縮後のサイズに対して発生します。 なおバックアップ取得中は、パフォーマンスが低下する可能性があります。ただし「基本 HDD」「基本 SSD」ティアではパフォーマンス影響がないとされています。 バックアップからは、既存の共有にファイルをリストアすることも、新しいインスタンスとしてリストアすることもできます。 参考 : バックアップの概要 バックアップの自動取得 バックアップを自動で取得する機能は Filestore 自体には備わっておらず、Cloud Scheduler と Cloud Functions を使用して、定期的に API をコールすることで実現します。詳細は以下をご参照ください。 参考 : バックアップのスケジュール設定 スナップショット スナップショット は、取得時点でのデータを保存しておく機能です。バックアップとは異なり、Filestore インスタンス内に保存される子リソースです。また「ゾーン」「リージョン」ティアのみで利用可能で、「基本 HDD」「基本 SSD」ティアでは利用できません。 ただし、スナップショットを取得した直後はストレージ容量は消費されません。共有内のファイルに変更があって初めて、過去データが複製されて保存され、容量を消費します。複数スナップショットを取得した場合、変更差分のみが保持されます。 リストアは .snapshot ディレクトリにアクセスすることで行います。スナップショットを作成すると全てのディレクトリに非表示の .snapshot ディレクトリが作成され、その中に過去の状態のファイルが保持されています。 また2023年11月現在 Preview ですがインスタンスをまるごとあるスナップショットの時点の状態に巻き戻すことも可能です。 詳細は以下のドキュメントをご参照ください。 参考 : スナップショットの概要 運用と監視 モニタリング Filestore の各種パフォーマンスメトリクスは Cloud Monitoring に自動的に記録されます。 Read/Write バイト数、I/O オペレーション数、レイテンシ、ディスク空き容量などが記録され、過去6週間分が保存されます。またメトリクスに対して、しきい値を超過/下回ったときにアラートを発報させることが可能です。 参考 : インスタンスと割り当てのモニタリング 割り当てと上限 (Quotas and Limits) 他の Google Cloud サービスと同じく、Filestore にも 割り当て (Quotas) と 上限 (Limits) があります。割り当ては緩和申請を行うことで緩和できる可能性があり、上限は原則的に変更できません。 例としてプロジェクト・リージョン内で利用可能な Filestore の共有の TiB 数は割り当て (Quota) で制限されています。大規模に利用する中で、割り当てが足りなくなった場合は、急遽サイズ拡張が必要になったときに対応できるよう、予め割り当て緩和申請を行うなどの運用が必要です。 参考 : 割り当てと上限 データ移行 公式ガイド で、いくつかのデータ移行のパターンが紹介されています。 オンプレミスから Filestore へのデータコピー (gcloud compute scp コマンド利用) Cloud Storage から Filestore へのデータコピー (gsutil rsync コマンド利用) Filestore から Cloud Storage へのデータコピー (gsutil rsync コマンド利用) データが大規模な場合は、以下の方法が紹介されています。 他クラウドから Cloud Storage へ : Storage Transfer Service オンプレミスから Cloud Storage へ : Transfer service for On-Premises Data これらのツールを使って一度データを大規模に Cloud Storage へ送信してから、それを Filestore 共有に gsutil rsync することができます。ただしこの場合、ファイル権限などのメタデータは失われます。 ファイルのアクセス権限管理 アクセス権限管理は通常の NFS の仕様と同様ですので、当記事では詳細は割愛します。 特に Windows に馴染み深い方への注意点を記載すると、NFS では Windows で一般的に使われる NTFS/CIFS (SMB) における権限管理とは異なる体系・インターフェイスとなります。 以下のスクリーンショットは、Windows マシンから Filestore 共有上のファイルを右クリックして「プロパティ」を表示させた際のものです。 NFS アクセス権限管理 暗号化 通信の暗号化 NFSv3 では通信の暗号化はされません。Google Cloud ネットワークの内部通信はどんなプロトコルでも透過的に暗号化されていますが、オンプレミスから Google Cloud までの間は暗号化されていないため、IPSec VPN 等で経路が暗号化されることが前提です。 保存時の暗号化 Google Cloud では全てのサービスで、保存されるデータは暗号化されています ( デフォルトの保存データの暗号化 )。このとき、暗号鍵は Google が管理しています。 厳密なセキュリティ要件を満たすため、自社管理の鍵でデータを暗号化したい場合、 Cloud KMS で管理される 顧客管理の鍵 ( CMEK = customer-managed encryption keys) で Filestore のストレージを暗号化することが可能です。 詳細は以下をご参照ください。 参考 : 顧客管理の暗号鍵でデータを暗号化する 監査ログ Filestore は Cloud Audit Logs と連携されています。 Filestore インスタンスに対する「インスタンス作成、削除」「スナップショット/バックアップ取得」などの管理オペレーションがデフォルトで記録されます。 一方でファイルの読み取り・書き込みなどファイルシステム上の監査については、Filestore としては提供していません。これらは、別の仕組みで取得する必要があります。 杉村 勇馬 (記事一覧) 執行役員 CTO / クラウドソリューション部 部長 元警察官という経歴を持つ現 IT エンジニア。クラウド管理・運用やネットワークに知見。AWS 12資格、Google Cloud認定資格11資格。X (旧 Twitter) では Google Cloud や AWS のアップデート情報をつぶやいています。 Follow @y_sugi_it
アバター
G-gen の佐々木です。当記事では、Google Cloud (旧称 GCP) のサーバーレスコンテナサービスである Cloud Run の Direct VPC Egress 機能について解説します。 前提知識 Cloud Run とは サーバーレス VPC アクセスコネクタとは 概要 Direct VPC Egress とは 使用方法 サーバーレス VPC アクセスコネクタと Direct VPC Egress の比較 コスト パフォーマンス 構成図 比較表 ユースケース 制限事項 Cloud Run services、Cloud Run jobs 共通の制限事項 サブネットに十分な IP アドレスが必要 Direct VPC Egress を使用できるコンテナインスタンス数の上限 メンテナンスイベント時の接続断 Cloud Run の実行環境(世代) Cloud Run jobs における制限事項 Direct VPC Egress で利用できないもの ロギング・モニタリングに関して VPC ファイアウォールルールの使用に関して 前提知識 Cloud Run とは Cloud Run は Google Cloud におけるサーバーレス コンテナコンピューティング サービスであり、HTTP リクエストをトリガーに実行される Cloud Run services と、任意のタイミングでジョブを実行できる Cloud Run jobs の 2種類があります。以下の記事をご参照ください。 blog.g-gen.co.jp blog.g-gen.co.jp サーバーレス VPC アクセスコネクタとは 従来の Cloud Run では、VPC 内のリソースやパブリック IP を使用しない Cloud SQL、Memorystore などのリソースに対して接続する場合、 サーバーレス VPC アクセスコネクタ のインスタンスを介して VPC に接続する必要がありました。 サーバーレス VPC アクセスコネクタは VPC にコネクタインスタンス用のサブネットを確保し、そのサブネットの IP アドレス範囲( /28 CIDR範囲)を使用して 2~10 インスタンスまでスケーリングすることができます。 参考: サーバーレス VPC アクセス 概要 Direct VPC Egress とは Cloud Run で Direct VPC Egress を有効にすると、 サーバーレス VPC アクセスコネクタを使用せず に VPC ネットワークにトラフィックを送信できるようになります。Direct VPC Egress は Cloud Run service、Cloud Run jobs の両方で使用することができます。 なお、Direct VPC Egress は名前の通り VPC に対する下り(外向き)の通信で機能するものであり、VPC から Cloud Run への上り(内向き)の通信には影響はありません。 参考: 公式ブログ 参考: 公式ドキュメント 使用方法 Direct VPC Egress を使用するには、Cloud Run サービス/ジョブの作成時に Cloud Run が接続する VPC、サブネットを指定します。 Direct VPC Egress では、以下の IPv4 範囲がサポートされています(IPv6 はサポートされていません)。 RFC 1918 ( 推奨 。 10.0.0.0/8 、 172.16.0.0/12 、 192.168.0.0/16 ) RFC 6598 ( 100.64.0.0/10 ) クラスE( 240.0.0.0/4 ) Cloud Run から送信されるトラフィックは、「 プライベート IP へのリクエストだけを VPC にルーティングする 」か「 すべてのトラフィックを VPC にルーティングする 」かのどちらかを選択できます。 また、Direct VPC Egress を使用する Cloud Run には、リビジョン単位で ネットワークタグ を設定することができます。 ネットワークタグを使用することで、Cloud Run からの下り(外向き)のトラフィックに対して適用されるファイアウォールルールを設定することができます。 サーバーレス VPC アクセスコネクタと Direct VPC Egress の比較 コスト サーバーレス VPC アクセスコネクタは VM インスタンスとして作成され、常に起動したままの状態となるため、従量課金のネットワークコストに加えて、マシンタイプに応じた 起動時間あたりの料金が常に発生 してしまいます。 また、サーバーレス VPC アクセスコネクタは最大インスタンス数の設定により負荷増大時に自動でスケールアウトすることが可能ですが、一度スケールアウトすると インスタンス数をスケールインすることができない という制限事項があります。 これらの制限は、アクセスがないときはゼロスケールすることができる Cloud Run の特性とは相性が悪いと言えるでしょう。 Direct VPC Egress にはコネクタインスタンスが存在しないため、ネットワーク通信料金以外は発生しません。 パフォーマンス サーバーレス VPC アクセスコネクタでは コネクタインスタンスが VPC へのトラフィックをプロキシする ため、レイテンシが多少発生していまいます。 Direct VPC Egress にはコネクタインスタンスが存在しないため、より低レイテンシ・高スループットが発揮できます。 構成図 例えば、Cloud Run からプライベート IP を使用する Cloud SQL インスタンスに接続する場合、サーバーレス VPC アクセスコネクタを使用すると以下のような構成になります。 サーバーレス VPC アクセスコネクタを使用する場合の Cloud SQL へのプライベート接続 Direct VPC Egress を使用する場合、プライベート IP を使用する Cloud SQL インスタンスへの接続は以下のような構成となります。 Direct VPC Egress を使用する場合の Cloud SQL へのプライベート接続 Direct VPC Egress ではコネクタインスタンスを使用しないため、 ネットワークコストのみ料金が発生します。 また、インスタンスを介さずに VPC に直接接続することで、 高パフォーマンスの接続 を実現しています。 比較表 Direct VPC Egress サーバーレス VPC アクセスコネクタ ネットワークパフォーマンス 高 低 コスト 低(ネットワークコストのみ) 高(VM 料金 & ネットワークコスト) IP 割り当て 使用する IP アドレス数 >= Cloud Run のコンテナインスタンス数 となるため、 多くの IP アドレスを使用する 傾向がある コネクタインスタンスの数だけ IP が割り当てられるため、 使用する IP アドレスが少ない 参考: 公式ドキュメント ユースケース Direct VPC Egress は、従来はサーバーレス VPC アクセスコネクタを使用していた以下のようなケースで使用することができます。 パブリック IP を使用しない Cloud SQL インスタンスや AlloyDB インスタンスに接続する。 Memorystore インスタンスに接続する。 VPC 内の Cloud NAT を使用して、Cloud Run から外部のサービスにアクセスする際に使用されるパブリック IP を固定する。 オンプレミス、Compute Engine、GKE 上のサービスにプライベート IP で接続する。 先述のコストメリットとパフォーマンスを考慮すると、 制限事項 により使用できない場合を除き、基本的にはコネクタよりも Direct VPC Egress を使用すると良いでしょう。 制限事項 Cloud Run services、Cloud Run jobs 共通の制限事項 サブネットに十分な IP アドレスが必要 Direct VPC Egress を使用する場合、Cloud Run に紐付けたサブネットのプライベート IP アドレス範囲が使用されますが、サブネットで使用できる IP アドレスが枯渇していると、コンテナインスタンスがスケールアウトできなくなります。 Cloud Run services は、 最大インスタンス数の2倍の IP アドレスを使用します。 また、リビジョンを更新する際、古いリビジョンのインスタンスは使用している IP アドレスを 最大20分保持 します。そのため、新しいリビジョンのインスタンスが IP アドレスを使用できるようにするためには、2倍よりも余裕を持たせておく必要があります。 Cloud Run jobs の場合、各タスクは タスクの実行中と実行後の5分間に1つの IP アドレスを使用します。 そのため、前のジョブの完了前(もしくは完了から5分以内)に次のジョブが実行されるようなケースでは、ジョブで定義しているタスク数以上の IP アドレスを確保しなければならない点に注意が必要です。 参考: Direct VPC egress with a VPC network - IP address allocation Direct VPC Egress を使用できるコンテナインスタンス数の上限 Cloud Run の クォータ ( Maximum number of container instances using Direct VPC egress )により、Direct VPC Egress を使用して VPC に接続することができるコンテナインスタンスの数が 100~200 に制限されています(リージョンにより異なる)。 リージョンごとのクォータは コンソール から確認することができ、 申請 することにより上限を増やすことができます。 メンテナンスイベント時の接続断 ネットワークインフラストラクチャのメンテナンスイベント発生時に、Direct VPC Egress を使用した接続が一時的に切断される可能性があります。 そのため、切断時の再接続を適切に処理することができるクライアントライブラリの使用が推奨されています。 Cloud Run の実行環境(世代) Cloud Run の第2世代の実行環境で Direct VPC Egress を使用する場合、コンテナのコールドスタート時間が長くなる可能性があります。 なお、Cloud Run jobs では必ず第2世代の実行環境が使用されるため、コールドスタートを許容できる場合のみ Direct VPC Egress を使用することを推奨します。 Cloud Run jobs における制限事項 Direct VPC Egress の使用するジョブは、同時実行するタスク数を 8 以内に抑えることが推奨されています。 少なくとも 1,024 個の IP アドレスを Direct VPC Egress 用に確保しておく必要があります。 Cloud Run jobs では 透過的なメンテナンスイベント が発生することがあります。このとき、Direct VPC Egress による VPC への接続が一時的に切断される可能性があります。 Direct VPC Egress で利用できないもの ロギング・モニタリングに関して Direct VPC Egress を使用した通信は、Cloud Run サービス、ジョブの名前やリビジョンの名前がファイアウォールルールのロギング、VPC フローログに記録されません。 Packet Mirroring 、 Network Intelligence Center はサポートされていません。 VPC ファイアウォールルールの使用に関して 接続先 VPC の上り(内向き)ファイアウォールルールで Cloud Run に付与した ネットワークタグ を使用できません。 接続先 VPC の上り(内向き)ファイアウォールルールで Cloud Run の サービス ID を使用することはできません。 接続先 VPC の上り(内向き)ファイアウォールルールで Cloud Run ワークロードに付加された Resource Manager タグ を使用できません。 これらの意味するところは、 Cloud Run からの VPC 内の VM に対するアクセスは、Cloud Run に付与されたタグやサービス ID を用いた 上り(内向き)ファイアウォールルールで制御することはできない ということです。 したがって、たとえば Cloud Run からアクセスさせたくない VM がある場合は、Cloud Run に設定したネットワークタグに適用される下り(外向き)のファイアウォールルールで拒否ルールを設定する必要があります。 または、Cloud Run が使用するプライベート IP アドレスをソース IP とすることで内向きの拒否ルールを設定することができます。 佐々木 駿太 (記事一覧) G-gen最北端、北海道在住のクラウドソリューション部エンジニア 2022年6月にG-genにジョイン。Google Cloud Partner Top Engineer 2025 Fellowに選出。好きなGoogle CloudプロダクトはCloud Run。 趣味はコーヒー、小説(SF、ミステリ)、カラオケなど。 Follow @sasashun0805
アバター
G-gen のタナです。Google Cloud (旧称 GCP) の生成 AI (Generative AI) である PaLM 2 を用いて、Slackと連携した簡易的なチャットボットの PoC を行いました。 はじめに 前提知識と事前準備 PaLM 2 Slack App Python Slack SDK Google App Engine APIの有効化 権限周りの設定 検証 概要と構成図 App Engine へのデプロイ 必要なファイル 1. requirements.txt 2. app.yaml 3. app.py 実行結果 Slack を介して PaLM 2 を活用するユーザー体験 BigQuery によるプロンプト履歴の分析 デプロイの手順 1. Slackのシークレット取得 2. シークレットをSecret Managerで保持 3. Cloud Loggingの設定 4. デプロイ 5. アプリを Slack App のイベントにサブスクライブ アプリケーションの改善 1. text-bison モデルの代わりに chat-bison モデルを使用 2. プロンプト履歴を保存する場所の変更 3. 応答品質を向上させるための追加のプロンプトレイヤー 4. モデルチューニング はじめに 今回の記事では、Google Cloud (旧称 GCP) の 生成 AI (Generative AI) である PaLM 2 を用いて、Slack と連携した簡易的なチャットボットの PoC を行いました。 生成 AI を社内で運用し、データを内部で管理することで、機密情報の保護ができます。また社員が入力したプロンプトの履歴をログとして保存することで、現場の実務を分析し、社員に対する課題や必要なサポート、関心のあるトピック・キーワードを見つけ出すことができます。人事の方や管理の方はこの情報を活用し、研修の計画作りなどにも役立てることができます。 Slack x PaLM2のアプリ 前提知識と事前準備 PaLM 2 PaLM 2 は Google の最新の生成 AI です。詳しくは以下の記事をご覧ください。 blog.g-gen.co.jp 2023年8月現在、Vertex AI PaLM API は日本語未対応ですが、筆者の環境は Trusted Testers プログラムに参加中のため日本語にも対応しております。 Slack App Slack API を使用するためには、まず Slack app を作成する必要があります。以下のリンク先をご覧いただき、Slack App を作成してください。 作成時に得られる「Signing Secret」、「Bot User OAuth Token」などのシークレット情報は、安全な場所に保管してください。また、作成した Slack App には適切な権限を付与することを忘れないでください。 必要な権限は channels:history channels:join channels:read chat:write im:history im:write などです。(Features / OAuth & Permissions 画面で設定できます。) 参考 : Quickstart Python Slack SDK Slack API を Python で簡単に利用するための ライブラリー が用意されています。このライブラリーを使えば、Slack メッセージを読み取ったり、投稿したりすることが可能です。詳細は以下のリンクをご覧ください。 参考 : Slack Bolt 参考 : Slack Bolt / Fast API examples Google App Engine Google App Engine (または単に App Engine) は Web アプリケーションをホストすることができるサーバーレスの Google Cloud プロダクトです。サービスの特徴については以下の記事をご参照ください。 blog.g-gen.co.jp APIの有効化 以下のAPIを有効化する必要があります。 Cloud Resource Manager API App Engine Secret Manager API 権限周りの設定 App Engine に紐づかれるサービスアカウントに以下のロールを付与する必要があります。 Secret Manager Secret Accessor 検証 概要と構成図 以下の図は、今回開発したアプリケーションの全体像を示しています。このアーキテクチャでは App Engine が中継サーバとして機能し、Slack と PaLM 2 間の通信をスムーズに行います。具体的には slack_bolt (FastAPI) フレームワークを用いた Python アプリケーションが、Slack からのメッセージ (プロンプト) を受け取り、それを PaLM 2 に転送します。その後、PaLM 2 からの応答 (レスポンス) を取得して Slack へ返します。 アーキテクチャ またこのアーキテクチャでは、Secret Manager を活用して、認証済みサービスアカウントにアタッチされた App Engine の Python アプリケーションへとシークレット情報をセキュアに提供します。 さらに、この Python アプリケーションは、ユーザーのプロンプトと PaLM 2 からのレスポンスを Cloud Logging へ記録します。Cloud Logging はログデータをログバケットに保存し、それを BigQuery へ自動的に同期するように設定します。これにより、管理者はプロンプトと応答の履歴を簡単に分析することができます。 なお今回は簡易的な検証のため「テキストプロンプト」モデルである text-bison を選択しました。 App Engine へのデプロイ 必要なファイル Python アプリケーションを App Engine にデプロイ運用するためには、特定のファイルが必須となります。具体的には requirements.txt 、 app.yaml 、そして app.py です。 以下に、これらのファイルを活用したアプリケーションのソースコードを示します。読者の皆様も、以下の サンプルコードや設定ファイル を参考に、自身のアプリケーションデプロイに役立てていただけます。 参考 : https://github.com/G-gen-Tech-Blog/using-palm2-with-slack-chat-bot.git 1. requirements.txt main.pyを実行するために必要なパッケージのリストは requirements.txt に記載します。このアプリケーションで主に使用するライブラリは以下の通りです。他にも必要なライブラリがありますので、詳細は requirements.txt をご参照ください。 google-api-core==2.11.1 google-api-python-client==2.93.0 google-auth==2.22.0 google-auth-httplib2==0.1.0 google-cloud-logging==3.6.0 google-cloud-aiplatform==1.28.0 google-cloud-bigquery==3.11.3 google-cloud-core==2.3.3 google-cloud-resource-manager==1.10.2 google-cloud-secret-manager==2.16.2 google-cloud-storage==2.10.0 google-crc32c==1.5.0 google-resumable-media==2.5.0 googleapis-common-protos==1.59.1 grpc-google-iam-v1==0.12.6 slackclient==2.9.4 slackeventsapi==3.0.1 2. app.yaml app.yaml は App Engine で使用される重要な設定ファイルで、特定のサービスのデプロイメント記述子として機能します。このファイルは以下を定義します。 App Engine がアプリケーションとどのようにやり取りするか ランタイム、インスタンスのスケーリングなどの設定 サービスのバージョン 環境変数 以下は例です。 runtime: python39 instance_class: F1 service: palm2-slack-chatbot automatic_scaling: target_cpu_utilization: 0.90 min_instances: 1 max_instances: 2 entrypoint: gunicorn -w 2 -k uvicorn.workers.UvicornWorker -b 0.0.0.0:8080 app:api それぞれの項目について簡単に説明します。 runtime: python39 : この行は、アプリケーションのランタイム環境を指定しています。この場合、Python 3.9を使用します。 instance_class: F1 : インスタンスのクラスを指定します。F1は最小のインスタンスクラスで、最も低いコストです(2023-07-31時点)。 service: palm2-slack-chatbot : App Engine では複数のサービスを管理することができます。この行は、このアプリケーションが「palm2-slack-chatbot」という名前のサービスに関連していることを示しています。 automatic_scaling : このセクションでは、アプリケーションの自動スケーリングの設定を定義します。 target_cpu_utilization: 0.90 は CPU 使用率が90%に達した時に新たなインスタンスを立ち上げることを示しています。 min_instances: 0 と max_instances: 2 は、自動スケーリングによって管理されるインスタンスの最小数と最大数をそれぞれ指定しています。 トラフィックが少ない場合でコストを極力に押さえることを希望する場合、 automatic_scaling の max_instances: を1にしてください。 entrypoint: gunicorn -w 2 -k uvicorn.workers.UvicornWorker -b 0.0.0.0:8080 app:api : エントリーポイントは App Engine がアプリケーションを起動する際に最初に実行するコマンドを指定します。今回は、FastAPIを利用するので、 gunicorn -w 2 -k uvicorn.workers.UvicornWorker -b 0.0.0.0:8080 app:api コマンドにより app.py が実行されます。 インスタントタイプごとの料金については、次のリンクをご参照ください。 参考 : App Engine の料金 3. app.py 以下は、Slack と PaLM 2 間の通信をサポートする中継サーバとして機能する Python アプリケーションのソースコードです。以下のように動作します。 Slack からのメッセージを受け取り、それをPaLM 2に転送 PaLM 2 からのレスポンスを取得し、それをSlackに返信 ユーザーのプロンプト(メッセージ)と PaLM 2 からのレスポンスを Cloud Logging で記録 from fastapi import FastAPI, Request from google.cloud import logging from slack_bolt import App from slack_bolt.adapter.fastapi import SlackRequestHandler from slack_sdk.web.async_client import AsyncWebClient import vertexai from vertexai.language_models import ChatModel, InputOutputTextPair, TextGenerationModel from modules import gc_utils, utils # Secret Managerから環境変数を読み込む(Secret Managerを使わなければ1.事前に環境変数にこれらの値を格納し、環境変数から読み込む。2.ハードコードで入力。) PROJECT_ID, PROJECT_NO = gc_utils.get_project_number() SIGNING_SECRET = gc_utils.access_secret_version( PROJECT_NO, "palm2-slack-chatbot-l-signing-secret" ) SLACK_TOKEN = gc_utils.access_secret_version( PROJECT_NO, "palm2-slack-chatbot-l-slack-token" ) RESOURCE_LOCATION = "us-central1" HISTORICAL_CHAT_BUCKET_NAME = "historical-chat-object" # FastAPI app = App(token=SLACK_TOKEN, signing_secret=SIGNING_SECRET) app_handler = SlackRequestHandler(app) api = FastAPI() @ api.post ( "/slack/events" ) async def endpoint (req: Request): return await app_handler.handle(req) # VertexAIを初期化 vertexai.init(project=PROJECT_ID, location=RESOURCE_LOCATION) text_model = TextGenerationModel.from_pretrained( "text-bison@001" ) PARAMETERS = { "max_output_tokens" : 1024 , "temperature" : 0.20 , "top_p" : 0.95 , "top_k" : 40 , } RESPONSE_STYLE = """""" # cloud logging logging_client = logging.Client() # cloud logging: 書き込むログの名前 logger_name = "palm2_slack_chatbot" # cloud logging: ロガーを選択する logger = logging_client.logger(logger_name) # 本動作はここから def generate_response ( client: AsyncWebClient, ts: str , conversation_thread: str , user_id: str , channel_id: str , prompt: str , ) -> None : """ ユーザーIDがボットのIDまたはNoneでなく、かつチャンネルIDが存在する場合、Slackチャンネルにメッセージを投稿する。 Parameters ---------- ts : str メッセージのタイムスタンプ user_id : str ユーザーID channel_id : str チャンネルID prompt : str プロンプト """ response = text_model.predict(prompt, **PARAMETERS) # ブロックされたか確認する is_blocked = response.is_blocked is_empty_response = len (response.text.strip( " \n " )) < 1 if is_blocked or is_empty_response: payload = "入力または出力が Google のポリシーに違反している可能性があるため、出力がブロックされています。プロンプトの言い換えや、パラメータ設定の調整を試してください。" else : # slackで**などのmarkdownを正しく表示できないので削除し、簡潔にする payload = utils.remove_markdown(response.text) # レスポンスをslackへ返す client.chat_postMessage(channel=channel_id, thread_ts=ts, text=payload) keyword = gc_utils.get_keyword(text_model, prompt, PARAMETERS) gc_utils.send_log(logger, user_id, prompt, payload, keyword) @ app.event ( "message" ) def handle_incoming_message (client: AsyncWebClient, payload: dict ) -> None : """ 受信メッセージを処理する Parameters ---------- payload : dict ペイロード """ channel_id = payload.get( "channel" ) user_id = payload.get( "user" ) prompt = payload.get( "text" ) ts = payload.get( "ts" ) thread_ts = payload.get( "thread_ts" ) conversation_thread = ts if thread_ts is None else thread_ts generate_response(client, ts, conversation_thread, user_id, channel_id, prompt) 一番最初に、準備しておいたgc_utils及びutils モジュールを読み込みます。gc_utils及びutils はそれぞれGoogle Cloud と連携する関数、基本操作の関数が格納されています。 app.pyでは、それぞれの関数は以下のような役割を果たしています: get_project_number : Google Cloud プロジェクト ID と番号を取得 access_secret_version : Secret Manager からシークレット情報を取得 remove_markdown : PaLM 2 からのレスポンスのテキストからマークダウンを削除 gc_utils.get_keyword : ユーザーのプロンプトからキーワードを生成。キーワードは、ログデータに含めるために使用するもの gc_utils.send_log : ユーザーのプロンプト、PaLM 2 からのレスポンス、キーワードを Cloud Logging に送信 generate_response : 回答を生成し、Slack チャンネルにメッセージを投稿 handle_incoming_message : Slack からのメッセージを受け取り処理。この関数は 全体のフローとしてはまず handle_incoming_message 関数が Slack からのメッセージを受け取ります。 このメッセージはユーザーのプロンプトとして generate_response 関数に渡されます。次に、この関数はプロンプトを PaLM 2 に送信し、レスポンスを取得します。 取得したレスポンステキストからマークダウンを削除します (Slack は限定的なマークダウンしか対応できないため)。次に、クレンジング済みのレスポンステキストを Slack へ返信します。 最後に、ユーザーのプロンプトと PaLM 2 からのレスポンス、そして生成されたキーワードを含むログを Cloud Logging に送信します。 gc_utils.get_keyword 関数では few-shot 学習 (few-shot learning) というテクニックを活用しています。few-shot 学習は、限られた数の学習例(「ショット」)から新しいタスクを学習する能力を持つモデルを訓練する手法です。この場合、PaLM 2 は既に数多くのテキストデータを学習しており、それに基づいて新しいタスク、すなわち特定のプロンプトからキーワードを生成するタスクを実行します。 具体的には、関数内部でプロンプトとして数個の「学習例」を PaLM 2 に提示しています。これらの例は、文章からキーワードを生成するための「input」を示し、それに対する「output」も提供します。その後、ユーザーからの実際のプロンプトを input として提示し、それに対するキーワード(output)を PaLM 2 に生成させます。 このように few-shot 学習により、PaLM 2 は新しいタスクであるキーワード生成を適応的に行うことができます。この手法は、特に大規模な言語モデルを用いる際に強力で、新しいタスクへの迅速な適応を可能にします。 コード内のコメントや docstring を参照したい方は、 ここ をご覧ください。 実行結果 Slack を介して PaLM 2 を活用するユーザー体験 手順は後述しますが、ユーザーが適切なプロンプトを設計し、それを Slack を通じて送信すると、PaLM 2 からは複雑なビジネス戦略やマーケティング課題についてのヒントとなる回答が得られます。これは、内部ツールとしての利用を通じて、ビジネスユーザーが業務効率を向上させる効果が期待できる一例です。 営業戦略に関するユーザーのプロンプト及びPaLM 2の回答 上記の例では、ビジネス戦略の課題に対する応答として PaLM 2 がどのように客観的かつ有益なアドバイスを提供してくれるかが分かります。 マーケティングキャンペーンに関するユーザーのプロンプト及びPaLM 2の回答 また、上記のマーケティング課題について、対象者をより詳細に特定することで、それに適したアイディアを PaLM 2 が生成してくれました。 BigQuery によるプロンプト履歴の分析 手順は後述しますが Cloud Logging を活用して BigQuery と連携させることで、プロンプトの履歴を BigQuery で確認し、分析を行うことが可能となります。このようなデータを用いて、職場の関心事や話題を把握し、それに基づいた職場環境の改善策を見つけることができます。 例えば、特定のプロンプトが頻繁に使用されている場合、それはそのトピックが現場で高い関心を持たれている可能性を示しています。また PaLM 2 からのレスポンスを分析することで、そのトピックに対する具体的なアドバイスや解決策を見つけることも可能です。 このように BigQuery を介したログデータの分析は、組織の課題解決に役立つ洞察を提供することができます。 BigQueryでプロンプト履歴の確認 デプロイの手順 1. Slackのシークレット取得 Slack API を使うために「Signing Secret」と「OAuth Tokens for Your Workspace」というシークレットが必要です。 1.1.作成済みの Slack App の 設定画面 を開く 1.2. Setting の Basic Information に移動し Signing Secret を取得。安全な場所に保存 Signing Secretを取得する方法 1.3. Features の OAuth & Permissions に移動し OAuth Tokens for Your Workspace を取得。安全な場所に保存 OAuth Tokens for Your Workspaceを取得する方法 取得したシークレットは、次のステップで Google Cloud の Secret Manager を利用してセキュアに保管します。これにより、アプリケーションがシークレットを必要とする際に、安全にアクセスできます。 2. シークレットをSecret Managerで保持 Google Cloud の Secret Manager は、シークレット(データベースのパスワード、APIキー、TLS証明書などの構成情報)を保存、管理、アクセスするためのツールです。非常に使いやすいツールなので、おすすめです。 Secret Manager でシークレットを作成する手順については以下の記事をご参照ください。 参考 : Secret Manager でシークレットを作成する 上記の手順に従って Signing Secret と OAuth Tokens for Your Workspace を保持してください。それぞれのシークレット名を「palm2-slack-chatbot-l-signing-secret」および「palm2-slack-chatbot-l-slack-token」にすると、私のコードでパラメータ名を変えずにすぐに活用できます。 3. Cloud Loggingの設定 このセクションでは、ユーザーのプロンプトと PaLM 2 のレスポンス履歴を効率的にログとして記録し、BigQuery との連携を実現するための Cloud Logging の設定方法を説明します。 3.1 アプリケーション専用のログバケットを作成 ログバケットの作成方法については以下のリンクを参照してください。 参考 : バケットの作成 設定画面は以下のイメージのようになります。 ログバケットの作成 BigQueryとの連携を可能にするために、「Upgrade to use Log Analysis」と「このバケットに新しいBigQueryデータセットをリンクする」にチェックを入れてください。 3.2 ログのルーティング設定 ログのルーティングを設定しない場合、Python から取得した Cloud Logging のメッセージがデフォルト (_Default) のログバケットに送信されます。今回は、作成したログバケット(BigQueryと連携している)へログを転送するため、シンクを作ります。 シンクの作成方法については、以下のリンクを参照してください。 参考 : サポートされている宛先にログをルーティングする 設定画面は以下のイメージのようになります。指定したログ名に基づいてメッセージを作成したログバケットに転送するように設定します。logName を私と同じ内容に設定すれば、私のサンプルコードをすぐに利用できます。 シンクの作成 4. デプロイ 4.1. Cloud Shell でアプリケーション関連のファイルを格納するディレクトリを作成 4.2. 準備した requirements.txt、app.yaml、main.pyを下記のように配置 App Engineでデプロイするファイル 4.3. gcloud app deploy コマンドでデプロイ App EngineでPaLM 2 アプリのデプロイ 4.4 エンドポイント URL 表示 App EngineのデプロイURL 次のステップではこのURLを使用して、我々の PaLM 2 アプリケーションを Slack イベントにサブスクライブします。 5. アプリを Slack App のイベントにサブスクライブ 5.1. 作成済みの Slack App の 設定画面 を開く 5.2. Event Subscriptions 設定 Features の Event Subscriptions に移動し、4で作成したアプリの URL を下図のように入力します。そのURLの後ろに「slack/events」を指定します。 Slackのイベントにサブスクライブ また、slackボットに送るイベントを次のように設定します。 Subscribe-to-bot-eventsの設定 設定は以上です。これで Slack からプロンプトを入力することができます。 アプリケーションの改善 今回紹介したアーキテクチャは、PaLM 2 のユースケースを示すための PoC です。このアーキテクチャを改善して、組織内で PaLM2 をデプロイすることが可能です。以下に改善例を示します。 1. text-bison モデルの代わりに chat-bison モデルを使用 ユーザーのプロンプトに対する応答を chat-bison モデルで行うことで、ユーザーエクスペリエンスを向上させることができます。各チャットセッションの維持を管理するために、会話履歴を保持することができます。 chat-bison モデルを使用するための参考コードはこちらです。 参考 : https://github.com/G-gen-Tech-Blog/using-palm2-with-slack-chat-bot/tree/main 生成AIでチャットボットを作るときの具体的なコツは以下の記事をご参照ください。 blog.g-gen.co.jp 2. プロンプト履歴を保存する場所の変更 今回は簡易的なアプリのため、Cloud Logging のログバケットにデータを保持することにしました。代わりに Cloud Storage や Cloud SQL などにプロンプト履歴を書き込むようにすることも可能です。 3. 応答品質を向上させるための追加のプロンプトレイヤー ユーザーのプロンプトと最終的な応答を行う PaLM 2 の間に、テキストベースの PaLM 2 モデルを用いたプロンプトレイヤーを追加することで、ユーザーからのプロンプトを改善することができます。このレイヤーの出力を元のプロンプトの改善版とするために、プロンプトエンジニアリングを行う必要があります。 例えば、追加のプロンプトレイヤーはユーザーの元のプロンプトから「コンテキスト」を抽出し、それを元のプロンプトの冒頭に挿入して出力することができます。例えば、元のプロンプトが 私のレストランで昼間の客数を増やす方法は?私のレストランはオフィスエリアにあるインド料理店で、ターゲットの客は日本人です。 である場合、改善されたプロンプトの一例は次のようになります。 ビジネス戦略 オフィスエリアに位置するインド料理店。ターゲットは日本人。 昼間の客数を増やす方法は? この改善されたプロンプトを取得したら、再度 text-bison/chat-bison モデルに送信して PaLM 2 からの最終的な応答を得ます。 4. モデルチューニング 特定のタスクを達成するためにプロンプトエンジニアリングをしても想定した回答や形式が得られないとき、チューニングモデルを作成することができます。 例えば、問い合わせ対応や転写のタイプミスの修正などのタスクです。その場合、以下の手順に従ってください。 参考 : Tune language foundation models タナ (記事一覧) データアナリティクス準備室 データエンジニア バックエンド開発を含むデータ分析とデータエンジニアの経験を持つ。AIの活用にも関心がある。Professional Machine Learningを取得。出身地はタイのバンコクで、現在は広島在住。
アバター
G-gen の佐々木です。当記事ではロードバランサを経由して Compute Engine 上の Web アプリケーションにアクセスしたときに "no healthy upstream" が表示されてしまうときの原因と対処方法を記載します。 事象 原因と対処方法 参考 事象 以下の図に示すように、当記事では仮の構成としてロードバランサのバックエンドに Compute Engine を配置し、インスタンスに Web アプリケーションをホストしています。 クライアントからロードバランサには HTTPS でアクセス(HTTP でも可)し、バックエンドの Compute Engine には HTTP 通信が行われます。 当記事で想定する構成 ブラウザからロードバランサ経由でWebアプリケーションにアクセスすると、以下のように "no healthy upstream" とだけ表示されてしまいます。 no healthy upstream が表示されてしまう 原因と対処方法 "no healthy upstream" は、 ロードバランサがトラフィックを送信することができる正常なバックエンドが存在していない ことを示します。 実際にコンソールからロードバランサのバックエンドの状態を確認すると、インスタンスがロードバランサのヘルスチェックに失敗していることがわかります。 バックエンドのインスタンスは何らかの設定不備により、ヘルスチェックに設定されたプロトコルでトラフィックを受信できる状態でないと考えられます。 バックエンドのインスタンスがヘルスチェックに失敗している 今回のような構成でバックエンドのインスタンスがヘルスチェックに失敗している場合、アプリケーションの設定ミスを疑う前に、まずは Compute Engine インスタンスに適用されているファイアウォールルールを確認します。 Google Cloud では、ロードバランサのバックエンドに Compute Engine インスタンスを配置する場合、 ヘルスチェックのトラフィックがインスタンスに到達できるように、ファイアウォールルールでヘルスチェックのソース IP アドレスに対する許可ルールを作成する必要があります。 具体的には、以下の IP アドレス範囲をソースとした、ヘルスチェックで設定したプロトコルの Ingress 通信を許可するルールを作成する必要があります。 35.191.0.0/16 130.211.0.0/22 バックエンドのトラフィックに IPv6 を使用する場合、もしくは外部パススルー ネットワーク ロードバランサを使用している場合、バックエンドにハイブリッド NEG を使用している場合などは例外的に設定内容が異なります。詳細については当記事の末尾に記載しているドキュメントを参照してください。 当記事の構成ではヘルスチェックに TCP のポート80番 を使用しているため、ヘルスチェックのソース IP 範囲に対して、ポート80番の Ingress トラフィックを許可します。 ヘルスチェックのソースIPアドレス範囲を許可する ヘルスチェック用のファイアウォールルールを作成すると、バックエンドのインスタンスのヘルスチェックが成功していることがわかります。 バックエンドのインスタンスがヘルスチェックに成功する 再度、ブラウザからロードバランサ経由でWebアプリケーションにアクセスすると、正常にページが表示されます。 当記事では WordPress が構成済みのインスタンスを使用しているため、WordPress のページが表示されています。 Webアプリケーションが正常に表示される 参考 ヘルスチェックの作成 - 必要なファイアウォール ルール 佐々木 駿太 (記事一覧) G-gen最北端、北海道在住のクラウドソリューション部エンジニア 2022年6月にG-genにジョイン。Google Cloud Partner Top Engineer 2024に選出。好きなGoogle CloudプロダクトはCloud Run。 趣味はコーヒー、小説(SF、ミステリ)、カラオケなど。 Follow @sasashun0805
アバター
G-genでセールスを担当している村上です。Gmail と Googleドキュメントで、スマートチップ機能と承認機能をつかって簡易的なメール承認フローを実装してみました。 はじめに 利用するサービスと機能 スマートチップ機能 承認機能 メール承認フローの作成 メールの下書き 承認のリクエスト リクエストの承認待ち 承認 承認済みバージョン メールの送信 はじめに メール承認フローとは、例えば新人さんなどがお客様へメールを送る際に、上長の承認がとれたもののみ送付するといったようなフローの事です。 当記事では Gmail と Googleドキュメントのスマートチップ(メールの下書き)と、承認機能をつかって作成してみました。 Google Apps Script などのプログラムは使わず、上記の Google Workspace の機能だけを利用しています。 利用するサービスと機能 以下のサービスと機能を利用します。 サービス : Gmail、Google ドキュメント 機能 : スマートチップ(メールの下書き)、承認 スマートチップ機能 スマートチップ機能とは、Google Docs や Google Sheets において、他のファイルへのリンクや日付情報などの各種データをチップ状のボタンとして配置することでコラボレーションを促進する機能です。 スマートチップの例 Docs や Sheets の編集画面で @ を入力する事で、以下の画像のように様々な要素が提案されます。Google Workspace 内のデータを引用してリンクを作成したり、会議メモのテンプレートを挿入するなどのユースケースに活用します。 参考 : Google ドキュメントにスマートチップと構成要素を挿入する 今回はスクリーンショット画像内の赤枠の「メールの下書き」を利用します。 スマートチップ(メールの下書き) 承認機能 承認機能とはその名のとおり上長などへ承認のリクエストを送信できる機能です。 リクエストを受けた側は承認、拒否を選択しコメントを残す事が出来ます。 参考 : Google ドライブ内のファイルの承認を得る メール承認フローの作成 それでは「メールの下書き」と「承認」を利用して、簡易的なメールの承認フローを試してみます。 メールの下書き まずは新規に Google ドキュメントを開き「@」を入力し「メールの下書き」を選択します。 以下の画像のような項目が表示されますので、それぞれの項目を入力します。 例えば宛先の項目で「@」を入力すると候補のメールアドレスがサジェストされます。 本文含め全て入力が完了しましたら、完了です。 メールの下書き 承認のリクエスト メールが作成できたら次は「承認」リクエストを作成します。 Google ドキュメントの上部タブの「ファイル」→「承認」を選択します。ここで承認者を追加したりリクエストメッセージを入力します。またレビューの期限を設定したり、承認者への編集権限の付与や、承認リクエスト後にファイルをロックする事もできます。 最後にリクエストを送信する事で、先程追加した承認者へメールが送付されます。 承認機能(承認のリクエスト) リクエストの承認待ち リクエストを送信すると、リクエスト者側では以下のようにこの Google ドキュメントが「承認待ち」状態に変わり、右側で現在のステータスがわかります。 また上部青色のバーの「詳細を表示」、または右側の「開く」をクリックする事でリクエストの詳細が表示できます。 承認機能(承認待ち) 承認 承認者側で受信したメールは以下のように表示されます。「開く」をクリックします。 承認機能(承認者への通知メール) 承認者側のGoogle ドキュメントでは「承認」「拒否」ボタンが表示されます。 承認機能(承認待ち「承認」「拒否」) また「詳細を表示」をクリックすると詳細が表示されますので「承認」「拒否」の選択をしたり、コメント残すしてこの画面上でコミュニケーションをとることもできます。 またその内容はリクエスト者のユーザーへメールで通知されます。 ここでは「承認」をクリックして先に進みます。 もし「このファイルはロック中です」と表示される場合は画面を更新してください。 承認機能(詳細画面) 承認済みバージョン 承認者による承認が完了した事で、リクエスト者側の画面でも「承認済みバージョン」である事が表示されています。また詳細画面では、承認が完了した事に加え、日付やコメントの履歴が残っています。 承認機能(承認済みドキュメント) メールの送信 最後にリクエスト者側の Google ドキュメント(承認済みバージョン)で Gmail のボタンをクリックすると、先程入力したメールアドレスや件名、本文が入力された状態で Gmail の送信画面が開きます。 「送信」をクリックして完了です。 メールの下書き(Gmail送信画面) 今すぐではなく、後で送信したいと言った場合には、Gmailの送信画面を「✕」で閉じます。閉じてもメールが破棄される訳ではなく、リクエスト者のアカウントのGmailの下書きに保管されていますので、下書きからメールを開き希望の時間に送信ができます。 メールの下書き(Gmail の下書きに自動保存) これで簡易的ではあるものの、Gmail と Google ドキュメントを利用したメールの承認フローの完成です。 現時点で Gmail 自体には承認フローの機能はありませんので、是非ご参考頂ければと思います。 村上 丈伸 (記事一覧) ビジネス推進部 営業2課 アパレル、工場の作業員を経てITの世界へ。弊社では Google Workspace をメインに活動しています!
アバター
G-gen 又吉です。当記事では、Cloud Vision API を用いて PDF ファイルからテキストを抽出し、Google Cloud の Generative AI モデルが利用できる Vertex AI PaLM API を呼び出して抽出したテキストの要約をやってみたので解説します。 前提知識 Generative AI Support on Vertex AI Cloud Vision API 今回扱うデータ 構成図 プロンプト設計 概要 コンテキスト 入出力例の追加 準備 ディレクトリ構成 main.tf gcf_source_code/pdf_to_text main.py requirements.txt gcf_source_code/sammarize main.py requirements.txt 動作検証 検証データ 実行 Cloud Vision API × Vertex AI PaLM API 前提知識 Generative AI Support on Vertex AI 先日 Vertex AI でも Generative AI がサポートされました。Generative AI モデル (基盤モデル) の裏側は PaLM 2 が利用されており、多言語、推論、コーディング機能が強化された最先端の大規模言語モデル (LLM) です。 Vertex AI で Generative AI がサポートされたことで、Vertex AI のエンドポイントから基盤モデルを呼び出せるようになりました。 Vertex AI の Generative AI サポートについての詳細は以下の記事をご参照下さい。 blog.g-gen.co.jp 2023 年 7 月現在、Vertex AI PaLM API は日本語未対応ですが、筆者の環境は Trusted Testers プログラムに参加中のため日本語にも対応しております。 Cloud Vision API Cloud Vision API とは、事前トレーニング済み Vision API モデル使用して、画像内のオブジェクトの検知や OCR だけでなく、PDF / TIFF ファイル中のテキスト抽出等も行なえます。 その他の機能、また詳細については以下のドキュメントをご参照下さい。 参考: Features list また、Cloud Vision API を用いたやってみた記事として、以下のようなものもあるので興味があれば御覧ください。 blog.g-gen.co.jp 今回扱うデータ 今回扱う PDF ファイルは、ダミーで作成した日報データです。 daily_report.pdf 構成図 今回の構成は以下のとおりです。 構成図 PDF 用バケットに PDF ファイル (日報) がアップロードされたことをトリガーに、 PDF からテキストを抽出する Cloud Functions 関数が起動し、抽出したテキストデータをテキスト用バケットに格納します。 次に、テキスト格納用バケットにデータがアップロードされたことをトリガーに、文章を要約する Cloud Functions 関数が起動し、要約したデータが要約された文章用バケットに格納します。 プロンプト設計 概要 プロンプトとは、簡単に言うと大規模言語モデル (LLM) に送信するリクエストのことです。 Vertex AI PaLM API からより良い回答を生成してもらうためには、プロンプトの設計が非常に重要です。 参考: プロンプト設計 今回は、自然な会話やチャットボット等のユースケースに特化した chat-bison@001 (チャット用言語モデル) を使用します。 チャット用言語モデルのプロンプトには、次の 3 つのコンポーネントで構成されます。 メッセージ [必須] コンテキスト [オプション] 入出力例の追加 [オプション] コンテキスト コンテキスト とは、モデルの応答方法を指示したり、モデルのペルソナを指定したりできます。 よって今回は、以下のようなコンテキストを設定します。 あなたはプロの編集者です。以下の制約条件に従って、入力する文章を要約してください。 制約条件 ・300文字以内にまとめて要約した文章として出力。 ・文章の意味を変更しない。 ・架空の表現や言葉を使用しない。 入出力例の追加 入出力例の追加 とは、特定の入力例と、その入力に対するモデルの出力例、つまり入出力のペアリストのことです。 PDF から抽出されるテキストデータのサンプルとして、以下を入力例とします。 日付名前2023年7月24日営業 太郎日報業務報告お疲れ様です。 本日の活動について報告いたします。本日は、 当社の製品に関心を示していただいた新規のお客様への訪問を中心に行いました。 まず、 午前中にはA社を訪問し、当社製品の特徴と価格競争力についてプレゼンテーションを行いました。 彼らは特に当社の製品のコストパフォーマンスに感心していましたが、 具体的な購入の意志は明らかになりませんでした。 引き続き 情報提供を行い、 ビジネスを進展させるべく、 交渉を続けます。午後は、新たに興味を示してくださったB社とのミーティングを行いました。 当社の製品についての詳細な質問や、予算に関する議論が交わされました。B社は即決ではありませんでしたが、 当社の製品に強い興味を持っていることが伺え、有望な見込み客と判断しています。その他、進行中のC社との契約交渉については、一部微調整が必要な部分が見つかりました。 来週中には改めて会議を設定し、これをクリアにする予定です。全体として、本日は新規顧客開拓と既存顧客との継続的な関係強化に重点を置いた一日となりました。 明日はさらなる顧客訪問と情報収集、ならびに製品のプレゼンテーションを行う予定です。 ご支援のほど、よろしくお願い申し上げます。以上、本日の報告となります。 モデルの出力例としては、「日付 : 」「名前 : 」「要約 : 」を分けて出力してもらえるよう、以下のように設定します。 日付:2023年7月24日 名前:営業 太郎 要約:本日は、当社の製品に関心を示していただいた新規のお客様への訪問を中心に行いました。午前中にはA社を訪問し、当社製品の特徴と価格競争力についてプレゼンテーションを行いました。午後は、新たに興味を示してくださったB社とのミーティングを行いました。その他、進行中のC社との契約交渉については、一部微調整が必要な部分が見つかりました。明日はさらなる顧客訪問と情報収集、ならびに製品のプレゼンテーションを行う予定です。 準備 ディレクトリ構成 開発環境は Cloud Shell を用いて行います。ディレクトリ構造は以下のとおりです。 terraform ディレクトリ配下は、以下のとおりです。 terraform |-- gcf_source_code | |-- pdf_to_text | | |-- main.py | | `-- requirements.txt | ` -- sammarize | |-- main.py | `-- requirements.txt ` -- main.tf main.tf main.tf には Terraform のコードを記述しています。 locals { terraform_service_account = $ { Terraform 実行に使われるサービスアカウントのメールアドレス } project_name = $ { プロジェクト名 } project_id = $ { プロジェクト ID } folder_id = $ { フォルダ ID } billing_account_id = $ { 請求先アカウント ID } } # terraform & provider の設定 terraform { required_providers { google = { source = "hashicorp/google" version = ">= 4.0.0" } } required_version = ">= 1.3.0" backend "gcs" { bucket = $ { tfstate ファイルを格納する Cloud Storage バケット名 } impersonate_service_account = $ { Terraform 実行に使われるサービスアカウントのメールアドレス } } } # サービスアカウント権限借用の設定 provider "google" { alias = "impersonation" scopes = [ "https://www.googleapis.com/auth/cloud-platform" , "https://www.googleapis.com/auth/userinfo.email" , ] } data "google_service_account_access_token" "default" { provider = google.impersonation target_service_account = local.terraform_service_account scopes = [ "userinfo-email" , "cloud-platform" ] lifetime = "1200s" } # Google プロバイダの設定 provider "google" { project = local.project_id region = "asia-northeast1" access_token = data.google_service_account_access_token.default.access_token request_timeout = "60s" } ###################################### ### プロジェクトの作成と API の有効化 ### ###################################### # プロジェクトの作成 resource "google_project" "poc" { name = local.project_name project_id = local.project_id folder_id = local.folder_id billing_account = local.billing_account_id } # API の有効化 module "tenant_a_project_services" { source = "terraform-google-modules/project-factory/google//modules/project_services" version = "14.2.1" project_id = google_project.poc.project_id enable_apis = true activate_apis = [ "iam.googleapis.com" , "cloudbuild.googleapis.com" , "run.googleapis.com" , "cloudfunctions.googleapis.com" , "pubsub.googleapis.com" , "eventarc.googleapis.com" , "artifactregistry.googleapis.com" , "storage.googleapis.com" , "vision.googleapis.com" , "aiplatform.googleapis.com" ] disable_services_on_destroy = false } ####################################### ### サービスアカウントの作成と権限の付与 ## ####################################### # Cloud Functions 用サービスアカウントの作成と権限付与 resource "google_service_account" "sa_gcf" { project = google_project.poc.project_id account_id = "sa-gcf" display_name = "Cloud Functions 用サービスアカウント" } resource "google_project_iam_member" "invoke_gcf" { project = google_project.poc.project_id role = "roles/run.invoker" member = "serviceAccount:${google_service_account.sa_gcf.email}" } resource "google_project_iam_member" "storage_admin" { project = google_project.poc.project_id role = "roles/storage.admin" member = "serviceAccount:${google_service_account.sa_gcf.email}" } resource "google_project_iam_member" "event_receiving" { project = google_project.poc.project_id role = "roles/eventarc.eventReceiver" member = "serviceAccount:${google_service_account.sa_gcf.email}" depends_on = [ google_project_iam_member.invoke_gcf ] } resource "google_project_iam_member" "artifactregistry_reader" { project = google_project.poc.project_id role = "roles/artifactregistry.reader" member = "serviceAccount:${google_service_account.sa_gcf.email}" depends_on = [ google_project_iam_member.event_receiving ] } resource "google_project_iam_member" "vertex_ai_user" { project = google_project.poc.project_id role = "roles/aiplatform.user" member = "serviceAccount:${google_service_account.sa_gcf.email}" } # Eventarc のサービスアカウントに権限付与 resource "google_project_iam_member" "serviceAccount_token_creator" { project = google_project.poc.project_id role = "roles/iam.serviceAccountTokenCreator" member = "serviceAccount:service-${google_project.poc.number}@gcp-sa-pubsub.iam.gserviceaccount.com" depends_on = [ module.tenant_a_project_services ] } # Cloud Storage のサービスアカウントに権限付与 data "google_storage_project_service_account" "gcs_account" { project = google_project.poc.project_id depends_on = [ module.tenant_a_project_services ] } resource "google_project_iam_member" "gcs_pubsub_publishing" { project = google_project.poc.project_id role = "roles/pubsub.publisher" member = "serviceAccount:${data.google_storage_project_service_account.gcs_account.email_address}" } ################################ ### バケットとオブジェクトの作成 ### ################################ # Cloud Functions のソースコード格納用バケットの作成 resource "google_storage_bucket" "source_gcf" { project = google_project.poc.project_id location = "asia-northeast1" name = "${google_project.poc.project_id}-source-gcf" force_destroy = true } # Cloud Functions で使うソースコードを ZIP 化 data "archive_file" "pdf_to_text" { type = "zip" source_dir = "./gcf_source_code/pdf_to_text" output_path = "./zip_source_code/pdf_to_text.zip" } data "archive_file" "sammarize" { type = "zip" source_dir = "./gcf_source_code/sammarize" output_path = "./zip_source_code/sammarize.zip" } # ZIP 化したソースコードをバケットに追加 resource "google_storage_bucket_object" "pdf_to_text" { name = "pdf-to-text.${data.archive_file.pdf_to_text.output_md5}.zip" bucket = google_storage_bucket.source_gcf.name source = data.archive_file.pdf_to_text.output_path } resource "google_storage_bucket_object" "sammarize" { name = "sammarize.${data.archive_file.sammarize.output_md5}.zip" bucket = google_storage_bucket.source_gcf.name source = data.archive_file.sammarize.output_path } # pdf_data バケットの作成 resource "google_storage_bucket" "pdf_data" { project = google_project.poc.project_id location = "asia-northeast1" name = "${google_project.poc.project_id}-pdf-data" force_destroy = true } # text_data バケットの作成 resource "google_storage_bucket" "text_data" { project = google_project.poc.project_id location = "asia-northeast1" name = "${google_project.poc.project_id}-text-data" force_destroy = true } # summarized_text_data バケットの作成 resource "google_storage_bucket" "summarized_text_data" { project = google_project.poc.project_id location = "asia-northeast1" name = "${google_project.poc.project_id}-summarized-text-data" force_destroy = true } ############################ ### Cloud Functions 作成 ### ############################ # pdf_to_text 関数 resource "google_cloudfunctions2_function" "pdf_to_text" { depends_on = [ google_project_iam_member.event_receiving, google_project_iam_member.artifactregistry_reader, google_project_iam_member.serviceAccount_token_creator ] name = "pdf-to-text" location = "asia-northeast1" description = "PDF からテキストを取得し text_data バケットに格納する関数" build_config { runtime = "python310" entry_point = "main" # Set the entry point in the code source { storage_source { bucket = google_storage_bucket.source_gcf.name object = google_storage_bucket_object.pdf_to_text.name } } } service_config { max_instance_count = 3 min_instance_count = 1 available_memory = "256M" timeout_seconds = 60 environment_variables = { DESTINATION_BUCKET_NAME = google_storage_bucket.text_data.name } service_account_email = google_service_account.sa_gcf.email } event_trigger { trigger_region = "asia-northeast1" event_type = "google.cloud.storage.object.v1.finalized" retry_policy = "RETRY_POLICY_DO_NOT_RETRY" service_account_email = google_service_account.sa_gcf.email event_filters { attribute = "bucket" value = google_storage_bucket.pdf_data.name } } } # sammarize 関数 resource "google_cloudfunctions2_function" "sammarize" { depends_on = [ google_project_iam_member.event_receiving, google_project_iam_member.artifactregistry_reader, google_project_iam_member.serviceAccount_token_creator ] name = "sammarize" location = "asia-northeast1" description = "Vertex AI PaLM API でテキストを要約し summarized_text_data バケットに格納する関数" build_config { runtime = "python310" entry_point = "main" # Set the entry point in the code source { storage_source { bucket = google_storage_bucket.source_gcf.name object = google_storage_bucket_object.sammarize.name } } } service_config { max_instance_count = 3 min_instance_count = 1 available_memory = "256M" timeout_seconds = 60 environment_variables = { PROJECT_ID = google_project.poc.project_id DESTINATION_BUCKET_NAME = google_storage_bucket.summarized_text_data.name } service_account_email = google_service_account.sa_gcf.email } event_trigger { trigger_region = "asia-northeast1" event_type = "google.cloud.storage.object.v1.finalized" retry_policy = "RETRY_POLICY_DO_NOT_RETRY" service_account_email = google_service_account.sa_gcf.email event_filters { attribute = "bucket" value = google_storage_bucket.text_data.name } } } gcf_source_code/pdf_to_text main.py gcf_source_code/pdf_to_text には、PDF ファイルからテキストを抽出する Cloud Functions 関数のソースコードを格納しています。 import json import re import os from cloudevents.http import CloudEvent import functions_framework from google.cloud import vision DESTINATION_BUCKET_NAME = os.environ.get( "DESTINATION_BUCKET_NAME" ) # クライアントの初期化 vision_client = vision.ImageAnnotatorClient() def async_detect_document ( gcs_source_uri, gcs_destination_uri, ): # 入力設定を構成 mime_type = "application/pdf" gcs_source = vision.GcsSource(uri=gcs_source_uri) input_config = vision.InputConfig(gcs_source=gcs_source, mime_type=mime_type) # 出力設定を構成 feature = vision.Feature( type_=vision.Feature.Type.DOCUMENT_TEXT_DETECTION ) gcs_destination = vision.GcsDestination(uri=gcs_destination_uri) output_config = vision.OutputConfig( gcs_destination=gcs_destination ) # 非同期リクエストを実行 async_request = vision.AsyncAnnotateFileRequest( features=[feature], input_config=input_config, output_config=output_config ) # operations リソースでステータスを確認 operation = vision_client.async_batch_annotate_files(requests=[async_request]) print ( "Waiting for the operation to finish." ) # 非同期処理が完了していない場合、最大 timeout 秒まで待機 operation.result(timeout= 420 ) return "ok" @ functions_framework.cloud_event def main (cloud_event: CloudEvent): # CloudEvent から渡されたデータを取得 data = cloud_event.data bucket_name = data[ "bucket" ] file_name = data[ "name" ] no_extension_file_name = file_name.split( '.' )[ 0 ] gcs_source_uri = f "gs://{bucket_name}/{file_name}" gcs_destination_uri = f "gs://{DESTINATION_BUCKET_NAME}/{no_extension_file_name}/" async_detect_document( gcs_source_uri = gcs_source_uri, gcs_destination_uri = gcs_destination_uri, ) return "ok" PDF からテキストを取得する際、 AsyncBatchAnnotateFilesRequest メソッドを利用していますが、こちらは非同期でリクエストが行われます。 operations リソースを用いることで、そのステータスが確認でき、尚タイムアウトの指定も可能となります。 また、今回は検証のため対応しておりませんが、出力されるテキストが大きくなると複数ファイルに分かれて Cloud Storage に書き込まれる可能性がある為、本番運用時はこのあたりの考慮も必要となります。本検証は、1 ファイルのインプットにつき 1 ファイルのアウトプットのみに対応した構成となっています。 参考: GcsDestination requirements.txt functions-framework==3.* cloudevents==1.9.0 google-cloud-vision==3.4.4 gcf_source_code/sammarize main.py gcf_source_code/sammarize には、テキストを要約する Cloud Functions 関数のソースコードを格納しています。 import json import os from cloudevents.http import CloudEvent import functions_framework import vertexai from vertexai.preview.language_models import ChatModel, InputOutputTextPair from google.cloud import storage PROJECT_ID = os.environ.get( "PROJECT_ID" ) DESTINATION_BUCKET_NAME = os.environ.get( "DESTINATION_BUCKET_NAME" ) # 基盤モデルとストレージクライアントの初期化 vertexai.init(project=PROJECT_ID, location= "us-central1" ) chat_model = ChatModel.from_pretrained( "chat-bison@001" ) storage_client = storage.Client() def download_json_from_bucket (bucket_name, blob_name): # バケットを取得 bucket = storage_client.get_bucket(bucket_name) # オブジェクトを取得 blob = bucket.blob(blob_name) json_string = blob.download_as_bytes().decode( "utf-8" ) response = json.loads(json_string) return response def summarize_text (text): # 言語モデルのリクエストパラメラータを指定 parameters = { "temperature" : 0.2 , "max_output_tokens" : 1024 , "top_p" : 0.8 , "top_k" : 40 } # コンテキストと INPUT / OUTPUT の例を追加 chat = chat_model.start_chat( context= """あなたはプロの編集者です。以下の制約条件に従って、入力する文章を要約してください。 制約条件 ・300文字以内にまとめて要約した文章として出力。 ・文章の意味を変更しない。 ・架空の表現や言葉を使用しない。 #出力形式 日付: 名前: 要約した文章:""" , examples=[ InputOutputTextPair( input_text= """日付名前2023年7月24日営業 太郎日報業務報告お疲れ様です。 本日の活動について報告いたします。本日は、 当社の製品に関心を示していただいた新規のお客様への訪問を中心に行いました。 まず、 午前中にはA社を訪問し、当社製品の特徴と価格競争力についてプレゼンテーションを行いました。 彼らは特に当社の製品のコストパフォーマンスに感心していましたが、 具体的な購入の意志は明らかになりませんでした。 引き続き 情報提供を行い、 ビジネスを進展させるべく、 交渉を続けます。午後は、新たに興味を示してくださったB社とのミーティングを行いました。 当社の製品についての詳細な質問や、予算に関する議論が交わされました。B社は即決ではありませんでしたが、 当社の製品に強い興味を持っていることが伺え、有望な見込み客と判断しています。その他、進行中のC社との契約交渉については、一部微調整が必要な部分が見つかりました。 来週中には改めて会議を設定し、これをクリアにする予定です。全体として、本日は新規顧客開拓と既存顧客との継続的な関係強化に重点を置いた一日となりました。 明日はさらなる顧客訪問と情報収集、ならびに製品のプレゼンテーションを行う予定です。 ご支援のほど、よろしくお願い申し上げます。以上、本日の報告となります。""" , output_text= """日付:2023年7月24日 名前:営業 太郎 要約:本日は、当社の製品に関心を示していただいた新規のお客様への訪問を中心に行いました。午前中にはA社を訪問し、当社製品の特徴と価格競争力についてプレゼンテーションを行いました。午後は、新たに興味を示してくださったB社とのミーティングを行いました。その他、進行中のC社との契約交渉については、一部微調整が必要な部分が見つかりました。明日はさらなる顧客訪問と情報収集、ならびに製品のプレゼンテーションを行う予定です。""" ) ] ) # 基盤モデルにリクエストを実行 response = chat.send_message(text, **parameters) return response.text def upload_text_to_bucket (bucket_name, blob_name, text_data): # バケットを取得 bucket = storage_client.get_bucket(bucket_name) # バケットにアップロードするためのblobを作成 blob = bucket.blob(blob_name) # テキストデータをアップロード blob.upload_from_string(text_data) return "OK" @ functions_framework.cloud_event def main (cloud_event: CloudEvent): # CloudEvent から渡されたデータを取得 data = cloud_event.data print (data) bucket_name = data[ "bucket" ] blob_name = data[ "name" ] # "Sheet2/output-1-to-1.json" folder_name = blob_name.split( '/' )[ 0 ] response = download_json_from_bucket( bucket_name = bucket_name, blob_name = blob_name ) # json からテキストデータの取得 first_page_response = response[ "responses" ][ 0 ] response_text = first_page_response[ "fullTextAnnotation" ][ "text" ] # 改行と「|」の削除 corrected_text = response_text.replace( ' \n ' , '' ).replace( '|' , '' ) print (f "=====原文=====" ) print (corrected_text) print (f "=====サマリ=====" ) summarized_text = summarize_text(corrected_text) print (summarized_text) upload_text_to_bucket( bucket_name = DESTINATION_BUCKET_NAME, blob_name = f "{folder_name}.txt" , text_data = summarized_text.encode( "shift_jis" ) ) return "ok" Vertex AI PaLM API エンドポイントへのリクエストには、先程のプロンプト設計で作成したコンテキストと入出力例の追加を行っています。 requirements.txt functions-framework==3.* cloudevents==1.9.0 google-cloud-vision==3.4.4 google-cloud-storage==2.10.0 google-cloud-aiplatform==1.28.1 動作検証 検証データ 以下の PDF ファイルで動作検証を行います。尚、業務報告内の文字数は 736 文字となります。 daily_report.pdf 実行 PDF 格納バケットに daily_report.pdf をアップロードします。 格納バケットコンソール画面 直後に Cloud Storage トリガー経由で Cloud Functions が起動し、最終的に要約されたテキスト格納バケットにオブジェクトが生成されました。 要約されたテキスト格納バケット 中身は以下のようになってました。 daily_report.txt 入力コンテキストの制限事項には 300 文字以内で要約するよう記載していましたが、要約後のテキストでは 411 文字で出力されました 。 圧縮率 (または要約率) 56% 。 何度か実行してみましたが 300 文字以内で要約することはできなかったため、制限事項に正確に従うことは現状難しいのかと思います。 しかし要約内容について、 重要な箇所は漏れなく記載されており、文章全体にも違和感なく要約 されています。 又吉 佑樹 (記事一覧) クラウドソリューション部 はいさい、沖縄出身のクラウドエンジニア! セールスからエンジニアへ転身。Google Cloud 全 11 資格保有。Google Cloud Partner Top Engineer 2024。Google Cloud 公式ユーザー会 Jagu'e'r でエバンジェリストとして活動中。好きな分野は AI/ML。 Follow @matayuuuu
アバター
G-gen の武井です。当記事では Cloud Build を使って Terraform 実行を自動化する方法を紹介します。 Cloud Build で Terraform 実行を自動化 はじめに 当記事の概要 前提知識 GitHub Terraform Cloud Build Cloud Build トリガー Terraform / Cloud Build 詳細 アーキテクチャ 設定手順 GitHub リポジトリとの連携 手順 Cloud Build トリガーの作成 コマンド解説 ソースコードの作成 Terraform ソースコード ビルド構成ファイル 動作確認 dev ブランチへのプッシュ main ブランチへマージ 関連記事 はじめに 当記事の概要 GitHub の任意のブランチへのプッシュを契機に Terraform を実行する CI/CD パイプラインを、GitHub と Code Build のみで実装することができます。 当記事ではその手順についてご紹介します。 前提知識 GitHub GitHub とは、開発者がコードを共有、管理、コラボレーションするためのプラットフォームです。 Git ベースのバージョン管理システムに加え、問題追跡、機能リクエスト、タスク管理、コミュニティ構築などのツールも提供しています。 Terraform Terraform とは、HashiCorp 社が開発したオープンソースの Infrastructure as Code (IaC) ツールです。 Terraform では、独自フォーマットの設定ファイルに宣言型のコードを記述することで、仮想マシン、ストレージ、ネットワーク等の各種リソースを管理できます。また、作成した各種リソースの状態 (state) についてもファイルとして保存されるため、実環境と設定ファイルの差分が把握できます。 ファイルでクラウドリソースを管理できるため、 バージョン管理や CI/CD (継続的インテグレーション / 継続的デリバリ) を可能にします。また、Google Cloud (旧称 GCP) だけでなく Amazon Web Services (AWS) や Microsoft Azure といった主要なパブリッククラウドにも対応しています。 Cloud Build Cloud Build とは、Google Cloud のフルマネージドなサーバレス CI/CD(継続的インテグレーション / 継続的デリバリー)プラットフォームです。 開発者はソースコードからインフラやアプリケーションを高速かつ確実にビルド、テスト、デプロイすることができ、効率的な開発ライフサイクルの実現をサポートします。 Cloud Build トリガー Cloud Build トリガー とはビルドプロセスを自動的に起動させる機能です。 例えば任意のブランチにプッシュやプルリクエストといったイベントが発生した際、それをトリガーに予め定義したビルドプロセスを自動的に実行します。 ビルドプロセスは YAML または JSON 形式のビルド構成ファイルで定義します。 Terraform / Cloud Build 詳細 Terraform や Cloud Build に関する詳細は以下の記事で解説しております。 こちらも合わせて参照ください。 blog.g-gen.co.jp blog.g-gen.co.jp アーキテクチャ 今回の例ではリポジトリに GitHub を使用し、任意のブランチへのプッシュイベントをトリガーに Terraform が動作します。 ローカル側で切り出した dev ブランチをリモート側にプッシュする terraform plan が自動実行 main ブランチにマージする terraform apply が自動実行 詳細は後述しますが、 terraform apply コマンドは main ブランチにマージされた場合にのみ実行されるようにしています。 これは、任意のブランチ (図の例では dev ) にプッシュされたソースコードや terraform plan の実行結果がきちんとレビューされたあとに実行されることを意図しているからです。 任意のブランチへの Push イベントをトリガーとして起動する Cloud Build トリガー 設定手順 GitHub リポジトリとの連携 まず Terraform のソースコードを管理する GitHub リポジトリとの連携を行います。手順は以下の公式ガイドを参考にしています。 参考 : GitHub リポジトリを接続する 手順 Cloud コンソール > Cloud Build トリガー と遷移し、任意のリージョンを選択したら リポジトリを接続 をクリックします。 リポジトリを接続 をクリック GitHub (Cloud Build GitHub アプリ) を選択し、 続行 をクリックします。 GitHub (Cloud Build GitHub アプリ) を選択 Cloud Build GitHub アプリのインストール先となる 組織 または GitHub ユーザー名 を選択します。 アプリのインストール先を選択 All repositories (すべての GitHub リポジトリ) あるいは Only select repositories (特定の GitHub リポジトリ) かを選択したのち、 Install をクリックします。 連携対象のリポジトリを選択 GitHub ユーザーアカウントに MFA 認証が設定されている場合は認証コードを入力します。 認証コードを入力 (MFA 認証が有効な場合) GitHub アカウント と リポジトリ の選択、利用規約への同意 ( チェックボックス ) が完了したら、最後に 接続 をクリックします。 対象のリポジトリを選択したら接続 サンプルトリガーは作成せずに 完了 をクリックします。 サンプルトリガーは作成せずに完了する Cloud Build トリガーの作成 次に Cloud Build トリガーを作成します。今回 Cloud Build トリガーの作成には gcloud コマンドを使用します。 参考 : gcloud beta builds triggers create github 参考 : ビルドトリガーの作成 gcloud beta builds triggers create github \ --name =" push-trigger-terraform " \ --region =" asia-northeast1 " \ --repo-name =" sample-repo-name " \ --repo-owner =" sample-repo-owner-name " \ --branch-pattern =" .* " \ --included-files =" sample/modules/** " \ --build-config =" sample/terraform.yaml " \ --substitutions =" _WORK_DIR "=" sample/ " コマンド解説 上記コマンドを実際の成果物 (Cloud Build トリガー) に照らし合わせて解説します。 Cloud コンソール > Cloud Build > トリガー から確認可能です。 トリガー名とリージョン --name と --region オプションで指定した値で作成されます。 トリガー名とリージョン イベント プッシュイベントをトリガーとする場合、コマンドオプションでの指定は不要です。 イベントのデフォルトはプッシュイベント リポジトリ --repo-name と --repo-owner は GitHub リポジトリの URL に記載されている値を入力します。 repo-name と repo-owner は GitHub リポジトリの URL から確認可能 GitHub リポジトリの URL ブランチ名とビルド構成ファイル --branch-pattern でプッシュ先のブランチを指定しますが、ビルド構成ファイルで制御したいためこちらの設定値は任意 ( .* ) としています。 また、 --included-files で sample/modules ディレクトリ配下のファイル更新に関するプッシュイベントをターゲットにしています。(ファイル構成は後述) プッシュ先ブランチはビルド構成ファイルで制御するため、トリガー設定上は任意とする ビルド構成ファイル --build-config でビルド構成ファイルのパスをルートディレクトリを起点に明示します。(ファイル構成は後述) ビルド構成ファイルのパスを指定 変数 ビルド構成ファイルで使用する変数を --substitutions で指定します。 変数 ( _WORK_DIR ) は Terraform コマンドを実行するディレクトリ ( sample/ ) を定義しています。 ビルド構成ファイルで使用する変数の定義 ソースコードの作成 Terraform のソースコードは GitHub に用意します。コードの編集には コードスペース を使用しており、 /workspaces/sample-repo-name がルートディレクトリに相当します。 @username ➜ /workspaces/sample-repo-name ( dev ) $ pwd /workspaces/sample-repo-name @username ➜ /workspaces/sample-repo-name ( dev ) $ tree . ├── sample │ ├── modules │ │ └── cloud_storage │ │ ├── main.tf │ │ └── variables.tf │ ├── backend.tf │ ├── main.tf │ ├── terraform.tfvars │ ├── terraform.yaml │ ├── variables.tf │ └── versions.tf Terraform ソースコード 以下の Terraform ソースコードを使ってストレージバケットを払い出します。 sample/modules/cloud_storage/main.tf locals { names = [ "cicd-testbucket-01" , ] } resource "google_storage_bucket" "buckets" { for_each = toset (local.names) name = each.value storage_class = "STANDARD" project = var.project_id location = var.region uniform_bucket_level_access = true } sample/modules/cloud_storage/variables.tf ( sample/variables.tf も同じ) variable "project_id" { type = string } variable "region" { type = string } sample/backend.tf terraform { backend "gcs" { bucket = "sample-project-tfstate-bucket" prefix = "terraform/state" } } sample/main.tf module "cicd_test" { source = "./modules/cloud_storage" project_id = var.project_id region = var.region } sample/terraform.tfvars project_id = "sample-project" region = "asia-northeast1" sample/versions.tf provider "google" { project = var.project_id } terraform { required_version = "~> 1.5.0" required_providers { google = { source = "hashicorp/google" version = "~> 4.65.2" } } } ビルド構成ファイル ビルド構成ファイル ( sample/terraform.yaml ) では以下の処理を定義しています。 実行される処理は terraform init / terraform plan / terraform apply の 3つ 各処理は _WORK_DIR 変数で定義した sample/ ディレクトリ上で実行される プッシュ先ブランチが main 以外の場合、 terraform plan までの処理が実行され、最後に echo で定義したメッセージ ( terraform apply がスキップされた旨) を表示して終了する マージ先ブランチが main の場合、 terraform apply までの処理が実行される steps : - id : "tf init" name : "hashicorp/terraform:1.5.0" dir : "$_WORK_DIR" entrypoint : "sh" args : - "-c" - | terraform init -upgrade || exit 1 - id : "tf plan" name : "hashicorp/terraform:1.5.0" dir : "$_WORK_DIR" entrypoint : "sh" args : - "-c" - | terraform plan || exit 1 - id : "tf apply" name : "hashicorp/terraform:1.5.0" dir : "$_WORK_DIR" entrypoint : "sh" args : - "-c" - | if [ "$BRANCH_NAME" = "main" ] ; then terraform apply -auto-approve else echo "***************************************************************************************" echo "terraform apply was skipped because it's not a merge into the main branch." echo "***************************************************************************************" fi 動作確認 dev ブランチへのプッシュ、 main ブランチへのマージでそれぞれの処理が意図通り実行されるかを確認します。 dev ブランチへのプッシュ コードスペース (ローカル) 上に main ブランチをクローンした後に dev ブランチを切り、上記で説明したソースコード一式を作成したらリモートにプッシュします。 以下はその際のコマンド操作履歴です。 @username ➜ /workspaces/sample-repo-name ( dev ) $ history 1 git checkout -b dev 2 git add . 3 git commit -m " add storage bucket " 4 git push --set-upstream origin dev GitHub (リモート側) にソースコードがプッシュされた旨が表示されました。 dev ブランチへのプッシュを検知 次に、 Cloud コンソール > Cloud Build > 履歴 から Cloud Build の実行履歴を確認します。一覧上のステータスが 成功 になっていますので ビルド ID をクリックして詳細を確認します。 ビルド履歴一覧 各処理が成功しており、ログ出力からも意図通り動作していることがわかります。 terraform plan が実行され、ストレージバケットが払い出される旨が表示 main ブランチへのマージではないので、 terraform apply はスキップ 各処理が正常終了 # ログ出力 (重要な部分のみ抜粋) starting build "294c2437-edcb-405c-9b5c-646e23de2fb5" Starting Step #0 - "tf init" Step #0 - "tf init": Step #0 - "tf init": Initializing the backend... Step #0 - "tf init": Step #0 - "tf init": Successfully configured the backend "gcs"! Terraform will automatically Step #0 - "tf init": use this backend unless the backend configuration changes. Step #0 - "tf init": Upgrading modules... Step #0 - "tf init": - cicd_test in modules/cloud_storage Step #0 - "tf init": Step #0 - "tf init": Initializing provider plugins... Step #0 - "tf init": - Finding hashicorp/google versions matching "~> 4.65.2"... Step #0 - "tf init": - Installing hashicorp/google v4.65.2... Step #0 - "tf init": - Installed hashicorp/google v4.65.2 (signed by HashiCorp) Step #0 - "tf init": Step #0 - "tf init": Terraform has been successfully initialized! Step #0 - "tf init": Step #0 - "tf init": You may now begin working with Terraform. Try running "terraform plan" to see Step #0 - "tf init": any changes that are required for your infrastructure. All Terraform commands Step #0 - "tf init": should now work. Step #0 - "tf init": Step #0 - "tf init": If you ever set or change modules or backend configuration for Terraform, Step #0 - "tf init": rerun this command to reinitialize your working directory. If you forget, other Step #0 - "tf init": commands will detect it and remind you to do so if necessary. Finished Step #0 - "tf init" Starting Step #1 - "tf plan" Step #1 - "tf plan": Already have image: hashicorp/terraform:1.5.0 Step #1 - "tf plan": Step #1 - "tf plan": Terraform used the selected providers to generate the following execution Step #1 - "tf plan": plan. Resource actions are indicated with the following symbols: Step #1 - "tf plan": + create Step #1 - "tf plan": Step #1 - "tf plan": Terraform will perform the following actions: Step #1 - "tf plan": Step #1 - "tf plan": # module.cicd_test.google_storage_bucket.buckets["cicd-testbucket-01"] will be created Step #1 - "tf plan": + resource "google_storage_bucket" "buckets" { Step #1 - "tf plan": + force_destroy = false Step #1 - "tf plan": + id = (known after apply) Step #1 - "tf plan": + location = "ASIA-NORTHEAST1" Step #1 - "tf plan": + name = "cicd-testbucket-01" Step #1 - "tf plan": + project = "sample-project" Step #1 - "tf plan": + public_access_prevention = (known after apply) Step #1 - "tf plan": + self_link = (known after apply) Step #1 - "tf plan": + storage_class = "STANDARD" Step #1 - "tf plan": + uniform_bucket_level_access = true Step #1 - "tf plan": + url = (known after apply) Step #1 - "tf plan": } Step #1 - "tf plan": Step #1 - "tf plan": Plan: 1 to add, 0 to change, 0 to destroy. Step #1 - "tf plan": Step #1 - "tf plan": ───────────────────────────────────────────────────────────────────────────── Step #1 - "tf plan": Step #1 - "tf plan": Note: You didn't use the -out option to save this plan, so Terraform can't Step #1 - "tf plan": guarantee to take exactly these actions if you run "terraform apply" now. Finished Step #1 - "tf plan" Starting Step #2 - "tf apply" Step #2 - "tf apply": Already have image: hashicorp/terraform:1.5.0 Step #2 - "tf apply": *************************************************************************************** Step #2 - "tf apply": terraform apply was skipped because it's not a merge into the main branch. Step #2 - "tf apply": *************************************************************************************** Finished Step #2 - "tf apply" PUSH DONE main ブランチへマージ dev ブランチへのプッシュ ( terraform plan の実行結果) は想定通りでしたので、次に main ブランチにマージ (プッシュ) します。 main ブランチにマージ (プッシュ) 先程同様ビルド履歴を確認します。 main ブランチへのマージとなるため、今回のビルドでは terraform apply まで実行されていることがわかります。 # ログ出力 (重要な部分のみ抜粋) starting build "625f1b9e-0446-4c07-93ef-efaa11ec5310" Starting Step #0 - "tf init" Step #0 - "tf init": Initializing the backend... Step #0 - "tf init": Step #0 - "tf init": Successfully configured the backend "gcs"! Terraform will automatically Step #0 - "tf init": use this backend unless the backend configuration changes. Step #0 - "tf init": Upgrading modules... Step #0 - "tf init": - cicd_test in modules/cloud_storage Step #0 - "tf init": Step #0 - "tf init": Initializing provider plugins... Step #0 - "tf init": - Finding hashicorp/google versions matching "~> 4.65.2"... Step #0 - "tf init": - Installing hashicorp/google v4.65.2... Step #0 - "tf init": - Installed hashicorp/google v4.65.2 (signed by HashiCorp) Step #0 - "tf init": Step #0 - "tf init": Terraform has been successfully initialized! Step #0 - "tf init": Step #0 - "tf init": You may now begin working with Terraform. Try running "terraform plan" to see Step #0 - "tf init": any changes that are required for your infrastructure. All Terraform commands Step #0 - "tf init": should now work. Step #0 - "tf init": Step #0 - "tf init": If you ever set or change modules or backend configuration for Terraform, Step #0 - "tf init": rerun this command to reinitialize your working directory. If you forget, other Step #0 - "tf init": commands will detect it and remind you to do so if necessary. Finished Step #0 - "tf init" Starting Step #1 - "tf plan" Step #1 - "tf plan": Already have image: hashicorp/terraform:1.5.0 Step #1 - "tf plan": Step #1 - "tf plan": Terraform used the selected providers to generate the following execution Step #1 - "tf plan": plan. Resource actions are indicated with the following symbols: Step #1 - "tf plan": + create Step #1 - "tf plan": Step #1 - "tf plan": Terraform will perform the following actions: Step #1 - "tf plan": Step #1 - "tf plan": # module.cicd_test.google_storage_bucket.buckets["cicd-testbucket-01"] will be created Step #1 - "tf plan": + resource "google_storage_bucket" "buckets" { Step #1 - "tf plan": + force_destroy = false Step #1 - "tf plan": + id = (known after apply) Step #1 - "tf plan": + location = "ASIA-NORTHEAST1" Step #1 - "tf plan": + name = "cicd-testbucket-01" Step #1 - "tf plan": + project = "sample-project" Step #1 - "tf plan": + public_access_prevention = (known after apply) Step #1 - "tf plan": + self_link = (known after apply) Step #1 - "tf plan": + storage_class = "STANDARD" Step #1 - "tf plan": + uniform_bucket_level_access = true Step #1 - "tf plan": + url = (known after apply) Step #1 - "tf plan": } Step #1 - "tf plan": Step #1 - "tf plan": Plan: 1 to add, 0 to change, 0 to destroy. Step #1 - "tf plan": Step #1 - "tf plan": ───────────────────────────────────────────────────────────────────────────── Step #1 - "tf plan": Step #1 - "tf plan": Note: You didn't use the -out option to save this plan, so Terraform can't Step #1 - "tf plan": guarantee to take exactly these actions if you run "terraform apply" now. Finished Step #1 - "tf plan" Starting Step #2 - "tf apply" Step #2 - "tf apply": Already have image: hashicorp/terraform:1.5.0 Step #2 - "tf apply": Step #2 - "tf apply": Terraform used the selected providers to generate the following execution Step #2 - "tf apply": plan. Resource actions are indicated with the following symbols: Step #2 - "tf apply": + create Step #2 - "tf apply": Step #2 - "tf apply": Terraform will perform the following actions: Step #2 - "tf apply": Step #2 - "tf apply": # module.cicd_test.google_storage_bucket.buckets["cicd-testbucket-01"] will be created Step #2 - "tf apply": + resource "google_storage_bucket" "buckets" { Step #2 - "tf apply": + force_destroy = false Step #2 - "tf apply": + id = (known after apply) Step #2 - "tf apply": + location = "ASIA-NORTHEAST1" Step #2 - "tf apply": + name = "cicd-testbucket-01" Step #2 - "tf apply": + project = "sample-project" Step #2 - "tf apply": + public_access_prevention = (known after apply) Step #2 - "tf apply": + self_link = (known after apply) Step #2 - "tf apply": + storage_class = "STANDARD" Step #2 - "tf apply": + uniform_bucket_level_access = true Step #2 - "tf apply": + url = (known after apply) Step #2 - "tf apply": } Step #2 - "tf apply": Step #2 - "tf apply": Plan: 1 to add, 0 to change, 0 to destroy. Step #2 - "tf apply": module.cicd_test.google_storage_bucket.buckets["cicd-testbucket-01"]: Creating... Step #2 - "tf apply": module.cicd_test.google_storage_bucket.buckets["cicd-testbucket-01"]: Creation complete after 2s [id=cicd-testbucket-01] Step #2 - "tf apply": Step #2 - "tf apply": Apply complete! Resources: 1 added, 0 changed, 0 destroyed. Finished Step #2 - "tf apply" PUSH DONE Step #2 - "tf apply": Cloud コンソール上でもストレージバケットが作成されています。 Cloud Build × Terraform でストレージバケットが作成された 関連記事 過去の記事では GitHub Actions を用いて今回同様のアーキテクチャの実装方法もご紹介していますのでこちらも是非ご参照ください。 blog.g-gen.co.jp blog.g-gen.co.jp 武井 祐介 (記事一覧) 2022年4月入社 / クラウドソリューション部 / 技術2課所属 趣味はゴルフにロードバイク。IaC や CI/CD 周りのサービスやプロダクトが興味分野です。 Google Cloud 認定全冠達成!(2023年6月)
アバター
G-gen の杉村です。Google Cloud 上のネットワークに対する可視性を高めるための管理ツールである Network Intelligence Center を紹介します。Network Intelligence Center は、パケットの到達性テストや、トポロジの可視化、パフォーマンスの可視化、ファイアウォールルール最適化など、ネットワーク管理者向けの複数のツールを備えたサービスです。 Network Intelligence Center とは 接続テスト 概要 分析の仕組み 対応ノード ライブデータプレーン分析 料金 ネットワークトポロジ 概要 エンティティ データの鮮度 料金 パフォーマンスダッシュボード 概要 プロジェクトのパフォーマンスビュー Google Cloud のパフォーマンスビュー 確認可能な指標 料金 ファイアウォールインサイト 概要 シャドウルール 制限が過度に緩いルール ヒットのある拒否ルール 料金 ネットワークアナライザ 概要 VPC ネットワーク ネットワークサービス Google Kubernetes Engine(GKE) ハイブリッド接続 マネージドサービス 料金 Flow Analyzer Network Intelligence Center とは Network Intelligence Center は、Google Cloud におけるネットワークの可視性向上やトラブルシューティングのための、ネットワーク管理者向けツール群です。主に Google Cloud コンソール上で利用します。 参考 : Network Intelligence Center の概要 Network Intelligence Center は以下のモジュール(機能)で構成されています。 No 名称 概要 1 接続テスト ネットワーク的な到達性をテスト 2 ネットワークトポロジ VPC や周辺のオンプレミスネットワークの構成を可視化 3 パフォーマンスダッシュボード ネットワーク基盤に起因するパフォーマンス情報を可視化 4 ファイアウォールインサイト ファイアウォールの不要なルール等を精査 5 ネットワークアナライザ ネットワーク周りの構成ミスや最適でない構成を検出 5 Flow Analyzer VPC Flow Logs を簡単な操作で分析 料金は各モジュールごとに設定されており、基本的には使用ボリュームに応じた従量課金です。 参考 : Network Intelligence Center pricing 接続テスト 概要 接続テスト (Connectivity Test)は、ノード間の通信の到達性をテストできるツールです。Google Cloud でのパケット到達性のトラブルシューティングに活用できます。 テストの際に実際にパケットが送信されるのか、机上シミュレートだけなのかは、接続シナリオによって異なります。実際にパケットを送信して分析する機能は ライブデータプレーン分析 と呼ばれます。 接続元の VM 等と、接続先の VM 等を指定して実行することで、ファイアウォールの設定やルーティングの設定などを論理的に診断し、通信の可否を表示してくれます。通信が到達できない場合は、その理由やどこでパケットが破棄されているかも確認できます。 参考 : 接続テストの概要 接続テスト 分析の仕組み ある VM が別の VM や、オンプレミスのノード等に到達するまで(あるいはその逆方向の通信)には、VPC ネットワークピアリングや Cloud VPN などのネットワーク経路や、VPC ルートテーブルや VPC ファイアウォールなどの通信制御機能を経由します。 接続テスト機能ではこういった様々なネットワーク経路や通信制御機能を考慮に入れた検証が行われます。例えば VPC ファイアウォールで通信がブロックされている場合は、その旨を表示してくれます。 また通信プロトコルとして TCP、UDP、ICMP のほか AH、ESP なども指定できます。 基本的には、クラウドリソース上の設定を読み取った論理的なシミュレーションによって診断が行われますが、後述のとおり、一部のノード間の通信の場合は、実際にパケットを送受信するライブデータプレーン分析が行われます。 対応ノード 接続テスト機能は、例として以下のノード間の通信をテストできます。 Compute Engine VM App Engine(Standard environment のみ) Cloud Run Cloud Run functions Cloud SQL インスタンス Google Kubernetes Engine(GKE) オンプレミスノード(宛先として) インターネット上の IP アドレス(宛先として) また接続テストは、VPC ネットワークピアリングや Network Connectivity Center(NCC)、Cloud VPN や Cloud Interconnect といった各種ネットワーキングサービスに対応しています。 反対に、以下はテストに 対応していない (テスト時に考慮されない)ので注意が必要です(一部抜粋)。 Cloud Armor ポリシー GKE network policies、IP masquerading App Engine(Flexible environment) どのようなネットワークサービスや構成が接続テストに対応しているかの詳細は、以下のドキュメントを参照してください。 参考 : 接続テストの概要 - サポートされている構成 参考 : 接続テストの概要 - サポートされていない構成 ライブデータプレーン分析 論理的なシミュレーションだけでなく、実際にパケットを送信して分析する機能は ライブデータプレーン分析 と呼ばれます。ただし、この機能はあくまでネットワーク到達性の診断のために使うものであって、継続的なモニタリング目的に用いるものではないことに留意して下さい。 ライブデータプレーン分析は、以下のノード間の TCP および UDP 通信のテストの際に行われます。 送信元 Compute Engine VM サーバーレス VPC アクセスコネクタを使う Cloud Run、Cloud Run functions、App Engine(Standard environment) Cloud SQL インスタンス Google Kubernetes Engine(GKE)コントロールプレーン 宛先 Compute Engine VM Internal passthrough Network Load Balancer インターネット IP アドレス(エッジロケーション間との通信をテスト) Cloud Interconnect 以下のプライベートエンドポイント Cloud SQL Memorystore for Redis Google Kubernetes Engine(GKE)コントロールプレーン また、対応しているネットワークリソースにいくつかの制限があります。詳細は以下のドキュメントを参照してください。 参考 : 接続テストの概要 - 接続テストによるライブ データプレーンの分析方法 料金 1か月の中で実行されたテスト回数に対して課金されます。月間20回までは無料で、21回目からは1回あたり $0.15 の料金が発生します。 参考 : Network Intelligence Center pricing - Connectivity Tests pricing details ネットワークトポロジ 概要 ネットワークトポロジ (Network Topology)は、Google Cloud の VPC を中心としたネットワーク構造とパフォーマンスを可視化するためのツールです。 Google Cloud 基盤からリアルタイムにデータを収集し、複数プロジェクトにまたがる構成も可視化できます。エージェント等は不要です。過去6週間の履歴が保存されます。 この機能により、ネットワーク構成をグラフィカルに把握できる他、ノード間のトラフィック量が表示されるため、パフォーマンス分析に活かすことも可能です。 ネットワークトポロジのグラフ表示 参考 : ネットワーク トポロジの概要 エンティティ ネットワークトポロジ機能における エンティティ とは、通常のネットワーク用語における「ノード」とほぼ同様です。ネットワーク通信を行うリソースの個々の単位を表します。エンティティはグラフ上に、特有のアイコンで表現されます。例として、以下のようなものがあります。 VM インスタンス VM インスタンスグループ ロードバランサー Cloud NAT VPC ネットワークピアリング 国(通信元クライアントの存在する国) Cloud Interconnect Cloud VPN オンプレミス(他のパブリッククラウドもこの表記) アイコン画像の凡例は、以下のドキュメントを参照してください。 参考 : ネットワーク トポロジの概要 - エンティティ データの鮮度 表示されるデータは、毎時0分を起点とする1時間ごとのスナップショットです。 Google Cloud コンソール上では、コントロールバーをスライドさせて特定の時刻のグラフを表示させます。 過去データは6週間分まで保存されます。 料金 当機能には、以下の料金体系が設定されていながら、ネットワークトポロジ機能の料金は 2026年2月現在、無償 で利用できます。公式の料金ページには「すべてのユーザーが100%割引で利用可能。課金開始の90日前には通知する」旨の記載がされています。 ネットワークトポロジ機能の料金は、後述のパフォーマンスダッシュボードの料金とセットです。 ネットワークトポロジとパフォーマンスダッシュボードを有効化すると、VM 数に応じた料金($0.0011 / VM / 時間)が発生することに加え、VM とインターネット間で通信が生じた場合は追加の料金($0.0008 / VM / 時間)が発生します。 VM が20台の場合、30日ある月では $0.0011 × 20台 × 24時間 × 30日 で $15.84/月 が発生し、これら全ての VM が常にインターネットと通信していたと仮定すると、追加で $0.0008 × 20台 × 24時間 × 30日 = $11.52/月 が発生します。 参考 : Network Intelligence Center pricing - Network Topology and Performance Dashboard pricing details パフォーマンスダッシュボード 概要 パフォーマンスダッシュボード 機能では、Google Cloud ネットワークのパフォーマンスを可視化できます。アプリケーション固有の問題と、Google Cloud ネットワーク基盤の問題を切り分けすることにも役立てることができます。 また収集されたデータは Cloud Monitoring にもエクスポートされます。データは過去6週間分まで保存されています。 パフォーマンスダッシュボードでは大きく分けて プロジェクトのパフォーマンスビュー と Google Cloud のパフォーマンスビュー の2種類が閲覧可能です。 参考 : パフォーマンス ダッシュボードの概要 パフォーマンスダッシュボード プロジェクトのパフォーマンスビュー プロジェクトのパフォーマンスビュー では「VM インスタンス間のトラフィック」と「Google Cloud とインターネットロケーション間のトラフィック」の2種類のパフォーマンスが確認できます。 「VM インスタンス間のトラフィック」では、別々のゾーンにある Compute Engine VM 同士の通信におけるパケットロスとレイテンシの指標が表示可能です。 「Google Cloud とインターネットロケーション間のトラフィック」では、Compute Engine VM が存在するリージョンと、その VM が通信するインターネット上のノードの間のレイテンシが表示できます。 Google Cloud のパフォーマンスビュー Google Cloud のパフォーマンスビュー では特定プロジェクトに依存しない、Google Cloud 全体としての指標が確認できます。 プロジェクトのパフォーマンスビューと同様に「VM インスタンス間のトラフィック」と「Google Cloud とインターネットロケーション間のトラフィック」の2種類のパフォーマンスが確認でき、プロジェクトで観測されたパフォーマンスとの比較が可能です。 ネットワーク関連のパフォーマンス低下が起こった際に、その問題がプロジェクト固有 (あるいは Google Cloud 物理基盤のごく一部) に発生した問題なのか、Google Cloud 基盤全体で発生している問題なのかの把握に役立ちます。 また、Google Cloud 上に新しいインフラを構築しようとしているときの設計にも役立ちます。予め、特定のリージョン間・ゾーン間の平均的なレイテンシを確認できるため、VM をわざわざ構築して検証しなくても、過去の実績に基づいた情報を確認できます。 ゾーン間のレイテンシの実績が分かる 確認可能な指標 各ビューでは 指標 (メトリクス)として パケットロス と レイテンシ が確認できます。 バックエンドの仕組みとして、Google Cloud の VM をホストしている物理マシン上でワーカーが実行されており、このワーカー間で検査用パケットがやりとりされ、指標が計測されています。我々ユーザは、このワーカーやパケットを意識することはありませんし、VM のパフォーマンスに影響はありません。 料金 パフォーマンスダッシュボードの料金は前述のネットワークトポロジ機能とセットです。 前述のとおり、当機能には詳細に料金体系が設定されていながら、 2026年2月現在、無償 で利用できます。公式の料金ページには「すべてのユーザーが100%割引で利用可能。課金開始の90日前には通知する」旨の記載がされています。 参考 : Network Intelligence Center pricing - Network Topology and Performance Dashboard pricing details ファイアウォールインサイト 概要 ファイアウォールインサイト はファイアウォールルールの最適化と整理に役立つツールです。緩すぎるルールに対するサジェストや、逆に無駄なルールの削除の推奨などを表示します。 当機能は、ファイアウォールが意図したとおりに使われていることを確認したり、あるいはルールへのヒット数へのスパイクを検知してアラート発報するなどの用途にも利用可能です。 当機能で観測可能な事項は主に「 シャドウルール 」「 制限が過度に緩いルール 」「 ヒットのある拒否ルール 」に大別できます。 このうち後者の2つはファイアウォールルールのログから情報を取得しています。そのためログが有効になっていないファイアウォールルールは検査対象になりません。 参考 : ファイアウォール インサイトの概要 ファイアウォールインサイト なお、当機能のバックエンドでは Recommender API が使われています。 参考 : インサイト シャドウルール シャドウルール (Shadowed rules)とは「より優先度の高い他のルールによってカバー済みのため、存在している意味がないルール」を意味します。 例えば「10.10.0.0/16 からの tcp:80 パケットは許可(優先度500)」というルールが存在するとします。同じ VPC に「10.10.0.0/24 からの tcp:80 パケットは許可(優先度1000)」というルールがあるとします。後者のルールが適用され得るパケットは、前者のルールによって先に許可されるため、後者のルールが使われることは決してありません。このようなルールが、シャドウルールと呼ばれます。 このようなルールは構成情報を分かりづらくなるもとであり、適切な設計を阻害したり、運用性を低下させるため、削除することが望ましいです。 ファイアウォールインサイトでは VPC ファイアウォールルールとファイアウォールポリシーの両方でこのようなシャドウルールを検知することができます。 VPC ファイアウォールルールとファイアウォールポリシーの違いについては、以下の記事も参照してください。 blog.g-gen.co.jp 制限が過度に緩いルール 制限が過度に緩いルール (Overly permissive rules)とはファイアウォールルールのうち、利用実態から判断して過剰な許可条件が設定されており緩すぎると判断される許可ルール(allow rules)を指します。具体的には、以下のような許可ルールです。 名称 概要 ヒットのない許可ルール 観察期間中にヒットがなかった。今後ヒットの可能性があるかどうか機械学習での予測が表示される (同組織内の類似ルールから予測) 傾向分析に基づいて使用されていない許可ルール 過去6週間の使用傾向に基づいた機械学習により、今後の使用可能性が低いルールを表示 未使用の属性を含む許可ルール 観察期間中にヒットしなかった IP アドレスやポート範囲などの属性を含む許可ルールを表示 制限が過度に緩い IP アドレスまたはポート範囲を含む許可ルール 広すぎる IP アドレスまたはポート範囲が存在する可能性がある許可ルールを特定 このように、機械学習に基づいて不用意なルール設定がされている許可ルールを特定することにより、許可ルールの設定を最小限に抑える判断の補助に利用できます。ただしいずれも、検知にはファイアウォールルールで ロギングがオン になっている必要があります。 VPC ファイアウォールのルールとファイアウォールポリシーのルールの両方が検査対象になります。 ヒットのある拒否ルール ヒットのある拒否ルール (Deny rules with hits)では指定した観察期間中に deny ルールにどのくらいのパケットがヒットしたかを確認できます。 ネットワークの構成ミスや、意図的な攻撃による deny ヒットの急増などを検知することができます。 ただし検知にはファイアウォールルールでロギングがオンになっている必要があります。 料金 シャドウルールの検知では、プロジェクトで機能を有効化した直後の最初の検査で、プロジェクトに存在するファイアウォールルールあたり $1 が発生します。その後、追加の検査では、1ルールあたり$0.1が発生します。検査は、ファイアウォールルールに変更が加わった日のみ、VPC ネットワークごとに行われます。 制限が過度に緩いルールの検査は、検査されたファイアウォールログのログエントリの数量に対して課金されます。100万〜100億(1M〜10,000M)エントリまでは $0.20/百万ログエントリ/月の料金が発生し、それ以上のログ量ではティア状に単価が安くなるように設定されています。 ヒットのある拒否ルールや、その他のファイアウォールインサイト関連のメトリクスは無料です。 なお、料金が発生するため、上記の各検知機能は個別にオン・オフができるようになっています。 参考 : Network Intelligence Center pricing - Firewall Insights pricing details ネットワークアナライザ 概要 ネットワークアナライザ とは、VPC ネットワークの構成ミスや最適でない構成を検出するためのツールです。 検知事項の重大度は重大>高>中>低として分類され、以下のカテゴリ分けがされます。 VPC ネットワーク ネットワークサービス Google Kubernetes Engine(GKE) ハイブリッド接続 マネージドサービス 分析は、関連する設定が変更されることをトリガにして約10分後に実行されます。また、少なくとも1日1回の定期分析も行われます。 参考 : ネットワーク アナライザの概要 ネットワークアナライザ VPC ネットワーク VPC ネットワーク関連の検知事項には、以下のようなものがあります。 名称 概要 無効なネクストホップを含むルートに関する分析情報 ネクストホップとなっていた VM 等が利用不可でルートが無効になっている IP アドレス使用状況の分析情報 サブネット内の空き IP が残り少ない 未使用 IP アドレスの分析情報 プロジェクトで 24 時間以上割り振られていない外部 IP アドレスがある ネットワークサービス ネットワークサービス関連の検知事項には、以下のようなものがあります。 ロードバランサのヘルスチェック用のファイアウォールが構成されていない ロードバランサのヘルスチェック用の IP アドレス範囲がブロックされている ロードバランサで実トラフィックとヘルスチェックで異なるポートを使っている Cloud NAT でリソース不足によるパケットドロップが起きている Google Kubernetes Engine(GKE) Google Kubernetes Engine(GKE)の検知事項には、以下のようなものがあります。 ルーティングの問題により、GKE ノードからコントロール プレーンへの接続がブロックされる Pod に割り当てられたアドレス範囲の使用率が 80% を超えている その他、GKE のネットワーク構成がベストプラクティスに従っていない ハイブリッド接続 ハイブリッド接続の検知事項には、以下のようなものがあります。 Cloud Router や VPC ネットワークピリング経由で VPC が受け取った動的ルートが、より優先度の高い静的ルートやサブネットルートによりシャドウルート化してしまっている マネージドサービス マネージドサービスの検知事項には、以下のようなものがあります。 下り(外向き)ファイアウォールによって Cloud SQL インスタンスへの接続がブロックされる ルーティングの問題によって Cloud SQL インスタンスへの接続がブロックされる Cloud SQL インスタンスへの接続に関する問題: インスタンスが実行されていない 料金 ネットワークアナライザ機能も、ネットワークトポロジ機能、パフォーマンスダッシュボード機能と同様に、詳細に料金体系が設定されていながら、2026年2月現在、無償で利用できます。公式の料金ページには「すべてのユーザーが100%割引で利用可能。課金開始の90日前には通知する」旨の記載がされています。 ネットワークアナライザの料金は、有効化したプロジェクト内で動作する Compute Engine VM 数 および Google Kubernetes Engine(GKE)ノード数によって決定します。$0.0011 / VM / 時間の料金が設定されています。 参考 : Network Intelligence Center pricing - Network Analyzer pricing details Flow Analyzer Flow Analyzer は、VPC Flow Logs を分析するためのツールです。従来、VPC Flow Logs を分析するには、BigQuery にエクスポートするか、Cloud Logging の Log Analytics 機能を用いて、複雑な SQL を記述して検索を実行する必要がありました。Flow Analyzer を使うと、VPC Flow Logs に対してより簡単にクエリを自動作成したり、ビジュアライズすることができます。 参考 : Flow Analyzer の概要 なお、Flow Analyzer のバックエンドでは Cloud Logging の Log Analytics 機能が使われています。 Flow Analyzer は、ログバケットに保存されている VPC Flow Logs に対してクエリを実行します。これにより、VPC ネットワーク内を通過して Compute Engine VM や GKE ノードに出入りしたパケットの情報をクエリできます。 クエリ結果は、データ量またはレイテンシ(RTT、Round-trip time の傾向を表示)で表示できます。 杉村 勇馬 (記事一覧) 執行役員 CTO 元警察官という経歴を持つ IT エンジニア。クラウド管理・運用やネットワークに知見。AWS 認定資格および Google Cloud 認定資格はすべて取得。X(旧 Twitter)では Google Cloud や Google Workspace のアップデート情報をつぶやいています。 Follow @y_sugi_it
アバター
G-gen 又吉です。Google Cloud (旧称 GCP) の事前トレーニング済みの API のひとつである Cloud Vision API を用いて車のナンバープレートをマスキングする処理をご紹介します。 はじめに Vision AI Vision API 事前確認でわかったこと 構成図 準備 ディレクトリ構成 main.tf gcf_source_code/detect_car main.py requirements.txt gcf_source_code/detect_license_plate main.py requirements.txt 動作検証 検証データ 実行 Cloud Vision API と Cloud Functions でナンバープレートをマスキング はじめに Vision AI Vision AI とは、Google Cloud 上で画像や動画から分析情報を取得することができる以下の 3 つのプロダクトの総称です。 Vertex AI Vision Custom ML models ( AutoML or Vertex AI 独自モデル) Vision API 今回は、その中の Vision API を使用します。 Vision API Vision API とは、事前トレーニング済み Vision API モデル使用して、オブジェクトの検知や OCR などが行なえます。 Vision API には、主に以下のような機能があります。 No 機能タイプ 説明 1 Text detection 画像の光学式文字認識 (OCR)。画像内の UTF-8 テキストを識別し抽出できる。 2 Landmark detection ランドマークの名前、信頼度スコア、および境界ボックス (画像内の位置) が取得できる。 3 Label detection 「People」や「Car」のような一般化されたラベルが取得でき、各ラベルには説明と信頼度スコアが記載されている。 4 Object localization 複数のオブジェクトに「People」や「Car」のような一般的なラベルと境界ボックス (画像内の位置) が取得できる。 5 Face detection 顔を特定し、目、耳、鼻、口などの特定の顔の「ランドマーク」を信頼度スコアとともに取得でき、また表情から感情の尤度評価も取得できる。 その他の機能、また詳細については以下をご参照下さい。 Features list また、 Cloud Vision API の概要ページ からローカルの画像を Vision API に読み込ませ、どのような出力がでるかすぐに確認できます。 (※ 当記事で扱う車の画像データに含まれるナンバープレートは、個人情報保護の観点からモザイク処理をかけております。) Cloud Vision API 概要ページの出力結果 Vision API は事前トレーニング済み Vision API モデルで解決できる場合に有用ですが、自社の製品をカスタムラベルとして設定したい等、別途トレーニングが必要な場合は Vertex AI Vision や AutoML などを用いる必要があります。 事前確認でわかったこと 事前に 2 枚の画像を、Cloud Vision API の概要ページからどのような出力結果がえられるのか試してみます。 出力結果① 出力結果② 出力結果①では、検出されたオブジェクトの中に Car (車)と License Plate (ナンバープレート) が含まれていることが確認できます。 しかし、出力結果②では Car は検出できているが License Plate が検出できていない ことがわかります。 そこで、出力結果②の画像から車周辺を切り取りった新たな画像で確認してみます。 出力結果③ 車周辺を切り取りった画像の出力 (出力結果③) では、License Plate がオブジェクトとして検出されました。 これらの事前確認結果から、Cloud Vision API へリクエストを送信する際、画像いっぱいに車体を入れることでナンバープレートを検出してくれる可能性が高まることがわかりました。 そこで今回は、画像内の車を検出したら車周辺を切り取ったものを新しい画像とし、その新しい画像からナンバープレートを検出しマスキング処理 (白塗り) を行う構成とします。 ナンバープレートをマスキングする処理の流れ 構成図 今回の構成図は以下のとおりです。 構成図 ユーザーが Raw Data バケットに画像データをアップロードすると、Cloud Storage トリガー経由で Cloud Functions (車検出&抽出関数) が起動します。画像内の車オブジェクトがあれば車周辺を切り取った新しい画像を Detected Car バケットに格納します。 次に、Detected Car バケットに画像データがアップロードされると、Cloud Storage トリガー経由で Cloud Functions (LP検出&マスキング関数) が起動します。画像内のナンバープレートオブジェクトがあればナンバープレートをマスキングした新しい画像を Detected LP バケットに格納します。 準備 ディレクトリ構成 開発環境は Cloud Shell を用いて行います。ディレクトリ構造は以下のとおりです。 terraform ディレクトリ配下は、以下のとおりです。 terraform |-- gcf_source_code | |-- detect_car | | |-- main.py | | `-- requirements.txt | ` -- detect_license_plate | |-- main.py | `-- requirements.txt ` -- main.tf main.tf main.tf には Terraform のコードを記述しています。 locals { terraform_service_account = ${Terraform 実行に使われるサービスアカウントのメールアドレス} project_name = ${プロジェクト名} project_id = ${プロジェクト ID} folder_id = ${フォルダ ID} billing_account_id = ${請求先アカウント ID} } # terraform & provider の設定 terraform { required_providers { google = { source = "hashicorp/google" version = ">= 4.0.0" } } required_version = ">= 1.3.0" backend "gcs" { bucket = ${tfstate ファイルを格納する Cloud Storage バケット名} impersonate_service_account = ${Terraform 実行に使われるサービスアカウントのメールアドレス} } } # サービスアカウント権限借用の設定 provider "google" { alias = "impersonation" scopes = [ "https://www.googleapis.com/auth/cloud-platform", "https://www.googleapis.com/auth/userinfo.email", ] } data "google_service_account_access_token" "default" { provider = google.impersonation target_service_account = local.terraform_service_account scopes = ["userinfo-email", "cloud-platform"] lifetime = "1200s" } # Google プロバイダの設定 provider "google" { project = local.project_id region = "asia-northeast1" access_token = data.google_service_account_access_token.default.access_token request_timeout = "60s" } ###################################### ### プロジェクトの作成と API の有効化 ### ###################################### # プロジェクトの作成 resource "google_project" "poc" { name = local.project_name project_id = local.project_id folder_id = local.folder_id billing_account = local.billing_account_id } # API の有効化 module "tenant_a_project_services" { source = "terraform-google-modules/project-factory/google//modules/project_services" version = "14.2.1" project_id = google_project.poc.project_id enable_apis = true activate_apis = [ "iam.googleapis.com", "cloudbuild.googleapis.com", "run.googleapis.com", "cloudfunctions.googleapis.com", "pubsub.googleapis.com", "eventarc.googleapis.com", "artifactregistry.googleapis.com", "storage.googleapis.com", "vision.googleapis.com" ] disable_services_on_destroy = false } ####################################### ### サービスアカウントの作成と権限の付与 ## ####################################### # Cloud Functions 用サービスアカウントの作成と権限付与 resource "google_service_account" "sa_gcf" { project = google_project.poc.project_id account_id = "sa-gcf" display_name = "Cloud Functions 用サービスアカウント" } resource "google_project_iam_member" "invoke_gcf" { project = google_project.poc.project_id role = "roles/run.invoker" member = "serviceAccount:${google_service_account.sa_gcf.email}" } resource "google_project_iam_member" "storage_admin" { project = google_project.poc.project_id role = "roles/storage.admin" member = "serviceAccount:${google_service_account.sa_gcf.email}" } resource "google_project_iam_member" "event_receiving" { project = google_project.poc.project_id role = "roles/eventarc.eventReceiver" member = "serviceAccount:${google_service_account.sa_gcf.email}" depends_on = [google_project_iam_member.invoke_gcf] } resource "google_project_iam_member" "artifactregistry_reader" { project = google_project.poc.project_id role = "roles/artifactregistry.reader" member = "serviceAccount:${google_service_account.sa_gcf.email}" depends_on = [google_project_iam_member.event_receiving] } # Eventarc のサービスアカウントに権限付与 resource "google_project_iam_member" "serviceAccount_token_creator" { project = google_project.poc.project_id role = "roles/iam.serviceAccountTokenCreator" member = "serviceAccount:service-${google_project.poc.number}@gcp-sa-pubsub.iam.gserviceaccount.com" depends_on = [module.tenant_a_project_services] } # Cloud Storage のサービスアカウントに権限付与 data "google_storage_project_service_account" "gcs_account" { project = google_project.poc.project_id depends_on = [ module.tenant_a_project_services ] } resource "google_project_iam_member" "gcs_pubsub_publishing" { project = google_project.poc.project_id role = "roles/pubsub.publisher" member = "serviceAccount:${data.google_storage_project_service_account.gcs_account.email_address}" } ################################ ### バケットとオブジェクトの作成 ### ################################ # Cloud Functions のソースコード格納用バケットの作成 resource "google_storage_bucket" "source_gcf" { project = google_project.poc.project_id location = "asia-northeast1" name = "${google_project.poc.project_id}-source-gcf" force_destroy = true } # Cloud Functions で使うソースコードを ZIP 化 data "archive_file" "detect_car" { type = "zip" source_dir = "./gcf_source_code/detect_car" output_path = "./zip_source_code/detect_car.zip" } data "archive_file" "detect_license_plate" { type = "zip" source_dir = "./gcf_source_code/detect_license_plate" output_path = "./zip_source_code/detect_license_plate.zip" } # ZIP 化したソースコードをバケットに追加 resource "google_storage_bucket_object" "detect_car" { name = "detect-car.${data.archive_file.detect_car.output_md5}.zip" bucket = google_storage_bucket.source_gcf.name source = data.archive_file.detect_car.output_path } resource "google_storage_bucket_object" "detect_license_plate" { name = "detect-license-plate.${data.archive_file.detect_license_plate.output_md5}.zip" bucket = google_storage_bucket.source_gcf.name source = data.archive_file.detect_license_plate.output_path } # raw_data バケットの作成 resource "google_storage_bucket" "raw_data" { project = google_project.poc.project_id location = "asia-northeast1" name = "${google_project.poc.project_id}-raw-data" force_destroy = true } # detected_car バケットの作成 resource "google_storage_bucket" "detected_car" { project = google_project.poc.project_id location = "asia-northeast1" name = "${google_project.poc.project_id}-detected-car" force_destroy = true } # not_detected_car バケットの作成 resource "google_storage_bucket" "not_detected_car" { project = google_project.poc.project_id location = "asia-northeast1" name = "${google_project.poc.project_id}-not-detected-car" force_destroy = true } # detected_license_plate バケットの作成 resource "google_storage_bucket" "detected_license_plate" { project = google_project.poc.project_id location = "asia-northeast1" name = "${google_project.poc.project_id}-detected-license-plate" force_destroy = true } # not_detected_license_plate バケットの作成 resource "google_storage_bucket" "not_detected_license_plate" { project = google_project.poc.project_id location = "asia-northeast1" name = "${google_project.poc.project_id}-not-detected-license-plate" force_destroy = true } ############################ ### Cloud Functions 作成 ### ############################ # detect_car 関数 resource "google_cloudfunctions2_function" "detect_car" { depends_on = [ google_project_iam_member.event_receiving, google_project_iam_member.artifactregistry_reader, google_project_iam_member.serviceAccount_token_creator ] name = "detect-car" location = "asia-northeast1" description = "画像から車を切り取って detect_car バケットに格納する関数" build_config { runtime = "python310" entry_point = "main" # Set the entry point in the code source { storage_source { bucket = google_storage_bucket.source_gcf.name object = google_storage_bucket_object.detect_car.name } } } service_config { max_instance_count = 3 min_instance_count = 1 available_memory = "256M" timeout_seconds = 60 environment_variables = { DETECTED_CAR_BUCHET_NAME = google_storage_bucket.detected_car.name NOT_DETECTED_CAR_BUCHET_NAME = google_storage_bucket.not_detected_car.name KEY = "Car" } service_account_email = google_service_account.sa_gcf.email } event_trigger { trigger_region = "asia-northeast1" event_type = "google.cloud.storage.object.v1.finalized" retry_policy = "RETRY_POLICY_RETRY" service_account_email = google_service_account.sa_gcf.email event_filters { attribute = "bucket" value = google_storage_bucket.raw_data.name } } } # detected_license_plate 関数 resource "google_cloudfunctions2_function" "detected_license_plate" { depends_on = [ google_project_iam_member.event_receiving, google_project_iam_member.artifactregistry_reader, google_project_iam_member.serviceAccount_token_creator ] name = "detected-license-plate" location = "asia-northeast1" description = "画像からナンバープレートをマスキングして detected_license_plate バケットに格納する関数" build_config { runtime = "python310" entry_point = "main" # Set the entry point in the code source { storage_source { bucket = google_storage_bucket.source_gcf.name object = google_storage_bucket_object.detect_license_plate.name } } } service_config { max_instance_count = 3 min_instance_count = 1 available_memory = "256M" timeout_seconds = 60 environment_variables = { DETECTED_LICENSE_PLATE = google_storage_bucket.detected_license_plate.name NOT_DETECTED_LICENSE_PLATE = google_storage_bucket.not_detected_license_plate.name KEY = "License plate" } service_account_email = google_service_account.sa_gcf.email } event_trigger { trigger_region = "asia-northeast1" event_type = "google.cloud.storage.object.v1.finalized" retry_policy = "RETRY_POLICY_RETRY" service_account_email = google_service_account.sa_gcf.email event_filters { attribute = "bucket" value = google_storage_bucket.detected_car.name } } } gcf_source_code/detect_car main.py gcf_source_code/detect_car には、画像から車を切り取ってバケットに格納する処理を行う Cloud Functions のソースコードを格納しています。 from io import BytesIO import os from cloudevents.http import CloudEvent import functions_framework from google.cloud import vision from google.cloud import storage from PIL import Image DETECTED_CAR_BUCHET_NAME = os.environ.get( "DETECTED_CAR_BUCHET_NAME" ) NOT_DETECTED_CAR_BUCHET_NAME = os.environ.get( "NOT_DETECTED_CAR_BUCHET_NAME" ) KEY = os.environ.get( "KEY" ) # クライアントを初期化 vision_image_annotator_client = vision.ImageAnnotatorClient() storage_client = storage.Client() def download_blob (bucket_name, blob_name): # バケットを取得 bucket = storage_client.get_bucket(bucket_name) # オブジェクト(画像)を取得 blob = bucket.blob(blob_name) image_data = blob.download_as_bytes() return image_data def localize_objects (image_data): # Vision API 実行 image = vision.Image(content=image_data) objects = vision_image_annotator_client.object_localization(image=image).localized_object_annotations # 辞書型に整形 localize_object_di = {} for object_ in objects: vertex_li = [] for vertex in object_.bounding_poly.normalized_vertices: vertex_li.append({ "x" : vertex.x, "y" : vertex.y}) localize_object_di[object_.name] = { "score" : object_.score, "vertex" : vertex_li } return localize_object_di def cropped_image_upload_detected_car_bucket ( image_data, key_di, detected_car_bucket_name, blob_name): # BytesIOを使って画像データを読み込み image = Image.open(BytesIO(image_data)) # 画像のサイズを取得 width, height = image.size # 左下と右上の座標に margin を追加 margin = 0.05 key_di[ 'vertex' ][ 0 ][ "x" ] = key_di[ 'vertex' ][ 0 ][ "x" ] - margin # 左下 x 軸 key_di[ 'vertex' ][ 0 ][ "y" ] = key_di[ 'vertex' ][ 0 ][ "y" ] - margin # 左下 y 軸 key_di[ 'vertex' ][ 2 ][ "x" ] = key_di[ 'vertex' ][ 2 ][ "x" ] + margin # 右上 x 軸 key_di[ 'vertex' ][ 2 ][ "y" ] = key_di[ 'vertex' ][ 2 ][ "y" ] + margin # 右上 y 軸 x_coords = [v[ "x" ] * width for v in key_di[ "vertex" ]] y_coords = [v[ "y" ] * height for v in key_di[ "vertex" ]] # 切り抜きの領域を設定 left = min (x_coords) upper = min (y_coords) right = max (x_coords) lower = max (y_coords) # 画像を切り抜く cropped_image = image.crop((left, upper, right, lower)) # ローカルに一時的に保存 tmp_file = f "/tmp/{blob_name}" cropped_image.save(tmp_file) # Cloud Storage へアップロード bucket = storage_client.get_bucket(detected_car_bucket_name) blob_cropped = bucket.blob(blob_name) blob_cropped.upload_from_filename(tmp_file) # ローカルから削除 os.remove(tmp_file) return "ok" def upload_not_detected_car_bucket (image_data, not_detected_car_bucket_name, blob_name): # BytesIOを使って画像データを読み込み image = Image.open(BytesIO(image_data)) # ローカルに一時的に保存 tmp_file = f "/tmp/{blob_name}" image.save(tmp_file) # Cloud Storage へアップロード bucket = storage_client.get_bucket(not_detected_car_bucket_name) blob_cropped = bucket.blob(blob_name) blob_cropped.upload_from_filename(tmp_file) return "ok" def is_check_key (localize_object_di, key): # 辞書のキーの中に特定の文字列が1つだけあるかどうかをチェック key_to_check = key key_count = list (localize_object_di.keys()).count(key_to_check) if key_count == 1 : return True else : return False @ functions_framework.cloud_event def main (cloud_event: CloudEvent): # CloudEvent から渡されたデータを取得 data = cloud_event.data bucket_name = data[ "bucket" ] file_name = data[ "name" ] image_data = download_blob(bucket_name=bucket_name, blob_name=file_name) localize_object_di = localize_objects(image_data=image_data) if is_check_key(localize_object_di=localize_object_di, key=KEY): cropped_image_upload_detected_car_bucket( image_data=image_data, key_di=localize_object_di[KEY], detected_car_bucket_name=DETECTED_CAR_BUCHET_NAME, blob_name=file_name) else : upload_not_detected_car_bucket( image_data=image_data, not_detected_car_bucket_name=NOT_DETECTED_CAR_BUCHET_NAME, blob_name=file_name) return "ok" requirements.txt functions-framework==3.* cloudevents==1.9.0 google-cloud-vision==3.4.4 google-cloud-storage==2.10.0 numpy==1.25.1 Pillow==10.0.0 opencv-python==4.8.0.74 gcf_source_code/detect_license_plate main.py gcf_source_code/detect_license_plate には、ナンバープレートをマスキングしてバケットに格納する処理を行う Cloud Functions のソースコードを格納しています。 from io import BytesIO import os from cloudevents.http import CloudEvent import functions_framework from google.cloud import vision from google.cloud import storage from PIL import Image from PIL import ImageDraw DETECTED_LICENSE_PLATE = os.environ.get( "DETECTED_LICENSE_PLATE" ) NOT_DETECTED_LICENSE_PLATE = os.environ.get( "NOT_DETECTED_LICENSE_PLATE" ) KEY = os.environ.get( "KEY" ) # クライアントを初期化 vision_image_annotator_client = vision.ImageAnnotatorClient() storage_client = storage.Client() def download_blob (bucket_name, blob_name): # バケットを取得 bucket = storage_client.get_bucket(bucket_name) # オブジェクト(画像)を取得 blob = bucket.blob(blob_name) image_data = blob.download_as_bytes() return image_data def localize_objects (image_data): # Vision API 実行 image = vision.Image(content=image_data) objects = vision_image_annotator_client.object_localization(image=image).localized_object_annotations # 辞書型に整形 localize_object_di = {} for object_ in objects: vertex_li = [] for vertex in object_.bounding_poly.normalized_vertices: vertex_li.append({ "x" : vertex.x, "y" : vertex.y}) localize_object_di[object_.name] = { "score" : object_.score, "vertex" : vertex_li } return localize_object_di def masked_image_upload_detected_license_plate_bucket ( image_data, key_di, detected_license_plate_bucket, blob_name): # BytesIOを使って画像データを読み込み image = Image.open(BytesIO(image_data)) # 画像のサイズを取得 width, height = image.size x_coords = [v[ "x" ] * width for v in key_di[ "vertex" ]] y_coords = [v[ "y" ] * height for v in key_di[ "vertex" ]] # 切り抜きの領域を設定 left = min (x_coords) upper = min (y_coords) right = max (x_coords) lower = max (y_coords) # 画像に白い矩形を描画 ImageDraw.Draw(image).rectangle(((left, upper), (right, lower)), fill= "white" ) # ローカルに一時的に保存 tmp_file = f "/tmp/{blob_name}" image.save(tmp_file) # Cloud Storage へアップロード bucket = storage_client.get_bucket(detected_license_plate_bucket) blob_cropped = bucket.blob(blob_name) blob_cropped.upload_from_filename(tmp_file) # ローカルから削除 os.remove(tmp_file) return "ok" def upload_not_detected_license_plate_bucket (image_data, not_detected_license_plate_bucket, blob_name): # BytesIOを使って画像データを読み込み image = Image.open(BytesIO(image_data)) # ローカルに一時的に保存 tmp_file = f "/tmp/{blob_name}" image.save(tmp_file) # Cloud Storage にアップロード bucket = storage_client.get_bucket(not_detected_license_plate_bucket) blob_cropped = bucket.blob(blob_name) blob_cropped.upload_from_filename(tmp_file) return "ok" def is_check_key (localize_object_di, key): # 辞書のキーの中に特定の文字列が1つだけあるかどうかをチェック key_to_check = key key_count = list (localize_object_di.keys()).count(key_to_check) if key_count == 1 : return True else : return False @ functions_framework.cloud_event def main (cloud_event: CloudEvent): # Cloud Storage から渡されたデータを取得 data = cloud_event.data bucket_name = data[ "bucket" ] file_name = data[ "name" ] image_data = download_blob(bucket_name=bucket_name, blob_name=file_name) localize_object_di = localize_objects(image_data=image_data) if is_check_key(localize_object_di=localize_object_di, key=KEY): masked_image_upload_detected_license_plate_bucket( image_data=image_data, key_di=localize_object_di[KEY], detected_license_plate_bucket=DETECTED_LICENSE_PLATE, blob_name=file_name) else : upload_not_detected_license_plate_bucket( image_data=image_data, not_detected_license_plate_bucket=NOT_DETECTED_LICENSE_PLATE, blob_name=file_name) return "ok" requirements.txt functions-framework==3.* cloudevents==1.9.0 google-cloud-vision==3.4.4 google-cloud-storage==2.10.0 numpy==1.25.1 Pillow==10.0.0 opencv-python==4.8.0.74 動作検証 検証データ 以下の 3 枚の画像データで動作検証を行います。 検証前データ 実行 生データ格納用バケットに検証データをアップロードします。 生データ格納用バケットコンソール画面 直後に Cloud Storage トリガー経由で Cloud Functions が起動し、最終的にナンバープレートマスキング加工後格納バケットにオブジェクトが生成されました。 ナンバープレートマスキング加工後格納バケットコンソール画面 中身は以下のようになってました。 検証後データ 検証前データと比較すると、車周辺部分で切り取られ、ナンバープレートがマスキングできていることを確認できました。 又吉 佑樹 (記事一覧) クラウドソリューション部 はいさーい!沖縄出身のクラウドエンジニアです!! 前職は SIer テクニカルセールス。Google Cloud の魅力に惚れ、技術を磨きたくセールスからエンジニアへ転身。Google Cloud 認定資格は全 11 資格保有。最近は AI/ML 分野に興味あり。 Follow @matayuuuu
アバター