App Service z VNet Integration nie rozwiązuje prywatnych nazw DNS
Naprawa rozwiązywania DNS w App Service z VNet Integration: konfiguracja Private DNS Zones, VNet link i WEBSITE_DNS_SERVER.
Ten runbook opisuje rozwiązywanie problemów z DNS w App Service z VNet Integration i Private Endpoints. Pełny przewodnik po migracji infrastruktury .NET do Azure znajdziesz w artykule Migracja do Azure krok po kroku - App Service, AKS, Landing Zone. W kwestii architektury sieciowej i Private Link umów konsultacje.
Objaw
Połączenie z App Service do zasobu z Private Endpoint kończy się timeoutem lub błędem. Kudu console (Advanced Tools) pokazuje publiczny IP zamiast prywatnego:
# W Kudu console (https://<app-name>.scm.azurewebsites.net)
# Narzędzia > Debug console > CMD lub PowerShell
# nslookup zwraca publiczny IP zamiast prywatnego (10.x.x.x)
nameresolver mysqlserver.database.windows.net
# Wynik problematyczny: zwraca np. 52.168.112.64 (publiczny)
# Wynik poprawny: powinien zwracać np. 10.0.1.5 (prywatny z Private Endpoint)
# Alternatywnie przez PowerShell w Kudu
[System.Net.Dns]::GetHostAddresses("mysqlserver.database.windows.net") | Select IPAddressToString
# Wynik problematyczny: 52.168.112.64
# Wynik poprawny: 10.0.1.5
# Sprawdzenie czy WEBSITE_DNS_SERVER jest ustawiony
env | grep DNS
# Jeśli brak wpisu WEBSITE_DNS_SERVER - to część problemu
# Test połączenia TCP do Private Endpoint
tcpping mysqlserver.database.windows.net:1433
# Wynik problematyczny: Connection timed out lub łączy się z publicznym IP
Aplikacja loguje błędy typu „Connection timed out” lub „Login failed” (jeśli firewall zasobu dopuszcza tylko Private Endpoint). W Application Insights widoczne są dependency failures do Azure SQL lub Storage.
Przyczyna
App Service z VNet Integration wymaga poprawnej konfiguracji DNS, aby rozwiązywać nazwy Private Endpoints do adresów prywatnych. Problem pojawia się, gdy:
-
Private DNS Zone nie jest zlinkowana do VNetu: Strefa
privatelink.database.windows.net(lub inna odpowiednia dla danego zasobu) istnieje, ale nie ma VNet Link do VNetu, w którym App Service ma subnet integracyjny. Bez linka DNS queries z VNetu nie trafiają do Private DNS Zone. -
WEBSITE_DNS_SERVER nie ustawiony na 168.63.129.16: App Service domyślnie używa publicznych serwerów DNS Azure. Po włączeniu VNet Integration należy jawnie ustawić
WEBSITE_DNS_SERVER=168.63.129.16(Azure DNS resolver), aby zapytania DNS przechodziły przez VNet i docierały do Private DNS Zones. -
Route All (vnetRouteAllEnabled) nie włączony: Bez tej opcji tylko ruch do zakresów RFC1918 (10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16) jest kierowany przez VNet. Zapytania DNS do 168.63.129.16 i inny ruch może nadal wychodzić poza VNet.
-
Nazwa DNS Zone nie pasuje do suffixu zasobu: Private DNS Zone musi mieć dokładną nazwę odpowiadającą suffiksowi Private Link danej usługi. Przykładowo: Azure SQL wymaga strefy
privatelink.database.windows.net, a niedatabase.windows.netczyprivatelink.sql.azuresynapse.net. Błędna nazwa strefy powoduje, że rekord A nie jest znajdowany.
Rozwiązanie
A) Zlinkuj Private DNS Zone do VNetu App Service:
# Sprawdź czy Private DNS Zone istnieje
az network private-dns zone list \
--query "[].{name:name, records:numberOfRecordSets, links:numberOfVirtualNetworkLinks}" -o table
# Sprawdź istniejące VNet linki dla strefy
az network private-dns link vnet list \
--resource-group rg-dns \
--zone-name privatelink.database.windows.net \
--query "[].{name:name, vnetId:virtualNetwork.id, state:provisioningState}" -o table
# Dodaj link do VNetu, w którym jest subnet App Service
az network private-dns link vnet create \
--resource-group rg-dns \
--zone-name privatelink.database.windows.net \
--name link-vnet-appservice \
--virtual-network /subscriptions/<sub-id>/resourceGroups/rg-network/providers/Microsoft.Network/virtualNetworks/vnet-production \
--registration-enabled false
# Zweryfikuj że rekord A istnieje w strefie
az network private-dns record-set a list \
--resource-group rg-dns \
--zone-name privatelink.database.windows.net \
--query "[].{name:name, ip:aRecords[0].ipv4Address}" -o table
# Expected: mysqlserver -> 10.0.1.5
B) Ustaw WEBSITE_DNS_SERVER na 168.63.129.16:
# Sprawdź aktualne app settings
az webapp config appsettings list \
--resource-group rg-app \
--name my-app-service \
--query "[?name=='WEBSITE_DNS_SERVER'].value" -o tsv
# Ustaw Azure DNS resolver jako serwer DNS dla App Service
az webapp config appsettings set \
--resource-group rg-app \
--name my-app-service \
--settings WEBSITE_DNS_SERVER=168.63.129.16
# Opcjonalnie - ustaw WEBSITE_DNS_ALT_SERVER jako fallback
az webapp config appsettings set \
--resource-group rg-app \
--name my-app-service \
--settings WEBSITE_DNS_ALT_SERVER=168.63.129.16
C) Włącz Route All (vnetRouteAllEnabled):
# Sprawdź aktualny stan VNet Integration i Route All
az webapp show \
--resource-group rg-app \
--name my-app-service \
--query '{vnetIntegration:virtualNetworkSubnetId, routeAll:vnetRouteAllEnabled}' -o json
# Włącz Route All - cały ruch z App Service będzie kierował przez VNet
az webapp update \
--resource-group rg-app \
--name my-app-service \
--set vnetRouteAllEnabled=true
# Alternatywnie przez az resource update (dla starszych wersji CLI)
az resource update \
--resource-group rg-app \
--name my-app-service \
--resource-type Microsoft.Web/sites \
--set properties.vnetRouteAllEnabled=true
// Równoważna konfiguracja w ARM template / Bicep
{
"type": "Microsoft.Web/sites",
"properties": {
"virtualNetworkSubnetId": "/subscriptions/<sub-id>/resourceGroups/rg-network/providers/Microsoft.Network/virtualNetworks/vnet-production/subnets/subnet-appservice",
"vnetRouteAllEnabled": true,
"siteConfig": {
"appSettings": [
{
"name": "WEBSITE_DNS_SERVER",
"value": "168.63.129.16"
}
]
}
}
}
D) Zweryfikuj poprawność rekordu DNS w Private DNS Zone:
# Sprawdź nazwę Private DNS Zone odpowiednią dla danej usługi Azure:
# Azure SQL Database: privatelink.database.windows.net
# Azure Blob Storage: privatelink.blob.core.windows.net
# Azure Key Vault: privatelink.vaultcore.azure.net
# Azure Cosmos DB: privatelink.documents.azure.com
# Azure Service Bus: privatelink.servicebus.windows.net
# Sprawdź czy Private Endpoint został poprawnie zarejestrowany w strefie
az network private-endpoint dns-zone-group list \
--resource-group rg-data \
--endpoint-name pe-sqlserver \
--query "[].{zone:privateDnsZoneConfigs[0].privateDnsZoneId, records:privateDnsZoneConfigs[0].recordSets}" -o json
# Jeśli rekord nie istnieje - dodaj go ręcznie (lub napraw dns-zone-group)
az network private-dns record-set a add-record \
--resource-group rg-dns \
--zone-name privatelink.database.windows.net \
--record-set-name mysqlserver \
--ipv4-address 10.0.1.5
# Sprawdź czy Private Endpoint ma poprawny IP w subnecie
az network private-endpoint show \
--resource-group rg-data \
--name pe-sqlserver \
--query '{ip:customDnsConfigs[0].ipAddresses[0], fqdn:customDnsConfigs[0].fqdn, subnet:subnet.id}' -o json
Po zastosowaniu zmian App Service może wymagać restartu, aby odświeżyć cache DNS:
# Restart App Service
az webapp restart \
--resource-group rg-app \
--name my-app-service
Walidacja
# 1. W Kudu console - sprawdź czy DNS rozwiązuje do prywatnego IP
# https://<app-name>.scm.azurewebsites.net > Debug console > CMD
nameresolver mysqlserver.database.windows.net
# Expected: Addresses: 10.0.1.5 (prywatny IP z subnetu Private Endpoint)
# 2. W Kudu PowerShell - test rozwiązywania DNS
[System.Net.Dns]::GetHostAddresses("mysqlserver.database.windows.net") | Select IPAddressToString
# Expected: 10.0.1.5
# 3. Test połączenia TCP do Private Endpoint
tcpping mysqlserver.database.windows.net:1433
# Expected: Connected to mysqlserver.database.windows.net:1433, time=Xms
# 4. Sprawdź z poziomu Azure CLI że konfiguracja jest kompletna
az webapp show \
--resource-group rg-app \
--name my-app-service \
--query '{routeAll:vnetRouteAllEnabled, vnet:virtualNetworkSubnetId}' -o json
# Expected: routeAll=true, vnet wskazuje na subnet integracyjny
az webapp config appsettings list \
--resource-group rg-app \
--name my-app-service \
--query "[?name=='WEBSITE_DNS_SERVER'].{name:name, value:value}" -o table
# Expected: WEBSITE_DNS_SERVER = 168.63.129.16
Jeśli nameresolver zwraca prywatny IP (10.x.x.x) i tcpping łączy się pomyślnie - problem jest rozwiązany. Zweryfikuj w Application Insights, że dependency calls do Azure SQL/Storage nie generują już błędów.
Private Endpoints nie działają pomimo VNet Integration?
Umów bezpłatną 30-minutową rozmowę. Przejrzymy konfigurację DNS, Private Link i VNet Integration, aby ruch szedł bezpiecznie przez prywatną sieć Azure.