2024年1月11日木曜日

Autonomous DatabaseでGoogle Cloudのサービス・アカウントを作成しGoogle Cloud Storageにアクセスする

GoogleのGemini APIでは、画像や動画のデータをAPIに渡すためにシリアル化してREST APIのリクエストに埋め込む(パラメータとしてはinlineData)他に、Google Cloud Storage上のファイルを指す(パラメータとしてはfileData)ことができます。

inlineDataとfileDataについては、以下のAPIリファレンスに記載されています。

Google Gemini APIにfileDataを渡すためには、あらかじめGoogle Cloud Storageにファイルをアップロードしておく必要があります。Autonomous Databaseでは、パッケージDBMS_CLOUDを使って、Google Cloud Storageの操作を行うことができます。

Oracle CorporationのPrincipal Product ManagerのCan Tuzlaさんによる以下の記事を参考にして、Google Cloud Storageのバケットに画像または動画ファイルをアップロードするAPEXアプリケーションを作成してみます。

Access GCP Resources from Your Autonomous Database
https://blogs.oracle.com/datawarehousing/post/access-gcp-resources-from-your-autonomous-database

作成したアプリケーションは以下のように動作します。


最初に説明が必要ですが、GoogleのGemini APIのfileDataとしてGoogle Cloud Storage上のファイルを扱う場合(gs:で始まるURIでないとエラーが発生するためGoogle Cloud Storage上のファイル以外は扱えない模様)、Gemini APIの認証にAPIキーは使えません。これはAPIキーではGoogle Cloud Storageにアクセスできないためです。また、呼び出し先もVertex AIである必要があるようです。

以上より、Gemini APIでfileDataを扱うにはVertex AI Gemini APIを呼び出すためのサービス・アカウントが必須です。それであれば、Gemini APIを呼び出すために作成したサービス・アカウントにGoogle Cloud Storageを操作するロールを追加すれば(Gemini APIからファイルを参照する必要があるため、サービス・アカウントにファイルの参照権限は必須です)、ブログ記事にある手順で、別途サービス・アカウントを作る必要はありません。

DBMS_CLOUDでは、Google Cloud Storageを操作するためにXML APIを使っている模様ですが(APIのエンドポイントがXML APIのエンドポイントです)、Gemini APIを呼び出すために作成したサービス・アカウントであれば、より柔軟に扱えるObject Storage JSON APIを呼び出すこともできます。

以上の理由により、Gemini APIで扱うファイルを操作するためにDBMS_CLOUDは使わないことにしました。とはいえ実際にAPEXアプリケーションを作って動作を確認しているので、DBMS_CLOUDを使ったGoogle Cloud Storageを操作するAPEXアプリケーションの作成手順を紹介します。

前置きが長くなりましたが、以下より作業手順です。Google Cloud Platformのサービス・アカウントの作成、バケットの作成や権限付与については、元にしたブログ記事をほとんどそのまま踏襲します。

最初にGCPのサービス・アカウントを有効にします。APEXのワークスペース・スキーマはパッケージDBMS_CLOUD_ADMINの実行権限はなく、また、与えるべきでもないため、このプロシージャの呼び出しはユーザーADMINで実施します。引数usernameとして、APEXのワークスペース・スキーマを与えます。今回の例ではWKSP_APEXDEVを指定しています。

ユーザーADMINでデータベース・アクションにサインインし、SQLより以下のコマンドを実行します。
BEGIN    
    DBMS_CLOUD_ADMIN.ENABLE_PRINCIPAL_AUTH(
        provider => 'GCP'
        ,username => 'WKSP_APEXDEV');                        
END;                                                                 
/

作成されたサービス・アカウントを確認します。
select * from cloud_integrations where param_name like 'gcp%';
PARAM_NAMEgcp_service_accountPARAM_VALUEが、GCPのサービス・アカウントのメールになります。この値を安全に保存しておきます。


異なるAPEXワークスペース・スキーマに対してDBMS_CLOUD_ADMIN.ENABLE_PRINCIPAL_AUTHを実行しても、サービス・アカウントのメールは同じになるようです。ENABLE_PRINCIPAL_AUTHが呼び出されているスキーマでは、GCPにアクセスする際に使用するクリデンシャルであるGCP$PAが作成されています。

Cloud Storageにバケットを作成します。

Google CloudのコンソールよりCloud Storageを開き、サイド・メニューのバケットより作成を実行します。


バケットに名前を付けます。今回はmy-gemini-data-1234としています。

続行をクリックします。


データの保存場所を選択します。Vertex AIのGeminiはサポートされているリージョンが限定されています。Gemini APIの呼び出し先をus-central1にする予定なので、データの保存場所としてもus-central1を選択します。

続行をクリックします。

https://cloud.google.com/vertex-ai/docs/generative-ai/model-reference/gemini


これ以降はデフォルトの設定を選択します。

ストレージ・クラスStandard、オブジェクトのアクセス制御は、公開アクセス防止し、かつアクセス制御均一(バケット単位での制御 )、保護ツールなしです。

作成をクリックします。


公開アクセスの設定の確認が求められるので、このバケットに対する公開アクセス禁止を適用するチェックが入っていることを確認します。

確認をクリックします。


バケットが作成され、詳細画面が開きます。

権限のタブを開き、アクセス権を付与をクリックします。


画面右にドロワーが開くので、新しいプリンシパルにAutonomous Database側で作成したサービス・アカウントメールを入力します。ロールにはStorageオブジェクトユーザーを指定します。

保存をクリックします。


作成した権限が一覧に表示されます。


以上でGoogle Cloud Storage側の準備は完了です。

Oracle APEXからアクセスをしてみます。以下のテストはこちらの記事で、AWSのS3にアクセスした際に使用したものとほぼ同じです。

SQLワークショップSQLコマンドより、ファイルのアップロードを行ってみます。

バケットmy-gemini-data-1234にファイルtest.txtをアップロードしてみます。
declare
    l_resp dbms_cloud_types.resp;
    l_blob blob;
    l_clob clob;
begin
    l_clob := 'my first upload';
    l_blob := apex_util.clob_to_blob(l_clob);
    l_resp := dbms_cloud.send_request(
        credential_name => 'GCP$PA'
        ,uri => 'https://my-gemini-data-1234.storage.googleapis.com/test.txt'
        ,method => 'PUT'
        ,body => l_blob
    );
end;

バケットmy-gemini-data-1234の内容を一覧します。ファイルtest.txtがアップロードされていることが確認できます。
select * from dbms_cloud.list_objects(
    credential_name => 'GCP$PA'
    ,location_uri => 'https://my-gemini-data-1234.storage.googleapis.com/'
)

ファイルtest.txtを取得します。ファイルの内容がmy first uploadと表示されます。
declare
    l_resp dbms_cloud_types.resp;
    l_clob clob;
begin
    l_resp := dbms_cloud.send_request(
        credential_name => 'GCP$PA'
        ,uri => 'https://my-gemini-data-1234.storage.googleapis.com/test.txt'
        ,method => 'GET'
    );
    l_clob := dbms_cloud.get_response_text(
        resp => l_resp
    );
    dbms_output.put_line(l_clob);
end;


ファイルtest.txtを削除します。
begin
    dbms_cloud.send_request(
        credential_name => 'GCP$PA'
        ,uri => 'https://my-gemini-data-1234.storage.googleapis.com/test.txt'
        ,method => 'DELETE'
    );
end;

再度、バケット内のファイルをリストします。データが見つかりませんと返されます。


Oracle APEXのワークスペースよりGoogle Cloud Storageにアクセスできることが確認できたので、これよりAPEXのアプリケーションを作成します。

アプリケーション・ビルダーより作成を開始し、名前Sample Google Cloud Storage Accessとした空のアプリケーションを作成します。


アプリケーションが作成されたら、アプリケーション定義置換を開きます。

置換文字列としてG_BUCKET_NAME置換値にGCPに作成したバケットの名前を設定します。


バケット内のファイルを対話モード・レポートで一覧します。ファイルのアップロードや削除はフォームに実装します。

ページの作成を開始します。


対話モード・レポートを選択します。


対話モード・レポートのページの名前Filesとします。フォーム・ページを含めるオンにし、フォーム・ページ名Fileとします。

データ・ソースソース・タイプSQL問合せに変更し、SQL SELECT文を入力に以下を記述します。ウィザードにSQL文を記述する際は置換文字列が使えないため、localtion_uriのバケット名は直書きします。これは、ページの作成後に置き換えます。また、列OBJECT_NAME主キー列としますが、その場合、列が非表示項目になるため列IDを追加しています。
select object_name id, object_name, bytes, checksum, created, last_modified
from dbms_cloud.list_objects(
    credential_name => 'GCP$PA'
    ,location_uri => 'https://my-gemini-data-1234.storage.googleapis.com/'
)
ナビゲーションはデフォルトのままとし、ブラッドクラムの使用ナビゲーションの使用ともにオンとします。

へ進みます。


主キー列1ID (Varchar2)を選択します。

ページの作成をクリックします。


ページが作成されます。

対話モード・レポートソースSQL問合せを、バケット名として置換文字列G_BUCKET_NAMEを使用するように変更します。
select object_name id, object_name, bytes, checksum, created, last_modified
from dbms_cloud.list_objects(
    credential_name => 'GCP$PA'
    ,location_uri => 'https://' || :G_BUCKET_NAME || '.storage.googleapis.com/'
)

ページ・デザイナでフォームのページを開き、ファイルのアップロードを実装します。

ファイルを選択するページ・アイテムとしてP2_FILESを作成します。ページ・アイテムのタイプファイルのアップロードです。ラベルFilesとします。

ストレージタイプ表APEX_APPLICATION_TEMP_FILESファイルをパージするタイミングリクエストの終わり複数ファイルの許可オンにします。

セッション・ステートストレージリクエストごと(メモリーのみ)、また、このページ・アイテムはファイルをアップロードするときのみ表示するため、サーバー側の条件タイプとしてアイテムはNULLを選択し、アイテムとしてP3_IDを指定します。


ページ・アイテムP3_OBJECT_NAMEP3_BYTESP3_CHECKSUMP3_CREATEDP3_LAST_MODIFIEDはファイルをアップロードする際には表示は不要です。

これらのページ・アイテムをすべて選択し、サーバー側の条件タイプとしてアイテムはNULLではないを選択し、アイテムP3_IDを指定します。


左ペインでプロセス・ビューを開き、デフォルトで作成されているプロセスプロセス・フォームFileコメント・アウトします。

このプロセスの代わりに、ファイルをアップロードするプロセスと削除するプロセスを作成します。更新は今回実装しません。


プロセスを作成します。

識別名前Upload Filesタイプコードの実行を選択し、ソースPL/SQLコードとして以下を記述します。

サーバー側の条件ボタン押下時CREATEを指定します。


ファイルを削除するプロセスを作成します。

識別名前Delete Fileタイプコードを実行です。ソースPL/SQLコードとして以下を記述します。

サーバー側の条件ボタン押下時DELETEを指定します。


最後にボタンSAVE(ラベルは変更の適用)をコメント・アウトします。


以上でアプリケーションは完成です。実行すると記事の先頭にあるGIF動画のように動作します。

今回作成したAPEXアプリケーションのエクスポートを以下に置きました。
https://github.com/ujnak/apexapps/blob/master/exports/sample-google-cloud-storage-access.zip

Oracle APEXのアプリケーション作成の参考になれば幸いです。