# Load Balancing Across Azure OpenAI Instances

**Last updated:** 2026-02
**Status:** GA
**Category:** API Management & AI Gateway

---

## Introduksjon

Lastbalansering på tvers av flere Azure OpenAI-instanser er en kritisk kapabilitet for enterprise AI-arkitekturer. Azure OpenAI har begrensninger på tokens per minutt (TPM) og requests per minutt (RPM) per deployment, og én enkelt instans vil sjelden dekke behovene til en hel organisasjon. Ved å distribuere trafikk over flere instanser -- gjerne i ulike regioner -- kan organisasjoner øke total kapasitet, forbedre tilgjengelighet og optimalisere kostnader.

Azure API Management (APIM) tilbyr innebygd backend pool-funksjonalitet som gjør dette uten egenutviklet kode. Backend pools støtter round-robin, weighted, priority-basert og session-aware lastbalansering, kombinert med circuit breaker for automatisk failover. For norsk offentlig sektor er dette spesielt relevant: flere etater kan dele infrastruktur, mens Provisioned Throughput Units (PTU) prioriteres for å maksimere investert kapasitet.

Det er viktig å forstå at APIM-lastbalansering er approksimasjon: ulike gateway-instanser synkroniserer ikke state seg imellom. Dette betyr at vektede fordelinger er omtrentlige, ikke eksakte. For de fleste AI-brukstilfeller er dette akseptabelt, da Azure OpenAI selv håndterer throttling med 429-responser og Retry-After headers.

---

## Backend Pool-konsepter

### Hva er en backend pool?

En backend pool i APIM er en samling av backend-tjenester som gatewayen behandler som én logisk enhet for lastbalansering. For Azure OpenAI betyr dette at flere OpenAI-instanser (potensielt i ulike regioner, med ulike deployment-typer) grupperes bak ett endepunkt.

| Egenskap | Verdi |
|----------|-------|
| Maks backends per pool | 30 |
| Synkronisering mellom gateway-instanser | Nei (approksimasjon) |
| Session awareness | Ja (cookie-basert) |
| Health checking | Via circuit breaker |
| Deployment-modell | Bicep, ARM, REST API, Portal |

### Backend-registrering

Hver backend i poolen registreres med URL, autentisering og valgfrie metadata:

```xml
<!-- Policy: Rut til backend pool -->
<set-backend-service backend-id="openai-pool" />
```

---

## Load Balancing-strategier

### Strategi 1: Round-Robin

Fordeler requests jevnt mellom alle backends i poolen.

```
Request 1 → Backend A (Norway East)
Request 2 → Backend B (Sweden Central)
Request 3 → Backend C (West Europe)
Request 4 → Backend A (Norway East)  ← syklusen gjentas
```

**Bruk når:**
- Alle instanser har lik kapasitet (samme TPM-allokering)
- Ingen preferanse for spesifikke regioner
- Enkel konfigurasjon er prioritert

**Bicep:**

```bicep
resource backendPool 'Microsoft.ApiManagement/service/backends@2023-09-01-preview' = {
  parent: apim
  name: 'openai-pool'
  properties: {
    type: 'Pool'
    pool: {
      services: [
        {
          id: '/backends/openai-norwayeast'
        }
        {
          id: '/backends/openai-swedencentral'
        }
        {
          id: '/backends/openai-westeurope'
        }
      ]
    }
  }
}
```

### Strategi 2: Weighted (vektet)

Fordeler requests basert på tildelte vekter. Nyttig når backends har ulik kapasitet.

```
Vekter: A=3, B=2, C=1 (totalt 6)
→ A mottar ~50% av trafikk
→ B mottar ~33% av trafikk
→ C mottar ~17% av trafikk
```

**Bruk når:**
- Backends har ulik TPM-allokering
- Blue-green deployment med gradvis trafikkskift
- Ulike pricing-modeller (PTU vs. pay-as-you-go)

**Bicep:**

```bicep
resource backendPool 'Microsoft.ApiManagement/service/backends@2023-09-01-preview' = {
  parent: apim
  name: 'openai-pool-weighted'
  properties: {
    type: 'Pool'
    pool: {
      services: [
        {
          id: '/backends/openai-norwayeast-ptu'
          weight: 5
          priority: 1
        }
        {
          id: '/backends/openai-swedencentral-paygo'
          weight: 3
          priority: 1
        }
        {
          id: '/backends/openai-westeurope-paygo'
          weight: 2
          priority: 1
        }
      ]
    }
  }
}
```

### Strategi 3: Priority-Based (anbefalt for AI)

Organiserer backends i prioritetsgrupper. Lavere prioritetsnummer = høyere prioritet. Backends i lavere prioritetsgrupper brukes kun når alle backends i høyere grupper er utilgjengelige (circuit breaker utløst).

```
Priority 1: PTU-instanser (fast pris, utnytt først)
  ├── openai-norwayeast-ptu (weight: 3)
  └── openai-swedencentral-ptu (weight: 2)

Priority 2: Pay-as-you-go fallback
  ├── openai-westeurope-paygo (weight: 1)
  └── openai-eastus-paygo (weight: 1)
```

**Typisk scenario for norsk offentlig sektor:**

| Prioritet | Deployment-type | Region | Begrunnelse |
|-----------|-----------------|--------|-------------|
| 1 | PTU | Norway East | Datasuverenitet + fast pris, bruk først |
| 1 | PTU | Sweden Central | Nær-region redundans |
| 2 | Pay-as-you-go | West Europe | Spillover ved høy last |
| 3 | Pay-as-you-go | East US | Nødfallback ved regional utfall |

**Bicep:**

```bicep
resource backendPool 'Microsoft.ApiManagement/service/backends@2023-09-01-preview' = {
  parent: apim
  name: 'openai-pool-priority'
  properties: {
    type: 'Pool'
    pool: {
      services: [
        {
          id: '/backends/openai-norwayeast-ptu'
          weight: 3
          priority: 1
        }
        {
          id: '/backends/openai-swedencentral-ptu'
          weight: 2
          priority: 1
        }
        {
          id: '/backends/openai-westeurope-paygo'
          weight: 1
          priority: 2
        }
        {
          id: '/backends/openai-eastus-paygo'
          weight: 1
          priority: 3
        }
      ]
    }
  }
}
```

### Strategi 4: Session-Aware

Sikrer at alle requests fra samme bruker-sesjon rutes til samme backend. Kritisk for Azure OpenAI Assistants API der thread state er bundet til en spesifikk instans.

```
Sesjon 1: Bruker A ──cookie──► Backend A (alle requests i sesjonen)
Sesjon 2: Bruker B ──cookie──► Backend B (alle requests i sesjonen)
Sesjon 3: Bruker C ──cookie──► Backend A (ny sesjon, tilfeldig valgt)
```

**Bruk når:**
- Assistants API (thread state)
- Chat-applikasjoner med stateful backends
- Når backend cacher bruker-kontekst

**Bicep:**

```bicep
resource backendPool 'Microsoft.ApiManagement/service/backends@2023-09-01-preview' = {
  parent: apim
  name: 'openai-pool-session'
  properties: {
    type: 'Pool'
    pool: {
      services: [
        {
          id: '/backends/openai-norwayeast'
          weight: 1
          priority: 1
        }
        {
          id: '/backends/openai-swedencentral'
          weight: 1
          priority: 1
        }
      ]
      sessionAffinity: {
        type: 'Cookie'
        cookieName: 'apim-session-id'
      }
    }
  }
}
```

**Cookie-håndtering for klienter:**
Klienter MÅ lagre `Set-Cookie`-headeren fra APIM og sende den tilbake i påfølgende requests for å opprettholde sesjonsaffinitet.

---

## Individual Backend-konfigurasjon

### Registrere en Azure OpenAI backend

```bicep
resource openaiBackend 'Microsoft.ApiManagement/service/backends@2023-09-01-preview' = {
  parent: apim
  name: 'openai-norwayeast-ptu'
  properties: {
    url: 'https://aoai-norwayeast.openai.azure.com/openai'
    protocol: 'http'
    description: 'Azure OpenAI PTU deployment i Norway East'
    circuitBreaker: {
      rules: [
        {
          name: 'openai-circuit-breaker'
          failureCondition: {
            count: 3
            interval: 'PT1M'
            statusCodeRanges: [
              { min: 429, max: 429 }
              { min: 500, max: 599 }
            ]
          }
          tripDuration: 'PT10S'
          acceptRetryAfter: true
        }
      ]
    }
  }
}
```

### Autentisering via Managed Identity

```xml
<policies>
    <inbound>
        <set-backend-service backend-id="openai-pool-priority" />
        <authentication-managed-identity
            resource="https://cognitiveservices.azure.com/" />
    </inbound>
</policies>
```

**RBAC-konfigurasjon:**

```bicep
// Grant Cognitive Services User role to APIM managed identity
resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
  scope: openaiResource
  name: guid(apim.id, openaiResource.id, 'cognitive-services-user')
  properties: {
    roleDefinitionId: subscriptionResourceId(
      'Microsoft.Authorization/roleDefinitions',
      'a97b65f3-24c7-4388-baec-2e87135dc908' // Cognitive Services User
    )
    principalId: apim.identity.principalId
    principalType: 'ServicePrincipal'
  }
}
```

---

## Deployment Slot Selection

### Routing til ulike modell-deployments

Når backends har ulike deployment-navn (f.eks. `gpt-4o` i en region, `gpt4-turbo` i en annen), kan APIM-policies transformere URL-en:

```xml
<policies>
    <inbound>
        <set-backend-service backend-id="openai-pool-priority" />

        <!-- Override deployment name basert på backend-region -->
        <choose>
            <when condition="@(context.Backend?.AzureRegion == "norwayeast")">
                <rewrite-uri template="/deployments/gpt-4o/chat/completions" />
            </when>
            <when condition="@(context.Backend?.AzureRegion == "swedencentral")">
                <rewrite-uri template="/deployments/gpt4-turbo/chat/completions" />
            </when>
        </choose>

        <authentication-managed-identity
            resource="https://cognitiveservices.azure.com/" />
    </inbound>
</policies>
```

### Modell-versjonshåndtering

```xml
<!-- Sett api-version basert på backend -->
<set-query-parameter name="api-version" exists-action="override">
    <value>2024-10-21</value>
</set-query-parameter>
```

---

## Regional Distribution

### Topologi 1: Single-Region APIM, Multi-Region Backends

```
              APIM (Norway East)
                     │
            ┌────────┼────────┐
            ▼        ▼        ▼
   OpenAI        OpenAI    OpenAI
(Norway East) (Sweden C.)  (West EU)
  Priority 1  Priority 1  Priority 2
```

**Fordeler:** Enkel arkitektur, ett kontrollpunkt
**Ulemper:** APIM er single point of failure, cross-region latens

### Topologi 2: Multi-Region APIM, Regional Backends

```
  Client → DNS (latency-based routing)
               │
      ┌────────┴────────┐
      ▼                 ▼
APIM Gateway        APIM Gateway
(Norway East)       (Sweden Central)
      │                 │
      ▼                 ▼
   OpenAI            OpenAI
(Norway East)    (Sweden Central)
```

**Fordeler:** Ingen regional single point of failure, lav latens
**Ulemper:** Krever APIM Premium, dyrere

**Bicep for multi-region APIM:**

```bicep
resource apim 'Microsoft.ApiManagement/service@2023-09-01-preview' = {
  name: 'apim-ai-gateway'
  location: 'norwayeast'
  sku: {
    name: 'Premium'
    capacity: 1
  }
  properties: {
    additionalLocations: [
      {
        location: 'swedencentral'
        sku: {
          name: 'Premium'
          capacity: 1
        }
      }
    ]
  }
}
```

### Topologi 3: Active-Active med Active-Passive Backends

Kombinerer regional redundans med kostnadsoptimalisering:

```
APIM Gateway (Norway East)
  ├── Active:  OpenAI PTU (Norway East)    Priority 1
  ├── Passive: OpenAI PAYGO (Norway East)  Priority 2
  └── Cross:   OpenAI PAYGO (Sweden C.)    Priority 3 (kun ved regional feil)

APIM Gateway (Sweden Central)
  ├── Active:  OpenAI PTU (Sweden C.)      Priority 1
  ├── Passive: OpenAI PAYGO (Sweden C.)    Priority 2
  └── Cross:   OpenAI PAYGO (Norway East)  Priority 3
```

**Regional policy routing:**

```xml
<choose>
    <when condition="@(context.Deployment.Region == "Norway East")">
        <set-backend-service backend-id="pool-norwayeast" />
    </when>
    <when condition="@(context.Deployment.Region == "Sweden Central")">
        <set-backend-service backend-id="pool-swedencentral" />
    </when>
</choose>
```

---

## Throttling og Retry-håndtering

### Smart Load Balancing

Når en backend returnerer 429 (Too Many Requests), skal gatewayen:
1. Lese `Retry-After`-headeren
2. Markere backend som utilgjengelig via circuit breaker
3. Umiddelbart retry til neste tilgjengelige backend i poolen
4. IKKE vente (ingen delay mellom retries til ulike backends)

```xml
<policies>
    <inbound>
        <set-backend-service backend-id="openai-pool-priority" />
    </inbound>

    <backend>
        <retry condition="@(context.Response.StatusCode == 429)"
               count="3"
               interval="0"
               first-fast-retry="true">
            <set-backend-service backend-id="openai-pool-priority" />
        </retry>
    </backend>
</policies>
```

### PTU + PAYGO Spillover-mønster

Det mest vanlige mønsteret for kostnadsoptimalisering:

```
Normal trafikk:
  All trafikk → PTU (Priority 1, fast pris)

Ved throttling (PTU kapasitet brukt opp):
  Circuit breaker utløst på PTU
  Trafikk → PAYGO (Priority 2, pay-per-token)

Etter PTU recovery:
  Circuit breaker reset
  Trafikk → PTU (Priority 1, tilbake til fast pris)
```

| Fase | Backend | Kostnad | Latens |
|------|---------|---------|--------|
| Normal | PTU | Fast (forutsigbar) | Lav (garantert) |
| Spillover | PAYGO | Variabel (høyere) | Variabel |
| Recovery | PTU | Fast | Lav |

---

## Komplett Bicep-eksempel

```bicep
@description('Komplett AI Gateway med priority-based load balancing')

param location string = 'norwayeast'
param environment string = 'prod'

// Azure OpenAI instances
resource aoaiNorway 'Microsoft.CognitiveServices/accounts@2024-10-01' existing = {
  name: 'aoai-norwayeast-${environment}'
}

resource aoaiSweden 'Microsoft.CognitiveServices/accounts@2024-10-01' existing = {
  name: 'aoai-swedencentral-${environment}'
}

// APIM Instance
resource apim 'Microsoft.ApiManagement/service@2023-09-01-preview' = {
  name: 'apim-ai-gw-${environment}'
  location: location
  sku: {
    name: 'StandardV2'
    capacity: 1
  }
  identity: {
    type: 'SystemAssigned'
  }
  properties: {
    publisherEmail: 'ai-team@example.no'
    publisherName: 'AI Gateway'
  }
}

// Backend: Norway East PTU
resource backendNorwayPTU 'Microsoft.ApiManagement/service/backends@2023-09-01-preview' = {
  parent: apim
  name: 'openai-norwayeast-ptu'
  properties: {
    url: '${aoaiNorway.properties.endpoint}openai'
    protocol: 'http'
    circuitBreaker: {
      rules: [
        {
          name: 'throttle-protection'
          failureCondition: {
            count: 3
            interval: 'PT1M'
            statusCodeRanges: [
              { min: 429, max: 429 }
              { min: 500, max: 599 }
            ]
          }
          tripDuration: 'PT10S'
          acceptRetryAfter: true
        }
      ]
    }
  }
}

// Backend: Sweden Central PAYGO
resource backendSwedenPAYGO 'Microsoft.ApiManagement/service/backends@2023-09-01-preview' = {
  parent: apim
  name: 'openai-swedencentral-paygo'
  properties: {
    url: '${aoaiSweden.properties.endpoint}openai'
    protocol: 'http'
    circuitBreaker: {
      rules: [
        {
          name: 'throttle-protection'
          failureCondition: {
            count: 3
            interval: 'PT1M'
            statusCodeRanges: [
              { min: 429, max: 429 }
              { min: 500, max: 599 }
            ]
          }
          tripDuration: 'PT10S'
          acceptRetryAfter: true
        }
      ]
    }
  }
}

// Backend Pool with priority-based routing
resource backendPool 'Microsoft.ApiManagement/service/backends@2023-09-01-preview' = {
  parent: apim
  name: 'openai-pool'
  properties: {
    type: 'Pool'
    pool: {
      services: [
        {
          id: '/backends/${backendNorwayPTU.name}'
          weight: 3
          priority: 1
        }
        {
          id: '/backends/${backendSwedenPAYGO.name}'
          weight: 1
          priority: 2
        }
      ]
    }
  }
}
```

---

## Overvåking og feilsøking

### Identifisere hvilken backend som serverte request

```xml
<outbound>
    <set-header name="X-Backend-Id" exists-action="override">
        <value>@(context.Backend?.Id ?? "unknown")</value>
    </set-header>
    <set-header name="X-Backend-Type" exists-action="override">
        <value>@(context.Backend?.Type ?? "unknown")</value>
    </set-header>
</outbound>
```

### KQL for load balancing-distribusjon

```kusto
ApiManagementGatewayLogs
| where OperationId == "ChatCompletions_Create"
| extend backendUrl = tostring(BackendUrl)
| summarize RequestCount = count() by backendUrl, bin(TimeGenerated, 1h)
| render columnchart
```

---

## For Cosmo

- Priority-based load balancing med PTU som Priority 1 og PAYGO som Priority 2 er det anbefalte mønsteret for enterprise AI-arkitekturer -- det maksimerer utnyttelsen av forhåndskjøpt kapasitet og faller automatisk tilbake til pay-per-use ved behov.
- Backend pools er approksimerte: ulike gateway-instanser synkroniserer ikke, så vektede fordelinger er omtrentlige. For AI-workloads er dette akseptabelt fordi Azure OpenAI selv håndterer throttling med 429/Retry-After.
- Session awareness er kritisk for Assistants API og chat-applikasjoner med stateful backends -- aktiver dette med cookie-basert sesjonsaffinitet i pool-konfigurasjonen.
- For norsk offentlig sektor med datasuverenitetskrav: prioriter Norway East og Sweden Central, bruk private endpoints, og vurder om cross-region failover til EU-regioner er akseptabelt under gjeldende regelverk.
- Kombiner alltid backend pools med circuit breaker (inkludert `acceptRetryAfter: true`) for intelligent failover ved 429-responser fra Azure OpenAI.
