1. 目標
このワークショップの目的は、ユーザーと実務担当者に Duet AI の実践的なトレーニングを提供することです。
この Codelab では、以下について学びます。
- GCP プロジェクトで Duet AI を有効にし、IDE と Cloud Console で使用するように構成します。
- Duet AI を使用して、コードの生成、補完、説明を行います。
- Duet AI を使用して、アプリケーションの問題を説明し、トラブルシューティングします。
- IDE チャットやマルチターンのチャット、チャットとインライン コード生成の比較、コードの説明や暗唱の確認などのスマート アクションなど、Duet AI の機能。
ナラティブ
Duet AI for Developers が日々の開発でどのように実際に使用されているかを示すため、このワークショップのアクティビティはナラティブ コンテキストで行われます。
新しいデベロッパーが e コマース企業に入社しました。複数のサービスで構成されている既存の e コマース アプリケーションに新しいサービスを追加するよう指示されました。この新しいサービスでは、商品カタログの商品に関する追加情報(寸法、重量など)が提供されます。このサービスにより、商品の寸法と重量に基づいて、より安価な送料を設定できます。
デベロッパーは入社したばかりなので、コードの生成、説明、ドキュメント作成に Duet AI を使用します。
サービスがコーディングされた後、プラットフォーム管理者は Duet AI(チャット)を使用して、アーティファクト(Docker コンテナ)と、アーティファクトを GCP にデプロイするために必要なリソース(Artifact Registry、IAM 権限、コード リポジトリ、コンピューティング インフラストラクチャ(GKE、CloudRun など))の作成を支援します。
アプリケーションが GCP にデプロイされると、アプリケーション オペレーター/SRE は Duet AI(と Cloud Ops)を使用して、新しいサービスのエラーのトラブルシューティングを行います。
ペルソナ
このワークショップでは、次のペルソナについて説明します。
- アプリケーション デベロッパー - プログラミングとソフトウェア開発に関する知識が必要です。
この Duet AI ワークショップは、デベロッパー専用です。GCP クラウドリソースに関する知識は必要ありません。このアプリケーションを実行するために必要な GCP リソースをビルドする方法のスクリプトは、こちらで確認できます。このガイドの手順に沿って、必要な GCP リソースをデプロイできます。
2. 環境を準備する
Duet AI を有効にする
GCP プロジェクトで Duet AI を有効にするには、API(gcloud や Terraform などの IaC ツール)または Cloud コンソール UI を使用します。
Google Cloud プロジェクトで Duet AI を有効にするには、Cloud AI Companion API を有効にして、Cloud AI Companion ユーザーと Service Usage 閲覧者の Identity and Access Management(IAM)ロールをユーザーに付与します。
gcloud を使用する
Cloud Shell をアクティブにします。
PROJECT_ID、USER を構成し、Cloud AI Companion API を有効にします。
export PROJECT_ID=<YOUR PROJECT ID>
export USER=<YOUR USERNAME> # Use your full LDAP, e.g. name@example.com
gcloud config set project ${PROJECT_ID}
gcloud services enable cloudaicompanion.googleapis.com --project ${PROJECT_ID}
出力は次のようになります。
Updated property [core/project]. Operation "operations/acat.p2-60565640195-f37dc7fe-b093-4451-9b12-934649e2a435" finished successfully.
Cloud AI Companion ユーザーと Service Usage 閲覧者の Identity and Access Management(IAM)ロールを USER アカウントに付与します。Cloud Companion API は、使用する IDE とコンソールの両方の機能の背後にあります。Service Usage 閲覧者の権限は、コンソールで UI を有効にする前に簡単なチェックとして使用されます(Duet UI は、API が有効になっているプロジェクトにのみ表示されます)。
gcloud projects add-iam-policy-binding ${PROJECT_ID} \
--member=user:${USER} --role=roles/cloudaicompanion.user
gcloud projects add-iam-policy-binding ${PROJECT_ID} \
--member=user:${USER} --role=roles/serviceusage.serviceUsageViewer
出力は次のようになります。
... - members: - user:<YOUR USER ACCOUNT> role: roles/cloudaicompanion.user ... - members: - user:<YOUR USER ACCOUNT> role: roles/serviceusage.serviceUsageViewer
Cloud コンソールを使用する
API を有効にするには、Google Cloud コンソールの Cloud AI Companion API ページに移動します。
プロジェクト セレクタで、プロジェクトを選択します。
[有効にする] をクリックします。
ページが更新され、[有効] のステータスが表示されます。これで、必要な IAM ロールを持つすべてのユーザーが、選択した Google Cloud プロジェクトで Duet AI を使用できるようになりました。
Duet AI の使用に必要な IAM ロールを付与するには、[IAM] ページに移動します。
[プリンシパル] 列で、Duet AI へのアクセスを有効にするユーザーを見つけて、その行の鉛筆アイコン ✏️ [プリンシパルを編集] をクリックします。
[アクセス権を編集] ペインで、[別のロールを追加] をクリックします。
[ロールを選択] で、[Cloud AI Companion ユーザー] を選択します。
[別のロールを追加] をクリックし、[Service Usage 閲覧者] を選択します。
[保存] をクリックします。
IDE の設定
開発者は、ニーズに最適なさまざまな IDE から選択できます。Duet AI コード アシスタンスは、Visual Studio Code、JetBrains IDE(IntelliJ、PyCharm、GoLand、WebStorm など)、Cloud Workstations、Cloud Shell エディタなど、複数の IDE で利用できます。
このラボでは、Cloud Workstations または Cloud Shell エディタを使用できます。
このワークショップでは、Cloud Shell エディタを使用します。
Cloud Workstations の設定には 20 ~ 30 分かかることがあります。
すぐに使用するには、Cloud Shell エディタを使用します。
Cloud Shell の上部にあるメニューバーの鉛筆アイコン ✏️ をクリックして、Cloud Shell エディタを開きます。
Cloud Shell エディタの UI と UX は VSCode と非常によく似ています。

Ctrl(Windows の場合)/ Cmd(Mac の場合)+ ,(カンマ)をクリックして、[設定] ペインを開きます。
検索バーに「duet ai」と入力します。
[Cloudcode] › [Duet AI: Enable] と [Cloudcode] › [Duet AI] › [Inline Suggestions: Enable Auto] が有効になっていることを確認するか、有効にします。

下部のステータスバーで [Cloud Code - Sign In] をクリックし、ログイン ワークフローに沿って操作します。
すでにログインしている場合、ステータスバーに [Cloud Code - No project] と表示されます。
[Cloud Code - No project] をクリックすると、上部にアクション プルダウン ペインが表示されます。[Google Cloud プロジェクトを選択] をクリックします。

プロジェクト ID の入力を開始すると、プロジェクトがリストに表示されます。

プロジェクトのリストから PROJECT_ID を選択します。
下部のステータスバーが更新され、プロジェクト ID が表示されます。表示されない場合は、Cloud Shell エディタのタブを更新する必要があります。
左側のメニューバーにある Duet AI アイコン
をクリックすると、Duet AI のチャット ウィンドウが表示されます。[Select GCP Project] というメッセージが表示された場合は、プロジェクトをクリックして、もう一度選択します。
Duet AI のチャット ウィンドウが表示されます。

3. インフラストラクチャの設定

GCP で新しい配送サービスを実行するには、次の GCP リソースが必要です。
- データベースを含む Cloud SQL インスタンス。
- コンテナ化されたサービスを実行する GKE クラスタ。
- Docker イメージを保存する Artifact Registry。
- コード用の Cloud Source Repositories。
Cloud Shell ターミナルで次のリポジトリのクローンを作成し、次のコマンドを実行して、GCP プロジェクトにインフラストラクチャを設定します。
# Set your project
export PROJECT_ID=<INSERT_YOUR_PROJECT_ID>
gcloud config set core/project ${PROJECT_ID}
# Enable Cloudbuild and grant Cloudbuild SA owner role
export PROJECT_NUMBER=$(gcloud projects describe ${PROJECT_ID} --format 'value(projectNumber)')
gcloud services enable cloudbuild.googleapis.com
gcloud projects add-iam-policy-binding ${PROJECT_ID} --member serviceAccount:${PROJECT_NUMBER}@cloudbuild.gserviceaccount.com --role roles/owner
# Clone the repo
git clone https://github.com/duetailabs/dev.git ~/duetaidev
cd ~/duetaidev
# Run Cloudbuild to create the necessary resources
gcloud builds submit --substitutions=_PROJECT_ID=${PROJECT_ID}
# To destroy all GCP resources, run the following
# gcloud builds submit --substitutions=_PROJECT_ID=${PROJECT_ID} --config=cloudbuild_destroy.yaml
4. Python Flask サービスの開発

作成するサービスは、最終的に次のファイルで構成されます。これらのファイルは今すぐ作成する必要はありません。以下の手順に沿って、1 つずつ作成します。
package-service.yaml- 高さ、幅、重量、特別な取り扱い手順などのデータを含む、荷物サービス用の Open API 仕様。data_model.py- package-service API 仕様のデータモデル。product_details DB にpackagesテーブルも作成します。connect_connector.py- CloudSQL 接続(エンジン、セッション、ベース ORM を定義します)db_init.py-packagesテーブルにサンプルデータを生成します。main.py- product_id に基づいてpackagesデータからパッケージの詳細を取得するGETエンドポイントを備えた Python Flask サービス。test.py- 単体テストrequirement.txt- Python の要件Dockerfile- このアプリケーションをコンテナ化する
演習中に問題が発生した場合は、この Codelab の付録に最終ファイルがすべて記載されていますので、参照してください。
前のステップでは、Cloud Source Repositories を作成しました。リポジトリのクローンを作成します。クローン作成されたリポジトリ フォルダにアプリケーション ファイルをビルドします。
Cloud Shell ターミナルで次のコマンドを実行して、リポジトリのクローンを作成します。
cd ~ gcloud source repos clone shipping shipping cd ~/shipping
Cloud Shell エディタの左側のメニューから Duet AI チャット サイドバーを開きます。アイコンは
のような形をしています。これで、Duet AI をコード アシスタントとして使用できるようになりました。
package-service.yaml
ファイルを開いていない状態で、配送サービスの Open API 仕様を生成するよう Duet に依頼します。
プロンプト 1: 数値のプロダクト ID を指定して、配送と荷物に関する情報を提供するサービスの OpenAPI yaml 仕様を生成します。サービスには、荷物の高さ、幅、奥行き、重量、特別な取り扱いに関する指示を含める必要があります。

生成されたコード ウィンドウの右上に 3 つのオプションが表示されます。
コードをCOPY
してファイルに貼り付けることができます。
コードを ADD
して、エディタで現在開いているファイルに貼り付けることができます。
または、新しいファイルにコードを OPEN
することもできます。
OPEN をクリック
新しいファイルにコードを貼り付けます。
CTRL/CMD + s をクリックしてファイルを保存し、package-service.yaml というファイル名でアプリケーション フォルダに保存します。[OK] をクリックします。

最終的なファイルは、この Codelab の付録セクションにあります。変更されていない場合は、手動で適切な変更を行います。
さまざまなプロンプトを試して、Duet AI の回答を確認することもできます。
Duet AI サイドバーの上部にあるゴミ箱アイコン
をクリックして、Duet AI のチャット履歴をリセットします。
data_model.py
次に、OpenAPI 仕様に基づいて、サービスのデータモデル Python ファイルを作成します。
package-service.yaml ファイルを開いた状態で、次のプロンプトを入力します。
プロンプト 1: python sqlalchemy ORM を使用して、この API サービスのデータモデルを生成します。データベース テーブルを作成する別の関数とメイン エントリ ポイントも追加します。

生成された各部分を見てみましょう。Duet AI はあくまでアシスタントです。コードの作成を迅速に行うことはできますが、生成されたコンテンツをレビューし、理解しながら進める必要があります。
まず、種類 Base の Package という クラスがあります。これは、次のように packages データベースのデータモデルを定義します。
class Package(Base):
__tablename__ = 'packages'
id = Column(Integer, primary_key=True)
product_id = Column(String(255))
height = Column(Float)
width = Column(Float)
depth = Column(Float)
weight = Column(Float)
special_handling_instructions = Column(String(255))
次に、次のようにデータベースにテーブルを作成する関数が必要です。
def create_tables(engine):
Base.metadata.create_all(engine)
最後に、create_tables 関数を実行して CloudSQL データベースに実際にテーブルを作成するメイン関数が必要です。
if __name__ == '__main__':
from sqlalchemy import create_engine
engine = create_engine('sqlite:///shipping.db')
create_tables(engine)
print('Tables created successfully.')
main 関数は、ローカルの sqlite データベースを使用してエンジンを作成しています。CloudSQL を使用するには、変更する必要があります。これは後で行います。
OPEN
を使用して、以前と同じように新しいファイル ワークフローでコードを作成します。コードを data_model.py という名前のファイルに保存します(名前にアンダースコアが含まれており、ダッシュではないことに注意してください)。
Duet AI サイドバーの上部にあるゴミ箱アイコン
をクリックして、Duet AI のチャット履歴をリセットします。
connect-connector.py
CloudSQL コネクタを作成します。
data_model.py ファイルを開いた状態で、次のプロンプトを入力します。
プロンプト 1: cloud-sql-python-connector ライブラリを使用して、Postgres の Cloud SQL インスタンスの接続プールを初期化する関数を生成します。

レスポンスでは cloud-sql-python-connector ライブラリを使用していません。同じチャット スレッドに詳細を追加することで、プロンプトを調整して Duet にヒントを与えることができます。
別のプロンプトを使用してみましょう。
プロンプト 2: cloud-sql-python-connector ライブラリを使用する必要があります。

cloud-sql-python-connector ライブラリを使用していることを確認します。
OPEN
を使用して、以前と同じように新しいファイル ワークフローでコードを作成します。コードを connect_conector.py という名前のファイルに保存します。pg8000 ライブラリを手動でインポートする必要がある場合があります。以下のファイルをご覧ください。
Duet AI のチャット履歴を削除し、connect_connector.py ファイルを開いた状態で、アプリケーションで使用する DB engine、sessionmaker、base ORM を生成します。
プロンプト 1: connect_with_connector メソッドを使用してエンジン、sessionmaker クラス、Base ORM を作成する

レスポンスでは、engine、Session、Base が connect_connector.py ファイルに追加されることがあります。
最終的なファイルは、この Codelab の付録セクションにあります。変更されていない場合は、手動で適切な変更を行います。
さまざまなプロンプトを試して、Duet AI の回答のバリエーションを確認することもできます。
Duet AI サイドバーの上部にあるゴミ箱アイコン
をクリックして、Duet AI のチャット履歴をリセットします。
data_model.py を更新する
CloudSQL データベースにテーブルを作成するには、前の手順で作成したエンジン(connect_connector.py ファイル内)を使用する必要があります。
Duet AI のチャット履歴を削除します。data_model.py ファイルを開きます。次のプロンプトを試してください。
プロンプト 1: メイン関数で、connect_connector.py からエンジンをインポートして使用する

connect_connector(CloudSQL の場合)から engine をインポートするレスポンスが表示されます。create_table は(デフォルトの sqlite ローカル DB ではなく)そのエンジンを使用します。
data_model.py ファイルを更新します。
最終的なファイルは、この Codelab の付録セクションにあります。変更されていない場合は、手動で適切な変更を行います。
さまざまなプロンプトを試して、Duet AI のさまざまな回答を確認することもできます。
Duet AI サイドバーの上部にあるゴミ箱アイコン
をクリックして、Duet AI のチャット履歴をリセットします。
requirements.txt
アプリケーションの requirements.txt ファイルを作成します。
connect_connector.py ファイルと data_model.py ファイルの両方を開き、次のプロンプトを入力します。
プロンプト 1: このデータモデルとサービス用の pip 要件ファイルを生成する
プロンプト 2: 最新バージョンを使用して、このデータモデルとサービスの pip 要件ファイルを生成する

名前とバージョンが正しいことを確認します。たとえば、上記のレスポンスでは、google-cloud-sql-connecter の名前とバージョンの両方が正しくありません。バージョンを手動で修正し、次のような requirements.txt ファイルを作成します。
cloud-sql-python-connector==1.2.4
sqlalchemy==1.4.36
pg8000==1.22.0
コマンド ターミナルで次のコマンドを実行します。
pip3 install -r requirements.txt
Duet AI サイドバーの上部にあるゴミ箱アイコン
をクリックして、Duet AI のチャット履歴をリセットします。
CloudSQL でパッケージ テーブルを作成する
CloudSQL データベース コネクタの環境変数を設定します。
export INSTANCE_NAME=$(gcloud sql instances list --format='value(name)')
export INSTANCE_CONNECTION_NAME=$(gcloud sql instances describe ${INSTANCE_NAME} --format="value(connectionName)")
export DB_USER=evolution
export DB_PASS=evolution
export DB_NAME=product_details
次に、data_model.py を実行します。
python data_model.py
出力は次のようになります(コードをチェックして、実際に想定される出力を確認してください)。
Tables created successfully.
CloudSQL インスタンスに接続し、データベースが作成されていることを確認します。
gcloud sql connect ${INSTANCE_NAME} --user=evolution --database=product_details
パスワード(evolution)を入力したら、テーブルを取得します。
product_details=> \dt
出力は次のようになります。
List of relations Schema | Name | Type | Owner --------+----------+-------+----------- public | packages | table | evolution (1 row)
データモデルとテーブルの詳細を確認することもできます。
product_details=> \d+ packages
出力は次のようになります。
Table "public.packages"
Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
-------------------------------+-------------------+-----------+----------+--------------------------------------+----------+-------------+--------------+-------------
id | integer | | not null | nextval('packages_id_seq'::regclass) | plain | | |
product_id | integer | | not null | | plain | | |
height | double precision | | not null | | plain | | |
width | double precision | | not null | | plain | | |
depth | double precision | | not null | | plain | | |
weight | double precision | | not null | | plain | | |
special_handling_instructions | character varying | | | | extended | | |
Indexes:
"packages_pkey" PRIMARY KEY, btree (id)
Access method: heap
「\q」と入力して Cloud SQL を終了します。
db_init.py
次に、packages テーブルにサンプルデータを追加します。
Duet AI のチャット履歴を削除します。data_model.py ファイルを開いた状態で、次のプロンプトを試してみましょう。
プロンプト 1: 10 個のサンプル パッケージ行を作成して packages テーブルに commit する関数を生成する
プロンプト 2: connect_connector のセッションを使用して、10 個のサンプル パッケージ行を作成し、それらを packages テーブルに commit する関数を生成します。

OPEN
を使用して、以前と同じように新しいファイル ワークフローでコードを作成します。コードを db_init.py という名前のファイルに保存します。
最終的なファイルは、この Codelab の付録セクションにあります。変更されていない場合は、手動で適切な変更を行います。
さまざまなプロンプトを試して、Duet AI のさまざまな回答を確認することもできます。
Duet AI サイドバーの上部にあるゴミ箱アイコン
をクリックして、Duet AI のチャット履歴をリセットします。
サンプル パッケージ データの作成
コマンドラインから db_init.py を実行します。
python db_init.py
出力は次のようになります。
Packages created successfully.
CloudSQL インスタンスに再度接続し、サンプルデータが packages テーブルに追加されていることを確認します。
CloudSQL インスタンスに接続し、データベースが作成されていることを確認します。
gcloud sql connect ${INSTANCE_NAME} --user=evolution --database=product_details
パスワード(evolution)を入力したら、packages テーブルからすべてのデータを取得します。
product_details=> SELECT * FROM packages;
出力は次のようになります。
id | product_id | height | width | depth | weight | special_handling_instructions ----+------------+--------+-------+-------+--------+----------------------------------- 1 | 0 | 10 | 10 | 10 | 10 | No special handling instructions. 2 | 1 | 10 | 10 | 10 | 10 | No special handling instructions. 3 | 2 | 10 | 10 | 10 | 10 | No special handling instructions. 4 | 3 | 10 | 10 | 10 | 10 | No special handling instructions. 5 | 4 | 10 | 10 | 10 | 10 | No special handling instructions. 6 | 5 | 10 | 10 | 10 | 10 | No special handling instructions. 7 | 6 | 10 | 10 | 10 | 10 | No special handling instructions. 8 | 7 | 10 | 10 | 10 | 10 | No special handling instructions. 9 | 8 | 10 | 10 | 10 | 10 | No special handling instructions. 10 | 9 | 10 | 10 | 10 | 10 | No special handling instructions. (10 rows)
「\q」と入力して Cloud SQL を終了します。
main.py
data_model.py ファイル、package-service.yaml ファイル、connect_connector.py ファイルを開いた状態で、アプリケーションの main.py を作成します。
プロンプト 1: python flask ライブラリを使用して、このサービスに http rest エンドポイントを使用する実装を作成します
プロンプト 2: python flask ライブラリを使用して、このサービスに http rest エンドポイントを使用する実装を作成します。connect_conector.py から SessionMaker をインポートして、パッケージ データに使用します。
プロンプト 3: python flask ライブラリを使用して、このサービスに http rest エンドポイントを使用する実装を作成します。data_model.py から Package を、connect_conector.py から SessionMaker をインポートして、パッケージ データに使用します。
プロンプト 4: python flask ライブラリを使用して、このサービスに http rest エンドポイントを使用する実装を作成します。packages データ用に、data_model.py から Package を、connect_conector.py から SessionMaker をインポートして使用します。app.run にホスト IP 0.0.0.0 を使用する

main.py の要件を更新します。
プロンプト: main.py の要件ファイルを作成する

これを requirements.txt ファイルに追加します。Flask バージョン 3.0.0 を使用してください。
OPEN
を使用して、以前と同じように新しいファイル ワークフローでコードを使用します。コードを main.py という名前のファイルに保存します。
最終的なファイルは、この Codelab の付録セクションにあります。変更されていない場合は、手動で適切な変更を行います。
Duet AI サイドバーの上部にあるゴミ箱アイコン
をクリックして、Duet AI のチャット履歴をリセットします。
5. アプリケーションのテストと実行
要件をインストールします。
pip3 install -r requirements.txt
main.py を実行します。
python main.py
出力は次のようになります。
* Serving Flask app 'main' * Debug mode: off WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead. * Running on all addresses (0.0.0.0) * Running on http://127.0.0.1:5000 * Running on http://10.88.0.3:5000 Press CTRL+C to quit
2 番目のターミナルから、/packages/<product_id> エンドポイントをテストします。
curl localhost:5000/packages/1
出力は次のようになります。
{"depth":10.0,"height":10.0,"special_handling_instructions":"No special handling instructions.","weight":10.0,"width":10.0}
サンプルデータ内の他の商品 ID をテストすることもできます。
ターミナルで「CTRL_C」と入力して、実行中の Docker コンテナを終了します。
単体テストの生成
main.py ファイルを開いた状態で、単体テストを生成します。
プロンプト 1: 単体テストを生成します。

OPEN
を使用して、以前と同じように新しいファイル ワークフローでコードを作成します。コードを test.py という名前のファイルに保存します。
test_get_package 関数では、product_id を定義する必要があります。手動で追加できます。
最終的なファイルは、この Codelab の付録セクションにあります。変更されていない場合は、手動で適切な変更を行います。
Duet AI サイドバーの上部にあるゴミ箱アイコン
をクリックして、Duet AI のチャット履歴をリセットします。
単体テストの実施
単体テストを実行します。
python test.py
出力は次のようになります。
. ---------------------------------------------------------------------- Ran 1 test in 1.061s OK
Cloud Shell エディタで開いているすべてのファイルを閉じ、上部のステータスバーにあるゴミ箱アイコン
をクリックしてチャット履歴をクリアします。
Dockerfile
このアプリケーションの Dockerfile を作成します。
main.py を開き、次のプロンプトを試します。
プロンプト 1: このアプリケーションの Dockerfile を生成します。
プロンプト 2: このアプリケーションの Dockerfile を生成します。すべてのファイルをコンテナにコピーします。

また、INSTANCE_CONNECTION_NAME、DB_USER、DB_PASS、DB_NAME の ENVARS を設定する必要があります。手動で実行できます。Dockerfile は次のようになります。
FROM python:3.10-slim
WORKDIR /app
COPY . ./
RUN pip install -r requirements.txt
# Add these manually for your project
ENV INSTANCE_CONNECTION_NAME=YOUR_INSTANCE_CONNECTION_NAME
ENV DB_USER=evolution
ENV DB_PASS=evolution
ENV DB_NAME=product_details
CMD ["python", "main.py"]
OPEN
を使用して、以前と同じように新しいファイル ワークフローでコードを作成します。コードを Dockerfile という名前のファイルに保存します。
最終的なファイルは、この Codelab の付録セクションにあります。変更されていない場合は、手動で適切な変更を行います。
アプリケーションをローカルで実行する
Dockerfile を開いた状態で、次のプロンプトを試してみましょう。
プロンプト 1: この Dockerfile を使用してコンテナをローカルで実行するにはどうすればよいですか?

画面上の指示に沿って操作します。
# Build docker build -t shipping . # And run docker run -p 5000:5000 -it shipping
出力は次のようになります。
* Serving Flask app 'main' * Debug mode: off WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead. * Running on all addresses (0.0.0.0) * Running on http://127.0.0.1:5000 * Running on http://172.17.0.2:5000 Press CTRL+C to quit
2 つ目のターミナル ウィンドウから、コンテナにアクセスします。
curl localhost:5000/packages/1
出力は次のようになります。
{"depth":10.0,"height":10.0,"special_handling_instructions":"No special handling instructions.","weight":10.0,"width":10.0}
コンテナ化されたアプリケーションが動作している。
ターミナルで「CTRL_C」と入力して、実行中の Docker コンテナを終了します。
Artifact Registry でのコンテナ イメージのビルド
コンテナ イメージをビルドして Artifact Registry に push します。
cd ~/shipping
gcloud auth configure-docker us-central1-docker.pkg.dev
docker build -t us-central1-docker.pkg.dev/${PROJECT_ID}/shipping/shipping .
docker push us-central1-docker.pkg.dev/${PROJECT_ID}/shipping/shipping
アプリケーション コンテナが us-central1-docker.pkg.dev/${PROJECT_ID}/shipping/shipping に配置され、GKE にデプロイできるようになりました。
6. アプリケーションを GKE クラスタにデプロイする
このワークショップの GCP リソースを構築したときに、GKE Autopilot クラスタが作成されました。GKE クラスタに接続します。
gcloud container clusters get-credentials gke1 \
--region=us-central1
Kubernetes のデフォルト サービス アカウントに Google サービス アカウントでアノテーションを付けます。
kubectl annotate serviceaccount default iam.gke.io/gcp-service-account=cloudsqlsa@${PROJECT_ID}.iam.gserviceaccount.com
出力は次のようになります。
serviceaccount/default annotated
k8s.yaml ファイルを準備して適用します。
cp ~/duetaidev/k8s.yaml_tmpl ~/shipping/.
export INSTANCE_NAME=$(gcloud sql instances list --format='value(name)')
export INSTANCE_CONNECTION_NAME=$(gcloud sql instances describe ${INSTANCE_NAME} --format="value(connectionName)")
export IMAGE_REPO=us-central1-docker.pkg.dev/${PROJECT_ID}/shipping/shipping
envsubst < ~/shipping/k8s.yaml_tmpl > k8s.yaml
kubectl apply -f k8s.yaml
出力は次のようになります。
deployment.apps/shipping created service/shipping created
Pod が実行され、Service に外部ロードバランサの IP アドレスが割り当てられるまで待ちます。
kubectl get pods kubectl get service shipping
出力は次のようになります。
# kubectl get pods NAME READY STATUS RESTARTS AGE shipping-f5d6f8d5-56cvk 1/1 Running 0 4m47s shipping-f5d6f8d5-cj4vv 1/1 Running 0 4m48s shipping-f5d6f8d5-rrdj2 1/1 Running 0 4m47s # kubectl get service shipping NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE shipping LoadBalancer 34.118.225.125 34.16.39.182 80:30076/TCP 5m41s
GKE Autopilot クラスタの場合は、リソースの準備が整うまでしばらく待ちます。
EXTERNAL-IP アドレスを介してサービスにアクセスします。
export EXTERNAL_IP=$(kubectl get svc shipping --output jsonpath='{.status.loadBalancer.ingress[0].ip}')
curl http://${EXTERNAL_IP}/packages/1
出力は次のようになります。
{"depth":10.0,"height":10.0,"special_handling_instructions":"No special handling instructions.","weight":10.0,"width":10.0}
7. 追加演習: アプリケーションのトラブルシューティング
cloudsqlsa サービス アカウントから CloudSQL クライアント IAM ロールを削除します。これにより、CloudSQL データベースへの接続でエラーが発生します。
gcloud projects remove-iam-policy-binding ${PROJECT_ID} \
--member="serviceAccount:cloudsqlsa@${PROJECT_ID}.iam.gserviceaccount.com" \
--role="roles/cloudsql.client"
配送 Pod を再起動します。
kubectl rollout restart deployment shipping
Pod が再起動したら、shipping サービスへのアクセスを再度試みます。
export EXTERNAL_IP=$(kubectl get svc shipping --output jsonpath='{.status.loadBalancer.ingress[0].ip}')
curl http://${EXTERNAL_IP}/packages/1
出力は次のようになります。
... <title>500 Internal Server Error</title> <h1>Internal Server Error</h1> <p>The server encountered an internal error and was unable to complete your request. Either the server is overloaded or there is an error in the application.</p>
[Kubernetes Engine] > [ワークロード] に移動して、ログを調べます。

shipping デプロイをクリックし、[ログ] タブをクリックします。

ステータスバーの右側にある [ログ エクスプローラで表示 ]
アイコンをクリックします。新しいログ エクスプローラ ウィンドウが開きます。

Traceback エラー エントリのいずれかをクリックし、[このログエントリの説明を確認する] をクリックします。

エラーの説明を読むことができます。
次に、Duet AI を使用してエラーのトラブルシューティングを行います。
次のプロンプトを試してください。
プロンプト 1: このエラーのトラブルシューティングをサポートして

プロンプトにエラー メッセージを入力します。
プロンプト 2: Forbidden: 認証された IAM プリンシパルに API リクエストを行う権限がないようです。GCP プロジェクトで「Cloud SQL Admin API」が有効になっており、IAM プリンシパルに「Cloud SQL クライアント」ロールが付与されていることを確認します。

そして。
プロンプト 3: gcloud を使用して Google サービス アカウントに Cloud SQL クライアント ロールを割り当てるにはどうすればよいですか?

Cloud SQL クライアントのロールを cloudsqlsa に割り当てます。
gcloud projects add-iam-policy-binding ${PROJECT_ID} \
--member="serviceAccount:cloudsqlsa@${PROJECT_ID}.iam.gserviceaccount.com" \
--role="roles/cloudsql.client"
しばらくしてから、もう一度アプリケーションにアクセスしてみてください。
export EXTERNAL_IP=$(kubectl get svc shipping --output jsonpath='{.status.loadBalancer.ingress[0].ip}')
curl http://${EXTERNAL_IP}/packages/1
出力は次のようになります。
{"depth":10.0,"height":10.0,"special_handling_instructions":"No special handling instructions.","weight":10.0,"width":10.0}
Cloud Logging、ログ エクスプローラ、ログ説明ツール機能で Duet AI を使用して、問題をトラブルシューティングできました。
8. まとめ
おめでとうございます!この Codelab は終了です。
この Codelab では、以下のことを学びました。
- GCP プロジェクトで Duet AI を有効にし、IDE と Cloud Console で使用するように構成します。
- Duet AI を使用して、コードの生成、補完、説明を行います。
- Duet AI を使用して、アプリケーションの問題を説明し、トラブルシューティングします。
- IDE チャットやマルチターンのチャット、チャットとインライン コード生成の比較、コードの説明や暗唱の確認などのスマート アクションなど、Duet AI の機能。
9. 付録
package-service.yaml
swagger: "2.0"
info:
title: Shipping and Package Information API
description: This API provides information about shipping and packages.
version: 1.0.0
host: shipping.googleapis.com
schemes:
- https
produces:
- application/json
paths:
/packages/{product_id}:
get:
summary: Get information about a package
description: This method returns information about a package, including its height, width, depth, weight, and any special handling instructions.
parameters:
- name: product_id
in: path
required: true
type: integer
format: int64
responses:
"200":
description: A successful response
schema:
type: object
properties:
height:
type: integer
format: int64
width:
type: integer
format: int64
depth:
type: integer
format: int64
weight:
type: integer
format: int64
special_handling_instructions:
type: string
"404":
description: The product_id was not found
data_model.py
from sqlalchemy import Column, Integer, String, Float
from sqlalchemy.ext.declarative import declarative_base
from connect_connector import engine
Base = declarative_base()
class Package(Base):
__tablename__ = 'packages'
id = Column(Integer, primary_key=True)
product_id = Column(Integer, nullable=False)
height = Column(Float, nullable=False)
width = Column(Float, nullable=False)
depth = Column(Float, nullable=False)
weight = Column(Float, nullable=False)
special_handling_instructions = Column(String, nullable=True)
def create_tables():
Base.metadata.create_all(engine)
if __name__ == '__main__':
create_tables()
print('Tables created successfully.')
connect_connector.py
import os
from google.cloud.sql.connector import Connector, IPTypes
import sqlalchemy
# You may need to manually import pg8000 and Base as follows
import pg8000
from sqlalchemy.ext.declarative import declarative_base
def connect_with_connector() -> sqlalchemy.engine.base.Engine:
"""Initializes a connection pool for a Cloud SQL instance of Postgres."""
# Note: Saving credentials in environment variables is convenient, but not
# secure - consider a more secure solution such as
# Cloud Secret Manager (https://cloud.google.com/secret-manager) to help
# keep secrets safe.
instance_connection_name = os.environ[
"INSTANCE_CONNECTION_NAME"
] # e.g. 'project:region:instance'
db_user = os.environ["DB_USER"] # e.g. 'my-database-user'
db_pass = os.environ["DB_PASS"] # e.g. 'my-database-password'
db_name = os.environ["DB_NAME"] # e.g. 'my-database'
ip_type = IPTypes.PRIVATE if os.environ.get("PRIVATE_IP") else IPTypes.PUBLIC
connector = Connector()
def getconn() -> sqlalchemy.engine.base.Engine:
conn: sqlalchemy.engine.base.Engine = connector.connect(
instance_connection_name,
"pg8000",
user=db_user,
password=db_pass,
db=db_name,
ip_type=ip_type,
)
return conn
pool = sqlalchemy.create_engine(
"postgresql+pg8000://",
creator=getconn,
# ...
)
return pool
# Create a connection pool
engine = connect_with_connector()
# Create a sessionmaker class to create new sessions
SessionMaker = sqlalchemy.orm.sessionmaker(bind=engine)
# Create a Base class for ORM
# You may need to manually fix the following
Base = declarative_base()
db_init.py
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from connect_connector import engine
from data_model import Package
def create_packages():
# Create a session
session = sessionmaker(bind=engine)()
# Create 10 sample packages
for i in range(10):
package = Package(
product_id=i,
height=10.0,
width=10.0,
depth=10.0,
weight=10.0,
special_handling_instructions="No special handling instructions."
)
# Add the package to the session
session.add(package)
# Commit the changes
session.commit()
if __name__ == '__main__':
create_packages()
print('Packages created successfully.')
main.py
from flask import Flask, request, jsonify
from data_model import Package
from connect_connector import SessionMaker
app = Flask(__name__)
session_maker = SessionMaker()
@app.route("/packages/<int:product_id>", methods=["GET"])
def get_package(product_id):
"""Get information about a package."""
session = session_maker
package = session.query(Package).filter(Package.product_id == product_id).first()
if package is None:
return jsonify({"message": "Package not found."}), 404
return jsonify(
{
"height": package.height,
"width": package.width,
"depth": package.depth,
"weight": package.weight,
"special_handling_instructions": package.special_handling_instructions,
}
), 200
if __name__ == "__main__":
app.run(host="0.0.0.0")
test.py
import unittest
from data_model import Package
from connect_connector import SessionMaker
from main import app
class TestPackage(unittest.TestCase):
def setUp(self):
self.session_maker = SessionMaker()
def tearDown(self):
self.session_maker.close()
def test_get_package(self):
"""Test the `get_package()` function."""
package = Package(
product_id=11, # Ensure that the product_id different from the sample data
height=10,
width=10,
depth=10,
weight=10,
special_handling_instructions="Fragile",
)
session = self.session_maker
session.add(package)
session.commit()
response = app.test_client().get("/packages/11")
self.assertEqual(response.status_code, 200)
self.assertEqual(
response.json,
{
"height": 10,
"width": 10,
"depth": 10,
"weight": 10,
"special_handling_instructions": "Fragile",
},
)
if __name__ == "__main__":
unittest.main()
requirements.txt
cloud-sql-python-connector==1.2.4
sqlalchemy==1.4.36
pg8000==1.22.0
Flask==3.0.0
gunicorn==20.1.0
psycopg2-binary==2.9.3
Dockerfile
FROM python:3.10-slim
WORKDIR /app
COPY . ./
RUN pip install -r requirements.txt
# Add these manually for your project
ENV INSTANCE_CONNECTION_NAME=YOUR_INSTANCE_CONNECTION_NAME
ENV DB_USER=evolution
ENV DB_PASS=evolution
ENV DB_NAME=product_details
CMD ["python", "main.py"]