Pythonで構築したAzure Functions関数を事前ビルドして閉域環境にデプロイする

アウトバウンドが制限された環境でAzure Functionsの環境構築を実施する際、裏側でpipコマンドを実行した時に外部pypiのサーバなどに接続しようとして、エラーが発生してしまいます。

本記事ではこのエラーの回避策として、事前にローカル環境で環境構築を行いAzure Cloud Shellを使用してzipファイルをデプロイする、という方法を紹介します。

前提

本手順ではPythonのAzure Functions v2プログラミングモデルを前提にします。 learn.microsoft.com

事前にローカル上で環境構築を実施し、Azure Cloud Shellを用いてアップロードしたZIPファイルをデプロイします。

検証環境

構成図

Azure上の閉域構成は以下のような形とします。

Azure FunctionsはPrivate Endpoint Subnetに接続されたその他APIやWebUIからのアクセスのみを受け付けて、処理を実施するような構成を想定します。

ただし今回は動作検証のため、特定IPアドレスに限ってFunctionsへの外部からの接続を直接許可します。

※NSGは閉域環境(ネットワーク制限)を表現しています

Azure リソース

  • Azure Functions
  • ストレージアカウント(Azure Functions 管理用)
  • VNET

ネットワーク設定

VNET構成
  • app_integration_subnet (10.0.0.0/26)
    • Azure FunctionsのVNET統合を行うサブネット
    • サブネットの委任をMicrosoft.Web/serverFarmsに設定する
    • NSGがある場合は設定する
  • private_endpoint_subnet (10.0.1.0/26)
    • Azure Functionsがバックエンドアプリケーション間で閉域通信を行うためのサブネット
    • サービスエンドポイントMicrosoft.Storageを設定する
    • NSGがある場合は設定する

サービスエンドポイントを設定しない場合はデプロイが失敗します。またデプロイ後に設定を切った場合も、関数が使用できなくなります。

VNET統合で使用するサブネットは最低/26のレンジを確保することが推奨されています。 learn.microsoft.com

Azure Functions

公開ネットワークアクセスを以下のように設定します。

  • メインサイト
    • 通信を行うユーザーのIPアドレスを許可(動作確認のため)
    • 一致しないルールのアクションを「拒否」に設定
  • 高度なツールサイト
    • サービスタグ「AzureCloud」の通信を許可(zipデプロイを実施するため)
    • 一致しないルールのアクションを「拒否」に設定

仮想ネットワークの統合を選択して、app_integration_subnet (10.0.0.0/26)を対象に、VNET統合を設定します。

このVNET統合設定では、[アプリケーションルーティング]-[送信インターネットトラフィック]のチェックをONにします。

ストレージアカウント

ネットワーク設定の項目を以下のように設定します。

  • ファイアウォールと仮想ネットワーク
    • パブリックネットワークアクセスを「選択した仮想ネットワークとIPアドレスから有効」に設定
    • app_integration_subnet (10.0.0.0/26)からの通信を許可
  • プライベートエンドポイント接続
    • private_endpoint_subnet (10.0.1.0/26)に接続するプライベートエンドポイントを作成

デプロイ手順

ローカル環境でのzipファイル作成

Azure Functionsリソースを、LinuxOSかつ同じバージョンのPython環境を用意します。例えば3.11であれば、同じ3.11バージョンの仮想環境を用意します。それ以外のバージョンを使用した場合、デプロイ後に関数が使用できない状態になります。

Windowsの場合はwslを使用することで、疑似的にLinux環境を用意することができます。

learn.microsoft.com

ローカル環境のコンソールを使用し、上記で用意したPython環境を有効化します。Azure Functionsプロジェクトの階層まで移動して、以下のコマンドを実行します。

pip install -r requirements.txt -t .python_packages/lib/site-packages --upgrade 

上記コマンドによってインストール済みの環境が同ディレクトリに配置されます。 このファイルを含めた必要ファイルをzip化します。zipの中には以下4ファイルが含まれます。

  • .python_packages
  • function_app.py
  • host.json
  • requirements.txt

zipデプロイの詳細は以下の記事をご参照ください。
learn.microsoft.com

Azure Functionsの事前設定

アプリケーションをデプロイする Azure Functionsに対して、以下の環境変数を設定しておきます。

  • SCM_DO_BUILD_DURING_DEPLOYMENT = false
  • ENABLE_ORYX_BUILD = false
  • WEBSITE_RUN_FROM_PACKAGE = 1
  • AzureWebJobsFeatureFlags = EnableWorkerIndexing

Azure Cloud Shellでデプロイ

Azureポータルを開いて、Azure Cloud Shellを起動します。

Cloud Shellが起動した後、zipファイルをアップロードします。

以下のコマンドを実行します。

az account set --subscription [サブスクリプションID]
az functionapp deployment source config-zip -g [リソースグループ名] -n [関数アプリ名] --src [アップロードしたzipファイル名]

コマンドはすぐに完了しますが、反映までは数分かかります。

概要ページからfunction_app.pyで定義した関数名が表示されればデプロイは成功です。Functionsのネットワーク設定で許可したIPアドレスから、直接FunctionsAPIを使用して動作確認を実施できます。

注意点としては、プログラム自体に何らかのバグが含まれていたり、依存関係にエラーがあった場合でも成功のログが出てしまいます。この場合はデプロイ後に何分待っても関数が表示されないため、プログラムや環境の見直しを実施する必要があります。

(補足) デプロイログの確認方法

Azure Functionsリソースの[デプロイメント]-[デプロイセンター]から、デプロイログを確認することができます。

外部に通信を行わずにzipデプロイが成功している場合は次のようなログになるはずです。

一方、外部に通信している場合は次のようなログになります。
「ログを表示する」の部分を展開すると、pipインストール等を実行している履歴が確認できます。

通常デプロイ時の外部通信について

特に制限の環境では、パッケージの配置されている pypi.org や、パッケージの依存関係を管理する files.pythonhosted.org に通信を行ってpipコマンドを実行します。

またPythonランタイム取得のために oryx-cdn.microsoft.io への通信を行うようです。

おわりに

本記事で紹介した手法を用いれば、アウトバウンド通信の制限が厳しい環境下でのAzure Functionsのデプロイであっても、ローカルでの事前準備とAzure Cloud Shellを活用することで、セキュリティを維持しつスムーズに環境を構築することが可能となります。

本手順が、同様の課題を抱える多くの開発者にとって有用なガイドとなることを願っています。

執筆担当者プロフィール
西野 佑基

西野 佑基(日本ビジネスシステムズ株式会社)

機械学習系ソリューション開発、業務Webアプリ開発、開発環境自動化などを担当。

担当記事一覧