Gestão de API
- 5 minutos
Então, qual é o problema que estou tendo que me faz querer procurar uma solução de gerenciamento de API? Você provavelmente tem os seguintes desafios:
- Dimensionamento, a sua API ou APIs são usadas por muitos clientes em diferentes regiões do mundo, e você precisa garantir que estejam disponíveis e com capacidade de resposta.
- Segurança, você precisa garantir que sua API seja segura e que apenas clientes autorizados possam acessá-la.
- Gerenciamento de erros, você precisa garantir que sua API possa lidar com erros normalmente.
- Monitorização, é necessário monitorizar as suas APIs para garantir que estejam a funcionar conforme o esperado.
- Resiliência, você precisa garantir que sua API seja resiliente e possa lidar com falhas normalmente.
Para cada um desses desafios, você pode optar por uma solução pontual, mas isso pode ser difícil de gerenciar. Considere também que suas APIs podem ser criadas em diferentes pilhas de tecnologia, o que significa que as soluções para os desafios acima podem significar que você precisa de soluções diferentes para cada API. Se você estiver tendo todos esses desafios, considere uma solução de gerenciamento de API centralizado, como o Gerenciamento de API do Azure.
Vamos nos aprofundar em alguns desafios e ver como uma solução centralizada de gerenciamento de API, como o Gerenciamento de API do Azure, pode ajudá-lo a enfrentá-los.
Infraestrutura como código, IaC
É perfeitamente bom criar seus recursos do Azure usando o portal do Azure, mas à medida que sua infraestrutura cresce, torna-se mais difícil de gerenciar. Um dos problemas que você enfrenta é que não é possível replicar facilmente sua infraestrutura em outro ambiente.
Também é difícil rastrear todas as alterações feitas na sua infraestrutura. É nesta situação que entra a Infraestrutura como Código (IaC). IaC é a prática de gerenciar sua infraestrutura usando código. Para aplicar o IaC no Azure, você tem várias opções, uma das quais é o Bicep. O Bicep é uma DSL (Domain Specific Language) para implantar recursos do Azure declarativamente. É uma ótima maneira de gerenciar seus recursos de nuvem. Aqui está um exemplo simples de como o Bicep se parece:
param location string = 'eastus'
resource storageAccount 'Microsoft.Storage/storageAccounts@2021-06-01' = {
name: 'mystorageaccount'
location: location
kind: 'StorageV2'
sku: {
name: 'Standard_LRS'
}
}
No exemplo anterior, definimos uma conta de armazenamento usando o Bicep. Definimos a localização da conta de armazenamento, o tipo de conta de armazenamento e a SKU (unidade de manutenção de estoque). Localização é um parâmetro que podemos passar quando implantamos o arquivo Bicep. Para implantar o arquivo apresentado, usaríamos a CLI do Azure da seguinte forma:
az deployment group create --resource-group myResourceGroup --template-file main.bicep
O comando anterior implanta a conta de armazenamento no grupo myResourceGroup de recursos e usa o arquivo main.bicep Bicep para criar os recursos no arquivo.
Manuseamento de carga através de um balanceador de carga
Adicionar uma construção de balanceamento de carga é a resposta quando o problema é que sua API está sobrecarregada por solicitações. Um balanceador de carga pode ajudá-lo a distribuir a carga em várias instâncias da API.
No serviço de Gerenciamento de API do Azure, o balanceamento de carga é implementado pela definição de um conceito chamado back-ends. A ideia é que você configure muitos back-ends que correspondam aos seus pontos de extremidade de API e, em seguida, crie um balanceador de carga que distribua a carga entre esses back-ends. Veja como é a arquitetura:
O que está acontecendo na arquitetura anterior é:
- O cliente envia uma solicitação para a instância de Gerenciamento de API.
- O pedido é autenticado e autorizado.
- A solicitação é então enviada para o balanceador de carga.
- O balanceador de carga distribui a solicitação para um dos back-ends (a API OpenAI do Azure selecionada é indicada em negrito).
O back-end processa a solicitação e envia uma resposta de volta ao cliente.
Definindo o balanceador de carga
Para configurar um balanceador de carga no Gerenciamento de API do Azure, você precisa fazer as seguintes partes:
- Back-ends, quantos back-ends quiser para distribuir a carga.
- Load balancer, um balanceador de carga que contém os back-ends que você deseja distribuir a carga.
- Uma política que direciona as chamadas de entrada para o balanceador de carga.
Criando os backends
Para criar um back-end no Gerenciamento de API do Azure, você precisa definir uma entidade de back-end. Veja como você pode definir um back-end no 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'
}
]
}
}
No código Bicep anterior, um back-end é definido para corresponder a uma URL de ponto de extremidade da API, observe também o nome backend2 esse nome é algo que podemos usar mais tarde. Para cada back-end que você tem, você deve codificá-lo como o código bicep anterior.
Nota
Lembre-se de que você pode ter vários backends, para que possa definir quantos backends quiser.
Criar conjunto de back-end
Em seguida, queremos criar um pool de back-end que configure quais back-ends queremos distribuir a carga entre. Podemos codificar esse pool de back-end como uma entidade de back-end assim:
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}'
}
]
}
}
}
O backend que criamos antes, backend2, é referenciado junto com outro backend backend1, este último que omitimos por brevidade.
Também podemos incluir uma priority propriedade e weight para cada item na services lista para determinar como o balanceador de carga distribui a carga. Veja como você pode definir a prioridade e o peso para cada 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
}
]
No exemplo anterior, o balanceador de carga distribui a carga para backend-1 três vezes mais do que backend-2.
Chamadas recebidas diretas
Por fim, precisamos direcionar todas as chamadas recebidas para esse back-end de balanceamento de carga. A instrução direction é criada a seguinte entidade 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'
}
}
Configurar a política
Agora, finalmente, podemos definir a política na API descrita anteriormente e direcionar as chamadas recebidas para o balanceador de carga:
// 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
}
}
O que fizemos foi criar uma política que direciona as chamadas recebidas para o balanceador de carga. A set-backend-service política é usada para direcionar as chamadas de entrada para o balanceador de carga. A backend-id propriedade é definida como o nome do balanceador de carga que criamos antes.
Com todas essas partes móveis instaladas, sua instância de Gerenciamento de API agora está com balanceamento de carga. Agora você pode dimensionar sua API adicionando mais back-ends ao balanceador de carga.
Disjuntor
Um disjuntor é algo que você usa quando deseja proteger sua API de ser sobrecarregado por solicitações. Como funciona é que você define um conjunto de regras que, quando atendidas, o disjuntor aciona e para de enviar solicitações para o back-end. No Gerenciamento de API do Azure, você pode definir um disjuntor configurando um back-end e definindo uma regra de disjuntor. Veja como você pode fazer isso:
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'
}
]
}
}
}
Na definição de back-end anterior, há uma propriedade failureCondition que define quando o disjuntor deve disparar. Neste caso, o disjuntor aciona se houver três erros de servidor em um dia. A tripDuration propriedade define por quanto tempo o disjuntor deve permanecer aberto antes de fechar novamente. É uma boa prática definir um disjuntor para cada back-end que você tem em sua instância de Gerenciamento de API.
Identidade gerida
Outro problema que estamos procurando resolver é a segurança. Você quer garantir que sua API seja segura e que apenas clientes autorizados possam acessá-la. Uma maneira de proteger sua API é usando a identidade gerenciada. A identidade gerenciada é uma maneira de autenticar sua API em outros serviços do Azure. No Gerenciamento de API do Azure, você precisa aplicar a identidade gerenciada em vários locais, a saber:
No nível da instância do APIM, você pode habilitar a identidade gerenciada na instância do APIM definindo a
identitypropriedade daSystemAssignedseguinte forma: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' } }Essa ação gera uma identidade gerenciada para a instância do APIM que podemos usar posteriormente para, por exemplo, uma instância do Azure OpenAI.
Nível de API, para sua instância de API, você pode associá-lo a uma política. Nessa política, você pode adicionar as instruções necessárias para que a identidade gerenciada funcione:
<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>Consulte as chamadas anteriores e
authentication-managed-identityset-headerestas instruções certifiquem-se de que a identidade gerenciada seja aplicada à API.Nível de back-end, finalmente, desde que os seus back-ends estejam a apontar para instâncias do Azure OpenAI. Precisamos conectar nossa instância de APIM com a(s) instância(s) do Azure OpenAI. Para fazer essa conexão, aqui está a instrução do Bíceps:
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) } }A ideia com a instrução Bicep acima é criar uma atribuição de função entre a instância do APIM e a instância do Azure OpenAI. Neste caso:
-
principalIdé o ID de identidade da instância do APIM, -
roleDefinitionIdé o usuário específico, neste caso é um usuário chamado "Usuário de Serviços Cognitivos", um usuário que tem acesso à instância do Azure OpenAI. -
name, essa propriedade garante que a atribuição de função seja aplicada ao escopo correto, que neste caso é uma assinatura específica e um grupo de recursos. (precisa ser o mesmo grupo de recursos que a instância do Azure OpenAI)
-