# Network Resilience Patterns for AI Workloads

**Last updated:** 2026-02
**Status:** GA
**Category:** Business Continuity & Disaster Recovery

---

## Introduksjon

Nettverksresiliens er en kritisk komponent i BCDR for AI-arbeidsbelastninger. AI-systemer er avhengige av pålitelig nettverkskommunikasjon mellom flere tjenester: applikasjonslaget, Azure OpenAI-endepunkter, AI Search-tjenester, embeddings-APIer og datastores. En nettverksforstyrrelse i ett punkt kan kaskadere og ta ned hele AI-løsningen.

Azure Well-Architected Framework definerer flere resiliensmønstre som er særlig relevante for AI-workloads: Circuit Breaker for å forhindre kaskadefeil, Retry med exponential backoff for transiente feil, Bulkhead for isolering av feildomener, og Throttling for å beskytte mot overbelastning. Disse mønstrene bør implementeres systematisk i alle AI-applikasjoner.

For norsk offentlig sektor er nettverkssikkerhet regulert gjennom NSMs grunnprinsipper, og mange organisasjoner bruker private endepunkter (Private Link) for sine Azure AI-tjenester. BCDR-designet må ta hensyn til disse nettverksrestriksjonene og sikre at failover fungerer også med private nettverkskonfigurasjoner.

## Redundante nettverksstier og tilkoblinger

### Multi-path nettverksarkitektur

```
┌─────────────┐
│  Brukere    │
└──────┬──────┘
       │
┌──────▼──────────────────────┐
│  Azure Front Door (Global)  │  ← DDoS Protection Standard
└──────┬──────────────────────┘
       │
  ┌────┴────┐
  │         │
┌─▼──┐   ┌─▼──┐
│ R1 │   │ R2 │  ← To Azure-regioner
└─┬──┘   └─┬──┘
  │         │
┌─▼──────┐ ┌▼───────┐
│ VNet A │ │ VNet B │  ← Isolerte VNets per region
│ ├─APIM │ │ ├─APIM │
│ ├─App  │ │ ├─App  │
│ ├─PE   │ │ ├─PE   │  ← Private Endpoints til AI-tjenester
│ └─NSG  │ │ └─NSG  │
└────────┘ └────────┘
```

### Azure ExpressRoute redundans

```bash
# Primær ExpressRoute-tilkobling
az network express-route create \
  --name "er-primary-norwayeast" \
  --resource-group "rg-networking" \
  --location "norwayeast" \
  --bandwidth-in-mbps 1000 \
  --peering-location "Oslo" \
  --provider "Telenor"

# Sekundær ExpressRoute (annen provider/lokasjon)
az network express-route create \
  --name "er-secondary-norwayeast" \
  --resource-group "rg-networking" \
  --location "norwayeast" \
  --bandwidth-in-mbps 1000 \
  --peering-location "Stavanger" \
  --provider "GlobalConnect"

# VPN som backup for ExpressRoute
az network vnet-gateway create \
  --name "vpn-gw-norwayeast" \
  --resource-group "rg-networking" \
  --location "norwayeast" \
  --vnet "vnet-ai-norwayeast" \
  --gateway-type Vpn \
  --sku VpnGw2AZ \
  --vpn-type RouteBased
```

### DNS-resiliens

```bash
# Azure Private DNS Zones for AI-tjenester med failover
az network private-dns zone create \
  --resource-group "rg-networking" \
  --name "privatelink.openai.azure.com"

# Link til VNets i begge regioner
az network private-dns link vnet create \
  --resource-group "rg-networking" \
  --zone-name "privatelink.openai.azure.com" \
  --name "link-vnet-norwayeast" \
  --virtual-network "vnet-ai-norwayeast" \
  --registration-enabled false

az network private-dns link vnet create \
  --resource-group "rg-networking" \
  --zone-name "privatelink.openai.azure.com" \
  --name "link-vnet-swedencentral" \
  --virtual-network "vnet-ai-swedencentral" \
  --registration-enabled false
```

## Circuit Breaker-mønster for API-kall

### Circuit Breaker implementering

Circuit Breaker-mønsteret forhindrer at en applikasjon gjentatte ganger forsøker å kalle en tjeneste som feiler, noe som kan forårsake kaskadefeil og ressursutmattelse.

```python
# Python Circuit Breaker for Azure OpenAI
import time
from enum import Enum
from threading import Lock

class CircuitState(Enum):
    CLOSED = "closed"      # Normal drift
    OPEN = "open"          # Stopp alle kall
    HALF_OPEN = "half_open"  # Prøv ett kall

class CircuitBreaker:
    """Circuit breaker for Azure AI service calls."""

    def __init__(
        self,
        failure_threshold=5,
        recovery_timeout=30,
        success_threshold=3
    ):
        self.failure_threshold = failure_threshold
        self.recovery_timeout = recovery_timeout
        self.success_threshold = success_threshold
        self.state = CircuitState.CLOSED
        self.failure_count = 0
        self.success_count = 0
        self.last_failure_time = None
        self.lock = Lock()

    def can_execute(self):
        """Check if a request can be made."""
        with self.lock:
            if self.state == CircuitState.CLOSED:
                return True
            elif self.state == CircuitState.OPEN:
                if time.time() - self.last_failure_time > self.recovery_timeout:
                    self.state = CircuitState.HALF_OPEN
                    return True
                return False
            elif self.state == CircuitState.HALF_OPEN:
                return True

    def record_success(self):
        """Record a successful call."""
        with self.lock:
            if self.state == CircuitState.HALF_OPEN:
                self.success_count += 1
                if self.success_count >= self.success_threshold:
                    self.state = CircuitState.CLOSED
                    self.failure_count = 0
                    self.success_count = 0
            else:
                self.failure_count = 0

    def record_failure(self):
        """Record a failed call."""
        with self.lock:
            self.failure_count += 1
            self.last_failure_time = time.time()
            if self.state == CircuitState.HALF_OPEN:
                self.state = CircuitState.OPEN
                self.success_count = 0
            elif self.failure_count >= self.failure_threshold:
                self.state = CircuitState.OPEN

# Bruk med Azure OpenAI
cb_openai = CircuitBreaker(failure_threshold=3, recovery_timeout=60)
cb_search = CircuitBreaker(failure_threshold=5, recovery_timeout=30)

async def call_openai_with_circuit_breaker(messages):
    if not cb_openai.can_execute():
        # Fallback: returner cached eller statisk respons
        return get_fallback_response(messages)

    try:
        response = await openai_client.chat.completions.create(
            model="gpt-4o",
            messages=messages,
            timeout=30
        )
        cb_openai.record_success()
        return response
    except Exception as e:
        cb_openai.record_failure()
        raise
```

### Circuit Breaker i .NET med Polly

```csharp
// C# med Polly for resilient Azure AI-kall
using Polly;
using Polly.CircuitBreaker;

var circuitBreakerPolicy = Policy
    .Handle<HttpRequestException>()
    .Or<TaskCanceledException>()
    .CircuitBreakerAsync(
        exceptionsAllowedBeforeBreaking: 5,
        durationOfBreak: TimeSpan.FromSeconds(30),
        onBreak: (ex, breakDuration) =>
            logger.LogWarning($"Circuit opened for {breakDuration.TotalSeconds}s: {ex.Message}"),
        onReset: () =>
            logger.LogInformation("Circuit closed, resuming normal operation"),
        onHalfOpen: () =>
            logger.LogInformation("Circuit half-open, testing with next request")
    );

var retryPolicy = Policy
    .Handle<HttpRequestException>()
    .WaitAndRetryAsync(
        retryCount: 3,
        sleepDurationProvider: retryAttempt =>
            TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)),
        onRetry: (ex, delay, retryCount, context) =>
            logger.LogWarning($"Retry {retryCount} after {delay.TotalSeconds}s: {ex.Message}")
    );

// Kombiner retry + circuit breaker
var resilientPolicy = Policy.WrapAsync(retryPolicy, circuitBreakerPolicy);

var result = await resilientPolicy.ExecuteAsync(async () =>
    await searchClient.SearchAsync<SearchDocument>(query)
);
```

## Graceful degradation av AI-tjenester

### Degraderingsstrategier

| Feiltilstand | Degraderingsstrategi | Brukeropplevelse |
|-------------|---------------------|------------------|
| Azure OpenAI nede | Returnér cached svar eller statiske meldinger | "Vi opplever tekniske problemer..." |
| AI Search nede | Fall tilbake til enklere tekstsøk | Redusert relevans, men funksjonelt |
| Embedding API nede | Bruk keyword-basert search | Ingen semantisk søk, men resultater |
| Alle AI-tjenester nede | Full graceful degradation | Manuell betjening eller køsystem |

### Implementering

```python
# Graceful degradation for RAG-applikasjon
class ResilientRAGService:
    """RAG service with multiple fallback levels."""

    async def get_response(self, user_query: str) -> dict:
        """Try full RAG, then degrade gracefully."""

        # Level 1: Full RAG (AI Search + Azure OpenAI)
        try:
            context = await self._search_with_ai(user_query)
            response = await self._generate_with_openai(user_query, context)
            return {"level": "full", "response": response}
        except ServiceUnavailableError:
            pass

        # Level 2: Keyword search + Azure OpenAI
        try:
            context = await self._keyword_search(user_query)
            response = await self._generate_with_openai(user_query, context)
            return {"level": "degraded_search", "response": response}
        except ServiceUnavailableError:
            pass

        # Level 3: Cached/FAQ responses
        try:
            response = await self._get_cached_response(user_query)
            if response:
                return {"level": "cached", "response": response}
        except Exception:
            pass

        # Level 4: Static fallback
        return {
            "level": "fallback",
            "response": "Vi opplever tekniske problemer med vår AI-tjeneste. "
                       "Vennligst prøv igjen senere eller kontakt oss direkte."
        }
```

## Private endepunkter og nettverksisolering

### Private Link for AI-tjenester

```bash
# Opprett Private Endpoints for AI-tjenester i begge regioner

# Azure OpenAI Private Endpoint — Primær region
az network private-endpoint create \
  --name "pe-aoai-norwayeast" \
  --resource-group "rg-ai-prod" \
  --vnet-name "vnet-ai-norwayeast" \
  --subnet "snet-private-endpoints" \
  --private-connection-resource-id "/subscriptions/{sub}/resourceGroups/rg-ai-prod/providers/Microsoft.CognitiveServices/accounts/aoai-prod" \
  --group-ids "account" \
  --connection-name "aoai-primary"

# Azure OpenAI Private Endpoint — DR region
az network private-endpoint create \
  --name "pe-aoai-swedencentral" \
  --resource-group "rg-ai-dr" \
  --vnet-name "vnet-ai-swedencentral" \
  --subnet "snet-private-endpoints" \
  --private-connection-resource-id "/subscriptions/{sub}/resourceGroups/rg-ai-dr/providers/Microsoft.CognitiveServices/accounts/aoai-dr" \
  --group-ids "account" \
  --connection-name "aoai-secondary"

# AI Search Private Endpoint — Primær region
az network private-endpoint create \
  --name "pe-search-norwayeast" \
  --resource-group "rg-ai-prod" \
  --vnet-name "vnet-ai-norwayeast" \
  --subnet "snet-private-endpoints" \
  --private-connection-resource-id "/subscriptions/{sub}/resourceGroups/rg-ai-prod/providers/Microsoft.Search/searchServices/search-prod" \
  --group-ids "searchService" \
  --connection-name "search-primary"
```

### VNet Peering mellom regioner

```bash
# VNet peering for cross-region kommunikasjon
az network vnet peering create \
  --name "peer-norwayeast-to-swedencentral" \
  --resource-group "rg-networking" \
  --vnet-name "vnet-ai-norwayeast" \
  --remote-vnet "/subscriptions/{sub}/resourceGroups/rg-networking/providers/Microsoft.Network/virtualNetworks/vnet-ai-swedencentral" \
  --allow-vnet-access true \
  --allow-forwarded-traffic true

az network vnet peering create \
  --name "peer-swedencentral-to-norwayeast" \
  --resource-group "rg-networking" \
  --vnet-name "vnet-ai-swedencentral" \
  --remote-vnet "/subscriptions/{sub}/resourceGroups/rg-networking/providers/Microsoft.Network/virtualNetworks/vnet-ai-norwayeast" \
  --allow-vnet-access true \
  --allow-forwarded-traffic true
```

## DDoS-beskyttelse og trafikkfiltrering

### Azure DDoS Protection

```bash
# Aktiver DDoS Protection Standard
az network ddos-protection create \
  --name "ddos-ai-protection" \
  --resource-group "rg-networking" \
  --location "norwayeast"

# Koble til VNet
az network vnet update \
  --name "vnet-ai-norwayeast" \
  --resource-group "rg-networking" \
  --ddos-protection-plan "ddos-ai-protection"
```

### NSG-regler for AI-tjenester

```bash
# Network Security Group for AI-subnet
az network nsg rule create \
  --resource-group "rg-networking" \
  --nsg-name "nsg-ai-app" \
  --name "AllowAzureOpenAI" \
  --priority 100 \
  --direction Outbound \
  --access Allow \
  --protocol Tcp \
  --destination-port-ranges 443 \
  --destination-address-prefixes "CognitiveServicesManagement" \
  --description "Allow outbound to Azure OpenAI"

az network nsg rule create \
  --resource-group "rg-networking" \
  --nsg-name "nsg-ai-app" \
  --name "AllowAzureSearch" \
  --priority 110 \
  --direction Outbound \
  --access Allow \
  --protocol Tcp \
  --destination-port-ranges 443 \
  --destination-address-prefixes "AzureCognitiveSearch" \
  --description "Allow outbound to Azure AI Search"

az network nsg rule create \
  --resource-group "rg-networking" \
  --nsg-name "nsg-ai-app" \
  --name "DenyAllOtherOutbound" \
  --priority 4000 \
  --direction Outbound \
  --access Deny \
  --protocol "*" \
  --destination-port-ranges "*" \
  --destination-address-prefixes "*" \
  --description "Deny all other outbound traffic"
```

## Referanser

- [Circuit Breaker pattern](https://learn.microsoft.com/en-us/azure/architecture/patterns/circuit-breaker) — Detaljert mønsterbeskrivelse
- [Retry pattern](https://learn.microsoft.com/en-us/azure/architecture/patterns/retry) — Retry-strategier
- [Architecture design patterns that support reliability](https://learn.microsoft.com/en-us/azure/well-architected/reliability/design-patterns) — WAF resiliensmønstre
- [Transient fault handling](https://learn.microsoft.com/en-us/azure/architecture/best-practices/transient-faults) — Best practices for transiente feil
- [Azure DDoS Protection overview](https://learn.microsoft.com/en-us/azure/ddos-protection/ddos-protection-overview) — DDoS-beskyttelse
- [Azure Private Link overview](https://learn.microsoft.com/en-us/azure/private-link/private-link-overview) — Private Endpoints

## For Cosmo

- **Bruk denne referansen** når kunden designer nettverksarkitektur for resiliente AI-løsninger, eller når de implementerer failover med private endepunkter.
- Circuit Breaker + Retry med exponential backoff er OBLIGATORISK for alle Azure AI API-kall — dette er ikke valgfritt.
- For private endpoints: Husk at failover mellom regioner krever at Private DNS-soner er linket til begge VNets.
- Graceful degradation bør alltid designes i lag — full AI → enklere søk → cached svar → statisk fallback.
- Anbefal Azure Front Door (Premium) for AI-workloads som trenger global load balancing med DDoS-beskyttelse og WAF i ett produkt.
