Azure App ServiceでのPythonアプリ起動エラー対策   ～Oryxビルドの不安定さとスタートアップコマンド設定の落とし穴～

# はじめに

Azure App Service に Python アプリをデプロイする場合、Oryx による自動ビルド機能（`SCM_DO_BUILD_DURING_DEPLOYMENT=true`）で仮想環境が作成され、`requirements.txt` に基づいて依存パッケージがインストールされる仕組みがあります。  
しかし、実際にはこの自動処理が必ずしも安定して動作せず、場合によっては仮想環境が作成されなかったり、システムの Python が起動されたりすることで、「No module named streamlit」などのエラーが発生することがあります。  
この記事では、Azure 環境で発生した問題の詳細なタイムラインと、エラー解消のために実際に行った対策を時系列に沿って解説します。



# Azure環境での問題の詳細タイムライン

## 1. 初期状態

- **ビルド・デプロイの流れ**  
  GitHub Actions を用いて、`requirements.txt` に基づくビルドが成功し、ZIPデプロイが実施されました。  
  本来、Azure上では Oryx が自動で仮想環境（ `antenv`）を作成し、  
  `pip install -r requirements.txt` を実行する仕組みになっています。  
  さらに、スタートアップコマンドとして  
  ```bash
  python -m streamlit run src/backend/main.py --server.port 8000 --server.address 0.0.0.0
  ```  
  を設定していました。

## 2. 問題発生

- **エラーメッセージ**  
  Azure のログストリームには以下のようなエラーが出力されました。

  ```
  WARNING: Could not find virtual environment directory /home/site/wwwroot/antenv.
  /opt/python/3/bin/python: No module named streamlit
  ```

- **状況の意味**  
  → 仮想環境（antenv）が正しく作成されず、システムの Python が使用されているため、  
     `python -m streamlit` で起動を試みた際に、依存パッケージ（例：streamlit や docx2txt など）が存在しない状態となり、エラーが発生していました。

## 3. 調査と検証

- **ログストリームの確認**  
  ログには「仮想環境が見つからない」という警告が表示され、実際に `/home/site/wwwroot/antenv` が存在しなかったことが確認されました。
- **スタートアップコマンドの検証**  
  もともと `python -m streamlit run ...` を使用していましたが、Azure の実行環境では、  
  仮想環境が作成されず、システムの Python が使われてしまったため、  
  `python -m streamlit` はグローバル環境の streamlit を参照し、依存モジュールが存在しない状態となりました。
- **原因の特定**  
  - Azure の Oryx ビルドが、必ずしも仮想環境を正しく作成できるとは限らない（場合によっては `requirements.txt` が正しく適用されなかった）。
  - その結果、Azure で `pip install` が実行されず、`streamlit` や他の依存モジュールがインストールされなかった。

## 4. 対策と解決策

### 対策A：スタートアップコマンドの変更

- **変更前**
  ```bash
  python -m streamlit run src/backend/main.py --server.port 8000 --server.address 0.0.0.0
  ```
- **変更後**
  ```bash
  streamlit run src/backend/main.py --server.port 8000 --server.address 0.0.0.0
  ```
- **理由**  
  この変更により、Azure の実行環境では、仮想環境があればその中の `streamlit` コマンドが直接呼び出され、  
  必要なモジュールが正しく参照されるようになりました。

### 対策B：カスタムスタートアップファイル（startup.sh）の利用

- **推奨方法**  
  Azure の公式ドキュメントでも、Flask / Django 以外のアプリやカスタム構成の場合は、  
  startup.sh を利用して環境を明示的に整えることが推奨されています。（もっと早く知りたかった）
  

https://learn.microsoft.com/ja-jp/azure/developer/python/configure-python-web-app-on-app-service#other-frameworks-and-web-servers

- **startup.sh のサンプル**
  ```bash
  #!/bin/bash
  # 仮想環境を作成（または再利用）し、明示的に有効化する
  python -m venv antenv
  source antenv/bin/activate
  # 依存パッケージを確実にインストールする
  pip install -r requirements.txt
  # 仮想環境内のPythonを使ってStreamlitを起動する
  python -m streamlit run src/backend/main.py --server.port 8000 --server.address 0.0.0.0
  ```
  これを Azure の「スタートアップコマンド」として設定すれば、  
  Oryx の自動処理に頼らず、自分のコードで環境を整えてからアプリを起動できるので、安定性が大幅に向上します。



# 最終まとめ

### Azure 環境での問題と解決までの流れ

1. **初期状態**  
   GitHub Actions により、`requirements.txt` に基づくビルドとZIPデプロイが成功。  
   スタートアップコマンドとして  
   ```bash
   python -m streamlit run src/backend/main.py --server.port 8000 --server.address 0.0.0.0
   ```  
   を設定していた。

2. **問題発生**  
   Azure のログに「仮想環境が見つからない」「No module named streamlit」というエラーが表示。  
   → Oryx が期待通り仮想環境を作成できず、システムの Python で起動してしまっていた。

3. **調査**  
   ログストリームから、仮想環境（antenv）が存在しないこと、pip installが実行されていないことが判明。  
   また、スタートアップコマンドとして`python -m streamlit`を使用した場合、システムのPythonが呼ばれるケースがあることがわかった。

4. **対策と解決策**  
   - スタートアップコマンドを `streamlit run ...` に変更することで、仮想環境内のstreamlitコマンドが正しく呼び出されるようになった。  
   - 長期的には、startup.sh を用いて仮想環境の作成・有効化、依存パッケージのインストール、Streamlit の起動を一連で実施する方法が推奨される。

5. **結果**  
   仮想環境内の streamlit が正しく起動し、モジュールエラーが解消された。  
   現在、Azure に SSH 接続して確認したところ、Python実行パスやモジュール検索パスが期待通りの仮想環境内を参照しており、安定して動作しています。



# おわりに

Azure App Service の自動ビルド（Oryx）に依存すると、仮想環境の作成や依存パッケージのインストールが不安定になる場合があります。  
そのため、FlaskやDjangoといった一般的な構成以外のアプリでは、カスタムスタートアップファイル（startup.sh）を利用して、環境構築を明示的に行うことが重要です。  
今回の経験から、Azure では一部のスタートアップコマンドの仕様や実行タイミングが変わる可能性があるため、スタートアップ方法を柔軟に変更することが、安定運用の鍵となることが分かりました。

この内容が、Azure でのPythonアプリ運用に悩む皆さんの参考になれば幸いです。  
コメントやフィードバック、認識の誤り等あればご指摘お待ちしています！

https://tech.tinybetter.com/Article/7a9cc199-3e71-be85-3429-3a011721bd24/View

https://qiita.com/georgeOsdDev@github/items/129ebe2c2780815f262e

https://blog.jbs.co.jp/entry/2024/12/05/094626