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


127

要部署我们网站的新版本,请执行以下操作:

  1. 压缩新代码,然后将其上传到服务器。
  2. 在实时服务器上,从IIS网站目录中删除所有实时代码。
  3. 将新的代码zip文件解压缩到现在空的IIS目录中

这个过程全部是脚本化的,并且很快完成,但是当删除旧文件并部署新文件时,仍然会有10到20秒的停机时间。

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


这不应该在ServerFault上吗?
Daniel Rodriguez

49
也许可以,但是ServerFault在08
Karl Glennon 2010年

3
IIS可以指向符号链接文件夹吗?更改符号链接会导致IIS进程回收吗?
尼尔·麦圭根

任何具有完整源代码脚本示例的最终解决方案?
Kiquenet

不可能有多个应用程序池并将流量从一个应用程序池切换到另一个应用程序池吗?
路加福音

Answers:


79

您需要2台服务器和一个负载均衡器。步骤如下:

  1. 打开服务器2上的所有流量
  2. 在服务器1上部署
  3. 测试服务器1
  4. 打开服务器1上的所有流量
  5. 在服务器2上部署
  6. 测试服务器2
  7. 开启两台服务器上的流量

事实是,即使在这种情况下,如果您使用“粘性会话”,您仍将重新启动应用程序并丢失会话。如果您有数据库会话或状态服务器,那么一切都应该很好。


4
您还可以配置负载均衡器,使其为给定服务器服务于现有会话,但不接受新会话。这样您就可以避免删除会话。但是,此技术需要等待会话结束,并且通常需要编写脚本。

35
当代码卷对数据库进行结构更改时,此方法往往会崩溃。升级服务器1的数据库后,服务器2将爆炸。现在,您可以备份/还原数据库以在服务器1上进行测试,但是在并行副本运行时,存在整理实时数据库中更改的数据的问题。
EBarr 2010年

1
@AndreiRinea-您认为这在大容量OLTP系统中如何工作?系统要么不同步,要么在切换时丢失数据,要么需要暂停数据输入并编写脚本以标识临时数据并将其迁移到新的DB结构中。
EBarr 2011年

9
@EBarr:从技术上讲,无论如何从技术上讲,您在ASP.NET应用程序上的停机时间仍然为零–问题不是“如何在停机时间为零的情况下部署到sql服务器数据库”。
Sklivvz 2011年

6
它们的关键是以一种不会破坏您的sql的方式进行开发。一旦不再使用以下版本,您通常必须对它进行破坏性的sql更改。练习并不难。
Bealer

60

微软Web部署工具支持这种在一定程度上:

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

交易似乎是整个同步的。另外,TxF是Windows Server 2008的功能,因此该事务功能不适用于早期版本。

我相信可以使用文件夹作为版本和IIS配置数据库来将脚本修改为0-停机时间:

  • 现有路径/网址:
  • 将新的(或修改的)网站复制到服务器下
    • \ web \ app \ v2.1 \
  • 修改IIS配置数据库以更改网站路径
    • 来自 \ web \ app \ 2.0 \
    • \ web \ app \ v2.1 \

此方法具有以下优点:

  • 如果新版本有问题,您可以轻松回滚到v2.0
  • 要部署到多个物理或虚拟服务器,您可以使用脚本进行文件部署。一旦所有服务器都具有新版本,就可以使用Microsoft Web部署工具同时更改所有服务器的配置数据库。

5
我已经通过调整我们的Powershell部署脚本实现了这种方法。您可以在此处查看更改IIS站点文件夹的脚本部分:stackoverflow.com/questions/330608 / ... 感谢您的指导。
Karl Glennon

17
不幸的是,此方法不能解决数据库的结构更改。一旦为v2.1升级数据库,v.2.0就会爆炸。
EBarr 2010年

8
IMO在这里使用TxF是过大的。同时在文件系统中同时拥有v2.0和v2.1并没有什么坏处。v2.1联机时发生了很大的变化,那时TxF事务已提交。零停机时间真正发生的原因是IIS从旧的AppPool迁移到新的AppPool,而不是因为TxF。
RickNZ

5
另一个问题是,如果大量用户数据存储在应用程序文件夹的子文件夹中。
肯尼·埃维特

4
这不是0秒部署,因为新应用需要启动。
usr

12

通过将IIS中的应用程序请求路由用作不同端口上的两个本地IIS站点之间的软件负载平衡器,可以在单个服务器上实现零停机时间部署。这被称为蓝绿色部署策略,其中在任何给定时间,负载均衡器中只有两个站点之一可用。部署到“停机”的站点,对其进行热身,然后将其带入负载均衡器(通常通过“应用程序请求路由”运行状况检查),然后将原来处于运行状态的原始站点移出“池”(再次)通过使其运行状况检查失败)。

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


7

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

对于我的配置,我为每个A和B站点都有一个Web目录,如下所示:c:\ Intranet \ Live A \ Interface c:\ Intranet \ Live B \ Interface

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

当要进行实时部署时,我只是发布到STOPPED站点的位置。因为可以使用B端口访问B站点,所以可以对站点进行预热,以便第一个用户不会启动应用程序。然后使用批处理文件将实时主机标头复制到B,停止A并启动B。


1
这有助于减少由于文件复制而导致的停机时间,但与@Sklivvz一样,存在同样的问题-代码卷对数据库进行结构更改后,该站点将迅速发展。
EBarr

对我来说,这似乎也很直观,但是为什么没有一种简单的内置方法来做到这一点呢?
Petrus Theron 2012年

3
@Ebarr然后不要推出破坏性的SQL更改。例如,如果您需要删除一列,在下一版本中做,当它不再使用A或B.
Bealer

@Bealer-同意(警告)。关于“代码角色期间的停机时间”,有一系列的问题。我还没有找到真正讨论过发展数据库模式的现实的方法。注意事项-模式的两阶段更改会带来各种复杂性。一个示例-如果表定义与它理解的定义不同(许多新列或缺少列),则许多ORM栏都将出现。
EBarr 2013年

2
@Rob如果站点已停止,您如何“预热”该站点?
Andrew Gee 2014年

7

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

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

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

问题是如何将更改同步到数据库等。

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

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

现在,您需要一种方法来阻止对新AppDomain的请求。我使用命名的互斥锁-由部署代理创建和拥有,由新Web应用程序的Application_Start等待,然后在完成数据库更新后由部署代理释放。

(我在Web应用程序中使用标记文件来启用互斥锁等待行为)新的Web应用程序运行后,我将删除标记文件。


6

好的,因为每个人都反对我在2008年写的答案* ...

我将告诉您我们在2014年现在如何做。由于我们现在正在使用ASP.NET MVC,因此不再使用网站。

我们当然不需要负载平衡器和两个服务器来完成此任务,如果您维护的每个网站都拥有3台服务器,那很好,但是对于大多数网站来说,这完全是多余的。

此外,我们也不依赖Microsoft的最新向导-速度太慢,隐藏的魔术太多,也很容易更改其名称。

这是我们的方法:

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

  2. 我们使用Beyond Compare(出色的**)来验证和同步更改的文件(通过FTP,因为它受到广泛支持)直到生产服务器

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

停机时间只有毫秒。应用重新启动可能需要一两秒钟,但是在重新启动期间会缓冲请求,因此有效的停机时间为零。

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

这样,您不必将整个网站复制到其他目录,而只需将bin文件夹复制。您还可以完全控制该过程,并确切了解正在发生的变化。

**我们始终会迅速关注正在部署的更改-作为最后一分钟的双重检查,因此我们知道要测试的内容以及是否有任何中断的问题,我们已经准备就绪。我们使用“超越比较”,因为它使您可以轻松地通过FTP比较文件。没有BC,我永远不会这样做,您不知道自己要覆盖什么。

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


但是,由于应用程序池回收,您仍然会停机。
testpattern'1

不,没有停机时间,因为IIS在应用程序重新启动期间自动缓冲了请求
Mike nelson,2016年


1

对于单个服务器,我将对George的回答进行一些改进,如下所示:

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

步骤4将导致IIS工作进程回收。

如果您不使用InProc会话,则停机时间仅为零;如果可以的话,请改用SQL模式(甚至更好,请完全避免会话状态)。

当然,当有多个服务器和/或数据库更改时,它会涉及更多……。


1
与@Sklivvz相同的问题-代码卷对数据库进行结构更改后,此方法就会崩溃。
EBarr 2010年

3
这就是为什么我说在数据库发生更改时会更多地参与其中的问题。在代码中也必须要有支持,也许在数据库中也要有支持。
RickNZ

1

为了扩展sklivvz的答案,后者依赖于某种负载均衡器(或者只是同一台服务器上的备用副本)

  1. 将所有流量定向到站点/服务器2
  2. (可选)稍等片刻,以确保尽可能少的用户在已部署的版本上拥有待处理的工作流
  3. 部署到站点/服务器1并尽可能对其进行预热
  4. 以事务方式执行数据库迁移(力争做到这一点)
  5. 立即将所有流量定向到站点/服务器1
  6. 部署到站点/服务器2
  7. 直接流量到两个站点/服务器

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

如果可能且需要,请使用“路由差异”(例如不同的租户URL:s(customerX.myapp.net)或不同的用户)首先部署到一组未知的豚鼠。如果一切正常,请释放给所有人。

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

有多种方法可以使应用程序在这些情况下更好地运行,例如使用事件队列和回放机制,但是由于我们正在谈论将更改部署到正在使用的内容上,因此实际上没有万无一失的方法。


1

这是我的方法:

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

  • 1个负载平衡器/反向代理(例如nginx)在端口80上运行
  • 2个ASP.NET-Core / mono反向代理/ fastcgi chroot-jails或docker-containers侦听2个不同的TCP端口
    (甚至在2个不同的TCP端口上仅两个反向代理应用程序,没有任何沙箱)

工作流程:

开始交易myupdate

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 

-7

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

尽管不确定这是否对“ Web应用程序”有所帮助(我想您是在说这就是您正在使用的内容),这就是为什么我们始终使用“ Web站点”。同样,使用“网站”进行部署不会重新启动您的网站并删除所有用户会话。


嗨,迈克,您可能想要删除此答案。
Sohail Ahmed
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.