ms-ai-architect/skills/ms-ai-infrastructure/references/bcdr/network-resilience-patterns-ai.md
Kjell Tore Guttormsen 3a73eeafdc refactor(ms-ai-architect): R13 del 1 — nøytraliser Cosmo-personaen i ref-korpusets headinger, etter å ha rettet en gate som var målt usann to ganger
Ordre 20260912T193441Z-7358817909. Steg 1 var ikke transformen, men å rette
roadmapens R13-gate og få den ratifisert. Gaten `grep -rl "Cosmo"
skills/*/references -> 0` var usann på to uavhengige måter:

1. Ordren fanget den første: 451 av forekomstene er Azure Cosmos DB, ekte
   produktinnhold. Diskriminatoren er ikke bokstaven «s» — `Cosmos <norsk
   substantiv>` er genitiv av personaen (`### Cosmos tonalitet`), mens
   `Cosmos DB`/`CosmosClient`/`cosmos_ru` er produkt.
2. Denne økten fant den andre: 132 persona-forekomster ligger i prosa,
   tabeller, dialog-replikker og proveniens-linjer. Heading-nøytralisering
   kan ikke nå dem, så «0 persona» er uoppnåelig også under den ratifiserte
   formen. Operatøren ratifiserte alternativ A: gaten speiler formen, og de
   132 bokføres til R13b/R14.

Tre korreksjoner av premisser som sto i ordren og STATE:
  «ca 320 produkt»   -> 451 (case-sensitivt nett manglet 327 lowercase
                        TOC-ankre + 99 identifikatorer; sann nevner 1 638)
  «169 headinger»    -> 401. 169 var `^## For Cosmo`-prefikset (168) og var
                        internt inkonsistent med sin egen topp-variant (204)
  «417 matcher ingen
   populasjon»       -> 417 er cosmo-headinger utenfor kodefences; briefens
                        nevner var reell hele tiden

Fence-bevissthet er målt skadelig, ikke nødvendig: begge toggle-regler er
gale på dette korpuset (naiv toggle skjuler en ekte heading i
chain-of-thought-prompting.md, CommonMark-regelen ubalanserer
service-level-documentation-dr.md). Fence-agnostisk deteksjon finner 401
heading-linjer i nøyaktig de samme 40 variantene som fence-bevisst finner
400 i — ingen kodeblokk-linje er byte-identisk til en persona-heading. Derfor
nøkles transformen på 40 enumererte heading-tekster og ignorerer fences. En
ukjent variant kaster; en slug-kollisjon kaster. Ingenting auto-fikses.

TOC-en regenereres ikke, den rettes kirurgisk: alle 327 persona-lenker hadde
lenketekst lik én av de 40 heading-tekstene og anker lik slugify av den
(327/327, 0 avvik), så heading og TOC-entry skrives i samme operasjon og
ingen mellomtilstand etterlater en død lenke.

Ratifisert målform: `For Cosmo`, `For Cosmo Skyberg` og `For arkitekten
(Cosmo)` konvergerer på `For arkitekten`. To filer kolliderte og er adjudisert
ved å lese dem, ikke ved regel.

Verifisering (alle 7 kriterier fra ordren):
  G1 persona på heading-linjer   401 -> 0
  G2 døde fragmentlenker         1 -> 1 (pre-eksisterende, unntatt)
  G3 produkt-forekomster         451 -> 451; `Cosmos DB|Azure Cosmos` 308 = 308
  de 3 kun-produkt-filene        byte-identiske
  nettet validert begge veier    injisert persona feller G1; genitiv feller G1;
                                 produkt-heading og de 3 filene passerer
  hele diffen                    802 heading-linjer + 654 TOC-linjer, ANNET = 0
  linjeantall                    728 lagt til = 728 slettet
  suite                          1120/1120 (1097 + 23 nye)
  validate-plugin                250 PASS / 0 FAIL
  stikkprøve                     10 filer, alle 5 skills, inkl. de 3 mest
                                 produkt-tunge (26/20/19) — kun heading+TOC

Utenfor scope, urørt: de 4 SKILL.md, de 23 commands, CLAUDE.md, README.md,
NOTICE.md, docs/ (alt R14).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-12 22:12:28 +02:00

16 KiB

Network Resilience Patterns for AI Workloads

Last updated: 2026-06-24 Status: GA Category: Business Continuity & Disaster Recovery Type: reference Source: https://learn.microsoft.com/azure/ddos-protection/ddos-protection-overview


Innhold

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)  │  ← innebygd DDoS-beskyttelse (edge)
└──────┬──────────────────────┘
       │
  ┌────┴────┐
  │         │
┌─▼──┐   ┌─▼──┐
│ 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

# 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

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

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

# 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

# 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

# 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

# Aktiver DDoS Network Protection
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

# 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

For arkitekten

  • 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 sterkt anbefalt for Azure AI API-kall — Microsoft omtaler dette som beste praksis for resiliens (Azure Well-Architected), ikke et absolutt krav.
  • 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.