Azure App Service の Python アプリ用のカスタム スタートアップ ファイルを構成する

この記事では、必要に応じて、Azure App Service でホストされている Python Web アプリのカスタム スタートアップ ファイルを構成する方法について説明します。 ローカルで実行する場合、スタートアップ ファイルは必要ありません。 ただし、Web アプリを Azure App Service にデプロイすると、コードは Docker コンテナーで実行され、スタートアップ コマンドが存在する場合はそれが使用される可能性があります。

カスタム スタートアップ ファイルは次の場合に必要です。

  • 既定値 (--bind=0.0.0.0 --timeout 600) 以外の追加の引数を使って Gunicorn の既定の Web サーバーを起動したい。

  • Flask および Django 以外のフレームワークを使用してアプリが作成されているか、または Gunicorn 以外の Web サーバーを使用したい。

  • Flask アプリのメイン コード ファイルに app.py または application.py* 以外の名前が付けられているか、アプリ オブジェクトに app以外の名前が付けられている。

    つまり、プロジェクトのルート フォルダーに app.py または application.py が存在し、"かつ" Flask アプリ オブジェクトの名前が app である場合以外は、カスタム スタートアップ コマンドが必要となります。

詳細については、Python アプリの構成 - コンテナーのスタートアップ プロセスに関する記事を参照してください。

スタートアップ ファイルを作成する

カスタム スタートアップ ファイルが必要な場合は、次の手順を使用します。

  1. スタートアップ コマンドを含んだ startup.txtstartup.sh (または他の任意の名前) というファイルをプロジェクトに作成します。 Django や Flask などのフレームワークについて詳しくは、この記事の後続のセクションを参照してください。

    スタートアップ ファイルには、必要に応じて複数のコマンドを含めることができます。

  2. このファイルを、アプリの他のファイルと一緒にデプロイできるようコード リポジトリにコミットします。

  3. Visual Studio Code で、アクティビティ バーの Azure アイコンを選択し、[リソース] を展開し、お使いのサブスクリプションを見つけて展開し、[App Services] を展開し、App Service を右クリックして、[ポータルで開く] を選択します。

  4. Azure portal の [App Service] の [構成] ページで [全般設定] を選択し、スタートアップ ファイルの名前 (startup.txt または startup.sh など) を [スタックの設定]>[スタートアップ コマンド] の下に入力し、[保存] を選択します。

    Setting the Startup Command file name in the Azure portal

    Note

    スタートアップ コマンド ファイルを使用する代わりに、Azure portal の [スタートアップ コマンド] フィールドにスタートアップ コマンド自体を直接指定できます。 ただし、コマンド ファイルの使用をお勧めします。その理由は、これにより構成のこの部分をリポジトリに保存し、そこで変更を監査したり、別の App Service インスタンスにまとめて再デプロイしたりできるためです。

  5. 変更を保存すると、App Service が再起動されます。

    ただし、まだアプリ コードをデプロイしていない場合、この時点でサイトにアクセスすると、"アプリケーション エラー" が表示されます。このメッセージは、Gunicorn サーバーが起動したがアプリを見つけることができなかったため、HTTP 要求に何も応答していないことを示します。

Django のスタートアップ コマンド

既定では、wsgi.py ファイルが格納されているフォルダーを App Service が自動的に特定し、次のコマンドで Gunicorn を起動します。

# <module> is the folder that contains wsgi.py. If you need to use a subfolder,
# specify the parent of <module> using --chdir.
gunicorn --bind=0.0.0.0 --timeout 600 <module>.wsgi

Gunicorn のいずれかの引数を変更したい場合は (--timeout 1200 を使用するなど)、それらの変更を含んだコマンド ファイルを作成します。 詳細については、「コンテナーのスタートアップ プロセス - Django アプリ」を参照してください。

Flask のスタートアップ コマンド

既定では、Flask アプリの WSGI 呼び出し可能型が app という名前であり、アプリのルート フォルダーに application.py または app.py というファイル名で存在することを App Service on Linux コンテナーは想定しています。

次のいずれかの種類を使用している場合は、カスタム スタートアップ コマンドでアプリ オブジェクトの場所を file:app_object 形式で指定する必要があります。

  • ファイル名またはアプリ オブジェクト名が異なる: たとえば、アプリのメイン コード ファイルが hello.py で、アプリ オブジェクトの名前が myapp である場合、スタートアップ コマンドは次のようになります。

    gunicorn --bind=0.0.0.0 --timeout 600 hello:myapp
    
  • スタートアップ ファイルがサブフォルダーに存在する: たとえば、スタートアップ ファイルが myapp/website.py で、アプリ オブジェクトが app である場合、Gunicorn の --chdir 引数を使用してそのフォルダーを指定した後、通常どおりにスタートアップ ファイルとアプリ オブジェクトの名前を指定します。

    gunicorn --bind=0.0.0.0 --timeout 600 --chdir myapp website:app
    
  • スタートアップ ファイルがモジュール内に存在する: python-sample-vscode-flask-tutorial コードでは、webapp.py スタートアップ ファイルが hello_app フォルダーに格納されています。このフォルダーは、それ自体が __init__.py ファイルを含んだモジュールとなっています。 このアプリ オブジェクトは app という名前で、__init__.py で定義されています。また、webapp.py には、相対インポートが使用されています。

    このような配置のため、Gunicorn から webapp:app を参照すると "Attempted relative import in non-package (非パッケージでの相対インポートが試行されました)" というエラーが表示され、アプリは起動に失敗します。

    この状況では、モジュールからアプリ オブジェクトをインポートする shim ファイルを作成し、その shim を使用してアプリを起動するよう Gunicorn に命令します。 たとえば、python-sample-vscode-flask-tutorial コードには、次の内容を含んだ startup.py が存在します。

    from hello_app.webapp import app
    

    これで、スタートアップ コマンドは次のようになります。

    gunicorn --bind=0.0.0.0 --workers=4 startup:app
    

詳細については、「コンテナーのスタートアップ プロセス - Flask アプリ」を参照してください。

その他のフレームワークと Web サーバー

Python アプリを実行する App Service コンテナーには、Django と Flask が Gunicorn Web サーバーと共に既定でインストールされています。

Django や Flask 以外のフレームワーク (FalconFastAPI など) を使用する場合、または異なる Web サーバーを使用する場合は、次を実行します。

  • 対象のフレームワークまたは Web サーバーを requirements.txt ファイルに追加します。

  • 先ほど Flask に関するセクションで取り上げた WSGI の呼び出し可能型をスタートアップ コマンドで特定します。

  • Gunicorn 以外の Web サーバーを起動するには、サーバーを直接呼び出す代わりに python -m コマンドを使用します。 たとえば、次のコマンドは、uvicorn サーバーを起動するものです。WSGI の呼び出し可能型は app という名前で、application.py に存在することを想定しています。

    python -m uvicorn application:app --host 0.0.0.0
    

    python -m を使用するのは、requirements.txt 経由でインストールされる Web サーバーは Python のグローバル環境には追加されず、直接呼び出すことができないためです。 python -m コマンドでは、現在の仮想環境内からサーバーが呼び出されます。