如何部署一个零停机时间的 ASP.NET 应用程序

为了部署我们网站的新版本,我们采取以下措施:

  1. 将新代码压缩并上传到服务器。
  2. 在活动服务器上,从 IIS 网站目录中删除所有活动代码。
  3. 将新代码 zipfile 解压缩到现在空的 IIS 目录中

这个过程都是脚本化的,并且发生得很快,但是在删除旧文件和部署新文件时仍然会有10-20秒的停机时间。

关于0秒停机时间方法有什么建议吗?

36084 次浏览

我能想到的唯一零停机时间的方法是在至少两台服务器上托管。

你需要两台服务器和一个负载平衡器,步骤如下:

  1. 关闭服务器2上的所有通信
  2. 在服务器1上部署
  3. 测试服务器1
  4. 关闭服务器1上的所有通信
  5. 在服务器2上部署
  6. 测试服务器2
  7. 在两个服务器上转换流量

问题是,即使在这种情况下,如果使用“粘性会话”,仍然会有应用程序重启和会话丢失。如果您有数据库会话或状态服务器,那么一切都应该没问题。

MicrosoftWeb 部署工具在某种程度上支持这一点:

启用 Windows 事务性文件 系统(TxF)支持 启用,则文件操作为 也就是说,他们要么成功 或者完全失败。这样可以确保数据 完整性,并防止数据或文件 从存在的“中途”或 已损坏状态。在 MS 部署中,TxF 是 默认情况下禁用。

看来交易是为了整个同步。另外,TxF 是 WindowsServer2008的一个特性,所以这个事务特性不能用于早期版本。

我相信使用文件夹作为版本和 IIS 元数据库来修改0-宕机脚本是可能的:

  • 对于现有的路径/url:
    • Path : web app v2.0
    • 网址: http://app
  • 下复制新的(或修改的)网站到服务器
    • Web app v2.1
  • 修改 IIS 元数据库以更改网站路径
    • 来自 web app 2.0
    • web app v2.1

这种方法有以下好处:

  • 如果新版本出现问题,可以很容易地回滚到 v2.0
  • 要部署到多个物理或虚拟服务器,可以使用脚本进行文件部署。一旦所有服务器都有了新版本,您就可以使用 MicrosoftWebDeploymentTool 同时更改所有服务器的元数据库。

我建议将旧文件保留在那里,并简单地覆盖它们。这样,停机时间仅限于单个文件覆盖时间,而且一次只丢失一个文件。

不知道这对“网络应用程序”有没有帮助(我想你是在说你正在使用这个) ,这就是为什么我们总是使用“网站”。另外,“网站”部署不会重新启动您的网站,并删除所有用户会话。

对于单个服务器,我会稍微改进一下 George 的答案:

  1. 使用 Web 部署项目将网站预编译为单个 DLL
  2. 将新站点压缩,并上传到服务器
  3. 解压缩到一个新的文件夹位于一个文件夹与正确的权限的网站,所以解压缩文件继承的权限正确(也许 e: 网络,与子文件夹 v20090901,v20090916,等)
  4. 使用 IIS 管理器更改包含站点的文件夹的名称
  5. 将旧文件夹保留一段时间,以便在出现问题时可以重新使用它

步骤4将导致 IIS 辅助进程进行回收。

如果不使用 InProc 会话,这只是零停机时间; 如果可以,请使用 SQL 模式(更好的是,完全避免会话状态)。

当然,当存在多个服务器和/或数据库更改时,这就有点复杂了... ..。

我最近经历了这个过程,我想到的解决方案是在 IIS 中设置两个站点并在它们之间切换。

对于我的配置,我为每个 A 和 B 站点都有一个网络目录,如下所示: 内部网实时接口 内部网实时 B 接口

在 IIS 中,我有两个相同的站点(相同的端口、身份验证等) ,每个站点都有自己的应用程序池。其中一个站点正在运行(A) ,另一个站点已停止(B)。活动的主机也有活动的主机头。

当涉及到部署生存时,我只是简单地发布到 STOPPED 站点的位置。因为我可以使用 B 站点的端口访问它,所以我可以预热站点,这样第一个用户就不会导致应用程序启动。然后使用批处理文件将活动主机头复制到 B,停止 A 并启动 B。

使用 Microsoft.Web.Administration 的 ServerManager 类,您可以开发自己的部署代理。

诀窍是更改 VirtualDirectory 的 Physical Path,这将导致在新旧 Web 应用程序之间进行在线原子切换。

请注意,这可能导致新旧 AppDomain 并行执行!

问题是如何同步对数据库的更改等。

通过轮询是否存在带有旧的或新的 Physical Path 的 AppDomain,可以检测旧的 AppDomain 何时终止,以及新的 AppDomain 是否已经启动。

要强制 AppDomain 启动,必须发出 HTTP 请求(IIS 7.5支持自动启动功能)

现在您需要一种方法来阻止对新 AppDomain 的请求。 我使用一个命名的互斥对象——它由部署代理创建和拥有,由新 Web 应用程序的 Application _ Start 等待,然后在数据库更新完成后由部署代理发布。

(我在 web 应用程序中使用一个标记文件来启用互斥等待行为) 一旦新的网络应用程序运行,我删除标记文件。

好吧,既然大家都对我2008年写的答案投了反对票 * 。

我会告诉你我们在2014年是怎么做的。我们不再使用 Web 站点,因为我们现在使用 ASP.NET MVC。

我们当然不需要一个负载平衡器和两个服务器来做到这一点,这是罚款,如果你有3个服务器,每个网站,你维护,但它的总杀伤力为大多数网站。

此外,我们不依赖于微软最新的向导-太慢,太多隐藏的魔力,太容易改变它的名称。

我们是这样做的:

  1. 我们有一个后期构建步骤,可以将生成的 DLL 复制到“ bin-pub”文件夹中。

  2. 我们使用 Beyond Compare (非常好的 * *)来验证已更改的文件并将其同步到生产服务器上(通过 FTP,因为它得到了广泛的支持)

  3. 我们在网站上有一个安全的 URL,其中包含一个按钮,可以将“ bin-pub”中的所有内容复制到“ bin”(首先进行备份以启用快速回滚)。这时应用程序会自动重启。然后,我们的 ORM 检查是否有任何表或列需要添加并创建它们。

这只有几毫秒的停机时间。应用程序重新启动可能需要一两秒钟,但在重新启动请求缓冲,所以有效地零停机时间。

整个部署过程需要5秒到30分钟不等,这取决于更改了多少文件以及要检查的更改数量。

这样,你不必复制整个网站到一个不同的目录,而只是在 bin 文件夹。您还可以完全控制整个过程,并确切地知道正在发生什么变化。

我们总是对正在部署的变更进行快速检查——作为最后一分钟的再次检查,这样我们就知道要测试什么,如果有什么问题,我们就准备好了。我们使用 Beyond Compare 是因为它可以让您轻松地在 FTP 上区分文件。没有 BC 我是不会这么做的,你根本不知道你在覆盖什么。

* 滚动到底部查看它: (顺便说一句,我不再推荐 Web 站点,因为它们构建速度较慢,而且可能因为编译了一半的临时文件而严重崩溃。我们过去使用它们是因为它们允许更敏捷的逐文件部署。非常快速地修复一个小问题,您可以准确地看到您正在部署的内容(当然,如果使用 Beyond Compare ——否则就忘了它)。

展开 sklivvz 的答案,它依赖于某种负载平衡器(或者只是同一服务器上的一个备用副本)

  1. 将所有流量导向 Site/Server 2
  2. 可以选择稍等,以确保尽可能少的用户在已部署的版本上拥有挂起的工作流
  3. 将其部署到 Site/Server 1并尽可能地进行预热
  4. 以事务的方式执行数据库迁移(努力使之成为可能)
  5. 立即将所有流量导向 Site/Server 1
  6. 部署到站点/服务器2
  7. 将流量导向两个站点/服务器

通过创建数据库快照/副本,可以引入一些冒烟测试,但这并不总是可行的。

如果可能并且需要使用“路由差异”,例如不同的租户 URL: s ( customerx.myapp.net )或不同的用户,首先部署到一组不知情的实验对象。如果没有失败,释放给大家。

由于涉及到数据库迁移,所以回滚到以前的版本通常是不可能的。

有一些方法可以让应用程序在这些场景中表现得更好,比如使用事件队列和回放机制,但是由于我们讨论的是将更改部署到正在使用的东西上,所以实际上没有什么万无一失的方法。

我是这么做的:

系统绝对最低要求:
1台服务器

  • 1个负载均衡器/反向代理(例如 nginx)在端口80上运行
  • 2 ASP.NET-核心/mono 反向代理/fastcgi chroot-jails 或 docker-Container 监听2个不同的 TCP 端口
    (或者甚至只是两个反向代理应用程序在两个不同的 TCP 端口没有任何沙箱)

工作流程:

开始交易我的更新

try
Web-Service: Tell all applications on all web-servers to go into primary read-only mode
Application switch to primary read-only mode, and responds
Web sockets begin notifying all clients
Wait for all applications to respond


wait (custom short interval)


Web-Service: Tell all applications on all web-servers to go into secondary read-only mode
Application switch to secondary read-only mode (data-entry fuse)
Updatedb - secondary read-only mode (switches database to read-only)


Web-Service: Create backup of database
Web-Service: Restore backup to new database
Web-Service: Update new database with new schema


Deploy new application to apt-repository
(for windows, you will have to write your own custom deployment web-service)
ssh into every machine in array_of_new_webapps
run apt-get update
then either
apt-get dist-upgrade
OR
apt-get install <packagename>
OR
apt-get install --only-upgrade <packagename>
depending on what you need
-- This deploys the new application to all new chroots (or servers/VMs)


Test: Test new application under test.domain.xxx
-- everything that fails should throw an exception here
commit myupdate;


Web-Service: Tell all applications to send web-socket request to reload the pages to all clients at time x (+/- random number)
@client: notify of reload and that this causes loss of unsafed data, with option to abort


@ time x:  Switch load balancer from array_of_old_webapps to array_of_new_webapps
Decomission/Recycle array_of_old_webapps, etc.


catch
rollback myupdate
switch to read-write mode
Web-Service: Tell all applications to send web-socket request to unblock read-only mode
end try

通过利用 IIS 中的应用程序请求路由作为不同端口上两个本地 IIS 站点之间的软件负载平衡器,可以在单个服务器上实现零停机部署。这就是所谓的 蓝绿色部署策略蓝绿色部署策略,在任何给定的时间,负载平衡器中只有两个站点中的一个可用。将其部署到“向下”的站点,对其进行预热,并将其放入负载平衡器(通常通过 Application Request Routing 健康检查) ,然后将已经向上的原始站点从“池”中取出(同样通过使其健康检查失败)。

完整的教程可以在这里找到。

我经常使用的 没有休息时间变通方法是:

  1. 将运行.NET 核心应用程序 dll 重命名为 filename.dll.backup

  2. 上传新的.dll (web 应用程序可用,并在上传文件时为请求提供服务)

  3. 上传完成后,回收应用程序池。要求 RDP 访问服务器或函数在宿主控制面板中回收应用程序池。

IIS 在回收时会重叠应用程序池,所以在回收期间通常不会有任何停机时间。所以请求仍然在不知道应用程序池已经被回收的情况下进入,请求被无缝地服务,没有停机时间。

我仍在寻找比这更好的方法