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.
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"}
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.