LoginSignup
5
0

Azure Functions の アウトバウンド通信を調べる

Last updated at Posted at 2023-12-15

はじめに

Azure Functions(App Service)では VNet 統合を用いることで、仮想ネットワーク内のリソースにアクセスすることができるようになります。

また、UDR を用いて、サブネットのルーティングを Azure Firewall などのネットワークアプライアンスを経由させることで送信トラフィックを制御することが可能となっています。

本記事では上記のようなネットワーク構成を用いて、Azure Functions からのアウトバウンド通信を調べてみます。
なお本記事はあくまでも検証から見えてきた事実を示すことが目的となります。

せっかくなのでアドベントカレンダーに紐づけておきます。
https://qiita.com/advent-calendar/2023/microsoft-azure-tech

発生しうる問題

VNet 統合を実施した場合、NSG にはデフォルトで AllowInternetOutBound が含まれているためインターネット向けのアクセスは許可された状態となっています。

image.png

外部への通信は極力制御したいいったことはよくある構成として見られるものかと思います。
しかしながら、Azure Functions を運用する上で、アプリケーションが意図して利用しているエンドポイント以外のインターネット向けのアクセスを拒否してしまうと、Azure Functions内部的に利用している(Azure Functions を構成する要素が発生させる)通信がブロックされてしまい、意図せぬエラーが発生する可能性があります。

有名なものとしては、リモートビルド時に動作する Oryx がランタイムを取得する際に利用する oryx-cdn.microsoft.io などがあります。

Network Dependencies
When using App Service with a Virtual Network or an App Service Environment, you will need to allow outbound access from the webapp to oryx-cdn.microsoft.io on port 443. oryx-cdn.microsoft.io hosts the Oryx packages corresponding to each SDK language and version. If this network dependency is blocked, then App Service will not be able to build your application using Oryx.

また、下記の記事でもデプロイ時の依存先についての紹介がされています。

過去には以下のような Issue を上げてもみましたが悲しいことに2年近く放置されています。

本記事では FW 側のログ等を確認することで、どのような通信が発生したかを確認します。
なお、2023年12月時点での動作となり、将来的に変更される可能性はあり得ます。
また、今回はLinux 環境にて専用(App Service)プランを用いていますが、Elastic Premium プランを用いた場合や、期待の Flex Consumption プランを用いた場合では動作が異なる可能性もあります。

構成例

基本的には下記チュートリアルに沿って、Azure Firewall を利用するように構成します。
今回はLinux 環境にて専用(App Service)プランを用いています。

image.png

以下のようなイメージとなります。

image.png

また、Firewallのポリシーは以下のように、Application Rulesではすべて許可するようにしておきます。

image.png

これで、Functions からの通信はすべて FW を経由し、FWのログに残るようになります。

通信例

Kudu にて SSH 接続し送信元 IP を確認します。

image.png

得られた IP は FW に割り当てらたパブリックIPとなっています。

また FW 側の診断設定を有効にしておくことで、以下のようなログを確認することができます。

下記例では、Application Rules 構成前には Deny だったものが、構成後には Allow となっていることがわかります。

image.png

デプロイしてみる

Python Functions をリモートビルド有効でデプロイしてみます。
アプリコードは以下のような感じでタイマートリガー、で blob input バインディングあり、アプリコード内で外部への通信ありとしてみました。

function_app.py
import logging
import requests
import azure.functions as func

app = func.FunctionApp()

@app.schedule(schedule="0 */1 * * * *", arg_name="myTimer", run_on_startup=True,
              use_monitor=False)
@app.blob_input(arg_name="inputblob",
                path="samples-workitems/test.txt",
                connection="BlobInputStorage")
def timer_trigger(myTimer: func.TimerRequest, inputblob: str) -> None:
    if myTimer.past_due:
        logging.info('The timer is past due!')

    logging.info(f'blob input binding: {len(inputblob)} bytes')


    r = requests.get('https://api.github.com/events')
    r.status_code
    logging.info(f'external api call: {r.status_code}')

デプロイしてみる

リモートビルドにおいて Oryx が動作している様子がわかります。

$ func azure functionapp publish func-202312 -b remote
Local python version '3.10.12' is different from the version expected for your deployed Function App. This may result in 'ModuleNotFound' errors in Azure Functions. Please create a Python Function App for version 3.10 or change the virtual environment on your local machine to match 'PYTHON|3.11'.
Getting site publishing info...
[2023-12-14T11:35:04.397Z] Starting the function app deployment...
Creating archive for current directory...
Performing remote build for functions project.
Uploading 2.88 KB [###############################################################################]
Remote build in progress, please wait...
Updating submodules.
Preparing deployment for commit id '370a3c31-f'.
PreDeployment: context.CleanOutputPath False
PreDeployment: context.OutputPath /home/site/wwwroot
Repository path is /tmp/zipdeploy/extracted
Running oryx build...
Command: oryx build /tmp/zipdeploy/extracted -o /tmp/build/expressbuild --platform python --platform-version 3.11 -i /tmp/8dbfc98bb62ba66 -p packagedir=.python_packages/lib/site-packages
Operation performed by Microsoft Oryx, https://github.com/Microsoft/Oryx
You can report issues at https://github.com/Microsoft/Oryx/issues

Oryx Version: 0.2.20230508.1, Commit: 7fe2bf39b357dd68572b438a85ca50b5ecfb4592, ReleaseTagName: 20230508.1

Build Operation ID: dcc5a5ef168c8b71
Repository Commit : 370a3c31-f665-49ef-9e03-6f523db35f39
OS Type           : bullseye
Image Type        : githubactions

Detecting platforms...
Detected following platforms:
  python: 3.11.6
Version '3.11.6' of platform 'python' is not installed. Generating script to install it...

Using intermediate directory '/tmp/8dbfc98bb62ba66'.

Copying files to the intermediate directory...
Done in 0 sec(s).

Source directory     : /tmp/8dbfc98bb62ba66
Destination directory: /tmp/build/expressbuild


Downloading and extracting 'python' version '3.11.6' to '/tmp/oryx/platforms/python/3.11.6'...
Detected image debian flavor: bullseye.
Downloaded in 2 sec(s).
Verifying checksum...
Extracting contents...
performing sha512 checksum for: python...
Done in 21 sec(s).

image detector file exists, platform is python..
OS detector file exists, OS is bullseye..
Python Version: /tmp/oryx/platforms/python/3.11.6/bin/python3.11
Creating directory for command manifest file if it does not exist
Removing existing manifest file

Running pip install...
Done in 9 sec(s).
[11:35:44+0000] Collecting azure-functions
[11:35:45+0000]   Downloading azure_functions-1.17.0-py3-none-any.whl (165 kB)
[11:35:45+0000] Collecting requests
[11:35:45+0000]   Downloading requests-2.31.0-py3-none-any.whl (62 kB)
[11:35:45+0000] Collecting certifi>=2017.4.17
[11:35:45+0000]   Downloading certifi-2023.11.17-py3-none-any.whl (162 kB)
[11:35:46+0000] Collecting idna<4,>=2.5
[11:35:46+0000]   Downloading idna-3.6-py3-none-any.whl (61 kB)
[11:35:47+0000] Collecting charset-normalizer<4,>=2
[11:35:47+0000]   Downloading charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (140 kB)
[11:35:47+0000] Collecting urllib3<3,>=1.21.1
[11:35:47+0000]   Downloading urllib3-2.1.0-py3-none-any.whl (104 kB)
[11:35:47+0000] Installing collected packages: urllib3, idna, charset-normalizer, certifi, requests, azure-functions
[11:35:49+0000] Successfully installed azure-functions-1.17.0 certifi-2023.11.17 charset-normalizer-3.3.2 idna-3.6 requests-2.31.0 urllib3-2.1.0
WARNING: You are using pip version 21.2.4; however, version 23.3.1 is available.
You should consider upgrading via the '/tmp/oryx/platforms/python/3.11.6/bin/python3.11 -m pip install --upgrade pip' command.
Not a vso image, so not writing build commands
Preparing output...

Copying files to destination directory '/tmp/build/expressbuild'...
Done in 0 sec(s).

Removing existing manifest file
Creating a manifest file...
Manifest file created.
Copying .ostype to manifest output directory.

Done in 32 sec(s).
Writing the artifacts to a Zip file
Running post deployment command(s)...

Generating summary of Oryx build
Deployment Log file does not exist in /tmp/oryx-build.log
The logfile at /tmp/oryx-build.log is empty. Unable to fetch the summary of build
Triggering recycle (preview mode disabled).
Deployment successful. deployer = Push-Deployer deploymentPath = Functions App ZipDeploy. Extract zip. Remote build.
Remote build succeeded!

Oryx の大まかな動作については WebApps のデプロイと同じと考えてよさそう。
詳細は以下の記事にて記載

アプリ側も正しく動作していることを確認

image.png

※たまたま作業中に VS Code 拡張の不具合っぽいのにはまったため↑では functions core tools でのデプロイを実施しています。

一晩たってVS Code Azure Resources拡張の 0.83 への更新および VS Code からデプロイすると以下のように SyncTrigger も呼ばれていることが確認できました。

10:55:23 PM func-202312: Starting deployment...
10:55:23 PM func-202312: Creating zip package...
10:55:23 PM func-202312: Zip package size: 13.2 kB
10:55:24 PM func-202312: Fetching changes.
10:55:25 PM func-202312: Cleaning up temp folders from previous zip deployments and extracting pushed zip file /tmp/zipdeploy/20c76f37-6d37-4882-b0d1-dbe5093050b9.zip (0.02 MB) to /tmp/zipdeploy/extracted
10:55:28 PM func-202312: Updating submodules.
10:55:29 PM func-202312: Preparing deployment for commit id '053bcfd3-d'.
10:55:30 PM func-202312: PreDeployment: context.CleanOutputPath False
10:55:30 PM func-202312: PreDeployment: context.OutputPath /home/site/wwwroot
10:55:30 PM func-202312: Repository path is /tmp/zipdeploy/extracted
10:55:30 PM func-202312: Running oryx build...
10:55:30 PM func-202312: Command: oryx build /tmp/zipdeploy/extracted -o /tmp/build/expressbuild --platform python --platform-version 3.11 -i /tmp/8dbfcf7c56d3dc4 -p packagedir=.python_packages/lib/site-packages
10:55:32 PM func-202312: Operation performed by Microsoft Oryx, https://github.com/Microsoft/Oryx
10:55:32 PM func-202312: You can report issues at https://github.com/Microsoft/Oryx/issues
10:55:33 PM func-202312: Oryx Version: 0.2.20230508.1, Commit: 7fe2bf39b357dd68572b438a85ca50b5ecfb4592, ReleaseTagName: 20230508.1
10:55:33 PM func-202312: Build Operation ID: 890dab50e94fed6f
10:55:33 PM func-202312: Repository Commit : 053bcfd3-d2dd-4665-82a0-3eb1d7d1a93e
10:55:33 PM func-202312: OS Type           : bullseye
10:55:33 PM func-202312: Image Type        : githubactions
10:55:33 PM func-202312: Detecting platforms...
10:55:36 PM func-202312: Detected following platforms:
10:55:36 PM func-202312:   python: 3.11.6
10:55:36 PM func-202312: Using intermediate directory '/tmp/8dbfcf7c56d3dc4'.
10:55:37 PM func-202312: Copying files to the intermediate directory...
10:55:37 PM func-202312: Done in 0 sec(s).
10:55:37 PM func-202312: Source directory     : /tmp/8dbfcf7c56d3dc4
10:55:37 PM func-202312: Destination directory: /tmp/build/expressbuild
10:55:37 PM func-202312: Python Version: /tmp/oryx/platforms/python/3.11.6/bin/python3.11
10:55:37 PM func-202312: Creating directory for command manifest file if it does not exist
10:55:37 PM func-202312: Removing existing manifest file
10:55:37 PM func-202312: Running pip install...
10:55:43 PM func-202312: Done in 6 sec(s).
10:55:43 PM func-202312: [22:55:39+0000] Collecting azure-functions
10:55:43 PM func-202312: [22:55:39+0000]   Using cached azure_functions-1.17.0-py3-none-any.whl (165 kB)
10:55:43 PM func-202312: [22:55:39+0000] Collecting requests
10:55:43 PM func-202312: [22:55:39+0000]   Using cached requests-2.31.0-py3-none-any.whl (62 kB)
10:55:43 PM func-202312: [22:55:41+0000] Collecting charset-normalizer<4,>=2
10:55:43 PM func-202312: [22:55:41+0000]   Using cached charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (140 kB)
10:55:43 PM func-202312: [22:55:41+0000] Collecting idna<4,>=2.5
10:55:43 PM func-202312: [22:55:41+0000]   Using cached idna-3.6-py3-none-any.whl (61 kB)
10:55:44 PM func-202312: [22:55:41+0000] Collecting certifi>=2017.4.17
10:55:44 PM func-202312: [22:55:41+0000]   Using cached certifi-2023.11.17-py3-none-any.whl (162 kB)
10:55:44 PM func-202312: [22:55:41+0000] Collecting urllib3<3,>=1.21.1
10:55:44 PM func-202312: [22:55:41+0000]   Using cached urllib3-2.1.0-py3-none-any.whl (104 kB)
10:55:44 PM func-202312: [22:55:41+0000] Installing collected packages: urllib3, idna, charset-normalizer, certifi, requests, azure-functions
10:55:44 PM func-202312: [22:55:43+0000] Successfully installed azure-functions-1.17.0 certifi-2023.11.17 charset-normalizer-3.3.2 idna-3.6 requests-2.31.0 urllib3-2.1.0
10:55:44 PM func-202312: WARNING: You are using pip version 21.2.4; however, version 23.3.1 is available.
10:55:44 PM func-202312: You should consider upgrading via the '/tmp/oryx/platforms/python/3.11.6/bin/python3.11 -m pip install --upgrade pip' command.
10:55:44 PM func-202312: Not a vso image, so not writing build commands
10:55:44 PM func-202312: Preparing output...
10:55:44 PM func-202312: Copying files to destination directory '/tmp/build/expressbuild'...
10:55:44 PM func-202312: Done in 1 sec(s).
10:55:44 PM func-202312: Removing existing manifest file
10:55:44 PM func-202312: Creating a manifest file...
10:55:44 PM func-202312: Manifest file created.
10:55:44 PM func-202312: Copying .ostype to manifest output directory.
10:55:44 PM func-202312: Done in 8 sec(s).
10:55:45 PM func-202312: Writing the artifacts to a Zip file
10:55:46 PM func-202312: Running post deployment command(s)...
10:55:46 PM func-202312: Generating summary of Oryx build
10:55:46 PM func-202312: Deployment Log file does not exist in /tmp/oryx-build.log
10:55:46 PM func-202312: The logfile at /tmp/oryx-build.log is empty. Unable to fetch the summary of build
10:55:46 PM func-202312: Triggering recycle (preview mode disabled).
10:55:47 PM func-202312: Deployment successful. deployer = ms-azuretools-vscode deploymentPath = Functions App ZipDeploy. Extract zip. Remote build.
10:56:02 PM func-202312: Syncing triggers...
10:56:04 PM func-202312: Querying triggers...
10:56:05 PM func-202312: No HTTP triggers found.

通信ログを確認する

デプロイの実施タイミングは 2023-12-14T11:35 (JST 08:35 PM)
image.png

以下のファイルからも確認可能

  • /home/LogFiles/kudu/trace/2023-12-14T11-35-04_bfe0b4_011_Background_POST_api-zipdeploy_pending.xml

該当時間帯の通信を確認する

Core Tools デプロイ時

| where TimeGenerated between(datetime(2023-12-14 11:34) .. 2m)
| order by TimeGenerated asc

image.png

VS Code デプロイ時

| where TimeGenerated between(datetime(2023-12-14 22:55) .. 2m)
| order by TimeGenerated asc

image.png

以下のFQDFNへの通信が確認できます。

  • api.github.com
  • func-202312.scm.azurewebsites.net
  • func-202312.azurewebsites.net
  • oryx-cdn.microsoft.io
  • pypi.org
  • files.pythonhosted.org
  • dc.services.visualstudio.com
  • japaneast-1.in.applicationinsights.azure.com
  • japaneast.livediagnostics.monitor.azure.com

アプリが利用するエンドポイント

api.github.com はアプリコード内で参照しているエンドポイントとなります。

    r = requests.get('https://api.github.com/events')

想定通り、Firewall を経由したことが確認できました。

ビルド時にOryxが利用するエンドポイント

  • oryx-cdn.microsoft.io
  • pypi.org
  • files.pythonhosted.org

この 3 つについては、リモートビルド実行(Oryx 動作)時に利用されます。
今回は Python 環境のため、pip install によって参照された pypi.orgfiles.pythonhosted.org が含まれていますが、Python 以外の場合、例えば Node 環境であれば registry.npmjs.org などが利用されることになります。

詳細は以下の記事で

各言語毎の参考情報は以下の記事で

Azure Functions 自身へのエンドポイント

Azure Functions リソース自身 func-202312.azurewebsites.net および、その Kudu サイト func-202312.scm.azurewebsites.net へのリクエストも発生しています。

これの発生元は、Azure Functions リソース自身に対しては SyncTrigger に起因しています。
デプロイクライアントである VS Code から https://func-202312.azurewebsites.net/admin/host/synctriggers へリクエストされた後、Functions 自身が https://func-202312.azurewebsites.net/operation/settriggersを呼び出すことで発生します。

SyncTrigger の詳細については下記をご参照ください。

Kudu に対してはデプロイプロセスの中で発生する、デプロイステータス更新のために Kudu から Kudu 自身へ行われる通信が該当します。デプロイログファイルの以下のような記述から確認できます。

<step title="Begin HttpPost https://func-202312.scm.azurewebsites.net/api/app/updatedeploystatus, x-ms-request-id: 00000000-0000-0000-0000-000000000000" date="2023-12-14T22:55:27.779" /><!-- duration: 7ms -->
<step title="End HttpPost, status: OK" date="2023-12-14T22:55:28.193" /><!-- duration: 5ms -->

※なお、今回プライベートエンドポイントは利用していません。仮にプライベートエンドポイントを利用しており、自身のサブネットからプライベートエンドポイントが利用できる場合、プライベートエンドポイントへのリクエストは FW へルーティングされないことが見込まれます。

Azure Monitor/Application Insights 関連のエンドポイント

  • dc.services.visualstudio.com
  • japaneast-1.in.applicationinsights.azure.com
  • japaneast.livediagnostics.monitor.azure.com

Azure Functions では、Application Insights 統合を有効にしています。Application Insights によって発生した通信が記録されています。

利用される可能性のあるエンドポイントは下記のドキュメントに記載があります。

image.png

ストレージアカウントへの通信が記録されないのはなぜか?

FW のログには blob.core.windows.net など ストレージアカウントへの通信が確認できません。これはなぜか。
今回利用しているストレージアカウントは、AzureWebjobsStorageで利用するものも、アプリ内のバインディング用に利用しているものもアプリと同一の japaneast リージョンのものを使用していました。そのため下記のドキュメントにある通り、プライベート Azure IP が利用されることから FW を経由しないものとなります。

ストレージ アカウントと同じリージョンでデプロイされたサービスでは、プライベート Azure IP アドレスが通信に使用されます。


https://learn.microsoft.com/ja-jp/azure/storage/common/storage-network-security?tabs=azure-portal#grant-access-from-an-internet-ip-range

image.png

ストレージ側の診断ログを見てみます。

image.png

10.0.1.254 は 関数実行インスタンスに割り当てられた Private IP となり、Functions App 系のコンテナへアクセスしていることがわかります。10.0.38.75 は何ですかね?ちょっとよくわかりません。

image.png

ストレージアカウントを別リージョンにしてみる

AzureWebJobsStorage と Blob input binding で用いているストレージアカウントをそれぞれ変更します。

image.png

FW のログに blob.core.windows.net へのアクセスが表示されることが確認できました。

image.png

まとめ

Azure Functions からのアウトバウンド通信では少なくとも以下のエンドポイントへの通信が発生する可能性がある。FW 等でこれらの接続をブロックすると予期せぬエラーが発生する可能性がある。

Functions/Kudu 自身

 - {AppName}.azurewebsites.net
 - {AppName}.scm.azurewebsites.net

リモートビルドで必要なエンドポイント

  • oryx-cdn.microsoft.io
  • pypi.org や、registry.npmjs.org などプロジェクトのビルド(依存ライブラリのダウンロード)に必要なエンドポイント

Azure Monitor で使用されるエンドポイント

  • dc.applicationinsights.azure.com
  • dc.applicationinsights.microsoft.com
  • dc.services.visualstudio.com
  • *.in.applicationinsights.azure.com
  • live.applicationinsights.azure.com
  • rt.applicationinsights.microsoft.com
  • rt.services.visualstudio.com
  • {region}.livediagnostics.monitor.azure.com

詳細は下記ドキュメント参照

5
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
0