次の方法で共有


Azure Functions のカスタム ハンドラー

Azure Functions では、言語固有のハンドラーを使用してアプリ コードが実行されます。 これらの言語固有のハンドラーを使用すると、Functions は 既定でほとんどの主要言語を サポートできます。 ただし、別の言語またはパッケージでコードを実行する必要がある場合があります。

カスタム ハンドラーは、Azure Functions ホスト プロセスからイベントを受信する軽量 Web サーバーです。 カスタム ハンドラーを使用して、HTTP プリミティブをサポートする任意のコード プロジェクトを Azure Functions にデプロイできます。

カスタム ハンドラーは、次のような場合に最適です。

  • Go や Rust など、現在すぐには提供されていない言語で関数アプリを実装します。
  • Deno など、現在既定でrサポートされていないランタイムで関数アプリを実装する。
  • 標準の MCP SDK で構築されたサーバーを Azure Functions にデプロイします。

カスタム ハンドラーを使用すると、拡張バンドルを介して、トリガーと入出力バインドを使用できます。

Go および Rust のクイックスタートを使用して Azure Functions カスタム ハンドラーの使用を開始します。

概要

次の図は、カスタム ハンドラーとして実装されている Web サーバーと Functions ホストの関係を示しています。

Azure Functions のカスタム ハンドラーの概要

  1. 各イベントは、Functions ホストに送信される要求をトリガーします。 イベントは、Azure Functions がサポートする任意のトリガーです。
  2. Functions ホストは、その後、Web サーバーに要求ペイロードを発行します。 このペイロードには、トリガーおよび入力バインド データと、関数のためのその他のメタデータが保持されます。
  3. Web サーバーは個々の関数を実行し、応答ペイロードを Functions ホストに返します。
  4. Functions ホストは、応答のデータを処理するために関数の出力バインドに渡します。

カスタム ハンドラーとして実装されている Azure Functions アプリでは、いくつかの規則に従って、host.jsonlocal.settings.json、および function.json ファイルを構成する必要があります。

セルフホステッド MCP サーバーをデプロイする

カスタム ハンドラーを使用すると、Azure Functions で公式の MCP SDK を使用してビルドした MCP サーバーをホストすることもできます。 カスタム ハンドラーは、Azure で MCP サーバーをホストするためのシンプルで合理化されたエクスペリエンスを提供します。 詳細については、 Azure Functions 上のセルフホステッド リモート MCP サーバーに関するページを参照してください。

Note

公式の MCP SDK を使用して作成した MCP サーバーを Azure Functions でホストする機能は、現在プレビュー段階です。

アプリケーション構造

カスタム ハンドラーを実装するには、アプリケーションに次の側面が必要です。

  • host.json ファイル: アプリのルート
  • local.settings.json ファイル: アプリのルート
  • function.json ファイル: 関数ごとに必要 (関数名と一致する名前のフォルダー内)
  • Web サーバーを実行するコマンド、スクリプト、または実行可能ファイル

次の図は、"MyQueueFunction" という名前の関数と、handler.exe という名前のカスタム ハンドラー実行可能ファイルのファイル システムでこれらのファイルがどのように表示されるかを示します。

| /MyQueueFunction
|   function.json
|
| host.json
| local.settings.json
| handler.exe

構成

host.jsonファイルと local.settings.json ファイルを使用してアプリケーション 構成します。

host.json

host.json は、HTTP イベントを処理できる Web サーバーを指すことによって、要求を送信する場所を Functions ホストに指示します。

セクションで Web サーバーを実行する方法の詳細を含む customHandler ファイルを構成して、カスタム ハンドラーを定義します。

{
  "version": "2.0",
  "customHandler": {
    "description": {
      "defaultExecutablePath": "handler.exe"
    }
  }
}

customHandler セクションは、defaultExecutablePath で定義されているターゲットを指します。 実行ターゲットには、Web サーバーが実装されているコマンド、実行可能ファイル、またはファイルを指定できます。

arguments 配列を使用して、引数を実行可能ファイルに渡します。 引数は、 %% 表記を使用した環境変数 (アプリケーション設定) の拡張をサポートします。

また、workingDirectory を使用して、実行可能ファイルによって使用される作業ディレクトリを変更することもできます。

{
  "version": "2.0",
  "customHandler": {
    "description": {
      "defaultExecutablePath": "app/handler.exe",
      "arguments": [
        "--database-connection-string",
        "%DATABASE_CONNECTION_STRING%"
      ],
      "workingDirectory": "app"
    }
  }
}
バインドのサポート

標準トリガーと入出力バインドは、host.json ファイル内の拡張バンドルを参照することによって利用できます。

local.settings.json

local.settings.json では、関数アプリをローカルで実行する際に使用されるアプリケーション設定を定義します。 シークレットが含まれている可能性があるため、ソース管理から local.settings.json を除外します。 Azure では、代わりにアプリケーション設定を使用します。

カスタム ハンドラーの場合、FUNCTIONS_WORKER_RUNTIMECustom に設定します。

{
  "IsEncrypted": false,
  "Values": {
    "FUNCTIONS_WORKER_RUNTIME": "Custom"
  }
}

関数のメタデータ

カスタム ハンドラーを使用する場合、 function.json の内容は、他のコンテキストで関数を定義する場合と同じです。 唯一の要件は、 function.jsonファイルを 関数名と一致するように名前付きのフォルダーに配置する必要があるということです。

次の function.json では、キュー トリガーとキュー出力バインドを含む関数を構成しています。 MyQueueFunction という名前のフォルダー内にあるため、MyQueueFunction という名前の関数が定義されます。

MyQueueFunction/function.json

{
  "bindings": [
    {
      "name": "myQueueItem",
      "type": "queueTrigger",
      "direction": "in",
      "queueName": "messages-incoming",
      "connection": "AzureWebJobsStorage"
    },
    {
      "name": "$return",
      "type": "queue",
      "direction": "out",
      "queueName": "messages-outgoing",
      "connection": "AzureWebJobsStorage"
    }
  ]
}

要求ペイロード

Functions ホストは、キュー メッセージを受信すると、本文にペイロードを含む HTTP post 要求をカスタム ハンドラーに送信します。

次のコードは、サンプルの要求ペイロードを示しています。 ペイロードには、2 つのメンバー (DataMetadata) を持つ JSON 構造体が含まれています。

Data メンバーには、function.json ファイルにバインド配列の形式で定義されている入力名およびトリガー名と一致するキーが含まれています。

Metadata メンバーには、イベント ソースから生成されたメタデータが含まれています。

{
  "Data": {
    "myQueueItem": "{ message: \"Message sent\" }"
  },
  "Metadata": {
    "DequeueCount": 1,
    "ExpirationTime": "2019-10-16T17:58:31+00:00",
    "Id": "800ae4b3-bdd2-4c08-badd-f08e5a34b865",
    "InsertionTime": "2019-10-09T17:58:31+00:00",
    "NextVisibleTime": "2019-10-09T18:08:32+00:00",
    "PopReceipt": "AgAAAAMAAAAAAAAAAgtnj8x+1QE=",
    "sys": {
      "MethodName": "QueueTrigger",
      "UtcNow": "2019-10-09T17:58:32.2205399Z",
      "RandGuid": "24ad4c06-24ad-4e5b-8294-3da9714877e9"
    }
  }
}

応答ペイロード:

規則により、関数の応答はキーと値のペアの形式で設定されます。 サポートされているキーは次のとおりです。

ペイロードのキー データ型 解説
Outputs オブジェクト bindings 配列によって定義される応答値を保持します。

たとえば、関数が "myQueueOutput" という名前のキュー出力バインドで構成されている場合、 Outputs には myQueueOutput という名前のキーが含まれます。このキーは、カスタム ハンドラーによってキューに送信されるメッセージに設定されます。
Logs アレイ Functions 呼び出しログに表示されるメッセージ。

Azure で実行すると、メッセージは Application Insights に表示されます。
ReturnValue 文字列 $return ファイルの として出力が構成されている場合に、応答を提供するために使用されます。

次の表に、応答ペイロードの例を示します。

{
  "Outputs": {
    "res": {
      "body": "Message enqueued"
    },
    "myQueueOutput": [
      "queue message 1",
      "queue message 2"
    ]
  },
  "Logs": [
    "Log message 1",
    "Log message 2"
  ],
  "ReturnValue": "{\"hello\":\"world\"}"
}

HTTP イベントの受信をサポートする任意の言語でカスタム ハンドラーを実装できます。 次の例では、Go プログラミング言語を使用してカスタム ハンドラーを実装する方法を示します。

関数とバインド

この例では、製品注文を表すペイロードを持つorder要求を受け入れる POST という名前の関数を示します。 関数に注文を投稿すると、キュー ストレージ メッセージが作成され、HTTP 応答が返されます。

実装

order という名前のフォルダー内の function.json ファイルで、HTTP によってトリガーされる関数を構成します。

order/function.json

{
  "bindings": [
    {
      "type": "httpTrigger",
      "direction": "in",
      "name": "req",
      "methods": ["post"]
    },
    {
      "type": "http",
      "direction": "out",
      "name": "res"
    },
    {
      "type": "queue",
      "name": "message",
      "direction": "out",
      "queueName": "orders",
      "connection": "AzureWebJobsStorage"
    }
  ]
}

この関数は HTTP によってトリガーされる関数として定義され、HTTP 応答 を返し、Queue Storage メッセージを出力します。

アプリのルートにある host.json ファイルは、handler.exe (Linux または macOS の場合は handler) という名前の実行可能ファイルを実行するように構成されています。

{
  "version": "2.0",
  "customHandler": {
    "description": {
      "defaultExecutablePath": "handler.exe"
    }
  },
  "extensionBundle": {
    "id": "Microsoft.Azure.Functions.ExtensionBundle",
    "version": "[4.*, 5.0.0)"
  }
}

これは、Functions ランタイムに送信される HTTP 要求です。

POST http://127.0.0.1:7071/api/order HTTP/1.1
Content-Type: application/json

{
  "id": 1005,
  "quantity": 2,
  "color": "black"
}

Functions ランタイムは、次の HTTP 要求をカスタム ハンドラーに送信します。

POST http://127.0.0.1:<FUNCTIONS_CUSTOMHANDLER_PORT>/order HTTP/1.1
Content-Type: application/json

{
  "Data": {
    "req": {
      "Url": "http://localhost:7071/api/order",
      "Method": "POST",
      "Query": "{}",
      "Headers": {
        "Content-Type": [
          "application/json"
        ]
      },
      "Params": {},
      "Body": "{\"id\":1005,\"quantity\":2,\"color\":\"black\"}"
    }
  },
  "Metadata": {
  }
}

Note

簡潔にするために、ペイロードの一部は削除されています。

handler.exe はコンパイル済みの Go カスタム ハンドラー プログラムで、Web サーバーを実行し、Functions ホストからの関数呼び出し要求に応答します。

package main

import (
	"encoding/json"
	"fmt"
	"log"
	"net/http"
	"os"
)

type InvokeRequest struct {
	Data     map[string]json.RawMessage
	Metadata map[string]interface{}
}

type InvokeResponse struct {
	Outputs     map[string]interface{}
	Logs        []string
	ReturnValue interface{}
}

func orderHandler(w http.ResponseWriter, r *http.Request) {
	var invokeRequest InvokeRequest

	d := json.NewDecoder(r.Body)
	d.Decode(&invokeRequest)

	var reqData map[string]interface{}
	json.Unmarshal(invokeRequest.Data["req"], &reqData)

	outputs := make(map[string]interface{})
	outputs["message"] = reqData["Body"]

	resData := make(map[string]interface{})
	resData["body"] = "Order enqueued"
	outputs["res"] = resData
	invokeResponse := InvokeResponse{outputs, nil, nil}

	responseJson, _ := json.Marshal(invokeResponse)

	w.Header().Set("Content-Type", "application/json")
	w.Write(responseJson)
}

func main() {
	customHandlerPort, exists := os.LookupEnv("FUNCTIONS_CUSTOMHANDLER_PORT")
	if !exists {
		customHandlerPort = "8080"
	}
	mux := http.NewServeMux()
	mux.HandleFunc("/order", orderHandler)
	fmt.Println("Go server Listening on: ", customHandlerPort)
	log.Fatal(http.ListenAndServe(":"+customHandlerPort, mux))
}

この例では、カスタム ハンドラーは Web サーバーを実行して HTTP イベントを処理し、 FUNCTIONS_CUSTOMHANDLER_PORT経由で要求をリッスンします。

Functions ホストは、 /api/orderで元の HTTP 要求を受信した場合でも、関数名 (フォルダー名) を使用してカスタム ハンドラーを呼び出します。 この例では、この関数が /order のパスで定義されています。 ホストは、/order のパスで HTTP 要求をカスタム ハンドラーに送信します。

POST要求をこの関数に送信すると、トリガー データと関数メタデータは HTTP 要求本文を介して使用できます。 ペイロードの Data.req.Bodyで元の HTTP 要求本文にアクセスできます。

関数の応答は、キーと値のペアの形式で設定されます。この場合、Outputs メンバーは、function.json ファイルで定義されている出力とキーが一致する JSON 値を保持します。

これは、このハンドラーが Functions ホストに返すペイロードの例です。

{
  "Outputs": {
    "message": "{\"id\":1005,\"quantity\":2,\"color\":\"black\"}",
    "res": {
      "body": "Order enqueued"
    }
  },
  "Logs": null,
  "ReturnValue": null
}

message 出力を、要求から送信された注文データと同じになるよう設定することで、この関数は、構成されたキューにその注文データを出力します。 また、Functions ホストは、res で構成された HTTP 応答を呼び出し元に返します。

HTTP のみの関数

追加のバインドや出力がない HTTP によってトリガーされる関数の場合は、カスタム ハンドラーの要求と応答のペイロードではなく、ハンドラーが HTTP 要求応答 を直接操作することが必要になる場合があります。 応答ストリーミングをサポートするenableProxyingHttpRequest設定を使用して、host.jsonでこの動作を構成できます。

重要

カスタム ハンドラー機能の主な目的は、現在 Azure Functions でファースト クラスのサポートを持っていない言語とランタイムを有効にすることです。 カスタム ハンドラーを使用して Web アプリケーションを実行できる場合は、Azure Functions は標準のリバース プロキシではありません。 特定のヘッダーやルートなど、HTTP 要求の一部のコンポーネントが制限される場合があります。 アプリケーションで過剰な コールド スタートが発生する可能性もあります。

このような状況に対処するには、Web アプリを Azure App Service で実行することを検討してください。

次の例では、追加のバインドまたは出力を使用せずに、HTTP によってトリガーされる関数を構成する方法を示します。 この例で実装されているシナリオは、hello または GET を受け入れる POST という名前の関数を特徴としています。

実装

hello という名前のフォルダー内の function.json ファイルで、HTTP によってトリガーされる関数を構成します。

hello/function.json

{
  "bindings": [
    {
      "type": "httpTrigger",
      "authLevel": "anonymous",
      "direction": "in",
      "name": "req",
      "methods": ["get", "post"]
    },
    {
      "type": "http",
      "direction": "out",
      "name": "res"
    }
  ]
}

関数は、 GET 要求と POST 要求の両方を受け入れるように構成され、結果の値は res という名前の引数を使用して提供されます。

アプリのルートにある host.json ファイルは、handler.exe を実行するように構成され、enableProxyingHttpRequesttrue に設定されています。

{
  "version": "2.0",
  "customHandler": {
    "description": {
      "defaultExecutablePath": "handler.exe"
    },
    "enableProxyingHttpRequest": true
  }
}

Functions ホストへの POST 要求を次に示します。 その後、Functions ホストはカスタム ハンドラーに要求を送信します。

POST http://127.0.0.1:7071/api/hello HTTP/1.1
Content-Type: application/json

{
  "message": "Hello World!"
}

handler.go ファイルは、Web サーバーと HTTP 関数を実装します。

package main

import (
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"os"
)

func helloHandler(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "application/json")
	if r.Method == "GET" {
		w.Write([]byte("hello world"))
	} else {
		body, _ := ioutil.ReadAll(r.Body)
		w.Write(body)
	}
}

func main() {
	customHandlerPort, exists := os.LookupEnv("FUNCTIONS_CUSTOMHANDLER_PORT")
	if !exists {
		customHandlerPort = "8080"
	}
	mux := http.NewServeMux()
	mux.HandleFunc("/api/hello", helloHandler)
	fmt.Println("Go server Listening on: ", customHandlerPort)
	log.Fatal(http.ListenAndServe(":"+customHandlerPort, mux))
}

この例では、カスタム ハンドラーは HTTP イベントを処理する Web サーバーを作成し、 FUNCTIONS_CUSTOMHANDLER_PORTを介して要求をリッスンします。

GET 要求は文字列を返すことによって処理され、POST 要求は要求本文にアクセスできます。

ここでの order 関数のルートは、元の要求と同じ /api/hello です。

Note

FUNCTIONS_CUSTOMHANDLER_PORTは、関数の呼び出しに使用される公開ポートではありません。 Functions ホストは、このポートを使用してカスタム ハンドラーを呼び出します。

デプロイ中

カスタム ハンドラーは、すべての Azure Functions ホスティング オプションにデプロイできます。 ハンドラーにオペレーティング システムまたはプラットフォームの依存関係 (言語ランタイムなど) が必要な場合は、 カスタム コンテナーの使用が必要になる場合があります。

カスタム ハンドラー用の関数アプリを Azure で作成する場合は、スタックとして .NET Core を選択します。

Azure Functions Core Tools を使用してカスタム ハンドラー アプリをデプロイするには、次のコマンドを実行します。

func azure functionapp publish $functionAppName

Note

カスタム ハンドラーを実行するために必要なすべてのファイルがフォルダー内にあり、デプロイに含まれていることを確認します。 カスタム ハンドラーがバイナリ実行可能ファイルであるか、プラットフォーム固有の依存関係がある場合は、これらのファイルがターゲット デプロイ プラットフォームと一致していることを確認します。

制限

  • カスタム ハンドラー Web サーバーは、60 秒以内に起動する必要があります。

サンプル

さまざまな言語で関数を実装する方法の例については、 カスタム ハンドラーサンプルの GitHub リポジトリを参照してください。

トラブルシューティングとサポート

トレース ログ

カスタム ハンドラー プロセスの開始に失敗した場合、または Functions ホストとの通信に問題がある場合は、関数アプリのログ レベルを Trace に上げて、ホストからの診断メッセージをさらに表示します。

関数アプリの既定のログ レベルを変更するには、logLevellogging セクションで 設定を構成します。

{
  "version": "2.0",
  "customHandler": {
    "description": {
      "defaultExecutablePath": "handler.exe"
    }
  },
  "logging": {
    "logLevel": {
      "default": "Trace"
    }
  }
}

Functions ホストは、カスタム ハンドラー プロセスに関連する情報を含む追加のログ メッセージを出力します。 ログを使用して、カスタム ハンドラー プロセスを開始するときの問題、またはカスタム ハンドラーで関数を呼び出すときの問題を調査します。

ローカルでは、ログはコンソールに出力されます。

Azure では、ログ メッセージを表示するには Application Insights トレースのクエリを実行します。 アプリによって大量のログが生成される場合は、ログ メッセージのサブセットのみが Application Insights に送信されます。 すべてのメッセージがログに記録されるようにするには、サンプリングを無効にします。

カスタム ハンドラーを分離してテストする

カスタム ハンドラー アプリは Web サーバー プロセスであるため、独自に開始し、モック HTTP 要求を送信して関数の呼び出しをテストすると便利な場合があります。 ペイロードを使用して HTTP 要求を送信する場合は、データを安全に保つツールを選択するようにしてください。 詳細については、「HTTP テスト ツール」を参照してください。

また、この方法を CI/CD パイプラインで使用して、カスタム ハンドラーに対する自動テストを実行することもできます。

実行環境

カスタム ハンドラーは、一般的な Azure Functions アプリと同じ環境で実行されます。 ハンドラーをテストして、実行に必要なすべての依存関係が環境に含まれていることを確認します。 追加の依存関係を必要とするアプリの場合は、Azure Functions Premium プランでホストされているカスタム コンテナー イメージを使用して実行することが必要になる場合があります。

サポートを受ける

カスタム ハンドラーがある関数アプリに関するヘルプが必要な場合は、通常のサポート チャネルを通じてリクエストを送信できます。 ただし、カスタム ハンドラー アプリの構築に使用されるさまざまな言語があるため、サポートは無制限ではありません。

Functions ホストの起動またはカスタム ハンドラー プロセスとの通信に問題がある場合は、サポートを利用できます。 選択した言語やフレームワークに関する問題など、カスタム ハンドラー プロセスの内部動作に固有の問題については、サポート チームはこのコンテキストでサポートを提供できません。

次のステップ

カスタム ハンドラーのクイックスタートを使用して、Go または Rust での Azure Functions アプリの構築を開始します。