こんにちは、クラウド事業部の島田です。
Oracle Cloud Infrastructure Architect Associateを最近取得したため、実際にOCIに触れてみよう!を目的として動作検証を行いました。
Oracle Cloud Infrastructure (OCI) Functions を活用すると、サーバーレスアーキテクチャで手軽に様々な処理を実行できます。今回は、OCI Functions を使って、オブジェクトストレージのバケットの中身(オブジェクトの一覧)を表示するシンプルな関数を作成し、デプロイ、テストする手順を解説します。
1. はじめに:OCI Functions とは?
OCI Functions は、Oracle Cloud Infrastructure のサーバーレスな関数実行サービスです。コードをデプロイするだけで、インフラの管理なしにイベント駆動型で関数を実行できます。リソースの自動スケーリングや従量課金制が特徴で、コスト効率の良い開発が可能です。
今回は、この Functions を使って、指定したオブジェクトストレージのバケットにあるファイルやフォルダの一覧を取得してみます。
2. 事前準備
まず、OCI Functions の開発・デプロイに必要な環境設定を行います。
- OCI アカウントとテナンシー: 必須
- VCN: インターネットにアクセスできるパブリックサブネットを作成済みの前提
- クラウドシェル:コマンドの実行。OCIコンソールより実行
3. 環境変数の設定
今回は後のコマンドが環境ごとに異ならないように、Functions をデプロイするテナンシーやコンパートメントの情報を環境変数として設定します。
export OCI_TENANCY_ID="ocid1.tenancy.oc1.." # あなたのテナンシーOCID export OCI_REGION="ap-tokyo-1" # 例: ap-tokyo-1 (東京リージョン) export OCI_COMPARTMENT_ID="ocid1.tenancy.oc1.." # 関数をデプロイするコンパートメントOCID export OCI_FUNCTION_APP_NAME="<appname>" # アプリケーション名
4. OCI Functions アプリケーションの作成
関数をデプロイする前に、Functions アプリケーションを作成します。
OCI コンソールでの作成:
OCI コンソールにログインし、ナビゲーションメニューから 「開発者サービス」->「Functions」 を選択します。
「アプリケーション」 セクションで 「アプリケーションの作成」 をクリックします。
名前には先ほど環境変数(OCI_FUNCTION_APP_NAME)に入力した名前(例:appname) を入力します。
コンパートメント、VCN、サブネットを選択し、「作成」をクリックします。
5. 関数コードの作成
コマンドシェルにて、作業ディレクトリを作成するために以下を実行します
mkdir object-storage-list-function cd object-storage-list-function fn init --runtime python object_list_function
作成されたrequirements.txtを編集します
cd object_list_function/ vi requirements.txt
内容は以下の通り(ociを追記)
fdk>=0.1.95 oci
func.pyを編集します
vi func.py
内容は以下の通り
import io import oci import json import logging from fdk import response # ロギングの設定: 関数の実行ログを見やすくします logging.basicConfig(level=logging.INFO) logger = logging.getLogger(__name__) def handler(ctx, data: io.BytesIO = None): """ OCI Functions のエントリポイントとなる関数です。 HTTP リクエストからバケット名を受け取り、そのバケット内のオブジェクトを一覧表示します。 """ # リソースプリンシパル認証を利用してOCIサービスにアクセスするための署名者を取得 # これにより、APIキーなどをコードに埋め込まずに安全に認証できます signer = oci.auth.signers.get_resource_principals_signer() # オブジェクトストレージサービスにアクセスするためのクライアントを初期化 object_storage_client = oci.object_storage.ObjectStorageClient({}, signer=signer) bucket_name = None try: # HTTP リクエストのボディ(JSON形式)をパース # 例: {"bucket_name": "your_bucket_name"} body = json.loads(data.getvalue()) bucket_name = body.get("bucket_name") except Exception as e: # 入力JSONのパースエラーをハンドル logger.error(f"Error parsing input: {e}") return response.Response( ctx, response_data=json.dumps({"message": "Invalid input format. Please provide 'bucket_name'."}), headers={"Content-Type": "application/json"} ) if not bucket_name: # バケット名が指定されていない場合のエラーハンドリング return response.Response( ctx, response_data=json.dumps({"message": "Please provide 'bucket_name' in the request body."}), headers={"Content-Type": "application/json"} ) try: # オブジェクトストレージのネームスペース(テナンシーごとの一意の名前)を取得 namespace = object_storage_client.get_namespace().data # 指定されたバケット内のオブジェクトを一覧表示 list_objects_response = object_storage_client.list_objects(namespace, bucket_name) # 取得したオブジェクト情報から、オブジェクト名(ファイル名)のみを抽出 object_names = [obj.name for obj in list_objects_response.data.objects] # 結果をJSON形式で整形 result = { "bucket_name": bucket_name, "objects": object_names, "count": len(object_names) } logger.info(f"Successfully listed objects in bucket '{bucket_name}'.") # 成功レスポンスを返却 return response.Response( ctx, response_data=json.dumps(result), headers={"Content-Type": "application/json"} ) except oci.exceptions.ServiceError as e: # OCI サービスからのエラー(例: バケットが存在しない、権限不足など)をハンドル logger.error(f"OCI Service Error: {e.code} - {e.message}") return response.Response( ctx, response_data=json.dumps({"error": f"OCI Service Error: {e.message}"}), headers={"Content-Type": "application/json"}, status_code=e.status # OCIが返したHTTPステータスコードをそのまま利用 ) except Exception as e: # その他の予期せぬエラーをハンドル logger.error(f"An unexpected error occurred: {e}") return response.Response( ctx, response_data=json.dumps({"error": f"An unexpected error occurred: {e}"}), headers={"Content-Type": "application/json"}, status_code=500 # 内部サーバーエラー )
func.yamlを確認します。自動生成されたもので特に変更は不要です。
schema_version: 20180708 name: object_list_function # 関数名 version: 0.0.1 runtime: python build_image: fnproject/fn-python-fdk-build:latest run_image: fnproject/fn-python-fdk:latest cmd: func.handler memory: 256 # 関数のメモリ制限 (MB)。必要に応じて調整 timeout: 30 # 関数のタイムアウト時間 (秒)。必要に応じて調整
6. 関数のデプロイ
コードの準備ができたので、OCI Functionsにデプロイします。認証トークンを作成後、OCIRegistryにDoockerログインします。この一連の操作はアプリケーション詳細画面に表示されているスタートガイドのクラウド・シェル設定ステップ5/6から確認できます
docker login -u '<ユーザー名>' nrt.ocir.io
次に、関数をデプロイします。--verbose オプションで詳細なビルド・デプロイプロセスを確認できます。
fn deploy -app $OCI_FUNCTION_APP_NAME --verbose
デプロイが成功すると、Function が指定したアプリケーションに登録されます。
7. IAM ポリシーの設定
OCI Functions がオブジェクトストレージにアクセスするためには、適切な IAM ポリシーが必要です。関数はリソースプリンシパルとして動作するため、その動的グループに権限を付与します。
OCI コンソールで 「アイデンティティとセキュリティ」->「動的グループ」 に移動します。
一意の名前で動的グループの作成をします。ルール例は以下
resource.id = '<object_list_functionのOCID>'
IAMポリシーを作成します
「アイデンティティとセキュリティ」->「ポリシー」 を選択します。「ポリシーの作成」をクリックし以下のようなステートメントを追加します。<動的グループ名>は先ほど作成した動的グループ名に置き換えてください。権限は動作確認のために広めにしています
Allow dynamic-group '<動的グループ名>' to manage object-family in tenancy
8. 関数のテスト
IAM ポリシーの設定が完了したら、いよいよデプロイした関数をテストします。
クラウドシェルで以下コマンドを実行します。※bucketnameはあなたの存在するバケット名に置き換えてください
echo '{"bucket_name": "<bucketname>"}' | fn invoke $OCI_FUNCTION_APP_NAME object_list_function
成功例
{"bucket_name": "yshimada20250626", "objects": ["event/", "event/test.json", "test.json", "test.txt", "tevent/", "tevent/test.json"], "count": 6}
これで、OCI Functions を使ってオブジェクトストレージのバケット内容を一覧表示する関数の動作確認ができました。(失敗する場合は権限の設定を見直す、また、少し時間を空けてから再度実行してみてください)
まとめ
今回は、OCI Functions とオブジェクトストレージを連携させ、サーバーレスでバケット内容を一覧表示するPython関数を構築しました。リソースプリンシパルによる認証とIAMポリシーの重要性も理解いただけたかと思います。(自分はOCIにあまり慣れていないこともあり、権限周りの設定が一番苦戦しました。。。)
OCI Functions は、このようなシンプルなタスクから複雑なデータ処理まで、様々な用途に活用できる強力なサービスです。ぜひ他のOCIサービスとの連携も試してみてください。
検証時に困ったこと
権限的があっていても関数実行時に以下エラーが表示されてだいぶはまりました。同様のエラーが出た場合、少し時間を置くと解決することがあります。
{"error": "OCI Service Error: Either the bucket named '<bucketname>' does not exist in the namespace '<namespace>' or you are not authorized to access it"}
お知らせ
私達クラウド事業部はクラウド技術を活用したSI/SESのご支援をしております。
また、一緒に働いていただける仲間も募集中です!
今年もまだまだ組織規模拡大中なので、ご興味持っていただけましたらぜひお声がけください。