macOS上.NET无法解析Azure CNAME(AI AgentsClient),A记录及系统工具正常
macOS上.NET无法解析Azure CNAME域名的问题排查
环境
- MacBook Air(Apple Silicon,arm64)
- macOS 15.4.1(Sonoma)
- .NET SDK 9.0.203、.NET Runtime 9.0.4(osx-arm64)
- 无VPN、防火墙或安全软件干扰
- DNS设置为Google(8.8.8.8)
- 使用基于
DefaultAzureCredential的Azure SDK客户端代码
问题描述
运行连接Azure AI Agents服务API端点eastus.api.azureml.ms的.NET应用时,出现DNS解析失败:.NET无法解析该CNAME域名,但终端通过dig工具可正常解析完整CNAME链,且.NET能正常解析其他域名(如www.google.com)及该CNAME对应的最终A记录(vienna-eastus-ip-ingress-nginx-02.eastus.cloudapp.azure.com)。
示例输出
[DEBUG] .NET DNS test for eastus.api.azureml.ms [DEBUG] .NET DNS resolution failed: nodename nor servname provided, or not known [DEBUG] .NET DNS test for vienna-eastus-ip-ingress-nginx-02.eastus.cloudapp.azure.com [DEBUG] .NET DNS resolved: 48.211.42.162 [DEBUG] .NET DNS test for www.google.com [DEBUG] .NET DNS resolved: 142.250.72.100
Azure SDK错误信息
❌ Error occurred: Retry failed after 2 tries. Retry settings can be adjusted in ClientOptions.Retry or by configuring a custom retry policy in ClientOptions.RetryPolicy. (nodename nor servname provided, or not known (eastus.api.azureml.ms:443)) [DEBUG] Exception Type: System.AggregateException [DEBUG] Inner Exception: nodename nor servname provided, or not known (eastus.api.azureml.ms:443) [DEBUG] Inner Exception Type: Azure.RequestFailedException
已尝试的措施
- 确认系统DNS正常(
dig可解析完整CNAME链) - .NET可正常解析
www.google.com及该CNAME对应的最终A记录 - 禁用所有VPN、防火墙及安全软件
- 切换至Google DNS(8.8.8.8)
- 检查
/etc/hosts和/etc/resolv.conf配置 - 使用最新arm64版本的.NET SDK和Runtime
- 未设置global.json或自定义环境变量
- 尝试
Dns.GetHostAddresses和Dns.GetHostEntry两种解析方法 - 完成上述操作后重启设备
疑问
为何macOS上的.NET无法解析该Azure CNAME域名?还有哪些可行的排查或修复措施能让.NET正常解析此Azure端点?
代码片段
// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. #nullable disable using Azure.Identity; using Azure.Core; namespace Azure.AI.Projects; public class Sample_Agent { static async Task Main() { Console.WriteLine($"Application started at: {DateTime.Now:yyyy-MM-dd HH:mm:ss}"); Console.WriteLine("Initializing agent configuration..."); // Use explicit connection string as requested var connectionString = "<project connection string I scrubbed, format: <HostName>;<AzureSubscriptionId>;<ResourceGroup>;<ProjectName>>"; Console.WriteLine($"[DEBUG] Using connection string: {connectionString.Substring(0, Math.Min(connectionString.Length, 20))}... (masked)"); Console.WriteLine($"[DEBUG] Machine Name: {Environment.MachineName}"); Console.WriteLine($"[DEBUG] OS Version: {Environment.OSVersion}"); Console.WriteLine($"[DEBUG] Current Directory: {Environment.CurrentDirectory}"); try { var host = "eastus.api.azureml.ms"; Console.WriteLine($"[DEBUG] .NET DNS test for {host}"); var entry = System.Net.Dns.GetHostEntry(host); Console.WriteLine($"[DEBUG] .NET DNS resolved: {entry}"); } catch (Exception ex) { Console.WriteLine($"[DEBUG] .NET DNS resolution failed: {ex.Message}"); } var options = new AIProjectClientOptions(); options.Retry.MaxRetries = 1; AgentsClient client = new AgentsClient(connectionString, new DefaultAzureCredential(), options); try { Console.WriteLine("[DEBUG] Attempting to retrieve agent by ID"); Response<Agent> agentResponse = await client.GetAgentAsync("<agent id I scrubbed>"); Agent agent = agentResponse.Value; Console.WriteLine($"✓ Agent retrieved successfully (ID: {agent.Id})"); // Verify agent in list Console.WriteLine("\nVerifying agent in list..."); Response<PageableList<Agent>> agentListResponse = await client.GetAgentsAsync(); Console.WriteLine($"✓ Found {agentListResponse.Value.Data.Count} agent(s) in total"); // Step 2: Create a thread Console.WriteLine("\n=== Creating Thread ==="); Response<AgentThread> threadResponse = await client.CreateThreadAsync(); AgentThread thread = threadResponse.Value; Console.WriteLine($"✓ Thread created successfully (ID: {thread.Id})"); // Step 3: Add a message Console.WriteLine("\n=== Adding Message to Thread ==="); string userMessage = "I need to solve the equation `3x + 11 = 14`. Can you help me?"; Console.WriteLine($"User message: {userMessage}"); Response<ThreadMessage> messageResponse = await client.CreateMessageAsync( thread.Id, MessageRole.User, userMessage); ThreadMessage message = messageResponse.Value; Console.WriteLine($"✓ Message added successfully (ID: {message.Id})"); // Verify message in thread Console.WriteLine("\nVerifying message in thread..."); Response<PageableList<ThreadMessage>> messagesListResponse = await client.GetMessagesAsync(thread.Id); Console.WriteLine($"✓ Found {messagesListResponse.Value.Data.Count} message(s) in thread"); // Step 4: Run the agent Console.WriteLine("\n=== Starting Agent Run ==="); Response<ThreadRun> runResponse = await client.CreateRunAsync( thread.Id, agent.Id, additionalInstructions: ""); ThreadRun run = runResponse.Value; Console.WriteLine($"✓ Run initiated (ID: {run.Id})"); // Monitor run progress int pollCount = 0; Console.WriteLine("\nMonitoring run progress:"); do { pollCount++; Console.WriteLine($"[{DateTime.Now:HH:mm:ss}] Status: {runResponse.Value.Status} (Poll #{pollCount})"); await Task.Delay(TimeSpan.FromMilliseconds(500)); runResponse = await client.GetRunAsync(thread.Id, runResponse.Value.Id); } while (runResponse.Value.Status == RunStatus.Queued || runResponse.Value.Status == RunStatus.InProgress); Console.WriteLine($"\n✓ Run completed with status: {runResponse.Value.Status}"); if (runResponse.Value.LastError != null) { Console.WriteLine($"⚠ Last error: {runResponse.Value.LastError.Message}"); } // Get final messages Console.WriteLine("\n=== Final Message History ==="); Response<PageableList<ThreadMessage>> afterRunMessagesResponse = await client.GetMessagesAsync(thread.Id); IReadOnlyList<ThreadMessage> messages = afterRunMessagesResponse.Value.Data; Console.WriteLine($"Retrieved {messages.Count} total messages\n"); Console.WriteLine("Message Timeline:"); Console.WriteLine("----------------"); foreach (ThreadMessage threadMessage in messages) { Console.WriteLine($"\n[{threadMessage.CreatedAt:yyyy-MM-dd HH:mm:ss}]"); Console.WriteLine($"Role: {threadMessage.Role}"); Console.WriteLine("Content:"); foreach (MessageContent contentItem in threadMessage.ContentItems) { if (contentItem is MessageTextContent textItem) { Console.WriteLine($" {textItem.Text}"); } else if (contentItem is MessageImageFileContent imageFileItem) { Console.WriteLine($" <image from ID: {imageFileItem.FileId}>"); } } Console.WriteLine("----------------"); } } catch (Exception ex) { Console.WriteLine($"\n❌ Error occurred: {ex.Message}"); Console.WriteLine($"[DEBUG] Exception Type: {ex.GetType().FullName}"); Console.WriteLine($"[DEBUG] Stack trace: {ex.StackTrace}"); if (ex.InnerException != null) { Console.WriteLine($"[DEBUG] Inner Exception: {ex.InnerException.Message}"); Console.WriteLine($"[DEBUG] Inner Exception Type: {ex.InnerException.GetType().FullName}"); Console.WriteLine($"[DEBUG] Inner Exception Stack trace: {ex.InnerException.StackTrace}"); } if (ex is Azure.RequestFailedException reqEx) { Console.WriteLine($"[DEBUG] Azure RequestFailedException Status: {reqEx.Status}"); Console.WriteLine($"[DEBUG] Azure RequestFailedException ErrorCode: {reqEx.ErrorCode}"); } } Console.WriteLine($"\nApplication completed at: {DateTime.Now:yyyy-MM-dd HH:mm:ss}"); } }
排查与修复措施
可能原因
- .NET DNS解析机制差异:.NET在macOS上可能使用与系统
dig不同的解析路径,比如直接调用系统API而非读取resolv.conf,对CNAME链的兼容性处理存在差异。 - DNS缓存冲突:系统或.NET自身的DNS缓存存在过期/错误记录,导致
dig能获取新结果,但.NET仍读取旧缓存。 - IPv6优先级问题:该域名的IPv6记录存在但不可达,.NET优先尝试IPv6解析失败,而
dig默认返回IPv4结果。
具体修复步骤
强制.NET使用IPv4解析
在代码中指定仅解析IPv4:var addresses = System.Net.Dns.GetHostAddresses(host, System.Net.Sockets.AddressFamily.InterNetwork);或通过环境变量全局禁用IPv6后运行应用:
export DOTNET_SYSTEM_NET_DNS_DISABLE_IPV6=true dotnet run清除.NET DNS缓存
- 直接重启应用(简单有效)
- 调试场景下可通过反射清理内部缓存:
typeof(System.Net.Dns).GetMethod("FlushCache", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static).Invoke(null, null);
手动指定DNS服务器
引用第三方DnsClientNuGet包,配置.NET使用指定DNS服务器:var dnsClient = new DnsClient.DnsClient("8.8.8.8"); var result = await dnsClient.QueryAsync(host, DnsClient.QueryType.A);添加临时hosts映射
在/etc/hosts中添加域名与A记录的映射,绕过DNS解析:48.211.42.162 eastus.api.azureml.ms添加后执行
sudo dscacheutil -flushcache刷新系统缓存。检查CNAME链完整性
使用dig +trace eastus.api.azureml.ms查看完整解析链,确认所有中间CNAME记录均有效,若存在无效记录需联系Azure支持修正。
内容的提问来源于stack exchange,提问作者Belkaz
相关产品推荐
相关产品推荐

