Add /ultraresearch-local for structured research combining local codebase analysis with external knowledge via parallel agent swarms. Produces research briefs with triangulation, confidence ratings, and source quality assessment. New command: /ultraresearch-local with modes --quick, --local, --external, --fg. New agents: research-orchestrator (opus), docs-researcher, community-researcher, security-researcher, contrarian-researcher, gemini-bridge (all sonnet). New template: research-brief-template.md. Integration: --research flag in /ultraplan-local accepts pre-built research briefs (up to 3), enriches the interview and exploration phases. Planning orchestrator cross-references brief findings during synthesis. Design principle: Context Engineering — right information to right agent at right time. Research briefs are structured artifacts in the pipeline: ultraresearch → brief → ultraplan --research → plan → ultraexecute. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
419 lines
15 KiB
Markdown
419 lines
15 KiB
Markdown
# 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.
|