Agent Engine PSC 明示的プロキシ

1. はじめに

Private Service Connect インターフェースは、プロデューサーの Virtual Private Cloud(VPC)ネットワークがコンシューマーの VPC ネットワーク内のさまざまな宛先への接続を開始できるようにするためのリソースです。プロデューサー ネットワークとコンシューマー ネットワークは、異なるプロジェクトや組織に属していてもかまわない。

ネットワーク アタッチメントが Private Service Connect インターフェースからの接続を受け入れると、Google Cloud はネットワーク アタッチメントで指定されたコンシューマー サブネットからインターフェースに IP アドレスを割り振ります。コンシューマ ネットワークとプロデューサー ネットワークが接続され、内部 IP アドレスを使用して通信が可能になります。

ネットワーク アタッチメントと Private Service Connect インターフェース間の接続は、Private Service Connect のエンドポイントとサービス アタッチメント間の接続に似ていますが、重要な違いが 2 つあります。

  • ネットワーク アタッチメントは、プロデューサー ネットワークからコンシューマー ネットワークへの接続(マネージド サービスの下り、外向き)を開始できるようにします。エンドポイントは、コンシューマー ネットワークからプロデューサー ネットワークへの接続(マネージド サービスの上り、内向き)を開始できるようにします。
  • Private Service Connect インターフェースの接続は推移的です。これは、プロデューサー ネットワークは、コンシューマー ネットワークに接続されている他のネットワークと通信できることを意味します。

Vertex AI PSC インターフェースの到達可能性に関する考慮事項

  • PSC インターフェースは、RFC1918 アドレス ブロック内の VPC またはオンプレミス ベースの宛先にトラフィックをルーティングできます。
  • RFC 1918 以外のアドレス ブロックをターゲットとする PSC インターフェースでは、RFC 1918 アドレスを使用してコンシューマーの VPC に明示的なプロキシをデプロイする必要があります。Vertex AI デプロイ内では、プロキシはターゲット エンドポイントの FQDN とともに定義する必要があります。
  • PSC インターフェースのみを使用してデプロイを構成すると、デフォルトのインターネット アクセスが保持されます。このアウトバウンド トラフィックは、Google が管理する安全なテナント ネットワークから直接送信されます。

Vertex AI PSC-Interface VPC-SC に関する考慮事項

  • プロジェクトが VPC Service Controls の境界に含まれている場合、データ漏洩を防ぐために、Google マネージド テナントのデフォルトのインターネット アクセスは境界によってブロックされます。
  • このシナリオでデプロイがパブリック インターネットにアクセスできるようにするには、VPC を介してトラフィックをルーティングする安全な下り(外向き)パスを明示的に構成する必要があります。
  • この目的を達成するおすすめの方法は、RFC1918 アドレスを使用して VPC ペリメータ内にプロキシ サーバーを設定し、プロキシ VM がインターネットにアクセスできるように Cloud NAT ゲートウェイを作成することです。

詳しくは、次のリソースをご覧ください。

エージェントをデプロイする | Vertex AI の生成 AI | Google Cloud

Vertex AI リソースの Private Service Connect インターフェースを設定する | Google Cloud

作成するアプリの概要

このチュートリアルでは、Private Service Connect(PSC)インターフェースでデプロイされた包括的な Agent Engine を構築し、RFC1918 アドレスを持つコンシューマーの VPC にデプロイされたプロキシ VM を介して、パブリック サイト(https://api.frankfurter.app/)への接続を可能にします。このデプロイ例は、VPC-SC が有効になっているプロジェクト、またはテナント VPC ではなく顧客のネットワークを介してインターネット上り(外向き)を必要とする管理者に適用できます。

図 1

f42f2db921f6d5af.png

コンシューマー VPC に単一の psc-network-attachment を作成し、DNS ピアリングを利用して、Agent Engine をホストするテナント プロジェクト内のコンシューマー ネットワーク プロキシ VM を解決します。これにより、次のユースケースが実現します。

Agent Engine をデプロイし、明示的なプロキシとして機能するようにプロキシ VM を構成して、パブリック URL https://api.frankfurter.app にアクセスできるようにします。

学習内容

  • ネットワーク アタッチメントを作成する方法
  • プロデューサーがネットワーク アタッチメントを使用して PSC インターフェースを作成する方法
  • DNS ピアリングを使用してプロデューサーからコンシューマーへの通信を確立する方法
  • インターネット下り(外向き)用のプロキシ VM をデプロイして使用する方法

必要なもの

Google Cloud プロジェクト

IAM 権限

2. 始める前に

チュートリアルをサポートするようにプロジェクトを更新する

このチュートリアルでは、$variables を使用して、Cloud Shell での gcloud 構成の実装を支援します。

Cloud Shell で、次の操作を行います。

gcloud config list project
gcloud config set project [YOUR-PROJECT-NAME]
projectid=YOUR-PROJECT-NAME
echo $projectid

API の有効化

Cloud Shell で、次の操作を行います。

gcloud services enable "compute.googleapis.com"
gcloud services enable "aiplatform.googleapis.com"
gcloud services enable "dns.googleapis.com"
gcloud services enable "notebooks.googleapis.com"
gcloud services enable "storage.googleapis.com"
gcloud services enable "iap.googleapis.com"

API が正常に有効になっていることを確認する

gcloud services list --enabled

3. コンシューマーの設定

コンシューマー VPC を作成する

この VPC はお客様のプロジェクトに存在します。この VPC に次のリソースが作成されます。

  • コンシューマー サブネット
  • ネットワーク アタッチメント サブネット
  • Cloud Router(Cloud NAT に必要)
  • Cloud NAT

Cloud Shell で、次の操作を行います。

gcloud compute networks create consumer-vpc --project=$projectid --subnet-mode=custom

コンシューマー サブネットを作成する

Cloud Shell 内で、プロキシ VM のサブネットを作成します。

gcloud compute networks subnets create rfc1918-subnet1 --project=$projectid --range=10.10.10.0/28 --network=consumer-vpc --region=us-central1

Private Service Connect ネットワーク アタッチメント サブネットを作成する

Cloud Shell 内で、PSC ネットワーク アタッチメントのサブネットを作成します。

gcloud compute networks subnets create intf-subnet --project=$projectid --range=192.168.10.0/28 --network=consumer-vpc --region=us-central1

Cloud Router と NAT の構成

このチュートリアルでは、パブリック IP アドレスを持たないプロキシ VM にインターネット アクセスを提供するために Cloud NAT を使用します。Cloud NAT を使用すると、プライベート IP アドレスのみを持つ VM がインターネットに接続して、ソフトウェア パッケージのインストールなどのタスクを実行できます。

Cloud Shell 内で、Cloud Router を作成します。

gcloud compute routers create cloud-router-for-nat --network consumer-vpc --region us-central1

Cloud Shell 内で、ロギングが有効になっている NAT ゲートウェイを作成します。ロギングを使用して、Frankfurter API(https://api.frankfurter.app/)の公開 IP へのアクセスを検証します。

gcloud compute routers nats create cloud-nat-us-central1 --router=cloud-router-for-nat --auto-allocate-nat-external-ips --nat-all-subnet-ip-ranges --region us-central1 --enable-logging --log-filter=ALL

4. IAP を有効にする

IAP に VM インスタンスへの接続を許可するには、次のファイアウォール ルールを作成します。

  • IAP を使用してアクセス可能にするすべての VM インスタンスに適用されます。
  • IP 範囲 35.235.240.0/20 からの上り(内向き)トラフィックを許可します。この範囲には、IAP が TCP 転送に使用するすべての IP アドレスが含まれています。

Cloud Shell 内で、IAP ファイアウォール ルールを作成します。

gcloud compute firewall-rules create ssh-iap-consumer \
    --network consumer-vpc \
    --allow tcp:22 \
    --source-ranges=35.235.240.0/20

5. コンシューマー VM インスタンスを作成する

Cloud Shell 内で、Agent Engine の明示的なプロキシとして機能するコンシューマー VM インスタンス proxy-vm を作成します。HTTP トラフィックのプロキシ処理には、tinyproxy をアプリケーションとして使用します。

gcloud compute instances create proxy-vm \
    --project=$projectid \
    --machine-type=e2-micro \
    --image-family debian-11 \
    --no-address \
    --can-ip-forward \
    --image-project debian-cloud \
    --zone us-central1-a \
    --subnet=rfc1918-subnet1 \
    --shielded-secure-boot \
    --metadata startup-script="#! /bin/bash
      sudo apt-get update
      sudo apt-get install tcpdump
      sudo apt-get install tinyproxy -y
      sudo apt-get install apache2 -y
      sudo service apache2 restart
      echo 'proxy server !!' | tee /var/www/html/index.html
      EOF"

6. Private Service Connect ネットワーク アタッチメント

ネットワーク アタッチメントは、Private Service Connect インターフェースのコンシューマー側を表すリージョン リソースです。単一のサブネットをネットワーク アタッチメントに関連付けると、プロデューサーはそのサブネットから Private Service Connect インターフェースに IP を割り当てます。サブネットは、ネットワーク アタッチメントと同じリージョンに存在する必要があります。ネットワーク アタッチメントは、プロデューサー サービスと同じリージョンに存在する必要があります。

ネットワーク アタッチメントを作成する

Cloud Shell 内で、ネットワーク アタッチメントを作成します。

gcloud compute network-attachments create psc-network-attachment \
    --region=us-central1 \
    --connection-preference=ACCEPT_AUTOMATIC \
    --subnets=intf-subnet

ネットワーク アタッチメントを一覧表示する

Cloud Shell 内で、ネットワーク アタッチメントを一覧表示します。

gcloud compute network-attachments list

ネットワーク アタッチメントの説明を取得する

Cloud Shell 内で、ネットワーク アタッチメントの説明を取得します。

gcloud compute network-attachments describe psc-network-attachment --region=us-central1

Private Service Connect インターフェースの作成時にプロデューサーが使用する PSC ネットワーク アタッチメント名 psc-network-attachment をメモします。

Cloud Console で PSC ネットワーク アタッチメントの URL を表示するには、次の場所に移動します。

[ネットワーク サービス] → [Private Service Connect] → [ネットワーク アタッチメント] → [psc-network-attachment]

8eec51cb197da218.png

7. 限定公開 DNS ゾーン

demo.com の Cloud DNS ゾーンを作成し、プロキシ VM の IP アドレスを指す A レコードを入力します。その後、DNS ピアリングが Agent Engine にデプロイされ、コンシューマーの DNS レコードにアクセスできるようになります。

Cloud Shell で、次の操作を行って DNS 名 demo.com を作成します。

gcloud dns --project=$projectid managed-zones create private-dns-codelab --description="" --dns-name="demo.com." --visibility="private" --networks="https://compute.googleapis.com/compute/v1/projects/$projectid/global/networks/consumer-vpc"

DNS A レコードに使用されるインスタンスの IP アドレスを取得して保存します。

Cloud Shell 内で、VM インスタンスに対して説明を実行します。

gcloud compute instances describe proxy-vm --zone=us-central1-a | grep  networkIP:

Cloud Shell で、VM(proxy-vm.demo.com)のレコードセットを作成します。環境の出力に基づいて IP アドレスを更新してください。

gcloud dns --project=$projectid record-sets create proxy-vm.demo.com. --zone="private-dns-codelab" --type="A" --ttl="300" --rrdatas="10.10.10.2"

PSC インターフェースからのアクセスを許可する Cloud Firewall ルールを作成する

次のセクションでは、PSC ネットワーク アタッチメントからコンシューマー VPC の proxy-vm へのアクセスを許可するファイアウォール ルールを作成します。

Cloud Shell で、上り(内向き)ファイアウォール ルールを作成します。

gcloud compute firewall-rules create allow-access-to-compute \
    --network=consumer-vpc \
    --action=ALLOW \
    --rules=ALL \
    --direction=INGRESS \
    --priority=1000 \
    --source-ranges="192.168.10.0/28" \
    --destination-ranges="10.10.10.0/28" \
    --enable-logging

8. Jupyter ノートブックを作成する

次のセクションでは、Jupyter Notebook の作成について説明します。このノートブックは、インターネット下り(外向き)の明示的なプロキシをターゲットとする Agent Engine をデプロイするために使用されます。

ユーザー管理のサービス アカウントを作成する

次のセクションでは、チュートリアルで使用する Vertex AI Workbench インスタンスに関連付けるサービス アカウントを作成します。

このチュートリアルでは、サービス アカウントに次のロールが適用されます。

Cloud Shell 内で、サービス アカウントを作成します。

gcloud iam service-accounts create notebook-sa \
    --display-name="notebook-sa"

Cloud Shell 内で、ロール「ストレージ管理者」を使用してサービス アカウントを更新します。

gcloud projects add-iam-policy-binding $projectid --member="serviceAccount:notebook-sa@$projectid.iam.gserviceaccount.com" --role="roles/storage.admin"

Cloud Shell 内で、Vertex AI ユーザーロールを使用してサービス アカウントを更新します。

gcloud projects add-iam-policy-binding $projectid --member="serviceAccount:notebook-sa@$projectid.iam.gserviceaccount.com" --role="roles/aiplatform.user"

Cloud Shell 内で、Artifact Registry 管理者のロールを使用してサービス アカウントを更新します。

gcloud projects add-iam-policy-binding $projectid --member="serviceAccount:notebook-sa@$projectid.iam.gserviceaccount.com" --role="roles/artifactregistry.admin"

Cloud Shell 内で、ノートブック サービス アカウントが Compute Engine のデフォルト サービス アカウントを使用できるようにします。

gcloud iam service-accounts add-iam-policy-binding \
    $(gcloud projects describe $(gcloud config get-value project) --format='value(projectNumber)')-compute@developer.gserviceaccount.com \
    --member="serviceAccount:notebook-sa@$projectid.iam.gserviceaccount.com" \
    --role="roles/iam.serviceAccountUser"

9. 明示的プロキシを更新する

次のセクションでは、明示的プロキシに SSH 接続し、tinyproxy.conf 構成ファイルを更新してからリセットを実行する必要があります。

Cloud Shell から

gcloud compute ssh --zone us-central1-a "proxy-vm" --tunnel-through-iap --project $projectid

tinyproxy 構成ファイルを開き、任意のエディタを使用して更新します。以下は、VIM を使用した例です。

sudo vim /etc/tinyproxy/tinyproxy.conf

# Locate the "Listen" configuration line to restrict listening to only its private IP address of the Proxy-VM, rather than all interfaces. 

Listen 10.10.10.2

# Locate the "Allow" configuration line to allow requests ONLY from the PSC Network Attachment Subnet

Allow 192.168.10.0/24

Save the configs by the following steps:
1. Press the `ESC` key to enter Command Mode.
2. Type `:wq` to save (w) and quit (q).
3. Press `Enter`

Restart the tinyproxy service to apply the changes:
sudo systemctl restart tinyproxy

Validate the tinyproxy service is running:
sudo systemctl status tinyproxy

Perform an exit returning to cloud shell
exit

10. Vertex AI Workbench インスタンスを作成する

次のセクションでは、前に作成したサービス アカウント notebook-sa を組み込んだ Vertex AI Workbench インスタンスを作成します。

Cloud Shell 内で private-client インスタンスを作成します。

gcloud workbench instances create workbench-tutorial --vm-image-project=cloud-notebooks-managed --vm-image-family=workbench-instances --machine-type=n1-standard-4 --location=us-central1-a --subnet-region=us-central1 --subnet=rfc1918-subnet1 --disable-public-ip --shielded-secure-boot=true --shielded-integrity-monitoring=true --shielded-vtpm=true --service-account-email=notebook-sa@$projectid.iam.gserviceaccount.com

11. Vertex AI サービス エージェントの更新

Vertex AI は、PSC インターフェースの作成に使用される PSC ネットワーク アタッチメント サブネットから IP アドレスを取得するなどのオペレーションをユーザーに代わって実行します。そのため、Vertex AI は ネットワーク管理者権限を必要とするサービス エージェント(下記)を使用します。

service-$projectnumber@gcp-sa-aiplatform.iam.gserviceaccount.com

Cloud Shell で、プロジェクト番号を取得します。

gcloud projects describe $projectid | grep projectNumber

Cloud Shell で、プロジェクト番号を設定します。

projectnumber=YOUR-PROJECT-Number

Cloud Shell 内で、AI Platform のサービス アカウントを作成します。プロジェクトに既存のサービス アカウントがある場合は、この手順をスキップします。

gcloud beta services identity create --service=aiplatform.googleapis.com --project=$projectnumber

Cloud Shell 内で、ロール compute.networkAdmin を使用してサービス エージェント アカウントを更新します。

gcloud projects add-iam-policy-binding $projectid --member="serviceAccount:service-$projectnumber@gcp-sa-aiplatform.iam.gserviceaccount.com" --role="roles/compute.networkAdmin"

Cloud Shell 内で、サービス エージェント アカウントをロール dns.peer で更新します。

gcloud projects add-iam-policy-binding $projectid --member="serviceAccount:service-$projectnumber@gcp-sa-aiplatform.iam.gserviceaccount.com" --role="roles/dns.peer"

デフォルトのサービス アカウントの更新

Vertex AI へのアクセス権をデフォルトのサービス アカウントに付与します。アクセス権の変更が反映されるまでに時間がかかることがあります。

Cloud Shell 内で、デフォルトのサービス アカウントを aiplatform.user ロールで更新します。

gcloud projects add-iam-policy-binding $projectid \
  --member="serviceAccount:$projectnumber-compute@developer.gserviceaccount.com" \
    --role="roles/aiplatform.user"

12. プロキシ VM の Tcpdump

Agent Engine からの IP 接続を検証するには、TCPDUMP を使用します。これにより、Agent Engine からパブリック URL に get リクエストを呼び出すときに、PSC ネットワーク アタッチメント サブネット(192.168.10.0/28)から発信された通信を監視できます。

Cloud Shell からプロキシ VM に ssh で接続します。

gcloud compute ssh --zone us-central1-a "proxy-vm" --tunnel-through-iap --project $projectid

proxy-vm OS から tcpdump を実行します。

sudo tcpdump -i any net 192.168.10.0/28 -nn

13. Agent Engine をデプロイする

注: このセクションのタスクを完了するには、GCP コンソールと JupyterLab ノートブックを使用します。

次のセクションでは、次のタスクを実行するノートブックを作成します。

  • Frankfurter API(https://api.frankfurter.app/)を使用して為替レート データを取得します
  • FQDN proxy-vm.demo.com を使用して、コンシューマー VPC の proxy-vm をターゲットとする明示的なプロキシ(proxy_server)を参照します。
  • dnsPeeringConfigs の "domain": "demo.com." を定義します。

Vertex AI Workbench インスタンスでトレーニング ジョブを実行します。

  • Google Cloud コンソールで、[Vertex AI] → [Workbench] に移動します。
  • Vertex AI Workbench インスタンス名(workbench-tutorial)の横にある [JupyterLab を開く] をクリックします。JupyterLab で Vertex AI Workbench インスタンスが開きます。
  • [ファイル] > [新規] > [ノートブック] の順に選択します。
  • [Kernel] > [Python 3] を選択します。

必要な Python ライブラリをインストールする: pyyaml、google-cloud-aiplatform、cloudpickle、google-cloud-api-keys、langchain-google-vertexai など、Agent Engine に必要なライブラリをインストールします。

JupyterLab ノートブックで、新しいセルを作成して次のコマンドを実行します。

!pip install pyyaml
!pip install google-cloud-aiplatform[agent_engines,langchain]==1.96.0
!pip install cloudpickle==3.1.1
!pip install google-cloud-api-keys
!pip install langchain-google-vertexai==2.0.24

Jupyter Notebook カーネルを再起動する: 新しくインストールしたライブラリが正しく読み込まれることを確認します。

JupyterLab ノートブックで、新しいセルを作成して次のコマンドを実行します。

# Restart the notebook kernel after install, so you can run langchain successfully.

import IPython

app = IPython.Application.instance()
app.kernel.do_shutdown(True)

プロジェクト変数とバケット変数を設定する: Google Cloud プロジェクト ID、プロジェクト番号、サービス名、GCS ディレクトリ、エンドポイント、バケット名、ロケーションを定義します。

セルを実行する前に、次のフィールドを更新します。

  • PROJECT_ID = "enter-your-projectid"
  • PROJECT_NUMBER = "enter-your-projectnumber"
  • BUCKET= "enter-a-unique-bucket-name"

注: 次のステップでは、BUCKET 変数を使用して Cloud Storage バケットを作成します。

JupyterLab ノートブックで、新しいセルを作成し、次の内容を更新して実行します。

PROJECT_ID = "enter-your-projectid"  #@param {type:"string"}
PROJECT_NUMBER = "enter-your-projectnumber"  #@param {type:"string"}
SERVICE_NAME = "aiplatform"  #@param ["autopush-aiplatform", "staging-aiplatform", "aiplatform"]
# @markdown  Specify where your agent code should be written in GCS:
GCS_DIR = "reasoning-engine-test"  #@param {type:"string"}
ENDPOINT = "https://us-central1-aiplatform.googleapis.com" # @param ["https://us-central1-aiplatform.googleapis.com", "https://us-central1-autopush-aiplatform.sandbox.googleapis.com", "https://us-central1-staging-aiplatform.sandbox.googleapis.com"]
BUCKET= "enter-a-unique-bucket-name" #@param {type:"string"}
LOCATION="us-central1" #@param {type:"string"}

GCS バケットを作成する: エージェント コードを保存する Cloud Storage バケットを作成します。

JupyterLab ノートブックで、新しいセルを作成して次のコマンドを実行します。

!gcloud storage buckets create gs://{BUCKET}

ネットワーク アタッチメント名を定義する: Private Service Connect ネットワーク アタッチメントの名前を指定します。

JupyterLab ノートブックで、新しいセルを作成して次のコマンドを実行します。

NETWORK_ATTACHMENT_NAME = 'psc-network-attachment' #@param {type:"string"}

Python クライアント ライブラリを初期化する: Google Cloud サービスに必要なクライアント ライブラリを設定します。

JupyterLab ノートブックで、新しいセルを作成して次のコマンドを実行します。

import json
import pprint

import cloudpickle
from google import auth as google_auth
from google.auth.transport import requests as google_requests
from google.cloud import storage
import yaml


def get_identity_token():
    """Gets ID token for calling Cloud Run."""
    credentials, _ = google_auth.default()
    auth_request = google_requests.Request()
    credentials.refresh(auth_request)
    return credentials.id_token

if not GCS_DIR or "your_ldap" in GCS_DIR:
    raise ValueError("GCS_DIR must be set or you must set your ldap.")

if not PROJECT_ID:
    raise ValueError("PROJECT_ID must be set.")


client = storage.Client(project=PROJECT_ID)
bucket = client.get_bucket(BUCKET)

エージェントとツールを構成する: StreamingAgent クラスと get_exchange_rate 関数を定義して、明示的なプロキシ経由で Frankfurter API を使用して通貨換算レートを取得します。

JupyterLab ノートブックで、新しいセルを作成して次の構成を実行します。次の点に注意してください。

  • def get_exchange_rate 関数は、Frankfurter API(https://api.frankfurter.app/)を使用して為替レート データを取得します。
  • proxy_server = "http://proxy-vm.demo.com:8888" FQDN は、コンシューマー VPC にデプロイされたプロキシ VM に関連付けられています。後の手順で DNS ピアリングを使用して FQDN を解決します。
from langchain_google_vertexai import ChatVertexAI
from langchain.agents import AgentExecutor
from langchain.agents.format_scratchpad.tools import format_to_tool_messages
from langchain.agents.output_parsers.tools import ToolsAgentOutputParser
from langchain.tools.base import StructuredTool
from langchain_core import prompts
from re import S
from typing import Callable, Sequence
import google.auth
import vertexai


class StreamingAgent:

    def __init__(
            self,
            model: str,
            tools: Sequence[Callable],
            project_id: str,
        ):
        self.model_name = model
        self.tools = tools
        self.project_id = project_id

    def set_up(self):
        """All unpickle-able logic should go here.

        The .set_up() method should not be called for an object that is being
        prepared for deployment.
        """
        creds, _ = google.auth.default(quota_project_id=self.project_id)
        vertexai.init(project=self.project_id, location="us-central1", credentials=creds)

        prompt = {
            "input": lambda x: x["input"],
            "agent_scratchpad": (
                lambda x: format_to_tool_messages(x["intermediate_steps"])
            ),
        } | prompts.ChatPromptTemplate.from_messages([
            ("user", "{input}"),
            prompts.MessagesPlaceholder(variable_name="agent_scratchpad"),
        ])

        llm = ChatVertexAI(model_name=self.model_name)
        if self.tools:
            llm = llm.bind_tools(tools=self.tools)

        self.agent_executor = AgentExecutor(
            agent=prompt | llm | ToolsAgentOutputParser(),
            tools=[StructuredTool.from_function(tool) for tool in self.tools],
        )

    def query(self, input: str):
        """Query the application.

        Args:
            input: The user prompt.

        Returns:
            The output of querying the application with the given input.
        """
        return self.agent_executor.invoke(input={"input": input})

    def stream_query(self, input: str):
        """Query the application and stream the output.

        Args:
            input: The user prompt.

        Yields:
            Chunks of the response as they become available.
        """
        for chunk in self.agent_executor.stream(input={"input": input}):
            yield chunk

def get_exchange_rate(
    currency_from: str = "USD",
    currency_to: str = "EUR",
    currency_date: str = "latest",
):
    """Retrieves the exchange rate between two currencies on a specified date.

    Uses the Frankfurter API (https://api.frankfurter.app/) to obtain
    exchange rate data.

    Args:
        currency_from: The base currency (3-letter currency code).
            Defaults to "USD" (US Dollar).
        currency_to: The target currency (3-letter currency code).
            Defaults to "EUR" (Euro).
        currency_date: The date for which to retrieve the exchange rate.
            Defaults to "latest" for the most recent exchange rate data.
            Can be specified in YYYY-MM-DD format for historical rates.

    Returns:
        dict: A dictionary containing the exchange rate information.
            Example: {"amount": 1.0, "base": "USD", "date": "2023-11-24",
                "rates": {"EUR": 0.95534}}
    """
    import requests

    proxy_server = "http://proxy-vm.demo.com:8888" # This is the VM's FQDN to reach the proxy vm in the consumers network

    proxies = {
       "http": proxy_server,
       "https": proxy_server,
    }
    response = requests.get(
        f"https://api.frankfurter.app/{currency_date}",
        params={"from": currency_from, "to": currency_to},
        proxies=proxies,
    )
    return response.json()

エージェント ファイルを Cloud Storage にアップロードする: シリアル化されたエージェントとその要件を指定された GCS バケットにアップロードします。

JupyterLab ノートブックで、新しいセルを作成して次のコマンドを実行します。

# Upload files to Cloud Storage.
if not GCS_DIR:
    raise ValueError("GCS_DIR must be set.")

FILE = "streaming_agent.pkl"
blob = bucket.blob(f"{GCS_DIR}/{FILE}")
with blob.open("wb") as f:
    cloudpickle.dump(
        StreamingAgent(
            model="gemini-2.0-flash-001",  # Required.
            tools=[get_exchange_rate],  # Optional.
            project_id=PROJECT_ID
        ), f)


requirements = """
google-cloud-aiplatform[agent_engines,langchain]==1.96.0
cloudpickle==3.1.1
"""

blob = bucket.blob(f"{GCS_DIR}/requirements-streaming.txt")
blob.upload_from_string(requirements)

!gsutil ls gs://{BUCKET}/{GCS_DIR}

Agent Engine をデプロイする: Agent Engine をデプロイし、PSC インターフェースと DNS ピアリングを使用して構成し、コンシューマー VPC でプロキシ VM の FQDN を解決します。

JupyterLab ノートブックで次のセルを作成して実行します。次の点に注意してください。

  • コンシューマー VPC への DNS ピアリングは、ドメイン名 demo.com の dnsPeeringConfigs(dnsPeeringConfigs)を使用して構成されます。
import requests


token = !gcloud auth application-default print-access-token

response = requests.post(
    f"{ENDPOINT}/v1beta1/projects/{PROJECT_ID}/locations/{LOCATION}/reasoningEngines",
    headers={
        "Content-Type": "application/json; charset=utf-8",
        "Authorization": f"Bearer {token[0]}"
    },
    data=json.dumps({
        "displayName": "PSC-I Explicit Proxy",
        "description": "test psc-i agent + proxy vm",
        "spec": {
            "packageSpec": {
                "pickleObjectGcsUri": f"gs://{BUCKET}/{GCS_DIR}/streaming_agent.pkl",
                "requirementsGcsUri": f"gs://{BUCKET}/{GCS_DIR}/requirements-streaming.txt",
                "pythonVersion": "3.10"
            },
            "deploymentSpec": {
                "pscInterfaceConfig": {
                    "networkAttachment": NETWORK_ATTACHMENT_NAME,
                    "dnsPeeringConfigs": [
                    {
                      "domain": "demo.com.",
                      "targetProject": PROJECT_ID,
                      "targetNetwork": "consumer-vpc", #Consumer VPC
                    },
                  ],
                }
            }
        },
    })
)

pprint.pprint(json.loads(response.content))
reasoning_engine_id = json.loads(response.content)["name"].split("/")[5]
pprint.pprint(reasoning_engine_id)

デプロイ ステータスのモニタリング: Agent Engine デプロイ オペレーションのステータスを確認します。

JupyterLab ノートブックで、新しいセルを作成して次のコマンドを実行します。

operation_id = json.loads(response.content)["name"].split("/")[7]
pprint.pprint(operation_id)

JupyterLab ノートブックで、新しいセルを作成して次のコマンドを実行します。

注: このオペレーションが完了するまでに 5 分ほどかかることがあります。セルを再実行して、進行状況を確認します。次のセクションに進む前に、次のスクリーンショットのような出力が表示されるまでお待ちください。

# You can run this multiple times to check the status of the deployment operation, operation takes approx 5 min.
token = !gcloud auth application-default print-access-token
response = requests.get(
    f"{ENDPOINT}/v1beta1/projects/{PROJECT_ID}/locations/{LOCATION}/operations/{operation_id}        ",
    headers={
        "Content-Type": "application/json; charset=utf-8",
        "Authorization": f"Bearer {token[0]}"
    }
)
pprint.pprint(json.loads(response.content))

正常に実行された場合の例:

3f6dcd1074af7651.png

デプロイされたエージェントにクエリを実行する: デプロイされた Agent Engine にクエリを送信して、その機能をテストします。

JupyterLab ノートブックで、新しいセルを作成して次のコマンドを実行します。

response = requests.post(
    f"{ENDPOINT}/v1beta1/projects/{PROJECT_ID}/locations/{LOCATION}/reasoningEngines/{reasoning_engine_id}:query",
    headers={
        "Content-Type": "application/json; charset=utf-8",
        "Authorization": f"Bearer {token[0]}"
    },
    data=json.dumps({ "input": {"input": "What is the exchange rate from US dollars to Euro?"} })
)
print(response.text)

クエリ結果をストリーミングする: Agent Engine クエリの出力をストリーミングします。

JupyterLab ノートブックで、新しいセルを作成し、次のコードを実行します。これにより、コンシューマーの VPC で明示的なプロキシを使用して、パブリック URL への API 呼び出しがトリガーされます。

token = !gcloud auth application-default print-access-token
print(f"{ENDPOINT}/v1beta1/projects/{PROJECT_ID}/locations/us-central1/reasoningEngines/{reasoning_engine_id}:streamQuery")

response = requests.post(
    f"{ENDPOINT}/v1beta1/projects/{PROJECT_ID}/locations/us-central1/reasoningEngines/{reasoning_engine_id}:streamQuery",
    headers={
        "Content-Type": "application/json; charset=utf-8",
        "Authorization": f"Bearer {token[0]}"
    },
    data=json.dumps({ "input": {"input": "What is the exchange rate from US dollars to Euro?"} })
)
for chunk in response.iter_lines():
    print(chunk.decode('utf-8'))
# pprint.pprint(json.loads(response.content))

正常に実行された場合の例:

1bd81d12426a348f.png

14. Tcpdump の検証

リクエストの投稿時に、Agent Engine で使用される PSC ネットワーク アタッチメントの IP アドレスと Prox-VM 間の通信の詳細を示す tcpdump の出力を確認します。

user@proxy-vm:~$ sudo tcpdump -i any net 192.168.10.0/28 -nn
tcpdump: data link type LINUX_SLL2
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on any, link-type LINUX_SLL2 (Linux cooked v2), snapshot length 262144 bytes
22:17:53.983212 ens4  In  IP 192.168.10.2.22261 > 10.10.10.2.8888: Flags [S], seq 3841740961, win 28800, options [mss 1440,sackOK,TS val 4245243253 ecr 0,nop,wscale 7], length 0
22:17:53.983252 ens4  Out IP 10.10.10.2.8888 > 192.168.10.2.22261: Flags [S.], seq 2232973833, ack 3841740962, win 64768, options [mss 1420,sackOK,TS val 2251247643 ecr 4245243253,nop,wscale 7], length 0
22:17:53.985167 ens4  In  IP 192.168.10.2.22261 > 10.10.10.2.8888: Flags [.], ack 1, win 225, options [nop,nop,TS val 4245243256 ecr 2251247643], length 0
22:17:53.986476 ens4  In  IP 192.168.10.2.22261 > 10.10.10.2.8888: Flags [P.], seq 1:45, ack 1, win 16384, options [nop,nop,TS val 4245243256 ecr 2251247643], length 44
22:17:53.986485 ens4  Out IP 10.10.10.2.8888 > 192.168.10.2.22261: Flags [.], ack 45, win 506, options [nop,nop,TS val 2251247646 ecr 4245243256], length 0
22:17:54.043347 ens4  Out IP 10.10.10.2.8888 > 192.168.10.2.22261: Flags [P.], seq 1:71, ack 45, win 506, options [nop,nop,TS val 2251247703 ecr 4245243256], length 70

15. PSC インターフェースの検証

Agent Engine で使用されているネットワーク アタッチメントの IP を確認するには、次の場所に移動します。

[ネットワーク サービス] → [Private Service Connect] → [ネットワーク アタッチメント] → [psc-network-attachment]

テナント プロジェクト(-tp で終わるプロジェクト名)を選択します。

8a4b5a6e5dfd63d7.png

ハイライト表示されたフィールドは、PSC ネットワーク アタッチメントから Agent Engine で使用される IP アドレスを示します。

c618359f6eafc0c6.png

16. Cloud Logging の検証

proxy-vm TCPDump セッションを終了し、Frankfurter api.frankfurter.app に PING を実行して、関連付けられたパブリック IP アドレスを取得します。

ping -c4 api.frankfurter.app 

この例では、api.frankfurter.app のパブリック IP として 104.26.1.198 を識別します。

user@proxy-vm:~$ ping -c4 api.frankfurter.app

PING api.frankfurter.app (104.26.1.198) 56(84) bytes of data.

64 bytes from 104.26.1.198 (104.26.1.198): icmp_seq=1 ttl=61 time=10.9 ms

64 bytes from 104.26.1.198 (104.26.1.198): icmp_seq=2 ttl=61 time=10.9 ms

64 bytes from 104.26.1.198 (104.26.1.198): icmp_seq=3 ttl=61 time=10.9 ms

64 bytes from 104.26.1.198 (104.26.1.198): icmp_seq=4 ttl=61 time=10.9 ms

NAT ロギングで、104.26.1.198 のトラフィックが観測されているかどうかを確認します。

次の場所に移動します。

[モニタリング] → [ログ エクスプローラ]

次のフィルタを使用します。

resource.type="nat_gateway"

31024dc29c39084.png

期間を選択してクエリを実行する

5976857e92d149d3.png

api.frankfurter.app の(宛先)パブリック IP(104.26.1.198)と、インターネット エグレスの明示的プロキシの使用を検証する proxy-vm の送信元 IP アドレスと名前を特定するログエントリを開きます。

14e293a7fea68db4.png

17. クリーンアップ

JupyterLab ノートブックで、新しいセルを作成し、次のコマンドを実行して Agent Engine デプロイの削除をトリガーします。

token = !gcloud auth application-default print-access-token

response = requests.delete(
    f"{ENDPOINT}/v1beta1/projects/{PROJECT_ID}/locations/us-central1/reasoningEngines/{reasoning_engine_id}",
    headers={
        "Content-Type": "application/json; charset=utf-8",
        "Authorization": f"Bearer {token[0]}"
    },
)
print(response.text)

Cloud Shell から、チュートリアルのコンポーネントを削除します。

gcloud dns record-sets delete proxy-vm.demo.com --zone=private-dns-codelab  --type=A

gcloud dns managed-zones delete private-dns-codelab

gcloud compute instances delete proxy-vm --zone=us-central1-a --quiet

gcloud compute instances delete workbench-tutorial --zone=us-central1-a --quiet

gcloud compute routers delete cloud-router-for-nat --region=us-central1 --quiet

gcloud compute network-attachments delete psc-network-attachment --region=us-central1 --quiet

gcloud compute networks subnets delete intf-subnet rfc1918-subnet1 --region=us-central1 --quiet

gcloud compute networks delete consumer-vpc --quiet

18. 完了

お疲れさまでした。これで、Private Service Connect インターフェースでデプロイされた Agent Engine を構成し、明示的なプロキシを介してインターネット下り(外向き)を実行して検証できました。

コンシューマー インフラストラクチャを作成し、プロデューサーがマルチ NIC VM を作成してコンシューマーとプロデューサーの通信をブリッジできるようにするネットワーク アタッチメントを追加しました。インターネット接続を可能にする明示的なプロキシと DNS ピアリングを作成する方法を学習しました。

Cosmopup はチュートリアルが大好きです。

c911c127bffdee57.jpeg

次のステップ

参考資料と動画

リファレンス ドキュメント