Azure Container Apps Dynamic Sessionで安全にPythonスクリプトを実行。.NETからも呼べるよ

.NETが大好きなエンジニアなので、AIに関しては最新のライブラリはPythonがほとんどです。
でもどうしても本体は.NETで作り、やむを得ない場合はPythonも使うスタンス

.NETでアプリ(Blazor WASM)を開発しデプロイする場合はContainer Appsをゲキ推ししています。
Containerの中でPythonを動かすのは出来ることは出来ますがなんともセキュリティ的に怪しい感じがします。

Youtubeを見ていたところ[Container Apps Dynamic sessions](https://www.youtube.com/live/FxrReC1DjHI?si=zdKTnSz3F0oCaMLx)を紹介していました。

# Container Apps Dynamic sessions
Container Apps Dynamic Sessions(以下CADS)
[CADS](https://learn.microsoft.com/ja-jp/azure/container-apps/sessions?tabs=azure-cli)はさらっとドキュメントが既に存在していました(GAは2024年11月?)

CADSは1回限りのPythonスクリプトを実行し結果を取得するのに適しています。

# やりたいこと
Container AppsからCADSにPythonスクリプトを送り込み、結果を取得する
今回はファイルを読み込みMarkdown(MarkItDownを使用)を出力します
いわゆるRAGの抽出ですね

# 準備

## サンプルプログラム

[サンプルプログラムリポジトリ](https://github.com/kyamamoto03/ACADynamicSessionServiceTest)

CADSをサービス化しました

ACADynamicSessionServiceはファイルのアップロード、Python実行、結果のダウンロードをサポートしています。

```Programs.cs
var sessionService = app.Services.GetRequiredService<IACADynamicSessionService>();

var sessionId = sessionService.CreateSessionId();
Console.WriteLine($"Session ID: {sessionId.Id}");

builder.Services.AddACADynamicSessionService(config =>
{
    config.Endpoint = new Uri("{{EndPpoint}");
});

await sessionService.FileUploadAsync(sessionId, fileName, bytes);

// Pythonスクリプト
string script = $"""
    import os
    from markitdown import MarkItDown

    file_url = "/mnt/data/{fileName}"
    markitdown = MarkItDown()
    result = markitdown.convert(file_url)

    output_dir = "/mnt/data"
    if not os.path.exists(output_dir):
        os.makedirs(output_dir)

    # 結果をファイルに出力
    output_path = os.path.join(output_dir, "output.md")
    try:
        with open(output_path, "w", encoding="utf-8") as f:
            f.write(result.text_content)
        print(f"Markdown content successfully written to output_path")
    except Exception as e:
        print(str(e))
    """;

var response = await sessionService.ExecuteAsync(sessionId, script);

var markdown = await sessionService.FileDownloadAsync(sessionId, "output.md");
Console.WriteLine(markdown);
```

これでpython実行時に必要なファイルをアップロード、実行、ダウンロードが行えます。

## 環境構築

### Container Apps
後で構築するCADSで認証するために、

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/136187/1a2451c5-4515-41b3-af2d-1c5ee670a1d3.png)
状態をオンにしマネージドIDを割り当てましょう

### CADS
CADSを構築する場合、Azure PortalでContainer Apps Dynamicと入力しても現れません

コンテナー アプリ セッション プールと入力しましょう

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/136187/43f669f3-414f-4692-8ddc-ef09310de45a.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/136187/d86a6a0b-52b3-40f1-8281-4277a79e2844.png)

プールの種類は迷わずPythonを選択
CADSはまだ全世界に展開されているわけではなく、一部のようです。日本からは一番近いのはEast Asiaでしょう。

Container AppsからCADSが呼べるようにIAMを設定します。
ロールの割り当て追加から

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/136187/69720519-ba78-414b-b6b2-7d77f5fc2e42.png)

ロールにAzure ContainerApps Session Executorを入力

マネージドIDを選択
上で作成したContainer Appsを選択
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/136187/4a00f3e9-12ca-4919-82c3-974ace10eaf8.png)


割当を行います。
これを行うとContainer AppsからCADSのトークンが取得できるようになります。
ちないに、Azureにアクセスできる環境(az login)していればローカルでデバッグも行えます。

### EndPpointを取得するには

```Bash
az containerapp sessionpool show \
    --name my-session-pool \
    --resource-group <RESOURCE_GROUP> \
    --query 'properties.poolManagementEndpoint' -o tsv
```
で取得が可能です。


# まとめ
.NETからPythonが実行できるのでLLMで作ってもらったスクリプトをCADSで実行し結果を取得することができます。
またSemantic Kernelからもシームレスに呼べるらしい(まだ未確認)なので.NETの民は歓喜です。