如何使启动任务幂等?

8
我有一些批处理启动任务。特别地,我调用IIS的appcmd.exe来配置IIS。Azure中的启动任务应该是幂等的(即能够重复运行并产生相同的结果),以防角色出现某些原因而重新启动。不幸的是,我的许多IIS配置命令第二次运行时将失败,例如因为它们在第一次删除了一个配置节点,然后在后续运行中不存在该节点。
我的问题是,如何使这些启动任务成为幂等的?是否有一种方法可以使appcmd.exe不抛出错误?是否有一种方法可以使shell捕获错误?是否有一种方法可以使Azure框架忽略错误?
以下是我的启动任务示例。所有内容都包含在一个命令文件configiis.cmd中。
@REM Enable IIS compression for application/json MIME type
%windir%\system32\inetsrv\appcmd.exe set config -section:system.webServer/httpCompression /+"dynamicTypes.[mimeType='application/json',enabled='True']" /commit:apphost
%windir%\system32\inetsrv\appcmd.exe set config -section:system.webServer/httpCompression /+"dynamicTypes.[mimeType='application/json; charset=utf-8',enabled='True']" /commit:apphost

@REM Set IIS to automatically start AppPools
%windir%\system32\inetsrv\appcmd.exe set config -section:applicationPools -applicationPoolDefaults.startMode:AlwaysRunning /commit:apphost

@REM Set IIS to not shut down idle AppPools
%windir%\system32\inetsrv\appcmd set config -section:applicationPools -applicationPoolDefaults.processModel.idleTimeout:00:00:00 /commit:apphost

@REM But don't automatically start the AppPools that we don't use, and do shut them down when idle
%windir%\system32\inetsrv\appcmd.exe set config  -section:system.applicationHost/applicationPools "/[name='Classic .NET AppPool'].startMode:OnDemand" "/[name='Classic .NET AppPool'].autoStart:False" "/[name='Classic .NET AppPool'].processModel.idleTimeout:00:01:00" /commit:apphost
%windir%\system32\inetsrv\appcmd.exe set config  -section:system.applicationHost/applicationPools "/[name='ASP.NET v4.0'].startMode:OnDemand" "/[name='ASP.NET v4.0'].autoStart:False" "/[name='ASP.NET v4.0'].processModel.idleTimeout:00:01:00" /commit:apphost
%windir%\system32\inetsrv\appcmd.exe set config  -section:system.applicationHost/applicationPools "/[name='ASP.NET v4.0 Classic'].startMode:OnDemand" "/[name='ASP.NET v4.0 Classic'].autoStart:False" "/[name='ASP.NET v4.0 Classic'].processModel.idleTimeout:00:01:00" /commit:apphost


@REM remove IIS response headers
%windir%\system32\inetsrv\appcmd.exe set config /section:httpProtocol /-customHeaders.[name='X-Powered-By']

我非常确定,那些应该停止未使用的AppPools自动启动的代码行将不起作用。你需要使用Clr2ClassicAppPool等名称,而不是使用“Classic .NET AppPool”等名称。 - Paul Hiles
实际上那些名称很好用,但需要稍微不同地引用。我已经更新了上面的代码,以防以后有人查看它。 - Brian Reischl
5个回答

4
除了 @Syntaxc4 的回答之外:在本地考虑使用面包屑 (文件)。在您的脚本中,检查已知文件 (由您创建) 是否存在。如果不存在,则通过启动脚本,同时创建一个 breadcrumb 文件。下次虚拟机启动时,它将再次检查 breadcrumb 文件是否存在,如果存在,则退出 cmd 文件。如果 breadcrumb 文件消失,这通常意味着您的虚拟机已经重新组合到其他位置(可能是新实例或不同硬件上的重生实例),需要进行 IIS 配置。

听起来是个好主意。有没有想法在.cmd脚本中实现它?我相信最终我可以弄清楚,但听起来你可能以前做过这样的事情。 - Brian Reischl
如果未来有人阅读此内容,我已在另一个答案中添加了实现此功能的代码。 - Brian Reischl

3

非常好。看起来这可能是“正确”的做法。可惜错误处理如此冗长。 - Brian Reischl
2
MSDN文章中似乎存在错误 - 因为“IF”命令的语法是:'IF [/I] string1 compare-op string2 command'。而DO关键字仅适用于“FOR”命令。因此,正确的命令应该是:'IF %ERRORLEVEL% EQU 183 VERIFY > NUL'。这个命令对我有效,而原始命令破坏了脚本,防止角色启动。 - Vertigo

3

在尝试删除配置设置之前,您需要检查该设置是否存在(添加条件逻辑)。可以通过以下方式实现:

'appcmd.exe list config -details'

捕获返回值将为您提供可与其进行比较的内容,无论是输出长度还是实际值。


2

根据David Makogon的建议,我在我的每个.cmd文件的顶部添加了以下内容。这似乎起到了作用。它将在执行脚本的相同目录中创建一个标志文件(David称之为面包屑文件),然后在后续运行中检查它。

@REM A file to flag that this script has already run
@REM because if we run it twice, it errors out and prevents the Azure role from starting properly
@REM %~n0 expands to the name of the currently executing file, without the extension
SET FLAGFILE=c:\%~n0-flag.txt

IF EXIST "%FLAGFILE%" (
  ECHO %FLAGFILE% exists, exiting startup script
  exit /B
) ELSE (
  date /t > %FLAGFILE%
)

你应该在标志文件名中也加入 %ComputerName%,这样会很有用! - kodybrown
为什么那会有用呢? - Brian Reischl

0

我强烈建议在list命令的末尾使用/config:* /xml。有关如何使iis幂等的更多信息,请查看:https://github.com/opscode-cookbooks/iis

Chef是多个配置管理平台之一,我只建议查看它的代码(用ruby编写),通过列出当前设置并将其与请求更改的设置进行比较来实现幂等。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接