File Storage NAS の動的マウント機能を使用すると、関数インスタンスの各ユーザーセッションを専用のサブディレクトリにマウントできます。これにより、マルチテナント環境で永続データを安全に分離できます。
背景
AI エージェントアプリケーションは急速に発展しています。多くのシナリオでは、サンドボックス化されたコンテナーでカスタムユーザーコードを実行する必要があります。これらのシナリオでは、セッション中に生成されたファイル、プロジェクトコード、データセットなどの主要なアセットを永続ストレージに保存する必要もあります。これらのアプリケーションは、多くの場合、マルチテナントの Software as a Service (SaaS) としてデプロイされます。何千ものユーザーセッションが同時に実行される可能性があります。各セッションには、独自の分離されたストレージスペースが必要です。
以前は、開発者はすべてのセッションを単一の共有 NAS ルートディレクトリにマウントしていました。これにより、不正アクセスの深刻なリスクが生じました。悪意のあるセッションや異常なセッションが、ディレクトリトラバーサルや権限昇格を使用して他のテナントのデータにアクセスしたり変更したりする可能性がありました。この方法は、企業のセキュリティおよびコンプライアンス要件を満たしていません。
この課題に対処するため、Alibaba Cloud Function Compute (FC) は、永続ストレージレイヤーにまで分離を拡張しました。これは、セッション保持とセッション分離の既存のサポートに基づいています。FC は、File Storage NAS の動的マウント機能を導入しました。この機能により、各 AI Sandbox セッションは、開始時に専用の NAS サブディレクトリに自動的かつ安全にマウントされます。これにより、コンピューティングからストレージまで、エンドツーエンドのマルチテナントデータ分離が提供されます。
特徴
コア機能
Function Compute の動的 NAS マウント機能を使用すると、セッションの作成時にセッション専用の NAS ディレクトリをマウントできます。必要に応じて、独立したユーザー ID (POSIX UID/GID) を指定することもできます。コア機能は次のとおりです。
動的かつオンデマンド: ストレージのマウントはセッションのライフサイクルに関連付けられています。ストレージは必要な場合にのみ割り当てられ、セッションが破棄されると自動的にデタッチされます。
強力な分離: 各セッションに一意のユーザー ID とグループ ID を割り当てることで、ファイルシステムレベルで異なるテナントのデータアクセス権限を完全に分離できます。
高い柔軟性: テナントごとに NAS ファイルシステムの異なるサブディレクトリをマウントできます。これにより、データパーティションが容易になります。
パフォーマンスの最適化: セッションのプリフェッチと組み合わせることで、インスタンスの起動時にデータディレクトリを事前にマウントできます。これにより、モデルやデータの読み込みが高速化されます。
仕組み
この機能は、NAS マウント操作を CreateSession API 呼び出しと深く統合します。次のプロセスでは、HeaderField アフィニティを例として使用します。
準備フェーズ: ストレージを持つセッションの作成と構成
このフェーズの目標は、後で使用するために特定のストレージにバインドされた分離されたセッション環境を作成することです。
作成リクエストの開始: バックエンド管理サービスが
CreateSessionAPI を呼び出します。リクエストでは、NAS マウント構成と分離のためのユーザー ID (UID/GID) を指定します。インスタンスの準備とマウント: Function Compute (FC) プラットフォームがリクエストを受信すると、関数インスタンスを準備します。このインスタンスは、構成に基づいて NAS マウント操作を実行し、専用ディレクトリをインスタンス内の指定されたパス (例:
/mnt/data) にマウントします。ID のバインドと返却: マウントが成功すると、プラットフォームはインスタンスを新しく生成された一意の
SessionIDにバインドし、この ID をバックエンドサービスに返します。
呼び出しフェーズ: 構成済みセッションの使用
このフェーズでは、準備されたセッション環境を使用して実際のビジネスロジックを実行します。
呼び出しリクエストの開始: バックエンド管理サービスが
InvokeFunctionAPI を呼び出す際、以前に取得したSessionIDを特定の HTTP リクエストヘッダー (例:x-affinity-header-v1) で渡します。リクエストのルーティング: Function Compute プラットフォームのゲートウェイとスケジューラは、この
SessionIDを使用して、リクエストを特定のバインドされた関数インスタンスに正確にルーティングします。コードの実行: 関数コードはインスタンス内で実行されます。環境と権限はすでに構成されているため、コードはマウントされた NAS ディレクトリに直接読み書きできます。
結果の返却: 関数の実行が完了すると、結果はさまざまなレイヤーを介して元の呼び出し元に返されます。
範囲
機能の可用性: セッションライフサイクル管理機能はパブリックプレビュー中です。この機能を使用するには、チケットを送信してアクセスをリクエストしてください。
リージョン制限: この機能は、中国 (フフホト) リージョンではサポートされていません。動的 NAS マウント機能は、米国 (シリコンバレー) リージョンでのみ利用可能です。他のリージョンのサポートが必要な場合は、チケットを送信する際に指定してください。
File Storage NAS: 事前に NAS ファイルシステムを作成し、マルチテナントデータ分離のためのディレクトリ構造を計画しておく必要があります。
セッション保持タイプ: この機能は、HeaderField および Cookie アフィニティタイプに適用されます。
手順
以下のセクションでは、Python SDK を例として、関数の動的 NAS マウントを構成および使用する方法を説明します。
ステップ 1: 関数の構成 (前提条件)
セッションの動的 NAS マウントを有効にするには、関数を作成するか、その構成を更新する際に、3 つの主要な設定を完了する必要があります。
セッション保持の有効化:
Function Compute コンソールにログインし、[関数管理] > [関数リスト] に移動します。
[関数リスト] ページで、関数名をクリックして関数の詳細ページに移動します。
[設定] ページの [詳細設定] セクションで、
をクリックします。[セッション保持] スイッチをオンにし、[HeaderField アフィニティ] を選択し、[ヘッダー名] を
x-affinity-header-v1などの値で構成します。説明名前は
x-fc-プレフィックスで始めることはできません。文字で始める必要があります。後続の文字には、数字、ハイフン (-)、アンダースコア (_)、または文字を使用できます。長さは 5 ~ 40 文字である必要があります。[デプロイ] をクリックして構成を適用します。
[インスタンスフェンシングの有効化]: [設定] > [詳細設定] > [インスタンスフェンシング] に移動し、[セッションフェンシング] を選択します。
VPC へのアクセスを許可
[設定] > [詳細設定] > [ネットワーク] に移動し、[VPC へのアクセスを許可] を有効にします。
[設定方法] で、[カスタム設定] を選択します。
[VPC] パラメーターで、マウントポイントを含む VPC を選択します。
ステップ 2: NAS 構成でセッションを作成する
依存関係のインストール
macOS / Linux
# pip3 を使用してインストール pip3 install alibabacloud_fc20230330 alibabacloud_credentials alibabacloud_tea_openapi alibabacloud_tea_util # 権限の問題が発生した場合は、--user パラメーターを使用します pip3 install --user alibabacloud_fc20230330 alibabacloud_credentials alibabacloud_tea_openapi alibabacloud_tea_util # macOS Homebrew Python 環境の場合は、--break-system-packages を使用します pip3 install --break-system-packages alibabacloud_fc20230330 alibabacloud_credentials alibabacloud_tea_openapi alibabacloud_tea_utilWindows
# pip を使用してインストール pip install alibabacloud_fc20230330 alibabacloud_credentials alibabacloud_tea_openapi alibabacloud_tea_util # または、Python 3 を指定します py -3 -m pip install alibabacloud_fc20230330 alibabacloud_credentials alibabacloud_tea_openapi alibabacloud_tea_utilセッションを作成するコードを記述する
Python コードファイル (例:
createSession.py) を作成します。次のコードをファイルにコピーし、コアパラメーターを置き換えます。この例では、「テナント A」のセッションを作成する方法を示します。テナント専用の NAS ディレクトリ (<YOUR-NAS-SERVER-ADDR>:/tenant-a-data) をインスタンスの/mnt/dataパスにマウントし、ユーザー ID をUID=1001およびGID=1001として指定します。コアメソッドとパラメーター:
重要セッションの動的 NAS マウントと、[設定] > [詳細設定] > [ストレージ] での関数の NAS マウントを同時に構成できます。ただし、次の点に注意してください。
以下の
NASConfigで定義されたユーザー ID/グループ IDは、関数マウント構成で使用されるユーザー (ユーザー ID)/ユーザーグループ (グループ ID) と一致している必要があります。同じマウントパス (
mount_dir) をセッションの動的マウントと関数マウントの両方に使用することはできません。
config.endpoint:<Account ID>: Alibaba Cloud アカウント ID に置き換えます。
<Endpoint>: 詳細については、「Function Compute 3.0 サービスエンドポイントのリスト」をご参照ください。フォーマットは
fcv3.[region_id].aliyuns.comです。
CreateSessionInput:session_ttlin_seconds: セッションの合計ライフサイクル (秒単位)。
session_idle_timeout_in_seconds: セッションのアイドルタイムアウト (秒単位)。
client.create_session_with_options: <Function Name> を、セッションを作成する関数の名前に置き換えます。NASMountConfig: NAS マウント構成。mount_dir: インスタンス内のマウントパス (例:/home/test)。server_addr: NAS ファイルシステムのアドレスとテナント専用のサブディレクトリ。
user_id: このセッションに独立した POSIX ユーザー ID を指定します。group_id: このセッションに独立した POSIX グループ ID を指定します。
# -*- coding: utf-8 -*- from alibabacloud_fc20230330.client import Client as FC20230330Client from alibabacloud_credentials.client import Client as CredentialClient from alibabacloud_tea_openapi import models as open_api_models from alibabacloud_fc20230330 import models as fc20230330_models from alibabacloud_tea_util import models as util_models # 1. アカウントクライアントを作成します。 credential = CredentialClient() config = open_api_models.Config(credential=credential) config.endpoint = f'<Account ID>.<Endpoint>' client = FC20230330Client(config) # 2. NAS マウント構成を準備します。 nas_mount_config = fc20230330_models.NASMountConfig( mount_dir='/mnt/data', # インスタンス内のマウントパス server_addr='<YOUR-NAS-SERVER-ADDR>:/<tenant-a-path>' # NAS ファイルシステムのアドレスとテナント専用のサブディレクトリ ) # 3. NAS とユーザー ID を構成します (テナント A に一意の UID/GID を割り当てます)。 nas_config = fc20230330_models.NASConfig( mount_points=[nas_mount_config], user_id=1001, # このセッションに独立した POSIX ユーザー ID を指定します。 group_id=1001 # このセッションに独立した POSIX グループ ID を指定します。 ) # 4. CreateSession リクエストを構築します。 create_session_input = fc20230330_models.CreateSessionInput( nas_config=nas_config, session_ttlin_seconds=3600, session_idle_timeout_in_seconds=600 ) create_session_request = fc20230330_models.CreateSessionRequest( body=create_session_input ) # 5. リクエストを送信します。 runtime = util_models.RuntimeOptions() response = client.create_session_with_options('<Function Name>', create_session_request, {}, runtime) # 6. レスポンスからセッション ID を取得します。 print(response.body.to_map()) session_id = response.body.session_id print(f"セッションが正常に作成されました。セッション ID: {session_id}")コードの実行
export ALIBABA_CLOUD_ACCESS_KEY_ID=LTAI**************** export ALIBABA_CLOUD_ACCESS_KEY_SECRET=<yourAccessKeySecret> python3 creatSession.pyパラメーター:
ALIBABA_CLOUD_ACCESS_KEY_ID: Alibaba Cloud アカウントまたは RAM ユーザーの AccessKey ID。
ALIBABA_CLOUD_ACCESS_KEY_SECRET: Alibaba Cloud アカウントまたは RAM ユーザーの AccessKey Secret。
レスポンスの例
{ 'containerId': 'c-********-********-************', 'createdTime': '2025-10-30T06:38:10Z', 'functionName': '****', 'lastModifiedTime': '2025-10-30T06:38:10Z', 'nasConfig': { 'groupId': 1001, 'mountPoints': [ { 'enableTLS': False, 'mountDir': '/home/test', 'serverAddr': '*-*.*.nas.aliyuncs.com:/test' } ], 'userId': 1001 }, 'qualifier': 'LATEST', 'sessionAffinityType': 'HEADER_FIELD', 'sessionId': '******************', 'sessionIdleTimeoutInSeconds': 600, 'sessionStatus': 'Active', 'sessionTTLInSeconds': 3600 } セッションが正常に作成されました。セッション ID: ************
ステップ 3: 関数でマウントされた NAS を使用する
これで、関数を呼び出すときに、前のステップで取得した SessionID を含めることができます。関数インスタンスは、マウントされた /mnt/data ディレクトリにアクセスできます。次の例では、Web 関数を使用します。
関数コードを変更して再デプロイします。
import os from flask import Flask, request, jsonify # ... (アプリのセットアップ) ... # NAS マウントポイントが /mnt/data にあると仮定します NAS_MOUNT_PATH = '/mnt/data' app = Flask(__name__) @app.route('/<path:path>', methods=['GET', 'POST']) def handle_nas_request(path): rid = request.headers.get('x-fc-request-id') print(f"FC 呼び出し開始 RequestId: {rid}") # NAS 上のファイルの完全なパスを構築します # 注: パストラバーサル攻撃を防止します safe_path = os.path.normpath(os.path.join(NAS_MOUNT_PATH, path)) if not safe_path.startswith(NAS_MOUNT_PATH): return "パストラバーサルの試みが検出されました!", 400 response_data = {} if request.method == 'POST': # ファイルに書き込みます body = request.data.decode('utf-8') try: with open(safe_path, 'w') as f: f.write(body) response_data['message'] = f"{safe_path} への書き込みに成功しました" print(f"{safe_path} に書き込みました") except Exception as e: return str(e), 500 elif request.method == 'GET': # ファイルから読み取ります try: if os.path.exists(safe_path) and os.path.isfile(safe_path): with open(safe_path, 'r') as f: content = f.read() response_data['content'] = content print(f"{safe_path} から読み取りました") else: return f"ファイルが見つかりません: {safe_path}", 404 except Exception as e: return str(e), 500 print(f"FC 呼び出し終了 RequestId: {rid}") return jsonify(response_data) # jsonify を使用して JSON 形式でレスポンスを返します # ... (if __name__ == '__main__': ブロック) ...関数を呼び出して結果を確認します。
InvokeFunction API を呼び出して、セッション付きで関数を呼び出します。
サンプルコードと説明:
InvokeFunctionHeaders: リクエストヘッダーを構築します。前のステップで返されたsessionIdをリクエストヘッダーに含めます。ヘッダーキーの値は、セッション保持を有効にしたときに設定した値 (例:x-affinity-header-v1) と同じである必要があります。これにより、ルーティングのためにセッションがバインドされます。# -*- coding: utf-8 -*- from alibabacloud_fc20230330.client import Client as FC20230330Client from alibabacloud_credentials.client import Client as CredentialClient from alibabacloud_tea_openapi import models as open_api_models from alibabacloud_fc20230330 import models as fc20230330_models from alibabacloud_tea_util import models as util_models session_id = '************' function_name = 'my-session-nas' # 1. アカウントクライアントを作成します。 credential = CredentialClient() config = open_api_models.Config(credential=credential) config.endpoint = f'<Account ID>.<Endpoint>' client = FC20230330Client(config) # 2. リクエストヘッダーを構築します。ヘッダーキー (x-affinity-header-v1) は、関数に構成されたセッション保持キーと一致する必要があります。 headers = fc20230330_models.InvokeFunctionHeaders( common_headers={ "x-affinity-header-v1": session_id } ) # 3. 呼び出しリクエストを構築します (必要に応じて本文を渡すことができます)。 invoke_request = fc20230330_models.InvokeFunctionRequest( body='your_request_payload'.encode('utf-8') # サンプルペイロード ) runtime = util_models.RuntimeOptions() try: # 4. 呼び出しを送信します。 invoke_response = client.invoke_function_with_options( function_name, invoke_request, headers, runtime ) # 5. レスポンスを処理します。 print(f"ステータスコード: {invoke_response.status_code}") print(f"レスポンス本文: {invoke_response.body.decode('utf-8')}") except Exception as error: print(error.message)
次のステップ: セッションの削除
タスクが完了したら、DeleteSession API を呼び出してセッションリソースを解放できます。
本番環境に関する推奨事項
UID/GID 計画: 分離を確実にするために、各テナントに一意の POSIX UID を割り当てる必要があります。
ディレクトリクォータ: 単一のテナントがすべての共有ストレージスペースを使い果たすのを防ぐために、NAS 側で各テナントのルートディレクトリにディレクトリクォータを構成します。
データガベージコレクション (GC):
<a baseurl="t3177146_v1_0_0.xdita" data-node="6117158" data-root="85764" data-tag="xref" href="t3144017.xdita#" id="3eaee6079d3g7">DeleteSession - セッションリソースの削除</a>操作では、NAS 上のファイルデータは自動的に削除されません。非同期のガベージコレクションメカニズムを設定して、参照されていないファイルディレクトリを定期的にスキャンおよび削除し、ストレージスペースを再利用する必要があります。
課金
課金は、CreateSession API を呼び出してセッションが正常に作成された後に開始されます。インスタンスの実行時間に基づいて課金されます。
セッションが期限切れまたは削除されていない限り、アイドル状態であっても課金されます。
DeleteSession API を呼び出した後の課金の動作は、次の 2 つのシナリオによって異なります。
非分離モードでは、DeleteSession は進行中の呼び出しを停止しません。セッションに関連付けられたインスタンスリソースは、実行が完了するまで課金され続けます。
分離モードでは、DeleteSession は実行中のリクエストを停止し、バインドされたインスタンスリソースを破棄し、課金を停止します。
セッションのアクティブ期間中、リクエストがある場合は、アクティブなエラスティックインスタンスのレートで課金されます。リクエストがない場合は、アイドル状態のエラスティックインスタンスのレートで課金されます。
リファレンスと API の説明
機能 | 動作 |
| |
| |
指定された関数のセッションを一覧表示します。`qualifier`、`status`、`sessionID` によるフィルタリングをサポートします。ページ分割クエリもサポートします。
| |
セッションのアイドルタイムアウト (`SessionIdleTimeoutInSeconds`) とセッションライフサイクル (`SessionTTLInSeconds`) パラメーターを更新します。更新はすぐに有効になり、`lastModifiedTime` は自動的に更新されます。これを使用して、セッションの有効期間を動的に延長または短縮できます。 | |
|