API Management
では、API 管理ソリューションを探さなければならない理由は何でしょうか? おそらく次のような課題を抱えていることでしょう。
- スケーリング: お客様の API または API 群は世界のさまざまな地域の多くのクライアントによって使用されており、その可用性と応答性を確保する必要があります。
- セキュリティ: API がセキュリティで保護され、承認されたクライアントのみがアクセスできるようにする必要があります。
- エラー管理: API がエラーを適切に処理できるようにする必要があります。
- 監視: API を監視して、期待どおりに動作していることを確認する必要があります。
- 復元力: API が復元力を持ち、障害を適切に処理できるようにする必要があります。
これらの課題ごとにポイント ソリューションを選択することもできますが、管理が困難になる可能性があります。 また、API はさまざまな技術スタックで構築される可能性があることも考慮してください。つまり、上記の課題に対する解決策として API ごとに異なるソリューションが必要になる可能性があります。 これらすべての課題に直面している場合は、Azure API Management のような集中型 API 管理ソリューションを検討する必要があります。
いくつかの課題についてさらに深く掘り下げて、Azure API Management のような集中型 API 管理ソリューションがそれらの課題の解決にどのように役立つかを見ていきましょう。
コードとしてのインフラストラクチャ (IaC)
Azure portal を使用して Azure リソースを作成してもまったく問題ありませんが、インフラストラクチャが大きくなるにつれて管理が難しくなります。 直面する問題の 1 つとして、別の環境にインフラストラクチャを簡単にレプリケートできないことが挙げられます。
また、インフラストラクチャに加えられたすべての変更を追跡することも困難です。 この状況では、コードとしてのインフラストラクチャ (IaC) が役に立ちます。 IaC とは、コードを使用してインフラストラクチャを管理する手法です。 Azure で IaC を適用するには、いくつかのオプションがあり、そのうちの 1 つに Bicep があります。 Bicep は、Azure リソースを宣言的にデプロイするためのドメイン固有言語 (DSL) です。 これはクラウド リソースを管理するための優れた方法です。 Bicep がどのようなものか、簡単な例をご紹介しましょう。
param location string = 'eastus'
resource storageAccount 'Microsoft.Storage/storageAccounts@2021-06-01' = {
name: 'mystorageaccount'
location: location
kind: 'StorageV2'
sku: {
name: 'Standard_LRS'
}
}
前の例では、Bicep を使用してストレージ アカウントを定義しました。 ストレージ アカウントの場所、ストレージ アカウントの種類、SKU (Stock Keeping Unit) を定義しました。 Location は、Bicep ファイルをデプロイするときに渡すことができるパラメーターです。 提示したファイルをデプロイするには、次のように Azure CLI を使用します。
az deployment group create --resource-group myResourceGroup --template-file main.bicep
上記のコマンドは、ストレージ アカウントをリソース グループ myResourceGroup
にデプロイし、Bicep ファイル main.bicep
を使用してファイル内にリソースを作成します。
ロード バランサーによる負荷処理
API が要求によって過負荷になるという問題がある場合は、負荷分散構造を追加することが解決策となります。 ロード バランサーは、API の複数のインスタンスに負荷を分散するのに役立ちます。
Azure API Management サービスでは、バックエンドと呼ばれる概念を定義することによって負荷分散が実装されます。 このアイデアでは、API エンドポイントに対応する多数のバックエンドを設定し、これらのバックエンド間で負荷を分散するロード バランサーを作成します。 アーキテクチャは次のようになります。
前述のアーキテクチャでは次のようなことが起こっています。
- クライアントは API Management インスタンスに要求を送信します。
- 要求は認証され、承認されます。
- その後、要求はロード バランサーに送信されます。
- ロード バランサーは、要求をいずれかのバックエンドに分散します (選択された Azure OpenAI API は太字で示されています)。
バックエンドは要求トを処理し、クライアントに応答を返します。
ロード バランサーの定義
Azure API Management でロード バランサーを設定するには、次の部分を定義する必要があります。
- バックエンド: 負荷を分散するバックエンドの数だけ。
- ロード バランサー: 負荷を分散するバックエンドを含むロード バランサー。
- ポリシー: 受信呼び出しをロード バランサーに転送します。
バックエンドの作成
Azure API Management でバックエンドを作成するには、バックエンド エンティティを定義する必要があります。 Bicep でバックエンドを定義する方法を次に示します。
resource backend2 'Microsoft.ApiManagement/service/backends@2023-09-01-preview' = {
parent: apimService
name: 'backend2'
properties: {
url: '${openai2Endpoint}openai'
protocol: 'http'
circuitBreaker: {
rules: [
{
failureCondition: {
count: 3
errorReasons: [
'Server errors'
]
interval: 'P1D'
statusCodeRanges: [
{
min: 500
max: 599
}
]
}
name: 'myBreakerRule'
tripDuration: 'PT1H'
}
]
}
}
前述の Bicep コードでは、バックエンドは API エンドポイント URL に対応するように定義されています。backend2
という名前にも注意してください。この名前は後で使用できます。 バックエンドごとに、前述の Bicep コードのようにエンコードする必要があります。
Note
複数のバックエンドを持つことができるので、必要な数だけバックエンドを定義できます。
バックエンド プールの作成
次に、負荷を分散するバックエンドを設定するバックエンド プールを作成します。 このバックエンド プールは、次のようにバックエンド エンティティとしてエンコードできます。
resource loadBalancing 'Microsoft.ApiManagement/service/backends@2023-09-01-preview' = {
parent: apimService
name: 'LoadBalancer'
properties: {
description: 'Load balancer for multiple backends'
type: 'Pool'
pool: {
services: [
{
id: '/subscriptions/${subscriptionId}/resourceGroups/${resourceGroupName}/providers/Microsoft.ApiManagement/service/${apimService.name}/backends/${backend1.id}'
}
{
id: '/subscriptions/${subscriptionId}/resourceGroups/${resourceGroupName}/providers/Microsoft.ApiManagement/service/${apimService.name}/backends/${backend2.id}'
}
]
}
}
}
前に作成したバックエンド backend2
は、別のバックエンド backend1
とともに参照されます (後者は簡潔にするために省略されています)。
また、services
リストの各項目に priority
および weight
プロパティを含めて、ロード バランサーが負荷を分散する方法を決定することもできます。 各バックエンドの優先順位と重みを設定する方法を次に示します。
services: [
{
id: '/subscriptions/<subscriptionID>/resourceGroups/<resourceGroupName>/providers/Microsoft.ApiManagement/service/<APIManagementName>/backends/backend-1'
priority: 1
weight: 3
}
{
id: '/subscriptions/<subscriptionID>/resourceGroups/<resourceGroupName>/providers/Microsoft.ApiManagement/service/<APIManagementName>/backends/backend-2'
priority: 1
weight: 1
}
]
前の例では、ロード バランサーは backend-1
に backend-2
の 3 倍の負荷を分散します。
受信呼び出しの転送
最後に、この負荷分散バックエンドに受信呼び出しを転送する必要があります。 転送命令は次の API エンティティによって作成されます。
resource api1 'Microsoft.ApiManagement/service/apis@2020-06-01-preview' = {
parent: apimService
name: apiName
properties: {
displayName: apiName
apiType: 'http'
path: apiSuffix
format: 'openapi+json-link'
value: 'https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cognitiveservices/data-plane/AzureOpenAI/inference/preview/2024-03-01-preview/inference.json'
subscriptionKeyParameterNames: {
header: 'api-key'
}
}
ポリシーを構成する
最後に、前述の API にポリシーを設定し、受信呼び出しをロード バランサーに転送できます。
// policy.xml
<policies>
<inbound>
<base />
<set-backend-service id="apim-generated-policy" backend-id="{0}" />
</inbound>
<backend>
<base />
</backend>
<outbound>
<base />
</outbound>
<on-error>
<base />
</on-error>
</policies>
var headerPolicyXml = format(loadTextContent('./policy.xml'), loadBalancing.name, 5000)
// Create a policy for the API, using the headerPolicyXml variable
resource apiPolicy 'Microsoft.ApiManagement/service/apis/policies@2020-06-01-preview' = {
parent: api1
name: 'policy'
properties: {
format: 'rawxml'
value: headerPolicyXml
}
}
ここでは、受信呼び出しをロード バランサーに転送するポリシーを作成しました。 set-backend-service
ポリシーは、受信呼び出しをロード バランサーに転送するために使用されます。 backend-id
プロパティは、前に作成したロード バランサーの名前に設定されます。
これらすべての可動部分が配置され、API Management インスタンスの負荷が分散されるようになりました。 ロード バランサーにバックエンドを追加することで、API をスケーリングできるようになりました。
サーキット ブレーカー
サーキット ブレーカーは、要求による過負荷から API を保護する場合に使用します。 仕組みとしては、一連のルールを定義し、そのルールが満たされるとサーキット ブレーカーがトリガーされ、バックエンドへの要求の送信が停止されます。 Azure API Management では、バックエンドを設定し、サーキット ブレーカー ルールを定義することで、サーキット ブレーカーを定義できます。 以下に、これを実行する方法を示します。
resource backend2 'Microsoft.ApiManagement/service/backends@2023-09-01-preview' = {
parent: apimService
name: 'backend2'
properties: {
url: '${openai2Endpoint}openai'
protocol: 'http'
circuitBreaker: {
rules: [
{
failureCondition: {
count: 3
errorReasons: [
'Server errors'
]
interval: 'P1D'
statusCodeRanges: [
{
min: 500
max: 599
}
]
}
name: 'myBreakerRule'
tripDuration: 'PT1H'
}
]
}
}
}
前述のバックエンド定義には、サーキット ブレーカーがいつ作動するかを定義するプロパティ failureCondition
があります。 この場合、1 日に 3 回のサーバー エラーが発生すると、サーキット ブレーカーが作動します。 tripDuration
プロパティは、サーキット ブレーカーが再び閉じるまでに開いたままにする期間を定義します。 API Management インスタンスに含まれる各バックエンドごとにサーキット ブレーカーを定義することをお勧めします。
マネージド ID
ここで取り組もうとしているもう 1 つの問題はセキュリティです。 API がセキュリティで保護され、承認されたクライアントのみがアクセスできるようにする必要があります。 API をセキュリティで保護する方法の 1 つは、マネージド ID を使用することです。 マネージド ID は、他の Azure サービスに対して API を認証する方法です。 Azure API Management では、次のいくつかの場所でマネージド ID を適用する必要があります。
APIM インスタンス レベル: 次のように
identity
プロパティをSystemAssigned
に設定することで、APIM インスタンスでマネージド ID を有効にできます。resource apimService 'Microsoft.ApiManagement/service@2023-09-01-preview' = { name: name location: location tags: union(tags, { 'azd-service-name': name }) sku: { name: sku capacity: (sku == 'Consumption') ? 0 : ((sku == 'Developer') ? 1 : skuCount) } properties: { publisherEmail: publisherEmail publisherName: publisherName // Custom properties are not supported for Consumption SKU } identity: { type: 'SystemAssigned' } }
このアクションにより、APIM インスタンスのマネージド ID が生成され、後で APIM インスタンス (Azure OpenAI インスタンスなど) で使用できるようになります。
API レベル: API インスタンスに対してポリシーに関連付けることができます。 上記のポリシーでは、マネージド ID を機能させるために必要な手順を追加できます。
<policies> <inbound> <base /> <authentication-managed-identity resource="https://cognitiveservices.azure.com" output-token-variable-name="managed-id-access-token" ignore-error="false" /> <set-header name="Authorization" exists-action="override"> <value>@("Bearer " + (string)context.Variables["managed-id-access-token"])</value> </set-header> </inbound> <backend> <base /> </backend> <outbound> <base /> </outbound> <on-error> <base /> </on-error> </policies>
前述の
authentication-managed-identity
およびset-header
の呼び出しを参照してください。これらの手順により、マネージド ID が API に適用されていることを確認します。バックエンド レベル: 最後に、バックエンドが Azure OpenAI インスタンスを指していることを確認します。 APIM インスタンスと Azure OpenAI インスタンスを接続する必要があります。 この接続を行うための Bicep 命令を次に示します。
resource role 'Microsoft.Authorization/roleAssignments@2022-04-01' = { name: guid(subscription().id, resourceGroup().id, principalId, roleDefinitionId) properties: { principalId: principalId principalType: "ServicePrincipal" roleDefinitionId: resourceId('Microsoft.Authorization/roleDefinitions', roleDefinitionId) } }
上記の Bicep 命令のアイデアでは、APIM インスタンスと Azure OpenAI インスタンスの間にロールの割り当てを作成します。 この場合、次のようになります。
principalId
は、APIM インスタンスのアイデンティティ ID です。roleDefinitionId
は特定のユーザーです。この場合は、Azure OpenAI インスタンスにアクセスできる "Cognitive Services ユーザー" というユーザーです。name
プロパティにより、ロールの割り当てが適切なスコープ (この場合は特定のサブスクリプションとリソース グループ) に適用されます。 (Azure OpenAI インスタンスと同じリソース グループである必要があります)