当我尝试在生产IIS服务器文件上使用FTP覆盖.NET Core应用程序DLL文件时,该文件已锁定,无法覆盖。
要部署新版本,我需要在IIS中停止应用程序以释放锁定,然后覆盖。
不停止应用程序就无法部署。
请参阅app_offline.htm: https: //docs.asp.net/zh/latest/hosting/aspnet-core-module.html#asp -net-core-module-app-offline-htm
如果最终需要执行PowerShell的方法,则可以使用IIS Admin cmdlet: https :
Stop-WebAppPool -Name $appPoolName
... deploy ...
Start-WebAppPool -Name $appPoolName
感谢您的解释@Tratcher 。
我不确定这是否在您的计划中,但是我认为这适用于以前的ASP.NET MVC?
您是否打算实施此功能?
@GuardRex我无法做到,因为它是共享的托管环境,并且无权停止AppPool
您能否使它与msdeploy.exe和Azure“兼容”? 如果我理解正确,则需要重新启动网站以防止文件锁定。 -enableRule:AppOffline
可以工作,但是整个网站都处于离线状态几分钟,这并不是一个很好的用户体验,特别是考虑到我们每天部署几次。
也许是@chuchuva ,但是所有魔术都是有代价的。 早期版本的ASP.NET进行了一些复杂的卷影复制,以解决文件锁定问题。
魔术与否,它运作良好。 迁移到ASP.NET核心后,我现在还是有点想念它。
同意@HarelM。 从自动部署到最终用户体验,我们都遇到了问题。 我们已经从每天使用旧的MVC进行大约10次部署,到每天晚上进行每日部署,已经接受了使用Core应用程序的人在脱机时会感到烦恼。 虽然不是秀场停止者,但它增加了采用Core的摩擦。
👍
不支持重叠回收是一种回归。
是否有计划重新使用此功能并将其放在路线图上? 如果每个人都希望支持零停机时间部署,则要求每个人部署.Net Core网站以手动实施分段时隙策略是非常不方便/不友好的。
拥有此功能将使许多人更轻松地过渡到.Net Core网站,并且可以更快地采用.Net Core网站。
我们也想知道。 将app_offline.htm放入应用程序目录不起作用。
在将许多站点移至AspNetCore之后,我才刚刚意识到这一“功能”。 我无法相信每次您要发布的网站离线几分钟都是可以接受的!
作为一个小团队的领导者,这对我来说已经够糟糕的了—那些正在大规模地进行持续集成的人会避开AspNetCore吗? 他们无法每小时将网站离线几分钟,以重新发布!
您正在部署使用FTP还是xcopy? 还是您正在使用webdeploy?
我正在通过webdeploy发布到IIS。
当前,我们正在通过仅删除web.config(有效地杀死应用程序)来解决此问题,但是从长远来看,这实际上不是可接受的解决方案。
理想情况下, @ DaleMckeown具有连续集成工作流,您将在负载均衡器后面拥有多个服务器。 然后,您将拉动一台服务器,对其进行更新,然后将其放回另一台服务器。 当然,这并非总是可能的(例如在我们的案例中),因此您必须忍受几分钟的停机时间。 在我们的情况下,该应用在30秒内即可备份,因此这并不是真正的问题。
@DaleMckeown
我正在通过webdeploy发布到IIS。
有一些选项可用于使使用webdeploy的部署工作良好(它支持重命名锁定的文件并自动使应用程序脱机)。 @shirhatti默认情况下是
@ajeckmans
当前,我们正在通过仅删除web.config(有效地杀死应用程序)来解决此问题,但是从长远来看,这实际上不是可接受的解决方案。
这是否意味着您正在进行xcopy部署?
有一些选项可用于使使用webdeploy的部署工作良好(它支持重命名锁定的文件并自动使应用程序脱机)。 @shirhatti默认情况下是
谢谢David-听起来比我目前在做的要好(手动在IIS中停止站点和应用程序池)。 您能否指出一些资源,以便我调查这些方法的含义?
@DaleMc掌握一些信息,直到找到真相的来源为止https://github.com/Microsoft/vsts-tasks/issues/5259#issuecomment -346202503
@davidfowl我们正在使用webdeploy部署到我们的测试服务器(顺便说一句,它永远不会给我们带来问题),然后我们使用robocopy将文件复制到我们的实时服务器中
@ajeckmans
当前,我们正在解决此问题,方法是先删除web.config
从部署中删除web.config时,IIS将为部署中的敏感文件提供服务。 攻击者可能只是日夜不停地请求文件,直到您的30s窗口打开。
@DaleMckeown
遵循@ajeckmans的建议,如果您想通过PS方法来解决问题,则可以编写脚本以一次性删除AppPools,以在整个Web场中进行部署。 有关如何完成此操作的示例,请参见https://github.com/guardrex/aspnetcore-iis-ps-publish ...,但仅将其作为实验示例而不是生产质量脚本。 我已经有一段时间没有使用它了,但是(理论上)它应该仍然有效。
@guardrex你是对的。 我们首先将app_offline.htm复制到dir,然后再删除web.config,在应用程序上复制,放回web.config并删除app_offline(全部带有脚本ofc)。 不幸的是,仅将app_offline文件放在网站目录中并不会破坏dll的锁定。 我们需要删除web.config,对于旧的asp.net完整应用程序(例如旧的Webforms等),我们不需要执行此操作
@ajeckmans那不行。 删除web.config文件后,IIS会立即选择更改,因此它应默认将请求发送到“静态文件模块”并开始提供文件。 试试看,看看那是怎么回事...
http://localhost:<PORT>/<ASSEMBLY_NAME>.deps.json
...替换PORT和ASSEMBLY_NAME)。我不相信仅使用...删除模块
<configuration>
<system.webServer>
<modules>
<remove name="StaticFileModule" />
</modules>
</system.webServer>
</configuration>
...因为其他模块将保留,并可能呈现其他攻击媒介。 也许可以删除所有非必需的模块,但是我们正在采取这样的举动进入未知领域,只是通过删除web.config来覆盖部署。 在官方指导下,这从来都不是一种选择,因此完全不受支持。 我建议使用PS脚本,例如,关闭AppPool或采取其他策略。
我想我应该对此表示同情...从安全角度来看确实引起了一些关注。 进行部署布局更改后,我们进行了讨论,包括将文件放回wwwroot
,请参阅...
讨论以下主题:为IIS发布对web.config位置的更改(IISIntegration 158)
将web.config
移回wwwroot
头部检查(IISIntegration 164)
[编辑]也许这是您(不受支持的)解决方法:将web.config移回wwwroot ,然后继续进行操作。 仍然...有点令人恐惧,破坏了应用程序使之离线。
这让我更加困惑在这种情况下我们应该做什么。
对于通过Web部署以避免文件锁定的方式将AspNetCore网站发布到IIS的当前建议是什么?
我也想知道。 这正在成为我组织采用.net核心的主要障碍。
当我的.net核心Web应用程序正在运行并且我尝试发布新的版本时,它给我提供了DLL正在使用的错误。 因此,我必须停止应用程序池,然后更新DLL并启动应用程序池。
.Net Core中是否有任何变通办法,我可以通过该变通办法发布新的Build / DLL,而无需停止应用程序池?
默认情况下,使用卷影复制机制的.Net框架支持此功能。 不幸的是,距.Net core推出已有两年了,似乎仍然不支持这种非常基本和必需的功能。 将来还有什么计划解决此问题?
在此先感谢您的帮助。
@guardrex确实很可怕,要破坏应用程序以获取IIS(或任何持有锁的东西)来释放dll,但是,更糟糕的是无法将修补程序推送到产品。 暂时破坏应用程序是我们唯一能做的,但是我们正在寻找其他更现代的方法来处理这个问题:)
@ shahjay748不要屏住呼吸。 如果我在这里重做该过程,则将应用程序放在一个容器中,然后放一个新容器,然后切换流量并拉下旧版本(或类似的东西)。 似乎更明智的做法是,将.net核心视为所有新的现代方式,仅通过复制新文件来推送应用程序的新版本就被认为(我同意)有些奥秘的方式去做吧。
哪一个ofc暂时对旧程序仍然束手无策呢?
我的工作(不确定这是否是正确的方法)是将网站上传到另一个目录并在iis中切换路径
我们对此问题进行了一些内部调查。 据我所知,ANCM在已部署的应用程序中不保存任何文件/句柄。 webdeploy本身似乎是一个问题,而且部署失败。 在删除app_offline之后,重试几次后,我从未遇到过始终无法部署应用程序的情况。
在其中添加一个快速的PowerShell旁注:该应用程序的程序集的锁定始终被释放...我从来没有遇到过问题(到目前为止!大声笑)。
@ bpetersen1 ...您的“分阶段”方法具有很好的副作用,如果新部署失败,则提供快速回滚选项。 如果需要的话,编写脚本应该很容易。
这在外部仍然很烦人。。。我们决定研究一种docker解决方案,以将IIS抛在后面。
我将尝试一种解决方案。 敬请关注。
我在使用FTP时也遇到了这个问题。
微软对此有何建议?
我在使用FTP时也遇到了这个问题。
微软对此有何建议?
删除一个appoffline.htm文件,然后进行ftp,然后将其删除。
@davidfowl是的,那是我现在正在使用批处理文件执行的操作。 IIS需要几秒钟的时间来释放锁,然后将文件复制过来。
谢谢-出于兴趣,您如何运行批处理文件?
还可以链接到MIcrosoft的文档。 我在Google上找不到。 谢谢。
@ niico100基本上,批处理文件只是将文件复制到项目目录中,在执行此操作之前,我先创建一个备份文件夹。 复制文件之前,请将appoffline.htm放入项目文件夹。
导致文件锁定问题,复制将重试。 我正在使用robcopy
https://docs.microsoft.com/zh-cn/windows-server/administration/windows-commands/robocopy
如果有人感兴趣,以下脚本将从appveyor下载部署文件,停止该站点,复制所需文件并备份该站点:
https://github.com/IsraelHikingMap/Site/blob/master/Scripts/Deploy.ps1
它需要以管理员身份运行。 希望一旦我们迁移,码头工人将解决所有这些废话...
它使用Stop-WebAppPool
和Start-WebAppPool
powershell命令。
对于本地开发,WebDeploy对我不起作用。 我尝试对本地IIS使用它,但它仍然会抱怨文件被锁定。 然后,我尝试使用VS Pre / Post构建事件中的上述powershell小命令,但这对我也不起作用,可能是因为32位powershell无法修改64位IIS设置吗? 无论如何,在ASP.Net Core项目中,以下预构建事件/后构建事件似乎很好地起作用了:
预建活动:
echo "App Offline" /a > $(ProjectDir)app_offline.htm
建置后活动:
del $(ProjectDir)app_offline.htm
我在这里简短地阅读了抱怨,看来这可能是相关的。 我将CI部署从通常“可以正常使用”的八达通部署转移到使用iis管理任务使用azure devops版本,并且仍然经常遇到部署新文件的问题,因为即使在停止webapp,停止应用程序域后,文件仍在采用。
这大概是每3个部署中的至少1个,还没有发现任何可以改善这种情况或使部署更可靠的东西。
@Tratcher @ronnyek这似乎与Application Insights有关; 添加App Insights扩展后,我的Web应用程序DLL突然很难更新,在此之前没有问题。
鉴于App Insights必须将分析器连接到应用程序进程和DLL,这将是有道理的。
我仍然面对这个问题。 我使用标志-e nableRule:AppOffline ,有时它运行良好,但大多数情况下部署失败,提示“ ERROR_FILE_IN_USE”。
开始发布后,我监视Web应用程序文件夹,并查看msdeploy删除的App_Offline.html。 接下来,发布失败并显示上述消息,并注意App_Offline文件仍在文件夹中,我再次启动发布,这次运行良好,因为模块在文件夹中看到了该文件。
最后安装的asp.net核心运行时-> aspnetcore.dll 12.1.18263.2
有任何想法吗 ? 还是个错误?
@dhtek如果您尝试在放置app_offline.htm
之后立即替换文件,则服务器只是没有足够的时间关闭应用程序。 在尝试替换文件之前,您应该稍等片刻。
好的,我理解了,但是我正在使用msdeploy的-e
我仍然看到这个问题。 我的项目文件引用了Microsoft.NET.Sdk.Web
,但是它并没有像文档中所述那样自动删除app_offline.htm
文件。
嗨,大家好。 最新的VS 2017的Core 2.1仍然存在此烦人的问题。我使用appoffline,但主要项目dll始终是使用状态,并且在发布期间需要完全停止网站。 这使得Web部署几乎毫无用处。
如果您无法使主机在负载均衡器之后脱机,并且想要更快地完成它,则这是使用符号链接和一些powershell的一种方法。
我当时正在考虑做这样的事情。
这样更快,因为您不必在复制+回收期间停止应用程序池,比硬停止要好,因为它可以完成当前请求,从而减少停机时间。
# Setup
Import-Module WebAdministration
# create 2 site root directories
$a = 'C:\inetpub\AspNetCoreSampleA'
$b = 'C:\inetpub\AspNetCoreSampleB'
$siteRoot = 'C:\inetpub\aspnetcoresample'
$siteName = 'AspNetCoreSample'
$poolName = "aspnetcore"
New-Item -Type Directory $a
New-Item -Type Directory $b
# create a symlink to targeting one side
New-Item -Type SymbolicLink -Path $siteRoot -Target $a
# point the site root to the symlink
Set-ItemProperty "IIS:\Sites\$siteName" -name physicalPath -value $siteRoot
# make sure it get's picked up
Restart-WebAppPool -Name $poolName
# this tells you the active side
Get-Item -Path $siteRoot | Select-Object -ExpandProperty target
# Flip the symlink
$current = (Get-Item -Path $siteRoot).Target
$newTarget = if ($current -eq $a) {$b} else {$a}
New-Item -Type SymbolicLink -Path $siteRoot -Target $newTarget -Force
# at this point w3wp.exe still locks the current target folder until it's getting recycled
# Deploy new version to the symlink which is now pointing to the other side which should have no locks
robocopy \\myshare\myapp $siteRoot /mir
# recycle app pool, so it picks up the new files
Restart-WebAppPool -Name $poolName
# bonus point: rollback is easy
$current = (Get-Item -Path $siteRoot).Target
$newTarget = if ($current -eq $a) {$b} else {$a}
New-Item -Type SymbolicLink -Path $siteRoot -Target $newTarget -Force
Restart-WebAppPool -Name $poolName
这是要点
https://gist.github.com/csharmath/b2af0f50700ce9fbdd8c5c3e582fd41b
Restart-WebAppPool基本上是回收站,如果启用了重叠回收站(默认设置),这将很有用,因为它将生成一个新的w3wp.exe,所有新请求将由该新进程处理,而当前正在执行的请求将完成由旧的w3wp.exe。
这样,它们就会重叠,直到旧的请求完成为止,最后您得到一个指向新版本的w3wp.exe,并且没有锁定问题。
这有点类似于卷影复制,但不是100%,因为它提供了一种更好的无缝xcopy方案。
到目前为止,如果您需要在发布期间保持在线状态,这似乎是我能想到的最佳方法。
@csharmath还是我猜也有码头工人。 没有文件以这种方式锁定,它可以通过swarm / k8s / okd进行滚动更新
当然,那当然更好。 叹息,不是每个地方都有。
对于其他老式设置,您需要先将其剃毛,直到它对您没有任何影响或停机时间最少。
@csharmath同意,但是在这一点上,考虑到这对于_years_来说是一个未解决的问题,因此不妨改编并寻求可行的解决方案,而不是等待奇迹发生...
我使用了下面的Power Shell脚本,它使IIS崩溃,甚至影响了其他应用程序池,因此我必须重新启动计算机以恢复所有内容...这个过程我是从官方文档中获得的,这是灾难,所以我的建议是不要使用app_offline.htm更好地使用脚本来停止应用程序池并重新启动,我在此线程中找到了一个https://github.com/IsraelHikingMap/Site/blob/master/Scripts/Deploy .ps1
$pathToApp = 'G:\prod_web_core'
$pathToAppOfflineHtml = 'G:\prod_web_core\app_offline.htm'
# Stop the AppPool
New-Item -Path $pathToApp -Name app_offline.htm
# Provide script commands here to deploy the app
Copy-Item "G:\prod_web_core_temp\*" -Destination $pathToApp -Recurse -Force
# Restart the AppPool
Remove-Item -Path $pathToAppOfflineHtml
Get-ChildItem -Path "G:\prod_web_core_temp" -Recurse | Remove-Item -Force
我被迫停止AppPool的成功部署,是否有解决此问题的任何进展?
由于dotnet core对我们来说是新的,所以我们今天才第一次遇到此文件锁定问题。 我真的很措手不及。 有趣的是,当我仍从测试版(〜2001)开始时,从Classic ASP切换到.Net Framework的第一原因是因为我可以在没有文件锁定的情况下进行热部署。 太棒了! 差不多18年后的今天,现在我回到了起点。 哦,好吧,你赢了一些,输了一些。
我刚刚提到了我的问题...在我的情况下,部署Blazor Server Side解决方案...。
几天前,这是一场噩梦……我应用了app_offline.htm,关闭了应用程序池,甚至重新启动了整个IIS实例来解锁文件。 下一步将是重新启动整个服务器,幸运的是,.dll在5分钟后即自动解锁。
2019,对我来说是同样的问题...这个问题是在2016年创建的...仍然没有解决方案吗?
2020年,对我来说同样的问题。 有史以来最大的回归。 .NET 4.6您替换了dll文件,并且该应用程序在新版本上重新加载。 .Net核心将锁定所有内容。
@bladefist到目前为止,您最好的选择是使用Docker容器:/
@kanadaj谢谢,但是Dockers是另一个令人头疼的问题。 我最终想出了一种解决方法,该方法是使用App_offline.htm,然后等待一分钟,并希望这些文件免费。 在他们99%的时间里,您只需要等待,我想我的应用程序需要一段时间才能关机。
.net 4.6的日子已经一去不复返了,您只需复制dll并自动回收应用程序。 我想那太简单了。 令人沮丧的是,这在Linux上不是问题。 您可以替换Linux中正在使用的文件。
是的,我想是的,但是这种方法在生产环境中并不是很简单。 如果没有HA,您的应用程序最终将一分钟没有响应,而使用HA,您将收到一些随机的错误请求,直到负载均衡器发现应用程序已关闭为止。 但这听起来更麻烦。
@kanadaj是的,我们使用负载平衡器,它会线性更新服务器。 无停机时间。
我们已经成功
appcmd
实用工具重新启动应用程序池。@davidglassborow这是一个创造性的解决方案。 谢谢。 但是,不一定需要创造性的解决方案。
以我的经验,当站点处于负载状态时,旧机制并不可靠,我们最终还是为高流量站点放置了反向代理。
正确的就地部署从来都不是可靠或原子的,但是如果您很幸运或没有太多流量,那会很好。 我假设大多数抱怨都围绕着这些场景。 我们将研究在.NET 5.0时间框架内进行改进。 一些警告:
@davidfowl您为5.0概述的计划听起来不错。 我相信大多数人都会使用依赖框架的代码。
完全不同意就地部署的可靠性。 我已经在负载非常高的站点上使用了多年,从来没有遇到过问题,只有一次。 检测到的第一个更改导致应用重置,负载均衡器看到实例没有响应,使实例脱机,一旦所有DLL都被替换,负载均衡器将完成运行状况检查,然后重新联机。 现在,如果没有负载平衡器运行状况检查,那么,这是一场赌博,但是我的意思是,谁在使用某种代理的高负载下替换生产中的DLL? 让我们关注规则而不是例外。 没有代理,我想不出任何方法来更新应用程序而不会造成中断。 即使您支持热重装,某些请求也可能会挂起或失败。
完全不同意就地部署的可靠性。 我已经在负载非常高的站点上使用了多年,从来没有遇到过问题,只有一次。 检测到的第一个更改导致应用重置,负载均衡器看到实例没有响应,使实例脱机,一旦所有DLL都被替换,负载均衡器将完成运行状况检查,然后重新联机。 现在,如果没有负载平衡器运行状况检查,那么,这是一场赌博,但是我的意思是,谁在使用某种代理的高负载下替换生产中的DLL? 让我们关注规则而不是例外。 没有代理,我想不出任何方法来更新应用程序而不会造成中断。 即使您支持热重装,某些请求也可能会挂起或失败。
您有一个负载平衡器。 很多人没有负载均衡器,只是替换磁盘上的文件并期待魔术😄
@davidfowl正确,但流量高+没有磅=
另外,app_offline.htm的问题是从ci / cd进程中您不知道进程何时停止。 我们必须在创建该文件和推送DLL之间添加一个巨大的sleep命令,因为关闭过程可能需要1到1分钟。 当您平衡大量实例的负载时,这些睡眠加起来会导致很长的部署周期。
将文件加载到内存可以解决所有问题。
@davidfowl正确,但流量高+没有磅=
👍
另外,app_offline.htm的问题是从ci / cd进程中您不知道进程何时停止。 我们必须在创建该文件和推送DLL之间添加一个巨大的sleep命令,因为关闭过程可能需要1到1分钟。 当您平衡大量实例的负载时,这些睡眠加起来会导致很长的部署周期。
正确,所以问题在于删除该文件不会告诉您何时可以开始部署,因为文件仍然被锁定,直到ANCM收到通知为止。
另外,app_offline.htm的问题是从ci / cd进程中您不知道进程何时停止。
正确,VS发布者使用短暂的睡眠和多次重试。
- 为了回收应用程序,您仍然需要添加和删除appoffline文件。 添加appoffline文件是一种信号,指示您希望应用程序由于部署而重新启动。
是否可以有一个“ apprestart”文件来标记要回收的应用程序,而不必随后删除appoffline文件?
@Socolin为什么?
@Socolin为什么?
如果我了解得很好(如果我写错了,请告诉我),您打算删除锁定,但是我们仍然必须创建appoffline来通知IIS,以在构建完成后回收应用程序?
为此,我们必须创建文件appoffline,然后将其删除。 创建文件后可以立即删除该文件吗? 还是应该等待IIS检测到它? 如果需要稍等一下,我希望有一个类似“ apprestart”的文件,IIS会监听它,然后在检测到它后将其删除并开始回收。
另外,我在想另一个问题。 这是关于构建期间的IDE优化的。
当前,我在一个使用依赖项的项目上工作,我们有一个用于Web层的项目,另一个是用于逻辑和数据的项目。 在构建过程中在Web层(ASP.NET Core)中工作时,我在构建之前创建了一个appoffline文件,然后在构建之后将其删除,并且可以正常工作。
但是,当我使用其中一个依赖项时,当我查看msbuild在做什么时,当我从Rider生成它们时(我认为它在VS上也是如此,因为它正在使用msbuild),一旦项目的构建完成, dll直接复制到.Web / bin目录中,而无需重建.Web项目。 而且由于dll被锁定,它无提示地失败,因此我必须手动重新启动服务器或手动触发.Web项目的生成。
您对此有什么解决方案吗?
到目前为止,我发现的解决方法是编写一个在后台运行的小程序,以查找.Web项目的/ bin。 文件更改后,它将在IIS侦听的目录中创建appoffline文件,然后复制修改后的文件,然后删除appoffline。 但这感觉有些棘手,但到目前为止,这是我发现能够构建的唯一方法,然后按F5键并测试我的更改。
为此,我们必须创建文件appoffline,然后将其删除。 创建文件后可以立即删除该文件吗? 还是应该等待IIS检测到它? 如果需要稍等一下,我希望有一个类似“ apprestart”的文件,IIS会监听它,然后在检测到它后将其删除并开始回收。
完成文件复制后,IIS将如何知道? 如何避免撕裂状态? 基本上,您需要发信号通知事务的开始和完成。 文件的创建表示开始,删除表示结束。
您对此有什么解决方案吗?
到目前为止,我发现的解决方法是编写一个在后台运行的小程序,以查找.Web项目的/ bin。 文件更改后,它将在IIS侦听的目录中创建appoffline文件,然后复制修改后的文件,然后删除appoffline。 但这感觉有些棘手,但到目前为止,这是我发现能够构建的唯一方法,然后按F5键并测试我的更改。
VS通过在启动新文件以解决可能影响该项目构建的任何事情之前终止进程来处理此问题。
为此,我们必须创建文件appoffline,然后将其删除。 创建文件后可以立即删除该文件吗? 还是应该等待IIS检测到它? 如果需要稍等一下,我希望有一个类似“ apprestart”的文件,IIS会监听它,然后在检测到它后将其删除并开始回收。
完成文件复制后,IIS将如何知道? 如何避免撕裂状态? 基本上,您需要发信号通知事务的开始和完成。 文件的创建表示开始,删除表示结束。
如果没有锁,为什么在构建开始时通知IIS? 我们不能仅在所有文件都已更新且需要回收应用程序时发出信号吗? 我想我误会了一些东西。
如果没有锁,为什么在构建开始时通知IIS? 我们不能仅在所有文件都已更新且需要回收应用程序时发出信号吗? 我想我误会了一些东西。
是的,那行得通。 我以为您在描述当前的技术水平。 今天甚至可以通过web.config来执行此操作。 如果您触摸文件,它将重新启动应用程序。
我通过以下方式在就地部署(https://flukefan.github.io/ZipDeploy/)上取得了成功。
无负载平衡; 单个IIS实例; 没有IIS更改。 YMMV。
@FlukeFan如何避免部署
在重命名/更新所有程序集之前,我不会触摸web.config。 我猜在那段时间内,有其他可能性可能导致应用程序池回收,但是这还没有发生。
我通常会关闭所有其他自动回收选项,并在IIS应用程序池设置中禁用重叠回收。
@FlukeFan不是回收站,但是如果您尚未加载已替换的程序集,则不要回收。 例如,假设您有一个程序集A.dll和B.dll。 假设当您进入设置页面时使用B,然后将其加载。 如果您在用新的B.dll替换B.dll时进入设置页面,则最终可能会运行带有新的B.dll的旧应用程序。
我确实想知道我们是否应该制作一个dotnet全局工具来协助这种类型的部署。 该工具基本上可以完成人们在该线程上提到的所有困难的事情。
有什么想法吗?
@FlukeFan不是回收站,但是如果您尚未加载已替换的程序集,则不要回收。 例如,假设您有一个程序集A.dll和B.dll。 假设当您进入设置页面时使用B,然后将其加载。 如果您在用新的B.dll替换B.dll时进入设置页面,则最终可能会运行带有新的B.dll的旧应用程序。
所以我暂时不处理这种情况。 https://github.com/FlukeFan/ZipDeploy/blob/master/ZipDeploy/ZipDeploy.cs#L55
我想我可以按照不同的顺序进行操作,首先触摸web.config,等待所有“其他”请求完成,然后在最后一个请求完成之前最后进行替换(而IIS将新请求排队等待大约回收应用程序池),但是我现在所拥有的才对。
对于您正在描述的场景,当前它可能还不够健壮(但是除了启动时,我还没有对动态程序集加载进行任何幻想)。
是否有计划重新使用此功能并将其放在路线图上? 如果每个人都希望支持零停机时间部署,则要求每个人部署.Net Core网站以手动实施分段时隙策略是非常不方便/不友好的。
拥有此功能将使许多人更轻松地过渡到.Net Core网站,并且可以更快地采用.Net Core网站。
我从来没有想到这一点。 但是您可能已经打中了头。 这可能是一项迫使他人使用天蓝色插槽的邪恶的商业决定。 如果过去有可能,为什么现在花这么长时间来“修复”呢? 从业务角度看,牢记这一点是没有错的。 也许我有点愤世嫉俗。 但是我一直在慢慢观察过去曾经免费使用的天蓝色功能,然后转移到更昂贵的层中。
我确实想知道我们是否应该制作一个dotnet全局工具来协助这种类型的部署。 该工具基本上可以完成人们在该线程上提到的所有困难的事情。
有什么想法吗?
这是要在CI服务器上运行的吗? 它将如何将二进制文件/程序包推送到目标服务器?
我从来没有想到这一点。 但是您可能已经打中了头。 这可能是一项迫使他人使用天蓝色插槽的邪恶的商业决定。 如果过去有可能,为什么现在花这么长时间来“修复”呢? 从业务角度看,牢记这一点是没有错的。 也许我有点愤世嫉俗。 但是我一直在慢慢观察过去曾经免费使用的天蓝色功能,然后转移到更昂贵的层中。
由于上述原因,没有协调的就地部署基本上是不可靠的。 我们永远不会再实现卷影复制,因为该功能历来不可靠,并且在.NET框架上的ASP.NET中引起了很多问题。 它也仅适用于特定类型的部署,而框架依赖于所有.NET Framework。 .NET核心支持自包含和单个文件,这使得它更难支持。 甚至上面的建议也没有涵盖这些情况。
我们仍然认为,就地部署应该尽可能地原子,并且这个问题上的很多问题都来自于bug和app_offline检测和回收的不可靠性,因此我们花了很多时间来修复它(以及pdb锁定)。
Web部署是当今唯一体现我们认为合理的部署流程的工具,但是它被困在该技术中,因此如果您将ftp部署到服务器或复制到文件共享,则它无济于事(尽管它可以)。
在这里,dotnet全局工具可能是一个答案。
这是要在CI服务器上运行的吗? 它将如何将二进制文件/程序包推送到目标服务器?
我不这么认为。 它可能会支持FTP和直接副本。 除此之外的其他任何东西都可能是专有协议。
希望当您说FTP时,也表示通过ssh的ftp。 这样的工具将非常有价值。
希望当您说FTP时,也表示通过ssh的ftp。 这样的工具将非常有价值。
不,我是说ftp。 但是我们会做任何有意义的事情。 也许最好是需要在目标计算机上运行的复制工具。 这使我们不必在任何地方发送位。
您还在通过SSH进入Windows计算机以部署到IIS吗?
@davidfowl请添加(选择加入)对部署插槽的支持。
https://github.com/dotnet/aspnetcore/issues/3719#issuecomment -473183712
https://github.com/dotnet/aspnetcore/issues/3793#issuecomment -335666414
我们计划研究将应用程序程序集作为字节加载到内存中,而不是从磁盘加载它们并锁定文件。 在大多数情况下,这应该没问题,但在某些情况下可能会导致更多的内存使用。
而且,这不会使我们实现零停机时间。
我们不可能用模块来投资类似的功能。
希望当您说FTP时,也表示通过ssh的ftp。 这样的工具将非常有价值。
不,我是说ftp。 但是我们会做任何有意义的事情。 也许最好是需要在目标计算机上运行的复制工具。 这使我们不必在任何地方发送位。
您还在通过SSH进入Windows计算机以部署到IIS吗?
是。 我们使用CI / CD并将其部署到linux和Windows系统,因此在Windows盒子上安装openssh容易得多。
$ appPool ='默认'
Stop-WebAppPool-名称$ appPool -Passthru
...部署...
Start-WebAppPool-名称$ appPool -Passthru
...部署.....
要么
$ appPool ='默认'
Stop-WebAppPool-名称$ appPool
while(((Get-WebAppPoolState -Name $ appPool).Value -ne'Stop'){
睡眠1#秒
}
...部署...
Start-WebAppPool-名称$ appPool
while(((Get-WebAppPoolState -Name $ appPool).Value -ne'开始'){
睡眠1#秒
}
...部署.....
穷人的部署槽
大约一年前,我发布了有关创建2个站点根文件夹的信息,并有一个符号链接指向为IIS站点配置的其中一个。
好处是,与其他更简单的解决方案相比,您的停机时间更少。
如果启用了重叠回收,则不会造成停机,反之则只会对第一个请求进行预热处罚。
如果部署/复制操作需要N秒才能完成,则无需在N秒内停止应用程序池。
离线处理可疑应用程序。
只需复制到非活动文件夹,在文件在那里时交换符号链接,然后回收应用程序池。
原子部署,没有停机时间,像往常一样放慢了第一次请求的时间。
https://github.com/dotnet/aspnetcore/issues/3793#issuecomment -459870870
有人尝试过吗?
如果类似的方法有效,则可以使用客户端部署工具将其转换为服务。
因此,部署锁定文件也存在问题。 在应用托管中。 我尝试停止应用程序池,但是文件仍处于锁定状态。 我曾尝试推送app_offline.htm,但也没有运气。
另外,如果要管理删除app_offline.htm文件,则可以在program.cs中启动应用程序时轻松地做到这一点。 这样我们就知道它何时开始在线。
我当前面临的问题是dll挂起了,即使您重命名然后在文件上进行复制,旧进程仍然会对重命名的文件运行。 因此,当我们尝试再次部署时,我们将不得不再次重命名文件。 我想我们可以使用某种日期作为文件名替换。
我们在IIS服务器Windows 2012 R2上为此使用dotnet core 3.1.4
@foxjazz如果停止应用程序池/进程,文件如何仍被锁定? 你能详细说明吗?
@foxjazz如果停止应用程序池/进程,文件如何仍被锁定? 你能详细说明吗?
尝试删除使用的dll时,图片显示由另一个进程使用。
应用程序池已离线一段时间。
检查任务管理器以查看该进程是否仍在运行。 按下停止按钮并不意味着它立即结束。
检查任务管理器以查看该进程是否仍在运行。 按下停止按钮并不意味着它立即结束。
上面的图片来自任务管理器。 不知道是哪一个,但是如果我删除了它们,它将释放文件。 我想我可以使用handle找出要终止的pid。 我认为我不必花那么多时间进行部署。 句柄说w3wp.exe 2进程已锁定文件之一。 即使其应用程序池已关闭。
真正好的是要有一条路线停止该过程。
api \ KillDeathKill
{
Program.KillProcess()
}
那不太可能。 该文件已被进程锁定。 如果w3wp是该进程,则它要么从不被杀死就开始,要么被新的以不同的pid锁定同一个文件。 这就是为什么您需要确保应用程序离线先关闭,以确保没有新的过程来锁定文件,杀死现有过程,部署新位,离线删除应用程序
那不太可能。 该文件已被进程锁定。 如果w3wp是该进程,则它要么从不被杀死就开始,要么被新的以不同的pid锁定同一个文件。 这就是为什么您需要确保应用程序离线先关闭,以确保没有新的过程来锁定文件,杀死现有过程,部署新位,离线删除应用程序
当您说app_offline.htm首先关闭时。 是的,就是这种情况。 我有一个状态路径,可以通过get请求检查它,它肯定是脱机的。 还手动停止了应用程序池。 但是进程w3wp仍然挂起了文件。
然后这个过程没有停止
然后这个过程没有停止
那么为什么要停止应用程序池而不是停止进程呢?
应用程序池确实代表该进程,如果您停止应用程序池,它将使该进程无效。 如果没有任何过程可以锁定文件,则无法将其锁定,因此似乎更可能采用其他理论之一。
应用程序池确实代表该进程,如果您停止应用程序池,它将使该进程无效。 如果没有任何过程可以锁定文件,则无法将其锁定,因此似乎更可能采用其他理论之一。
哈哈,你说屎。 我的意思不是要我打你。 我是说停止应用程序池不能像所有文档中所提示的那样工作。 该过程没有停止,即使90秒后仍在运行。 即使您清楚地看到应用程序池已停止,该文件也被锁定。 该过程包含一个单例以获取一些数据。 和其他控制器。 拥有一个可以根据锁定文件关闭进程的脚本会很好。 然后,我可以毫无恐惧地部署。 重命名也可能起作用。 可以重命名文件。 (dotnet核心3.1.4)风味
大约一年前,我发布了有关创建2个站点根文件夹的信息,并有一个符号链接指向为IIS站点配置的其中一个。
有人尝试过吗?
如果类似的方法有效,则可以使用客户端部署工具将其转换为服务。
@CJHarmath-我确实尝试过,并在下面使用了修改后的代码。 效果很好,谢谢!
如果我发布的更新更多,我可能会考虑使用客户端部署工具,但实际上我可以将远程Shell插入IIS服务器并执行powershell脚本来翻转它。
psexec.exe \\IIS_SERVER cmd /c "powershell -noninteractive -file C:\FlipSymLink.ps1"
# FlipSymLink.ps1
Import-Module WebAdministration # run as admin
# create 2 site root directories
$a = 'C:\Websites\MySiteA'
$b = 'C:\Websites\MySiteB'
$appRoot = 'C:\Websites\MySite'
$appName = 'MyAppName'
$siteName = 'MySiteName'
$poolName = "MyPoolName"
New-Item -Type Directory $a -ErrorAction SilentlyContinue
New-Item -Type Directory $b -ErrorAction SilentlyContinue
# create a symlink to targeting one side
New-Item -Type SymbolicLink -Path $appRoot -Target $a -Name A -ErrorAction SilentlyContinue
New-Item -Type SymbolicLink -Path $appRoot -Target $b -Name B -ErrorAction SilentlyContinue
# point the site root to the symlink
$currentPath = (Get-ItemProperty "IIS:\Sites\$siteName\$appName" -name physicalPath)
if ($currentPath -eq "$appRoot\A") {
Write-Host "Switched to B"
New-Item $b\active.txt
Remove-Item $a\active.txt
Set-ItemProperty "IIS:\Sites\$siteName\$appName" -name physicalPath -value $appRoot\B
} else {
Write-Host "Switched to A"
New-Item $a\active.txt
Remove-Item $b\active.txt
Set-ItemProperty "IIS:\Sites\$siteName\$appName" -name physicalPath -value $appRoot\A
}
Restart-WebAppPool -Name $poolName
psexec.exe \\IIS_SERVER cmd /c "powershell -noninteractive -file C:\FlipSymLink.ps1"
这也可以变成JEA端点,因此您可以以非提升用户的身份运行flip
(例如,从CI / CD服务器部署步骤)
Invoke-Command -ComputerName IIS_SERVER -ConfigurationName IIS-Flip -ScriptBlock { Switch-SymlinkTarget -SiteName MySite}
使用Switch
代替Flip
因为它是经过批准的动词。
使用
Switch
代替Flip
因为它是经过批准的动词。
Swap
是处理插槽时使用的动词-在这里似乎可以重新使用?
与4年前描述的问题相同。
这事有进一步更新吗?
我不想打开“远程桌面”以仅上传新的dll文件。
当我尝试在生产IIS服务器文件上使用FTP覆盖.NET Core应用程序DLL文件时,该文件已锁定,无法覆盖。
要部署新版本,我需要停止IIS中的应用程序(打开RDP)以释放锁定,然后覆盖。
不停止应用程序就无法部署。
有什么解决办法吗? 谢谢
如果您无法使主机在负载均衡器之后脱机,并且想要更快地完成它,则这是使用符号链接和一些powershell的一种方法。
我当时正在考虑做这样的事情。这样更快,因为您不必在复制+回收期间停止应用程序池,比硬停止要好,因为它可以完成当前请求,从而减少停机时间。
# Setup Import-Module WebAdministration # create 2 site root directories $a = 'C:\inetpub\AspNetCoreSampleA' $b = 'C:\inetpub\AspNetCoreSampleB' $siteRoot = 'C:\inetpub\aspnetcoresample' $siteName = 'AspNetCoreSample' $poolName = "aspnetcore" New-Item -Type Directory $a New-Item -Type Directory $b # create a symlink to targeting one side New-Item -Type SymbolicLink -Path $siteRoot -Target $a # point the site root to the symlink Set-ItemProperty "IIS:\Sites\$siteName" -name physicalPath -value $siteRoot # make sure it get's picked up Restart-WebAppPool -Name $poolName # this tells you the active side Get-Item -Path $siteRoot | Select-Object -ExpandProperty target # Flip the symlink $current = (Get-Item -Path $siteRoot).Target $newTarget = if ($current -eq $a) {$b} else {$a} New-Item -Type SymbolicLink -Path $siteRoot -Target $newTarget -Force # at this point w3wp.exe still locks the current target folder until it's getting recycled # Deploy new version to the symlink which is now pointing to the other side which should have no locks robocopy \\myshare\myapp $siteRoot /mir # recycle app pool, so it picks up the new files Restart-WebAppPool -Name $poolName # bonus point: rollback is easy $current = (Get-Item -Path $siteRoot).Target $newTarget = if ($current -eq $a) {$b} else {$a} New-Item -Type SymbolicLink -Path $siteRoot -Target $newTarget -Force Restart-WebAppPool -Name $poolName
这是要点
https://gist.github.com/csharmath/b2af0f50700ce9fbdd8c5c3e582fd41b
我认为这已经解决了一些问题。 我只尝试过使用ASP.NET,但我想它可以与用于Net Core的托管模块相同。 第一次运行确实使站点停了一下,所以可能需要调整一下。
param(
[Parameter(Mandatory = $true)]
[string] $iisRootPath,
[Parameter(Mandatory = $true)]
[string] $siteName,
[Parameter(Mandatory = $true)]
[string] $artifactPath,
[Parameter(Mandatory = $false)]
[string] $siteFolderName,
[Parameter(Mandatory = $false)]
[string] $appPoolName
)
Import-Module WebAdministration
#populate optional parameters
if (!($PSBoundParameters.ContainsKey('siteFolderName'))) { $siteFolderName = $siteName }
if (!($PSBoundParameters.ContainsKey('appPoolName'))) { $appPoolName = $siteName }
#set A, B and C paths
$a = "$iisRootPath\$siteFolderName" + 'A'
$b = "$iisRootPath\$siteFolderName" + 'B'
$c = "$iisRootPath\$siteFolderName"
#existence test
$cName = Get-Item -Path $c -ErrorAction SilentlyContinue | Select-Object -ExpandProperty name -ErrorAction SilentlyContinue
$bName = Get-Item -Path $b -ErrorAction SilentlyContinue | Select-Object -ExpandProperty name -ErrorAction SilentlyContinue
#get the shudown timeout for the app pool
$shutdownTimeout = Get-WebConfigurationProperty -Filter 'system.web/httpRuntime' -PSPath "IIS:\Sites\$siteName" -Name shutdownTimeout | Select-Object -ExpandProperty Value
#create symlink, if symlink source is an existing directory, rename existing
if($cName -eq $null)
{
Write-Output "Creating SymbolicLink $c -> $a"
New-Item -Type SymbolicLink -Path $c -Target $a
}
else
{
#directories don't have a target property
$cTarget = Get-Item -Path $c | Select-Object -ExpandProperty target -ErrorAction SilentlyContinue
#this is a directory, rename and create symlink
if($cTarget -eq $null)
{
#restart AppPool first so no locked files
#Write-Output "Restarting AppPool $appPoolName"
#Restart-WebAppPool -Name $appPoolName
#stop AppPool first so no locked files
Write-Output "Stopping AppPool $appPoolName"
Stop-WebAppPool -Name $appPoolName
#sleep for shutdownTimeout
Write-Output "Sleeping for $shutdownTimeout"
Start-Sleep -Seconds (([System.TimeSpan]::Parse("$shutdownTimeout").TotalSeconds) * 1.1)
try {
#if the rename fails, there are files in use. stop the script
$newName = $siteFolderName + 'A'
Write-Output "Renaming $c to $newName"
Rename-Item -Path $c -NewName $newName -ErrorAction Stop
}
catch {
Write-Error -Message "Failed to rename $c to $newName due to files in use."
#start AppPool
Write-Output "Starting AppPool $appPoolName due to failed folder rename. Consider trying again durring a time with reduced traffic."
Start-WebAppPool -Name $appPoolName
}
#create symlink
Write-Output "Creating SymbolicLink $c -> $a"
New-Item -Type SymbolicLink -Path $c -Target $a
#start AppPool
Write-Output "Starting AppPool $appPoolName"
Start-WebAppPool -Name $appPoolName
#copy a to b to pick up directory permissions
if($bName -eq $null)
{
Write-Output "Copying Directory $a to $b"
robocopy $a $b /e /sec /sl
}
}
}
#existence test
$aName = Get-Item -Path $a -ErrorAction SilentlyContinue | Select-Object -ExpandProperty name -ErrorAction SilentlyContinue
$bName = Get-Item -Path $b -ErrorAction SilentlyContinue | Select-Object -ExpandProperty name -ErrorAction SilentlyContinue
#create if needed
if($aName -eq $null)
{
Write-Output "Creating Directory $a"
New-Item -Type Directory $a
}
if($bName -eq $null)
{
Write-Output "Creating Directory $b"
New-Item -Type Directory $b
}
#make sure site pointing to symlink
Write-Output "Pointing Website $siteName to Path $c"
Set-ItemProperty "IIS:\Sites\$siteName" -name physicalPath -value $c
#restart so we know it's loading from symlink
Write-Output "Restarting AppPool $appPoolName"
Restart-WebAppPool -Name $appPoolName
#sleep for shutdownTimeout
Write-Output "Sleeping for $shutdownTimeout"
Start-Sleep -Seconds ([System.TimeSpan]::Parse("$shutdownTimeout").TotalSeconds)
#get current symlink target and set new target
Get-Item -Path $c | Select-Object -ExpandProperty target
$current = (Get-Item -Path $c).Target
$newTarget = if ($current -eq $a) {$b} else {$a}
$fileOrDirectoryName = Get-Item $artifactPath | Select-Object -ExpandProperty name
#test for .zip
if(($fileOrDirectoryName).ToLower().EndsWith(".zip") -eq $true)
{
#copy to temp location
$guid = [System.Guid]::NewGuid()
$tempPath = "$iisRootPath\temp-$guid"
$tempDest = "$iisRootPath\temp-dest-$guid"
Write-Output "Creating temp directory $tempPath"
New-Item -Type Directory $tempPath
$artifactDirectory = [System.IO.Path]::GetDirectoryName($artifactPath)
$artifactFile = [System.IO.Path]::GetFileName($artifactPath);
#TODO: first robocopy arg needs to be a directory, not a file
Write-Output "Copying archive from $artifactPath to $tempPath"
robocopy $artifactDirectory $tempPath $artifactFile
#extract to temp destination (seems a failed extract can leave empty folders)
Write-Output "Extracting archive to $tempDest"
Expand-Archive -Path "$tempPath\$fileOrDirectoryName" -DestinationPath $tempDest -Force
#copy from temp destination to destination
Write-Output "Copying files from $tempDest to $newTarget"
robocopy $tempDest $newTarget /e
#delete temp directory
Write-Output "Removing temp directory $tempPath"
Remove-Item -Path $tempPath -Recurse -Force
#delete temp directory
Write-Output "Removing temp directory $tempDest"
Remove-Item -Path $tempDest -Recurse -Force
}
else
{
#copy new files
Write-Output "Copying files from $artifactPath to $newTarget"
robocopy $artifactPath $newTarget /e
}
#swap symlink
Write-Output "Pointing SymbolicLink $c -> $newTarget"
New-Item -Type SymbolicLink -Path $c -Target $newTarget -Force
#restart so it loads from newly swapped symlink
Write-Output "Restarting AppPool $appPoolName"
Restart-WebAppPool -Name $appPoolName
@LucidObscurity是什么? 一个cmd代码? 我必须在哪里添加该代码?
谢谢
对于增加msdeploy
命令的重试次数(参数: -retryAttempts:X
)或重试时间间隔(参数: -retryInterval:XXXX
)的想法如何,直到该进程释放文件锁?
我认为@davidfowl是正确的,该进程仍会以某种方式存在并锁定文件。 即使重置IIS没有帮助,但它仍然等待过程,直到超时。 如果您删除app_offline.htm并等待90秒(默认超时设置为关闭AppPool),然后重试,您将看到它起作用。
对于我在本地的情况,我不想等待那么长时间,因此我将此关机时间限制(秒)更新为5,并在构建之前更新了项目文件以生成app_offline.htm,在构建之后将其删除。 效果很好。
对于服务器,我使用msdeploy删除app_offline.htm,同步新版本,然后删除app_offline.htm。 使用msdeploy,它会重试是否锁定了文件,但有时在90秒之前就放弃了,因此我必须再次触发部署,然后才能再次尝试
关于这个有任何更新吗? 新版本何时准备就绪?
谢谢您联络我们。
我们正在将此问题移至Next sprint planning
里程碑,以供将来评估/考虑。 当我们为下一个里程碑计划工作时,我们将评估请求。 要了解更多关于下一步的期望以及如何处理此问题,您可以在此处阅读有关我们的分类程序的更多信息。
在将.NET 4.5.2应用程序转换为.NET 5.0之后,我本周发现了有关此问题的信息。 我有另一种方法可以克服这个问题。 我创建了在IIS服务器上运行的Windows服务。 在该服务上,我有一个在自定义端口上运行的简单HTTP Server。 Web应用程序可以向该服务器发出请求后以启动升级。 它停止了应用程序池,以确保终止该应用程序的所有进程,创建当前版本的备份,删除当前版本,下载升级包并将其解压缩。 然后再次启动应用程序池。
我创建了一个简单的Windows Forms应用程序,以创建将升级包推送到上述Windows Service从中获取的存储库中。
最有用的评论
👍
不支持重叠回收是一种回归。