API Management

Effectué

Quel problème rencontrez-vous pour vouloir chercher une solution de gestion d’API ? Vous êtes très probablement face aux défis suivants :

  • Mise à l’échelle : Votre ou vos API sont utilisées par de nombreux clients dans différentes régions du monde et vous devez vous assurer qu’elles sont disponibles et réactives.
  • Sécurité : Vous devez vous assurer que votre API est sécurisée et que seuls les clients autorisés peuvent y accéder.
  • Gestion des erreurs : Vous devez vous assurer que votre API peut gérer correctement les erreurs.
  • Surveillance : Vous devez surveiller vos API pour vous assurer qu’elles s’exécutent comme prévu.
  • Résilience : Vous devez vous assurer que votre API est résiliente et peut gérer correctement les pannes.

Pour chacun de ces défis, vous pouvez choisir une solution dédiée, mais cela peut être difficile à gérer. Pensez également que vos API peuvent être créées dans différentes piles techniques, ce qui signifie que les solutions aux défis ci-dessus risquent d’être différentes pour chaque API. Si vous êtes face à tous ces défis, vous devez envisager une solution de gestion d’API centralisée comme la Gestion des API Azure.

Examinons plus en détail certains défis et voyons comment une solution de gestion d’API centralisée comme la Gestion des API Azure peut vous aider à les résoudre.

Infrastructure en tant que code (IaC)

Elle est très bien en créant vos ressources Azure à l’aide du portail Azure, mais à mesure que votre infrastructure se développe, elle devient plus difficile à gérer. L’un des problèmes que vous rencontrez est que vous ne pouvez pas répliquer facilement votre infrastructure dans un autre environnement.

Il est également difficile de suivre tous les changements apportés à votre infrastructure. C’est là où entre en jeu l’infrastructure en tant que code (IaC). IaC consiste à gérer votre infrastructure en utilisant du code. Pour appliquer IaC sur Azure, vous avez plusieurs options, dont l’une est Bicep. Bicep est un langage spécifique à un domaine (DSL, Domain Specific Language) qui déploie des ressources Azure de manière déclarative. Il s’agit d’un excellent moyen de gérer vos ressources cloud. Voici un exemple simple illustrant Bicep :

param location string = 'eastus'

resource storageAccount 'Microsoft.Storage/storageAccounts@2021-06-01' = {
  name: 'mystorageaccount'
  location: location
  kind: 'StorageV2'
  sku: {
    name: 'Standard_LRS'
  }
}

Dans l’exemple précédent, nous avons défini un compte de stockage à l’aide de Bicep. Nous avons défini l’emplacement du compte de stockage, le type de compte de stockage et la référence SKU (stock-keeping unit). L’emplacement est un paramètre que nous pouvons transmettre lorsque nous déployons le fichier Bicep. Pour déployer le fichier présenté, nous allons utiliser Azure CLI comme suit :

az deployment group create --resource-group myResourceGroup --template-file main.bicep

La commande précédente déploie le compte de stockage sur le groupe de ressources myResourceGroup et utilise le fichier Bicep main.bicep pour créer les ressources dans le fichier.

Gestion de la charge via un équilibreur de charge

Si votre API est submergée par les requêtes, l’ajout d’une construction à équilibrage de charge est la réponse à ce problème. Un équilibreur de charge peut vous aider à répartir la charge entre plusieurs instances de votre API.

Dans le service Gestion des API Azure, c’est vous qui implémentez l’équilibrage de charge en définissant un concept appelé back-ends. L’idée consiste à configurer de nombreux back-ends qui correspondent à vos points de terminaison d’API, puis à créer un équilibreur de charge qui répartit la charge entre ces back-ends. Voici comment l’architecture se présente :

Capture d’écran d’un équilibreur de charge.

Voici ce qui se passe dans l’architecture précédente :

  1. Le client envoie une requête à l’instance Gestion des API.
  2. La requête est authentifiée et autorisée.
  3. La requête est ensuite envoyée à l’équilibreur de charge.
  4. L’équilibreur de charge distribue la requête à l’un des back-ends (l’API Azure OpenAI sélectionnée est indiquée en gras).

Le back-end traite la requête et renvoie une réponse au client.

Définition de l’équilibreur de charge

Pour configurer un équilibreur de charge dans la Gestion des API Azure, vous devez définir les éléments suivants :

  • Back-ends : autant de back-ends que vous voulez, entre lesquels répartir la charge.
  • Équilibreur de charge : équilibreur de charge qui contient les back-ends que vous voulez, entre lesquels répartir la charge.
  • Stratégie : une stratégie qui dirige les appels entrants vers l’équilibreur de charge.

Création des back-ends

Pour créer un back-end dans la Gestion des API Azure, vous devez définir une entité back-end. Voici comment définir un back-end dans 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'
        }
      ]
    }
}

Dans le code Bicep précédent, un back-end est défini pour correspondre à une URL de point de terminaison d’API. Notez également le nom backend2, car il pourra nous servir ultérieurement. Pour chaque back-end que vous avez, vous devez l’encoder comme le code Bicep précédent.

Remarque

N’oubliez pas que vous pouvez avoir plusieurs back-ends, donc définissez autant de back-ends que vous voulez.

Créer le pool principal

Ensuite, nous voulons créer un pool de back-ends qui configure les back-ends entre lesquels nous voulons répartir la charge. Nous pouvons encoder ce pool de back-ends en tant qu’entité back-end comme suit :

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}'
        }
      ]
    }
  }
}

Le back-end que nous avons créé avant, backend2, est référencé avec un autre back-end, backend1, que nous avons omis par concision.

Nous pouvons également inclure une propriété priority et weight pour chaque élément de la liste services afin de déterminer comment l’équilibreur de charge répartit la charge. Voici comment définir la priorité et le poids pour chaque back-end :

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
    }
  ]

Dans l’exemple précédent, l’équilibreur de charge répartit la charge sur backend-1 trois fois plus que sur backend-2.

Diriger les appels entrants

Pour finir, nous devons diriger tous les appels entrants vers ce back-end d’équilibrage de charge. L’instruction de direction a créé l’entité API suivante :

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'
    }
    
}

Configurer la stratégie

À présent, nous pouvons enfin définir la stratégie sur l’API décrite précédemment et diriger les appels entrants vers l’équilibreur de charge :

// 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
  }
}

Nous avons créé une stratégie qui dirige les appels entrants vers l’équilibreur de charge. La stratégie set-backend-service est utilisée pour diriger les appels entrants vers l’équilibreur de charge. La propriété backend-id est définie sur le nom de l’équilibreur de charge que nous avons créé précédemment.

Avec tous ces éléments en place, votre instance Gestion des API a maintenant une charge équilibrée. Vous pouvez maintenant mettre à l’échelle votre API en ajoutant d’autres back-ends à l’équilibreur de charge.

Disjoncteur

Un disjoncteur est quelque chose que vous utilisez lorsque vous souhaitez éviter que votre API soit submergée par les requêtes. Voici comment cela fonctionne : définissez un ensemble de règles, et lorsqu’elles sont remplies, le disjoncteur se déclenche et stoppe l’envoi de requêtes au back-end. Dans la Gestion des API Azure, vous pouvez définir un disjoncteur en configurant un back-end et en définissant une règle de disjoncteur. Voici comment procéder :

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'
            }
          ]
        }
       }
     }

Dans la définition de back-end précédente, il existe une propriété failureCondition qui définit quand le disjoncteur coupe. Dans le cas présent, le disjoncteur coupe si trois erreurs de serveur se produisent dans une journée. La propriété tripDuration définit la durée pendant laquelle le disjoncteur doit rester ouvert avant d’être de nouveau fermé. Il est recommandé de définir un disjoncteur pour chaque back-end de votre instance Gestion des API.

Identité managée

Un autre problème que nous cherchons à résoudre est la sécurité. Vous voulez vous assurer que votre API est sécurisée et que seuls les clients autorisés peuvent y accéder. Un moyen de sécuriser votre API consiste à utiliser une identité managée. L’identité managée est un moyen d’authentifier votre API auprès d’autres services Azure. Dans la Gestion des API Azure, vous devez appliquer une identité managée à plusieurs niveaux, à savoir :

  • Niveau d’instance APIM : Vous pouvez activer une identité managée dans l’instance APIM en définissant la propriété identity sur SystemAssigned, comme suit :

    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'
        }
    }
    

    Cette action génère une identité managée pour l’instance APIM que nous pouvons utiliser ultérieurement, par exemple pour une instance Azure OpenAI.

  • Niveau d’API : Pour votre instance d’API, vous pouvez l’associer à une stratégie. Dans cette stratégie, vous pouvez ajouter les instructions nécessaires pour que l’identité managée fonctionne :

    <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>
    

    Consultez les appels précédents à authentication-managed-identity et set-header, ces instructions vérifient que l’identité managée est bien appliquée à l’API.

  • Niveau de back-end : pour finir, à condition que vos back-ends pointent vers des instances Azure OpenAI. Nous devons connecter notre instance APIM aux instances Azure OpenAI. Pour établir cette connexion, voici l’instruction 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)
      }
    }
    

    L’idée avec l’instruction Bicep ci-dessus est de créer une attribution de rôle entre l’instance APIM et l’instance Azure OpenAI. Dans ce cas :

    • principalId est l’ID d’identité de l’instance APIM.
    • roleDefinitionId est l’utilisateur spécifique ; ici, il s’agit d’un utilisateur appelé « Utilisateur Cognitive Services », utilisateur qui a accès à l’instance Azure OpenAI.
    • name, cette propriété garantit que l’attribution de rôle est appliquée à l’étendue appropriée, qui est, ici, un abonnement et un groupe de ressources spécifiques. (doit être le même groupe de ressources que l’instance Azure OpenAI)

Contrôle de vos connaissances

1.

Quel est l’un des principaux défis pouvant vous inciter à rechercher une solution de gestion d’API ?

2.

À quoi sert l’infrastructure en tant que code (IaC) dans la gestion des ressources Azure ?

3.

À quoi sert un disjoncteur dans la gestion des API ?