APC 技術ブログ

株式会社エーピーコミュニケーションズの技術ブログです。

株式会社 エーピーコミュニケーションズの技術ブログです。

【OCI】OCI Functionsでオブジェクトストレージのバケット内容を一覧表示する


こんにちは、クラウド事業部の島田です。

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のご支援をしております。

www.ap-com.co.jp


また、一緒に働いていただける仲間も募集中です!
今年もまだまだ組織規模拡大中なので、ご興味持っていただけましたらぜひお声がけください。

www.ap-com.co.jp