AWS Azure Fargate Container Instances networking migracja

AWS Fargate do Azure Container Instances - rozwiązywanie problemów z networkingiem przy migracji

Rozwiązywanie problemów sieciowych przy migracji z AWS Fargate do Azure Container Instances: DNS resolution, service discovery, NSG vs Security Groups, private endpoints.

Jerzy Kopaczewski ·
Po migracji kontenerów z AWS ECS/Fargate do Azure Container Instances (ACI) lub Azure Container Apps (ACA) aplikacje nie komunikują się między sobą prawidłowo. DNS resolution zwraca błędy, service discovery nie działa, a kontenery w prywatnych sieciach nie mają dostępu do zależnych usług. Problem wynika z fundamentalnych różnic w modelu networkingu między AWS VPC + ECS Service Connect a Azure VNet + ACI/ACA networking.

Ten runbook opisuje rozwiązywanie typowych problemów z networkingiem przy migracji kontenerów z AWS Fargate do Azure. Szczegółowe porównanie strategii konteneryzacji na obu platformach znajdziesz w artykule AWS vs Azure - porównanie strategii wdrażania kontenerów. Mapowanie usług kontenerowych AWS na odpowiedniki Azure opisuje nasz runbook mapowania usług kontenerowych. Jeśli planujesz migrację infrastruktury kontenerowej do Azure - umów konsultacje.

Objaw

Po wdrożeniu kontenerów w Azure (ACI z VNet integration lub ACA) pojawiają się błędy komunikacji sieciowej:

# Typowe komunikaty błędów:
# "Name or service not known" - DNS nie rozwiązuje nazw serwisów
# "Connection refused" - port otwarty, ale ruch blokowany przez NSG
# "Connection timed out" - brak routingu do prywatnych zasobów
# "no healthy upstream" - load balancer nie widzi backendów

# Diagnoza z wnętrza kontenera ACI (exec into container)
az container exec \
  --resource-group rg-app-prod \
  --name my-api-container \
  --exec-command "/bin/sh"

# Wewnątrz kontenera - test DNS
nslookup my-backend-service
# Expected na AWS (ECS Service Connect): rozwiązuje na 127.255.0.x lub prywatny IP tasku
# Actual na Azure ACI: "server can't find my-backend-service: NXDOMAIN"

# Test połączenia do bazy danych
nc -zv my-database.postgres.database.azure.com 5432
# "Connection timed out" = NSG lub brak VNet integration

# Sprawdź konfigurację sieciową ACI
az container show \
  --resource-group rg-app-prod \
  --name my-api-container \
  --query '{ip:ipAddress.ip, ports:ipAddress.ports, subnet:subnetIds, networkProfile:networkProfile}' -o json

# Sprawdź effective NSG rules na subnecie
az network nsg show \
  --resource-group rg-app-prod \
  --name nsg-aci-subnet \
  --query 'securityRules[].{name:name, direction:direction, access:access, destPort:destinationPortRange, destAddr:destinationAddressPrefix, srcAddr:sourceAddressPrefix}' -o table

Jak odróżnić problem sieciowy od problemów z aplikacją:

Objaw Problem DNS/Service Discovery Problem NSG/Firewall Problem routingu VNet
nslookup NXDOMAIN Rozwiązuje poprawnie Rozwiązuje poprawnie
ping IP Działa (jeśli ICMP dozwolony) “Request timeout” “No route to host”
nc -zv port “Name not resolved” “Connection refused” “Connection timed out”
curl endpoint DNS error Connection reset Timeout po 30s

Przyczyna

AWS Fargate i Azure ACI/ACA mają fundamentalnie różne modele sieciowe i service discovery:

1. Service Discovery - brak odpowiednika ECS Service Connect:

Na AWS, ECS Service Connect lub Cloud Map automatycznie rejestruje każdy task w prywatnym DNS namespace (np. backend.local → prywatny IP tasku). Na Azure nie ma bezpośredniego odpowiednika. ACI nie ma wbudowanego service discovery - kontenery nie widzą się nawzajem po nazwie bez dodatkowej konfiguracji.

AWS (Fargate + ECS) Azure (ACI/ACA)
ECS Service Connect → automatyczny DNS Brak odpowiednika - trzeba użyć Private DNS Zone lub Azure Container Apps internal
Cloud Map namespace (.local) Azure Private DNS Zone (trzeba skonfigurować ręcznie)
awsvpc network mode = ENI per task VNet integration per container group (nie per kontener)
Security Groups per task ENI NSG per subnet (nie per kontener)
Service mesh via App Mesh / Envoy Dapr sidecar w Container Apps (osobna usługa)

2. NSG vs Security Groups - inny model granularności:

AWS Security Groups przypisuje się per ENI (czyli per task w Fargate). Na Azure, NSG przypisuje się per subnet lub per NIC. Kontenery ACI w VNet dzielą subnet - wszystkie mają te same reguły NSG. Nie można mieć różnych reguł dla różnych kontenerów w tym samym subnecie.

3. DNS resolution w prywatnych VNet:

Na AWS, VPC DNS resolver (169.254.169.253) automatycznie rozwiązuje nazwy z Cloud Map i Route 53 Private Hosted Zones. Na Azure, kontenery ACI w VNet domyślnie używają Azure DNS (168.63.129.16), ale nie mają dostępu do Azure Private DNS Zones bez jawnego powiązania (link) DNS zone z VNetem.

4. Private endpoints - inny model routingu:

Na AWS, PrivateLink + VPC endpoint tworzą ENI w subnecie z prywatnym IP. Na Azure, Private Endpoints również tworzą NIC w subnecie, ale wymagają Private DNS Zone z rekordem A wskazującym na prywatny IP endpointu. Bez tego DNS resolution zwraca publiczny IP, a ruch może być blokowany przez NSG.

5. Container group vs task definition:

AWS task definition może zawierać wiele kontenerów komunikujących się po localhost. Azure ACI container group działa podobnie (kontenery w grupie dzielą localhost), ALE limit to 1 container group per VNet subnet delegation. To oznacza, że nie możesz mieć wielu container groups w tym samym subnecie delegowanym do ACI.

Rozwiązanie

A) Service discovery - Azure Private DNS Zone:

Zastąp ECS Service Connect / Cloud Map konfiguracją Azure Private DNS Zone:

# 1. Utwórz Private DNS Zone
az network private-dns zone create \
  --resource-group rg-app-prod \
  --name app.internal

# 2. Połącz DNS zone z VNet (konieczne dla resolution)
az network private-dns link vnet create \
  --resource-group rg-app-prod \
  --zone-name app.internal \
  --name link-vnet-prod \
  --virtual-network vnet-prod \
  --registration-enabled false

# 3. Dodaj rekordy A dla każdego serwisu
# Najpierw sprawdź prywatny IP kontenera
BACKEND_IP=$(az container show \
  --resource-group rg-app-prod \
  --name my-backend-service \
  --query 'ipAddress.ip' -o tsv)

az network private-dns record-set a add-record \
  --resource-group rg-app-prod \
  --zone-name app.internal \
  --record-set-name backend \
  --ipv4-address $BACKEND_IP

# 4. Powtórz dla każdego serwisu
DATABASE_IP=$(az container show \
  --resource-group rg-app-prod \
  --name my-database-proxy \
  --query 'ipAddress.ip' -o tsv)

az network private-dns record-set a add-record \
  --resource-group rg-app-prod \
  --zone-name app.internal \
  --record-set-name database-proxy \
  --ipv4-address $DATABASE_IP

Automatyzacja z Terraform (zalecane):

# dns.tf - Private DNS Zone z auto-registration
resource "azurerm_private_dns_zone" "app" {
  name                = "app.internal"
  resource_group_name = azurerm_resource_group.prod.name
}

resource "azurerm_private_dns_zone_virtual_network_link" "prod" {
  name                  = "link-vnet-prod"
  resource_group_name   = azurerm_resource_group.prod.name
  private_dns_zone_name = azurerm_private_dns_zone.app.name
  virtual_network_id    = azurerm_virtual_network.prod.id
  registration_enabled  = false
}

# Rekordy A dla każdego serwisu
resource "azurerm_private_dns_a_record" "backend" {
  name                = "backend"
  zone_name           = azurerm_private_dns_zone.app.name
  resource_group_name = azurerm_resource_group.prod.name
  ttl                 = 60
  records             = [azurerm_container_group.backend.ip_address]
}

W aplikacji zmień adresy serwisów:

# Przed (AWS ECS Service Connect):
# backend-service: http://backend.app-namespace:8080

# Po (Azure Private DNS):
# backend-service: http://backend.app.internal:8080

B) Popraw konfigurację NSG - reguły per subnet:

# Sprawdź istniejące reguły NSG
az network nsg rule list \
  --resource-group rg-app-prod \
  --nsg-name nsg-aci-subnet \
  -o table

# Dodaj regułę zezwalającą na komunikację między kontenerami w VNet
az network nsg rule create \
  --resource-group rg-app-prod \
  --nsg-name nsg-aci-subnet \
  --name AllowVnetInbound \
  --priority 100 \
  --direction Inbound \
  --source-address-prefixes VirtualNetwork \
  --destination-address-prefixes VirtualNetwork \
  --destination-port-ranges '*' \
  --protocol '*' \
  --access Allow

# Dodaj regułę dla dostępu do Azure SQL / PostgreSQL
az network nsg rule create \
  --resource-group rg-app-prod \
  --nsg-name nsg-aci-subnet \
  --name AllowSqlOutbound \
  --priority 200 \
  --direction Outbound \
  --source-address-prefixes VirtualNetwork \
  --destination-address-prefixes Sql \
  --destination-port-ranges 1433 5432 \
  --protocol Tcp \
  --access Allow

# Zezwól na ruch wychodzący do Azure Storage (wymagane przez ACI runtime)
az network nsg rule create \
  --resource-group rg-app-prod \
  --nsg-name nsg-aci-subnet \
  --name AllowStorageOutbound \
  --priority 300 \
  --direction Outbound \
  --source-address-prefixes VirtualNetwork \
  --destination-address-prefixes Storage \
  --destination-port-ranges 443 \
  --protocol Tcp \
  --access Allow

C) VNet integration i subnet delegation:

# ACI wymaga subnet delegation - sprawdź czy jest skonfigurowana
az network vnet subnet show \
  --resource-group rg-app-prod \
  --vnet-name vnet-prod \
  --name subnet-aci \
  --query 'delegations[].{service:serviceName}' -o table

# Jeśli brak delegacji - dodaj (UWAGA: subnet musi być pusty)
az network vnet subnet update \
  --resource-group rg-app-prod \
  --vnet-name vnet-prod \
  --name subnet-aci \
  --delegations Microsoft.ContainerInstance/containerGroups

# Utwórz osobne subnety dla różnych serwisów (każdy ACI group potrzebuje osobnego)
az network vnet subnet create \
  --resource-group rg-app-prod \
  --vnet-name vnet-prod \
  --name subnet-aci-backend \
  --address-prefix 10.0.10.0/24 \
  --delegations Microsoft.ContainerInstance/containerGroups

az network vnet subnet create \
  --resource-group rg-app-prod \
  --vnet-name vnet-prod \
  --name subnet-aci-worker \
  --address-prefix 10.0.11.0/24 \
  --delegations Microsoft.ContainerInstance/containerGroups

D) Private Endpoints dla zarządzanych usług (bazy danych, storage):

# Utwórz Private Endpoint dla Azure PostgreSQL
az network private-endpoint create \
  --resource-group rg-app-prod \
  --name pe-postgres \
  --vnet-name vnet-prod \
  --subnet subnet-endpoints \
  --private-connection-resource-id /subscriptions/SUB_ID/resourceGroups/rg-app-prod/providers/Microsoft.DBforPostgreSQL/flexibleServers/my-postgres \
  --group-id postgresqlServer \
  --connection-name conn-postgres

# Utwórz Private DNS Zone dla PostgreSQL
az network private-dns zone create \
  --resource-group rg-app-prod \
  --name privatelink.postgres.database.azure.com

# Połącz DNS zone z VNet
az network private-dns link vnet create \
  --resource-group rg-app-prod \
  --zone-name privatelink.postgres.database.azure.com \
  --name link-postgres \
  --virtual-network vnet-prod \
  --registration-enabled false

# Dodaj rekord DNS wskazujący na prywatny IP Private Endpoint
PE_IP=$(az network private-endpoint show \
  --resource-group rg-app-prod \
  --name pe-postgres \
  --query 'customDnsConfigs[0].ipAddresses[0]' -o tsv)

az network private-dns record-set a add-record \
  --resource-group rg-app-prod \
  --zone-name privatelink.postgres.database.azure.com \
  --record-set-name my-postgres \
  --ipv4-address $PE_IP

E) Alternatywa: Azure Container Apps z wbudowanym service discovery:

Jeśli migrujesz wiele wzajemnie komunikujących się serwisów, rozważ Azure Container Apps zamiast ACI. ACA oferuje wbudowany service discovery (podobny do ECS Service Connect):

# Utwórz Container Apps Environment (odpowiednik ECS Cluster)
az containerapp env create \
  --resource-group rg-app-prod \
  --name cae-prod \
  --location westeurope \
  --infrastructure-subnet-resource-id /subscriptions/SUB_ID/resourceGroups/rg-app-prod/providers/Microsoft.Network/virtualNetworks/vnet-prod/subnets/subnet-cae \
  --internal-only true

# Deploy backend - automatycznie dostaje internal DNS: backend.internal.cae-prod.westeurope.azurecontainerapps.io
az containerapp create \
  --resource-group rg-app-prod \
  --name backend \
  --environment cae-prod \
  --image myregistry.azurecr.io/backend:latest \
  --target-port 8080 \
  --ingress internal \
  --min-replicas 2 \
  --max-replicas 10

# Inne serwisy w tym samym environment mogą komunikować się po nazwie
# URL: http://backend.internal.cae-prod.westeurope.azurecontainerapps.io
# Lub krótko (wewnątrz environment): http://backend

Walidacja

# 1. Test DNS resolution z wnętrza kontenera
az container exec \
  --resource-group rg-app-prod \
  --name my-api-container \
  --exec-command "nslookup backend.app.internal"
# Expected: zwraca prywatny IP z zakresu VNet

# 2. Test połączenia TCP do backend service
az container exec \
  --resource-group rg-app-prod \
  --name my-api-container \
  --exec-command "nc -zv backend.app.internal 8080"
# Expected: "Connection to backend.app.internal 8080 port [tcp/*] succeeded!"

# 3. Test połączenia do bazy danych przez Private Endpoint
az container exec \
  --resource-group rg-app-prod \
  --name my-api-container \
  --exec-command "nc -zv my-postgres.privatelink.postgres.database.azure.com 5432"
# Expected: succeeded

# 4. Sprawdź efektywne reguły sieciowe
az network nic list-effective-nsg \
  --resource-group rg-app-prod \
  --network-interface-name nic-aci-backend \
  --query 'value[].effectiveSecurityRules[?direction==`Inbound` && access==`Allow`].{dest:destinationPortRange, src:sourceAddressPrefix}' -o table

# 5. End-to-end test aplikacji
curl -s http://my-api.app.internal:8080/health | jq .
# Expected: {"status": "healthy", "database": "connected", "backend": "reachable"}
Największa pułapka przy migracji z AWS Fargate na Azure ACI/ACA to założenie, że service discovery będzie działać „out of the box". Na AWS, ECS Service Connect konfiguruje się jedną linijką w task definition. Na Azure, odpowiednia konfiguracja wymaga: Private DNS Zone + VNet link + rekordy A + NSG rules + subnet delegation. Pominięcie któregokolwiek elementu kończy się cichymi timeoutami w komunikacji między serwisami. Azure Container Apps (ACA) oferuje uproszczony model bliższy do ECS Service Connect - jeśli migrujesz więcej niż 3 serwisy, rozważ ACA zamiast surowego ACI.

 

Jerzy Kopaczewski

Migrujesz kontenery z AWS do Azure?

Umów bezpłatną 30-minutową rozmowę. Przeanalizujemy architekturę sieciową Twoich serwisów na AWS i zaplanujemy migrację do Azure z zachowaniem service discovery i prywatnego networkingu.