ARTICLE DETAIL

资讯详情

深耕商务建站与企业官网运营的一线实战洞察。

Windows Server 2019 定时重启:PowerShell计划任务实现每4小时重启

Windows Server 2019 定时重启:PowerShell计划任务实现每4小时重启 如果你也在维护一台 Windows Server 2019 Datacenter接到“每4小时自动重启一次从0点开始”这种需求时第一反应多半是用 PowerShell 写个命令直接安排上省得手工去点任务计划程序。这个需求本身不难但实际落地有几个容易被忽略的坑尤其是“从0点开始”这个限定条件以及 Server 2019 在任务计划、系统账户、强制关机这几个环节上的特殊表现。这篇文章我会直接给出可复制的命令也会解释为什么不能简单写一个while ($true)循环就完事。适合刚接手服务器运维的同事同样适合已经写过简单重启脚本、但想确认自己的方案会不会埋雷的人。看完之后你不仅能写出命令还能理解这条命令背后每一个参数的意义以及遇到“任务没跑”“重启卡住”“时间不对”时去哪里排查。1. 先搞清楚需求从0点开始的4小时周期到底怎么算1.1 这类重启需求的典型场景在正经的生产环境里没人会无缘无故让服务器一天重启6次。会出现这种需求通常是下面几种情况。第一种是测试环境。比如你搭了一台临时的验证服务器白天可能有人连上去跑测试、装软件、改配置为了确保每个人拿到的都是一台“干净”的机器干脆每隔4小时强制重启一次让系统回到初始状态。这种情况下重启不是为了维护而是为了“重置”。第二种是无人值守的采集节点、渲染节点或者边缘计算设备。这类机器经常长时间跑批处理任务内存会慢慢涨句柄可能泄漏进程也可能僵死。定期重启是最简单粗暴也是最有效的兜底手段。比如我之前接触过一台做视频转码的机器跑三天之后可用内存就从32GB掉到不到8GB后来就是靠定时重启撑过了项目周期。第三种是临时规避某些系统层面的问题。比如某个服务或者驱动存在内存泄漏补丁还没出来只能靠定时重启“续命”。这种情况虽然不体面但在实际运维里确实存在。顺便说一句如果是核心生产业务机动手之前一定要评估重启对业务的影响最好是先申请变更窗口再操作。自动重启一旦配好它是不会管你是不是正在跑月结的。1.2 “每4小时一次 从0点开始”是什么意思不要把“每4小时”理解成“服务器开机之后开始计时4小时”也不要理解成“每运行4小时就重启”。这句话最关键的是“从0点开始”所以它实际上是固定在每天的整点倍数上重启0:004:008:0012:0016:0020:00也就是说一天 24 小时一共重启 6 次。到了第二天 0 点重新开始新一轮。这个区分很重要因为它直接决定了方案的写法。如果是“开机后每4小时重启”你会倾向于写一个 PowerShell 脚本记录开机时间然后 Sleep 4小时再重启重启后再重新计时。但如果是“从0点开始”最合适的方式是做一个每天0点触发、每4小时重复一次的计划任务。这样一来重启时间的计算就完全交给 Windows 任务计划程序不需要脚本里去算“下一个4倍数小时”也不容易因为脚本进程被杀导致整个重启机制失效。2. 推荐方案用 PowerShell 命令创建计划任务2.1 可直接复制的百分百 PowerShell 方案既然问题问的是“PowerShell 命令怎么写”那就先给一套完全跑在 PowerShell 里的创建方案。下面这段命令以管理员身份打开 PowerShell 执行即可$action New-ScheduledTaskAction -Execute shutdown.exe -Argument /r /f /t 0 $trigger New-ScheduledTaskTrigger -Daily -At 00:00 $trigger.Repetition (New-ScheduledTaskTrigger -Once -At 00:00 -RepetitionInterval (New-TimeSpan -Hours 4) -RepetitionDuration (New-TimeSpan -Days 1)).Repetition $settings New-ScheduledTaskSettingsSet -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries -StartWhenAvailable -ExecutionTimeLimit (New-TimeSpan -Hours 1) Register-ScheduledTask -TaskName RebootEvery4Hours -Action $action -Trigger $trigger -Settings $settings -RunLevel Highest -User SYSTEM -Force拆开解释一下每一段在干什么。New-ScheduledTaskAction是指定任务要执行的程序。这里执行的是shutdown.exe参数是/r /f /t 0。含义是重启、强制关闭正在运行的应用程序、0秒后执行。后面专门有一节讲为什么用shutdown.exe而不是大家更熟悉的Restart-Computer这里先记住用shutdown.exe更稳。New-ScheduledTaskTrigger -Daily -At 00:00创建了一个基础触发器每天0点启动。但只有这个触发器的话一天只会执行一次。所以下面那行$trigger.Repetition ...是给这个触发器附加一个重复规则从0点开始每4小时重复一次持续1天。这里的核心参数有两个-RepetitionInterval (New-TimeSpan -Hours 4)重复间隔4小时。-RepetitionDuration (New-TimeSpan -Days 1)重复持续时间24小时。组合起来就会生成0:00、4:00、8:00、12:00、16:00、20:00这6个触发点。到了第二天-Daily -At 00:00这个主触发器又会重新启动一轮重复如此循环。为什么要把重复持续时长设为1天因为如果不设置RepetitionDuration或者设置得太短任务可能只在当天重复一两次就停了达不到“全天每4小时一次”的效果。这里设置成24小时正好覆盖完整的一天。New-ScheduledTaskSettingsSet是任务设置。-AllowStartIfOnBatteries和-DontStopIfGoingOnBatteries对服务器来说基本无感但保留这两个参数可以避免任务计划程序因为电源条件把任务拦下来。实际上更重要是-StartWhenAvailable它的意思是如果系统因为正在重启等原因错过了计划的触发时间等系统可用后立刻补执行一次。对于定时重启类任务这个参数很关键不然一旦错过某个点可能这一天后续的重启就都乱了。最后的Register-ScheduledTask把上面这些对象注册成一个名为RebootEvery4Hours的计划任务。-User SYSTEM表示以系统账户运行不依赖任何用户是否登录。-RunLevel Highest表示以最高权限运行。如果不加这两个参数任务默认可能在“只在用户登录时运行”的模式下一旦没人登录它就不执行而 Server 2019 很多场景下是没人登录的。任务创建好之后可以这样验证Get-ScheduledTask -TaskName RebootEvery4Hours | Select-Object TaskName, State Get-ScheduledTaskInfo -TaskName RebootEvery4HoursGet-ScheduledTaskInfo能看到任务的LastRunTime上次运行时间、LastTaskResult上次运行结果和NextRunTime下次运行时间。如果NextRunTime显示的是今天或明天的0:00:00说明触发逻辑已经挂上了。想手动测试一次任务是否正常可以执行Start-ScheduledTask -TaskName RebootEvery4Hours如果只是想临时测试又不想真的重启机器可以把shutdown.exe的参数改成/a但这只能取消一次已经发起的关机/重启而且只对当前会话有效。对计划任务本身不太建议这样玩容易把任务搞混乱。更好的测试办法是先把任务里的命令改成一个写日志的脚本确认计划任务触发正常后再改回shutdown.exe。这个思路在后面第5章还会提到。2.2 更简洁的 schtasks 命令如果你觉得上面那段 PowerShell 太长也可以用schtasks这个命令行工具。它本质上不是 PowerShell 专属命令但在 PowerShell 窗口里可以直接运行。写法短很多schtasks /Create /TN RebootEvery4Hours /TR shutdown.exe /r /f /t 0 /SC DAILY /ST 00:00 /RI 240 /DU 24:00 /F /RU SYSTEM /RL HIGHEST参数逐个说/SC DAILY按天触发这是主触发频率。/ST 00:00开始时间也就是0点。/RI 240重复间隔单位是分钟。240分钟 4小时。/DU 24:00重复持续时间单位是“小时:分钟”24:00 表示持续24小时也就是一天内按4小时间隔循环。/RU SYSTEM以SYSTEM账户运行。/RL HIGHEST以最高权限运行。/F如果已存在同名任务直接覆盖不弹确认。这里有个容易踩的坑/RI必须和/SC DAILY一起用而且最好同时显式设置/DU。我见过有人只写/SC DAILY /ST 00:00 /RI 240结果任务只在当天重复了一两次就再也不跑了因为默认的重复持续时间不够覆盖全天。所以24:00这个参数不是可选项我建议每次都带上。如果想查看任务当前的触发信息可以执行schtasks /Query /TN RebootEvery4Hours /V /FO LIST输出里会列出“开始时间”“重复: 每隔 240 分钟”“持续: 24 小时 0 分”这样的信息。看清楚这几行基本就能确认任务配置没问题。删除任务用schtasks /Delete /TN RebootEvery4Hours /F2.3 两种方式怎么选Register-ScheduledTask的好处是它本身就是 PowerShell对于需要在自动化脚本里动态创建任务的场景更友好参数也更语义化缺点是代码块长新手容易在某一行写错。schtasks的好处是极简一行就能搞定而且在老系统、新系统上行为几乎一致缺点是参数语义不够直观比如/RI 240这种纯分钟数时间一长再回来看很容易忘。我自己的习惯是临时手动配一台机器用schtasks要写进自动化运维脚本里用Register-ScheduledTask。两条路都能达到同样的效果选自己顺手的就行。3. 为什么用 shutdown.exe 而不是 Restart-Computer3.1 一次真实的踩坑记录这个章节很有必要单独拿出来讲因为我第一次给 Server 2019 配这种循环重启任务时用的不是shutdown.exe而是 PowerShell 的Restart-Computer -Force。当时配完计划任务后手动触发了一次结果机器卡在“正在关闭”界面很久最后只能去控制台强制断电重启。后来排查才发现Restart-Computer在计划任务的 SYSTEM 账户环境下有可能会出现等待交互会话、等待远程 PowerShell 会话释放的情况最终导致进程挂起系统无法干净地进入重启流程。shutdown.exe /r /f /t 0就不一样。它是 Windows 底层的关机入口由系统进程直接处理不依赖当前的 PowerShell 会话状态也不太会因为“当前的会话没有可交互桌面”而卡住。配合/f强制关闭应用程序基本能保证机器稳定地重启。所以在计划任务里需要重启系统时我现在的标准做法是动作里写shutdown.exe参数写成/r /f /t 0不用Restart-Computer。虽然Restart-Computer在普通管理员会话里很好用但在计划任务这个特殊场景下用系统底层的shutdown.exe更稳妥。3.2 shutdown.exe 参数速查shutdown.exe的参数不算多下面这几个在运维里最常用参数含义说明/r重启计算机不加这个就是关机/s关机重启场景用不到/f强制关闭正在运行的程序不加的话可能因为程序弹窗拒绝关机而卡住/t xxx多少秒后执行/t 0就是立即执行/c 注释添加注释可以写一句提示语方便排查/a取消一次已发起的关机/重启只在倒计时期间有效如果希望给用户留一点缓冲时间可以把参数改成/r /f /t 60表示60秒后重启。这时系统会弹出一个倒计时提示用户还能保存一下手头的工作。对于生产机器给用户留一点时间是个人性化的选择但如果你的服务器根本没人登录或者里面跑的是无人值守任务直接用/t 0就好不要拖泥带水。我个人还习惯在参数里加一段注释比如shutdown.exe /r /f /t 60 /c Scheduled maintenance reboot. Save your work now.这样万一有人在上面跑任务弹出的提示会告诉他这是一次计划内的自动重启不是系统故障。4. 如果你坚持要用纯 PowerShell 脚本实现4.1 一个能跑通的脚本写法前面说过最稳的方案是计划任务。但有些场景下确实更适合用脚本比如你希望每次重启之前写入一段日志或者希望重启动作本身带一点附加逻辑比如先清理临时文件再重启。这里给一个纯 PowerShell 脚本的写法。它的逻辑是从当前时间开始计算下一个4倍数小时的整点时刻然后休眠到那个时刻后执行重启。$now Get-Date $currentHour $now.Hour # 如果当前正好落在 0/4/8/12/16/20 的整点上并且当前分钟小于等于1直接重启 if (($currentHour % 4) -eq 0 -and $now.Minute -le 1) { shutdown.exe /r /f /t 0 exit } # 计算下一个 4 倍数小时 $nextHour (([math]::Floor($currentHour / 4) 1) * 4) % 24 $target $now.Date.AddHours($nextHour) # 如果计算出来的目标时间已经过去了说明要等到明天 if ($target -le $now) { $target $target.AddDays(1) } # 休眠到目标时间 Start-Sleep -Seconds (($target - $now).TotalSeconds) # 执行重启 shutdown.exe /r /f /t 0这里面最关键的是这一行$nextHour (([math]::Floor($currentHour / 4) 1) * 4) % 24$currentHour / 4先算出当前小时是第几个4小时段Floor向下取整再加1然后乘4就是下一个4倍数小时。最后% 24是为了防止跨天时算出24这种非法值。比如当前时间是23点Floor(23/4)1 66*424取模后变成0也就是第二天0点。这个脚本的局限也很明显它假设每次重启之后脚本会被重新拉起来继续跑。如果没人拉它重启一次之后就结束了。所以它必须配合一个“系统启动时触发”的计划任务否则只适合“当前会话手动跑一次让它自己算下次重启时间”的临时场景。另一个问题是如果脚本恰好在某个整点过后的几秒内启动比如0:00:30算出来的下一个4倍数小时是4:00那就会漏掉0:00这次重启。我在脚本里加了“当前分钟小于等于1”的判断就是为了尽量兜住这种边界情况。但如果你要求每次都必须严格准确还是用第2章的计划任务方案更省心。4.2 把脚本注册成“启动时运行”的计划任务如果你确实需要上面这种脚本逻辑可以把它保存为C:\Scripts\RebootEvery4Hours.ps1然后注册成开机启动任务$action New-ScheduledTaskAction -Execute powershell.exe -Argument -NoProfile -ExecutionPolicy Bypass -File C:\Scripts\RebootEvery4Hours.ps1 $trigger New-ScheduledTaskTrigger -AtStartup $settings New-ScheduledTaskSettingsSet -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries -StartWhenAvailable Register-ScheduledTask -TaskName RunRebootScriptAtStartup -Action $action -Trigger $trigger -Settings $settings -RunLevel Highest -User SYSTEM -Force注意这里用的是powershell.exe不是pwsh.exe。因为 Server 2019 自带的 Windows PowerShell 5.1 已经足够跑这个脚本没必要依赖 PowerShell 7 的安装路径。-ExecutionPolicy Bypass是为了防止脚本执行策略拦截。这个方案和直接建“0点触发4小时重复”的任务计划相比优点是你可以在重启前后加日志、做清理动作缺点是重启时机依赖脚本休眠计算一旦脚本进程被任务计划程序因为超时杀掉或者脚本本身抛异常退出后续的重启就全断了。所以如果你没有附加逻辑的需求我仍然建议优先用第2章的纯计划任务方案。5. 常见问题与排查技巧实录5.1 任务创建后不触发或者不重复任务创建后第一时间检查NextRunTime是否正常。如果发现任务状态是“已禁用”或者触发器没有生效先用下面这条命令看详细信息Get-ScheduledTaskInfo -TaskName RebootEvery4Hours | Format-List LastRunTime, LastTaskResult, NextRunTime如果NextRunTime是空说明触发器配置有问题大概率是重复持续时长没设置好。这时候建议直接用schtasks /Create重新创建任务并且严格带上/DU 24:00。还有一种情况是任务计划程序把任务标记为“错过开始时间不启动”。在正常配置下由于服务器可能正好在某个触发点处于重启过程中任务计划程序可能会错过触发。对策就是在Settings里打开-StartWhenAvailable也就是“如果错过了计划开始时间尽快启动任务”。用schtasks命令创建时可以在最后追加/STARTWHENAVAILABLE。5.2 到点不重启权限和账户问题如果任务计划程序显示“上次结果0x41301”或者“已停止”而机器没有重启常见原因就是账户权限不足或者任务没有以最高权限运行。检查一下创建任务时是否带了/RU SYSTEM和/RL HIGHEST。SYSTEM 账户是 Windows 内置的超管账户用它来运行关机命令不会受到“用户没有关机权限”之类的限制。如果你图省事用当前管理员账号创建任务并且选择了“只在用户登录时运行”那当服务器没人登录时这个任务根本不会触发。在任务计划程序 GUI 里检查时注意看任务属性的“常规”选项卡左下角的“不管用户是否登录都要运行”必须勾上并且勾选“使用最高权限运行”。命令行创建的/RU SYSTEM就是等效的操作。5.3 重启会卡在“正在关机”这个坑我前面提过。如果你在创建任务时写的是Restart-Computer -Force然后在计划任务里以 SYSTEM 身份运行很容易遇到“正在关机”界面长时间不消失的情况。赶紧把动作改成shutdown.exe /r /f /t 0。如果已经卡住了只能手动强行断电重启一次然后修改任务配置。另外即使用了shutdown.exe如果系统里有某些程序在关机时执行了自定义的阻止逻辑也可能导致关机慢。这时候可以把/t从0改成比如说30再配合计划任务的“如果运行时间超过指定时长则停止”选项效果会好一些。不过对于纯无人值守机器/t 0依然是最省心的。5.4 和 Windows Update 自动重启撞车Windows Server 2019 默认开启了自动更新更新安装完成后可能会自己发起一次重启。如果你的任务恰好也在同一个时间段触发两台“重启引擎”同时发力容易导致业务恢复时间不可控。我的处理方法是把 Windows Update 的活动时间设为业务低峰期并且让更新引发的重启尽量避开整点重启计划。命令如下Set-ItemProperty -Path HKLM:\SOFTWARE\Microsoft\WindowsUpdate\UX\Settings -Name ActiveHoursStart -Value 2 Set-ItemProperty -Path HKLM:\SOFTWARE\Microsoft\WindowsUpdate\UX\Settings -Name ActiveHoursEnd -Value 6把活跃时段设置成凌晨2点到6点表示更新尽量避开这个时间段之外的重启操作。但这只是一个辅助手段不能完全保证避免撞车。更稳妥的方法是直接把更新策略配置成“下载后自动安装但不自动重启”这个涉及组策略这里不展开你按自己环境的安全规范来就行。5.5 时间边界和时区的坑最后分享一个我真实遇到过的边界问题任务要求“从0点开始”但服务器时区如果被设置成 UTC那你看到的0点和业务上理解的“北京时间0点”完全不是一回事。所以配置之前先确认服务器时区Get-TimeZone如果显示的是(UTC) Coordinated Universal Time而你的业务希望按照北京时间来调度可以改成中国标准时间Set-TimeZone -Id China Standard Time这个操作本身很简单但很多人容易忽略。服务器时区错误会导致所有定时任务的触发点整体偏移排查起来非常费劲。另外个别服务器如果启用了 CMOS 时钟使用 UTC 时间的注册表设置也会影响系统对本地时间的判断。遇到“所有计划任务都差8小时”这类现象时优先查时区。5.6 验证和回滚的小技巧定时重启这种操作最怕的就是“配完之后等半天才发现根本没生效”。所以我建议在正式启用之前先做一个不影响业务的验证。最简单的方法创建一个临时计划任务命令改成写日志的 PowerShell 脚本触发时间和正式任务完全一致。跑一天看日志里的时间点是否和预期一样。确认无误后再注册真正的shutdown.exe任务。如果你已经配好了正式任务想手动触发一次观察流程可以先在任务动作里把shutdown.exe /r /f /t 0改成shutdown.exe /r /f /t 120然后手动运行任务等2分钟确认机器进入重启流程。确认没问题后再把参数改回/t 0。我个人在实际运维中的体会是定时重启这种自动化操作宁可多花半小时做验证也不要图快直接上生产。一旦重启机制本身出了问题比如无限循环重启、时间点错乱你连远程桌面都进不去最后只能去机房或者 IPMI 控制台处理那才是真的头疼。上面这套命令我在 Server 2019 Datacenter 上用了很长时间几乎没有出过问题但我依然会建议你在自己的环境里先试跑一次确认输出符合预期后再交给自动化平台托管。
返回列表
PREV
查看更多资讯
NEXT
返回资讯列表