DNS 更新超时时的事件 ID 4016 和 4004

本文可帮助解决以下问题:从轻型目录访问协议(LDAP)到 Active Directory(AD)的 DNS 更新超时时,在域名系统(DNS)中记录事件 ID 4016 和 4004。

在域控制器(Windows Server 2012 R2 或更高版本)上托管的 AD 集成 DNS 区域中,DNS 无法枚举区域或间歇性地无法创建或写入记录。 此外,事件 ID 4016 和 4004 记录在 DNS 事件日志中:

  • 事件 ID 4016

      LogName:       DNS Server
      Source:        Microsoft-Windows-DNS-Server-Service
      Date:          <DateTime>
      Event ID:      4016
      Task Category:
      Level:         Error
      User:          S-1-5-18
      Computer:      Contoso.com
      Description:
      The DNS server timed out attempting an Active Directory service operation on DC=xx.x,DC=xxx.xx.in-addr.arpa,cn=MicrosoftDNS,DC=ForestDnsZones,DC=xxx,DC=com. Check Active Directory to see that it is functioning properly. The event data contains the error.
    
  • 事件 ID 4004

      LogName:       DNS Server
      Source:        Microsoft-Windows-DNS-Server-Service
      Date:          <DateTime>
      Event ID:      4004
      Task Category:
      Level:         Error
      User:          S-1-5-18
      Computer:      Contoso.com
      Description:
      The DNS server was unable to complete directory service enumeration of zone xx.xxx.xx.in-addr.arpa.  This DNS server is configured to use information obtained from Active Directory for this zone and is unable to load the zone without it. Check that the Active Directory is functioning properly and repeat enumeration of the zone. The extended error debug information (which may be empty) is "". The event data contains the error.
    

如果记录事件 ID 4016 和 4004,则 DNS 记录将更新在其他域控制器上,并在 ADSI Edit 中可见(adsiedit.msc)。 但是,在重新启动 DNS 服务器服务之前,无法写入记录,DNS 更新才会停止。 在此期间,可以在有问题的域控制器上使用 ADSI Edit 同时创建记录。 然后,这些记录将复制到所有域控制器,这意味着 AD 正常工作。 dns.exe进程的内存使用率较低。 同时,域控制器上的 CPU 和内存使用率也很低,但它们仍然无响应。

通过检查 DNS 审核日志、事件日志和数据包捕获,即使 DNS 查询快速响应,DNS 更新也会在服务器上停止。 此外,还会记录错误0x55。

重启 DNS 服务器服务并删除 Kerberos 票证缓存

若要解决此问题,请使用 Windows PowerShell 脚本删除 Kerberos 票证缓存后重启 DNS 服务器服务。 有关示例,请参阅以下脚本:

注意

由于默认值 $EventIntervalMinutes$NumberOfEvents 值可能不是最佳值,因此请相应地调整值。

    #NOTE: 
# The following two parameters should be adjusted according to your environment.
# The current values are only defaults and may not be optimal for you.

# How long to wait to ensure the 4016 event occurs consistently (that is, not one-offs)
[int]$EventIntervalMinutes=3

# Number of events within $EventIntervalMinutes to indicate we're in an error state
[int]$NumberOfEvents=10

# Monitor forever
while ($True)
{
	# Detect $NumberOfEvents for 4016 or 4011 occurred in the past $EventIntervalMinutes.
	$EntryType = @("Error","Warning")
	$Events = Get-EventLog -LogName 'DNS Server' -After ((get-date).AddMinutes(-$($EventIntervalMinutes))) -EntryType $EntryType

	[int]$NumEvents=0
	[int]$ErrorStateFound=0
	foreach($Event in $Events)
	{
		 if(($Event.InstanceId -eq "4016") -or ($Event.InstanceId -eq "4011"))
		 {
		   $NumEvents += 1;
		 }
	}

	if($NumEvents -ge $NumberOfEvents)
	{
		$ErrorStateFound=1
		"Detected DNS Event ID 4016 and/or 4011 within the past '$($EventIntervalMinutes)' minutes.  Take mitigation actions."  *>> C:\temp\dnsResetLog.txt

		# Stop DNS
		"`n`nStop DNS at $(Get-Date)" *>> C:\temp\dnsResetLog.txt
		Stop-Service DNS -Force *>> C:\temp\dnsResetLog.txt

		do { Start-Sleep 1 } until ((Get-Service DNS).Status -ne "Running")

		# Purge tickets 
		"`nPurge system tickets at $(Get-Date)" *>> C:\temp\dnsResetLog.txt
		klist purge -li 0x3e7 *>> C:\temp\dnsResetLog.txt

		# Start DNS
		"`nStart DNS at $(Get-Date)" *>> C:\temp\dnsResetLog.txt
		Start-Service DNS *>> C:\temp\dnsResetLog.txt

		# Record DNS Server process details to a file
		Get-Process dns | Select-Object Name, Id, StartTime | Format-List | Out-String *>> C:\temp\dnsResetLog.txt

		"`nEnd at $(Get-Date)" *>> C:\temp\dnsResetLog.txt
	}

	if ($ErrorStateFound)
	{
		# Don't loop again until waiting long enough to ensure no new events after restarting the service
		# Otherwise, we'll keep restarting!
		"`n`Sleeping for ($EventIntervalMinutes+1) minutes" *>> C:\temp\dnsResetLog.txt
		Start-Sleep -seconds (60*($EventIntervalMinutes+1))
		"`n`n Starting new monitoring cycle at $(Get-Date)" *>> C:\temp\dnsResetLog.txt
	}
	else{
		# Give a 1-minute pause before checking again
		Start-Sleep -seconds 60
	}
}