<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>部署 | 酷 壳 - CoolShell</title>
	<atom:link href="https://coolshell.cn/tag/%e9%83%a8%e7%bd%b2/feed" rel="self" type="application/rss+xml" />
	<link>https://coolshell.cn</link>
	<description>享受编程和技术所带来的快乐 - Coding Your Ambition</description>
	<lastBuildDate>Sun, 24 Jun 2012 11:44:33 +0000</lastBuildDate>
	<language>zh-CN</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.2</generator>
	<item>
		<title>持续部署，并不简单！</title>
		<link>https://coolshell.cn/articles/7657.html</link>
					<comments>https://coolshell.cn/articles/7657.html#comments</comments>
		
		<dc:creator><![CDATA[马基雅弗利]]></dc:creator>
		<pubDate>Mon, 25 Jun 2012 00:20:08 +0000</pubDate>
				<category><![CDATA[企业应用]]></category>
		<category><![CDATA[技术管理]]></category>
		<category><![CDATA[杂项资源]]></category>
		<category><![CDATA[流程方法]]></category>
		<category><![CDATA[agile]]></category>
		<category><![CDATA[CI]]></category>
		<category><![CDATA[部署]]></category>
		<guid isPermaLink="false">http://coolshell.cn/?p=7657</guid>

					<description><![CDATA[<p>【感谢 @常新居士 投递此文 】 这几年，持续集成随着敏捷在国内的推广而持续走热，与之相伴的持续部署也一直备受关注。自前两年，持续交付这个延续性概念又闯进了国内...</p>
<p class="read-more"><a class="btn btn-default" href="https://coolshell.cn/articles/7657.html"> Read More<span class="screen-reader-text">  Read More</span></a></p>
The post <a href="https://coolshell.cn/articles/7657.html">持续部署，并不简单！</a> first appeared on <a href="https://coolshell.cn">酷 壳 - CoolShell</a>.]]></description>
										<content:encoded><![CDATA[<p><script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158"
     crossorigin="anonymous"></script></p>
<p style="text-align: left;">【<strong>感谢 <a href="http://weibo.com/renfake" target="_blank">@常新居士</a> 投递此文</strong> 】</p>
<p style="text-align: left;">这几年，持续集成随着敏捷在国内的推广而持续走热，与之相伴的持续部署也一直备受关注。<strong>自前两年，持续交付这个延续性概念又闯进了国内IT圈，慢慢开始在社区和会议中展露头角。许多不明真相的群众跟风哭着喊着要“上”，而许多前CI的半吊子玩家换件衣服就接着干，有的甚至衣服都来不及换……</strong>。国内的这些土财主如果不巧请了某些所谓的战略家，除了建了一堆持续集成环境，以及每天嚷嚷着要这个要那个，混乱的状况在根本上没有得到改善。本文无意费力探讨持续集成和持续交付的概念，而是打算谈谈对于大型软件企业，以持续集成为基础实现持续部署（交付）时，所要面对的问题以及可行的解决方案。地主老财们，夜黑风正猛，山高路又远，注意脚下……</p>
<p style="text-align: right;"><strong>And God Said, Let there be light: and there wa</strong>&#8212; GENSIS, Charpter 1, King James</p>
<h4>一、起步</h4>
<p>先来讲个故事……</p>
<p>几年前，一对留美的夫妇通过朋友找到我，让我帮忙在国内组建一个开发团队，该团队负责为其开发一款基于社交网络的客户关系管理软件,（暂且称之为项目A）。这个项目除了尚不清晰的需求范围和很紧的期限外，作为业内人士的老公Richard根据眼下流行的软件开发过程还提了诸多额外的要求：</p>
<ul>
<li><strong>功能要及早交付</strong>（以便拿去和潜在的投资人洽谈）</li>
<li><strong>功能在部署到生产环境前要先部署的一个测试环境</strong>（Richard要试用后给予反馈）</li>
<li><strong>功能必须经过测试</strong>（长期作为软件外包的甲方，对质量要求严格）</li>
<li><strong>要减少后期维护的工作</strong>（美国人精贵，少雇一个是一个）</li>
<li><strong>支持协同开发</strong>（以便维护人员及早介入）</li>
<li>&#8230;&#8230;</li>
</ul>
<p><span style="color: #000000;"><strong>这正是持续集成所要解决的典型场景</strong></span>。针对Richard的要求，我们只要建立一个基于Hudson（现在叫Jenkins）+Maven +SVN 的持续集成环境（再加上持续集成所要求的测试和过程）就可以很好地满足上述要要求，此方案的结构如下：</p>
<p><span id="more-7657"></span></p>
<p><a href="https://coolshell.cn/?attachment_id=7686" rel="attachment wp-att-7686"><img decoding="async" loading="lazy" class="alignnone  wp-image-7686 aligncenter" src="https://coolshell.cn/wp-content/uploads/2012/06/hudsonCI2.jpg" alt="" width="693" height="719" srcset="https://coolshell.cn/wp-content/uploads/2012/06/hudsonCI2.jpg 866w, https://coolshell.cn/wp-content/uploads/2012/06/hudsonCI2-289x300.jpg 289w, https://coolshell.cn/wp-content/uploads/2012/06/hudsonCI2-768x797.jpg 768w, https://coolshell.cn/wp-content/uploads/2012/06/hudsonCI2-260x270.jpg 260w" sizes="(max-width: 693px) 100vw, 693px" /></a></p>
<p>对于上述方案，让我们近距离看看各个服务器的内部情况，以及人员在这种方案下的分工协作：</p>
<p><a href="https://coolshell.cn/?attachment_id=7708" rel="attachment wp-att-7708"><img decoding="async" loading="lazy" class="alignnone  wp-image-7708 aligncenter" src="https://coolshell.cn/wp-content/uploads/2012/06/response.jpg" alt="" width="658" height="438" srcset="https://coolshell.cn/wp-content/uploads/2012/06/response.jpg 822w, https://coolshell.cn/wp-content/uploads/2012/06/response-300x199.jpg 300w" sizes="(max-width: 658px) 100vw, 658px" /></a></p>
<p>我们先谈谈上面的图中涉及的一些概念性问题：</p>
<h5><strong>1.1）编译时依赖</strong>和<strong>运行时依赖</strong></h5>
<p>从字面上不难理解这两种依赖的类型。但要注意虽然编译时依赖常常也是运行时依赖，但并不能推断出一方必然是另一方。比如，在开发的过程中需要某些提供API的Jar包，而运行时可能是具体API实现的Jar包。再者，被依赖的包会有其自身的依赖，因此,项目对这些包产生间接依赖（<strong>运行时依赖</strong>），依此类推，最终形成一个<strong>依赖树</strong>。当项目运行时，这些依赖树上的包必须全部就位。</p>
<p>Maven在POM中通scope来界定依赖的类型，从而帮助开发和运维人员摆脱手动处理依赖树的工作，然而运行时所依赖包最终是要安装到生产环境的，这部分工作Maven并不能自动完成。因此，一个常用方式是将运行时所依赖的包拷贝到项目文件中，比如Java Web应用的WEB-INF/lib，然后将项目总的打一个包。<strong>在安装项目包后，修改环境变量，将这些包所在的路径加入相应的环境变量中，如ClassPath</strong>。</p>
<p>再看个例子，现代的操作系统和其它系统框架都考虑到了运行时依赖树的处理问题，比如Ubuntu的apt-get，CentOS的yum，Ruby的RubyGem，Node的npm等等。</p>
<h5>1.2）依赖时的复杂度</h5>
<p>项目除了对程序包的依赖，对于运行环境也有些具体的要求，比如，Web应用需要安装和配置Web服务器，应用服务器，数据服务器等，企业应用中可能需要消息队列，缓存，定时作业，或是对其它系统以Web Service方式暴露的服务。这些可以看做项目在系统层面对外部的依赖。这些依赖有些可以由项目自行处理，而有些则是项目无法处理的，比如运行容器，操作系统等，这些是项目的运行环境。</p>
<p>总之，依赖的复杂度主要有两个：</p>
<ol>
<li>依赖包间的版本兼容性问题。兼容性问题是软件开发的恶梦</li>
<li>间接依赖，或多重依赖问题。这个问题可以类比想像一下C++中的多重继续种出现的很多问题。</li>
</ol>
<div>比如：Ａ依赖于python 2.7，A还依赖于B，但是B却依赖于python 3，而Python 2.7和Python 3不兼容。这是依赖中最恶心的事。</div>
<h5>1.3）任务分工</h5>
<p>由于项目简单，因此并不需要专门的运维人员。以一个100人左右以交付为主业（恩，就是做外包）的公司为例，由于没有任何历史项目和代码的拖累，且各个项目间也没有任何关联，故而只需要配备一个IT支持人员进行资源方面的管理：分配机器，报修，初始化系统，分配IP地址等。各个项目的运行环境、数据库、开发环境等都由具体项目的开发人员手动完成。 环境出问题怎么办？很简单，凉拌——重装系统。实际的运行效果不错。</p>
<h5>1.4）自动化部署</h5>
<p>由于Hudson这样的持续集成环境提供了自动编译（定时或触发式）的功能，而且可以在编译过程中提供了一些扩展点，因此通过提供一个部署用的脚本，就可以非常容易实现简单的自动化部署。</p>
<p>毫无疑问，持续集成就是敏捷的魔法药，它见效快、副作用小、业界的争论少。每每运用在混乱的项目中时，几周内项目就开始持续的产出经过测试的功能。对于独立项目，以持续集成为中心的持续部署绝对是不二选择。</p>
<p><strong>但是，我们有没有想过，这会是一个自动化部署的通用解决方案吗？持续集成应该位于持续交付的中心吗？</strong></p>
<h4>二、困境</h4>
<p>回到我们的故事：项目A上线两年后，运营业绩不错，投资人第一轮注资后，Richard的公司进行了扩张，他们对项目进行了重构，而且随着用户数量的增长，公司分别在美国、英国和日本等地建立了运营中心，并且对亚洲市场进行的定制功能开发（项目A+），接下来，公司又投入开发了团购系统（项目B）。在获得了新一轮投资后，各条本来比较简单的业务和功能线上越来越复杂，需要不断地细分，于是公司再度扩张（开发人员达到了300人，国内200多人，而运维团队主要在美国），随后又为项目A/A+的高级用户开发了问答系统（项目C）。目前，他们正准备开发手机系统。 看看下面的图，公司增长的过程中，整个项目环境也变得复杂。（注意，这里是一种逻辑结构，而在物理层面项目B和项目A的生产环境可能部署在相同的机器上）。</p>
<p><a href="https://coolshell.cn/?attachment_id=7694" rel="attachment wp-att-7694"><img decoding="async" loading="lazy" class="alignnone  wp-image-7694 aligncenter" src="https://coolshell.cn/wp-content/uploads/2012/06/sampleT1-1024x529.jpg" alt="" width="717" height="370" srcset="https://coolshell.cn/wp-content/uploads/2012/06/sampleT1-1024x529.jpg 1024w, https://coolshell.cn/wp-content/uploads/2012/06/sampleT1-300x155.jpg 300w, https://coolshell.cn/wp-content/uploads/2012/06/sampleT1.jpg 1990w" sizes="(max-width: 717px) 100vw, 717px" /></a></p>
<p>同时，原本单一的项目软件结构随着业务系统的增加也不再简单： <img decoding="async" loading="lazy" class="alignnone  wp-image-7697" src="https://coolshell.cn/wp-content/uploads/2012/06/software.jpg" alt="" width="763" height="414" srcset="https://coolshell.cn/wp-content/uploads/2012/06/software.jpg 954w, https://coolshell.cn/wp-content/uploads/2012/06/software-300x162.jpg 300w" sizes="(max-width: 763px) 100vw, 763px" /></p>
<p>而软件间的版本依赖使这个问题变得更为复杂：</p>
<p><a href="https://coolshell.cn/?attachment_id=7700" rel="attachment wp-att-7700"><img decoding="async" loading="lazy" class="alignnone size-full wp-image-7700 aligncenter" src="https://coolshell.cn/wp-content/uploads/2012/06/dependency.jpg" alt="" width="626" height="343" srcset="https://coolshell.cn/wp-content/uploads/2012/06/dependency.jpg 626w, https://coolshell.cn/wp-content/uploads/2012/06/dependency-300x164.jpg 300w" sizes="(max-width: 626px) 100vw, 626px" /></a></p>
<p>现在，Richard的公司已经不再是一条快乐的小鱼，而是渐渐成为一直庞大的巨兽。虽然只有四个产品，但公司却要支持几百台开发机，几十台生产服务器，还有对应的测试环境，数据库服务器，以及几十个开发小组，和一大堆的内部项目。我们尽可以使用持续集成来为我们完成自动化部署。但，<strong>当我们为各个项目建立起持续集成环境后，它能满足我们对于持续部署的要求吗？我们前期的工作可以简化我们今后项目的持续交付的工作的难度吗？它需要我们为之建立一个庞大的运维团队，还是可以让我们能节省下每一毛钱来投入到真正的业务价值中去？</strong></p>
<p><strong>让我们先来看看复杂的项目环境中的几个场景</strong>：</p>
<p><strong>场景1：环境升级</strong></p>
<p style="padding-left: 30px;">项目A和项目B都依赖于Web容器，公司决定升级Web容器版本，而公司要升级的机器有上百台，依赖人肉升级已不现实，维护团队因此针对各种软件开发了相应的自动化脚本，但当新的软件出现时，必须要开发新的脚本。而且当同时升级若干环境软件时，则难度随之增大，手工调度的方式极易出错，当升级失败时仍需要大量人工处理。由于存在大量升级脚本，有一定的维护成本。</p>
<p><strong>场景2：依赖于环境的软件升级与回滚</strong></p>
<p style="padding-left: 30px;">针对环境升级，公司为项目A和项目B开发了新的版本。但环境的升级和软件的升级不是同步进行，出错的可能性非常大（想一想间接依赖和多重依赖的情况）。当新版本部署到生产系统时，发现问题，需要回滚到之前的版本——所有运行时版本都需要回滚，而且环境也需要同步回滚。几百台机器……</p>
<p><strong>场景3：运行时依赖</strong></p>
<p style="padding-left: 30px;">在第一节的方案中，我们将所有的运行时依赖都打包到一起。当项目依赖关系复杂时，这样产生的包将非常臃肿，潜在地延长了部署的时间（想一想全世有几百台服务器，一个部署计划需要部署几百兆文件的情况），而且产生冲突的可能性非常大，而且对于不同类型的项目（Java和Ruby项目）缺乏通用性。06年左右，Nortel可是拿Excel统计过运行时依赖的，牵涉若干项目组，反复多次，没有个把月真搞不定。</p>
<p><strong>场景4：泛滥的部署</strong></p>
<p style="padding-left: 30px;">每个项目相关的持续集成环境都需要开发自己的部署脚本，重复投入大，而且各个项目的部署过程不一致，并且对于同一个项目无法同时满足不同目的部署要求，例如，环境或系统配置参数改变后，无需安装包，只需做清理和激活的工作。最后，持续集成只是支持了和代码修改有关的部署。</p>
<p><strong>场景5：不一致的环境</strong></p>
<p style="padding-left: 30px;">简单项目中，开发环境和运行环境都由开发人员搭建，当公司变大时，系统的运行环境将由运维人员搭建，而开发环境如果由运维人员搭建则工作量太大，由开发人员自己搭建则操作复杂又容易产生不一致的情况。</p>
<p><strong>场景6：热切换</strong></p>
<p style="padding-left: 30px;">对于某些部署，需要尽量减少服务的停止时间，需要在服务的同时进行部署。</p>
<p>这些场景只是以持续集成为中心的持续部署在面对大型企业时所遇到的部分问题。大型企业，人多，项目多，机器多，项目环境复杂，部署维护工作繁多。以持续集成为基础的部署可以解决各个项目的集成问题，却无法帮助企业应对复杂的项目环境和各种不同的部署要求。<strong>究其更本，大型企业中的部署不再是一个简单的问题，而是一个交付生态圈，基础设施和环境管理必须要纳入考虑之中。</strong>要实现真正意义上的持续部署，我们就必须<strong>把环境和项目同等对待</strong>，通通纳入管理之中。同时，部署本身要得到统一。<strong>一个好的部署机制，应该是易于建立，易于使用，易于维护。</strong></p>
<h4>三、任脉——环境管理</h4>
<p>什么是环境？</p>
<p>系统运行所依赖和包含的一切就是其环境：硬件、操作系统，网络资源（IP地址、域名），服务容器，服务器软件配置，环境亦是，运行时依赖的命令和包，项目本身的包和配置都是环境的一部分。对于部署而言，广义上，这些通通应该纳入环境管理的范畴，但狭义上，从软件系统的角度看，一个环境就是其运行需要的软件及其配置（我们先把操作系统和网络资源当做基础设施，其在部署时已处于就位的情况）。因此：</p>
<p><strong>项目A的生产环境 = 项目A本身的软件包 + 项目A运行时依赖的软件包 + 项目A运行时依赖的其它软件 + 项目A的配置信息</strong></p>
<p>由于，项目本身的软件包、项目运行时依赖的软件包，以及项目运行时依赖的其它软件在本质上没有区别——都是软件，上面的定义可以进一步抽象为：</p>
<p><strong>环境 = 软件包 + 配置信息</strong></p>
<p>在这个定义下，我们就必须将运行环境的软件解构，并以包的形式导入到公司的整个项目资源库中，比如Apache将作为一个包被导入，而Apache依赖的其它包也将依次被导入，并建立起正确的依赖关系。而且，在导入的过程中还必须做些相应的调整，如，环境变量的读取和设置，必须来自于环境配置模块，而不要修改系统的环境变量，防止不同环境在系统环境配置上相互影响和依赖。</p>
<p>再回头审视我们的示例，项目A的生产环境可以部署在不同的区域，对于各个区域可能有定制化的设定。这就像面向对象中的类，可以通过继承使子类重用父类的公有属性和行为并添加自己特有的信息。因此，环境的概念模型如图：</p>
<p><a href="https://coolshell.cn/?attachment_id=7725" rel="attachment wp-att-7725"><img decoding="async" loading="lazy" class="alignnone size-full wp-image-7725 aligncenter" src="https://coolshell.cn/wp-content/uploads/2012/06/Env.jpg" alt="" width="523" height="118" srcset="https://coolshell.cn/wp-content/uploads/2012/06/Env.jpg 523w, https://coolshell.cn/wp-content/uploads/2012/06/Env-300x67.jpg 300w" sizes="(max-width: 523px) 100vw, 523px" /></a></p>
<p>通过这样的关系，我们很容易为示例的复杂环境建立一种简单的结构，对于项目A：</p>
<p><a href="https://coolshell.cn/?attachment_id=7729" rel="attachment wp-att-7729"><img decoding="async" loading="lazy" class="alignnone size-full wp-image-7729 aligncenter" src="https://coolshell.cn/wp-content/uploads/2012/06/org1.jpg" alt="" width="599" height="519" srcset="https://coolshell.cn/wp-content/uploads/2012/06/org1.jpg 599w, https://coolshell.cn/wp-content/uploads/2012/06/org1-300x259.jpg 300w" sizes="(max-width: 599px) 100vw, 599px" /></a></p>
<p>这里，环境依然是处于知识层面（Knowledge Level），它并未与具体的基础设施相关联。当我们将一个环境“具现化”成一个运行系统时，我们就产生了一个真正的环境实例。在这两者之间，我们还必须要考虑环境实例的使用目的（开发？测试？……）以及安装所依赖的其它信息（如机器），因此，我们需要增加一个环境目标来集中这些信息，而且由于不同目标的环境可能会有所差别，因此，环境目标也需要配置的能力。概念模型如图：</p>
<p><a href="https://coolshell.cn/?attachment_id=7731" rel="attachment wp-att-7731"><img decoding="async" loading="lazy" class="alignnone size-full wp-image-7731 aligncenter" src="https://coolshell.cn/wp-content/uploads/2012/06/target.jpg" alt="" width="523" height="438" srcset="https://coolshell.cn/wp-content/uploads/2012/06/target.jpg 523w, https://coolshell.cn/wp-content/uploads/2012/06/target-300x251.jpg 300w" sizes="(max-width: 523px) 100vw, 523px" /></a></p>
<p>图中的环境实例是如何产生的呢？<strong>部署</strong>，<strong>一次部署可能会产生一个环境实例。</strong>一系列部署将产生对应于环境目标的多个环境实例，除去当前起作用的环境实例外（最新的），其它的是历史环境实例。<strong>通过在历史环境实例中切换，我们自然而然的就可以使整个环境回滚，因为项目所依赖的一切都已经成为的环境中的软件包，而且环境依赖的包的版本会随着部署具体确定下来。</strong>如此一来，我们便可以给每个环境实例分配一个版本号，再通过环境实例的版本号与软件包的版本对应起来，从而得知一次部署时应用的具体软件包，如图：</p>
<p><a href="https://coolshell.cn/?attachment_id=7732" rel="attachment wp-att-7732"><img decoding="async" loading="lazy" class="alignnone  wp-image-7732 aligncenter" src="https://coolshell.cn/wp-content/uploads/2012/06/version.jpg" alt="" width="669" height="394" srcset="https://coolshell.cn/wp-content/uploads/2012/06/version.jpg 743w, https://coolshell.cn/wp-content/uploads/2012/06/version-300x176.jpg 300w" sizes="(max-width: 669px) 100vw, 669px" /></a></p>
<p>目前的环境管理结构，已经可以解决场景1、2和5的问题。那<strong>么对于场景2，运行时依赖，环境管理应该如何解决呢？</strong></p>
<p>细心的朋友，可能已经发现，<strong>在环境层面上我们确定了环境依赖的软件包</strong>，这里有两个隐藏的含义：</p>
<ul>
<li>环境定义的是对软件包的运行时依赖</li>
<li>由于环境是一个逻辑上的概念，因此其所用的软件包也是一个逻辑上的概念（相对于版本控制系统中的软件包）</li>
</ul>
<p>我们也已经知道，在部署时，一个环境实例将具体的确定其依赖的软件包的版本。某个版本的软件包最终与代码库中的物理的软件包相关联。但软件包是运行时的安装包，因此，它应该是代码库中包编译的结果。在对代码库的包编译时，既要将结果打上版本保存起来，也好在两者的版本间建立关系，最后，编译结果应该是某种既定的安装包目录文件结构。</p>
<p>另外，当环境包含的包比较多时，运行时版本树会非常大，手动的指定全部的包的版本将是一个非常大的体力劳动，这部分工作也要得到简化。由此，我们必须</p>
<div>
<ul>
<li><strong>建立逻辑软件包版本和版本库中软件包版本间的关系</strong></li>
<li><strong><strong>为相互依赖的包编译并打上统一的标签</strong></strong></li>
<li><strong>简化运行时包依赖关系的生产</strong></li>
<li><strong>简化运行时包依赖的指定（可参考apt-get和RubyGem，环境只需指定直接依赖的包，间接依赖的包从运行时依赖树中自动导入）</strong></li>
</ul>
<div>一个可能的简单结构如下：</div>
<p><a href="https://coolshell.cn/?attachment_id=7736" rel="attachment wp-att-7736"><img decoding="async" loading="lazy" class="alignnone size-full wp-image-7736 aligncenter" src="https://coolshell.cn/wp-content/uploads/2012/06/pkg1.jpg" alt="" width="783" height="195" srcset="https://coolshell.cn/wp-content/uploads/2012/06/pkg1.jpg 783w, https://coolshell.cn/wp-content/uploads/2012/06/pkg1-300x74.jpg 300w" sizes="(max-width: 783px) 100vw, 783px" /></a></p>
<p>上述讨论还没有涉及操作系统，<strong>如果我们的运行机器要支持多个系统，我们又该怎么办？？？</strong></p>
<p>配置信息也是个大问题，大家可以思考</p>
<ul>
<li><strong>环境配置和应用配置如何区分？</strong></li>
<li><strong>如何简化环境配置工作？</strong></li>
<li><strong>如何使环境配置的效果只对具体环境有效，而不会泄露到环境外部？</strong></li>
</ul>
</div>
<p>再者，</p>
<ul>
<li><strong>如何使应用支持多运行目标？</strong></li>
<li><strong>环境管理如何能方便开发环境的调试？</strong></li>
<li><strong>要如何简化版本的选择?</strong></li>
<li><strong><strong>在多个包有编译和运行时依赖时，编译时如何检查以减少引入兼容性问题的风险？</strong></strong></li>
</ul>
<p>这些都留待大家思考。</p>
<h4>四、督脉——部署系统</h4>
<p>《持续集成》和《持续交付》中都对部署有详细的讨论，不在赘述。<strong>在我看来，部署其就是按照其目的执行一系列步骤将环境置于其目的所指向的状态中</strong>。我们一会再回国头来看这段文绉绉的话，先看看第一部分持续集成的环境下，我们部署的步骤可能会是下面这个样子：</p>
<ol>
<li>登陆目标机（ssh）</li>
<li>停止服务</li>
<li>清理环境</li>
<li>准备安装环境（创建文件夹等）</li>
<li>安装项目包（rsync，解压，权限设置等）</li>
<li>配置环境变量</li>
<li>启动服务</li>
<li>&#8230;&#8230;</li>
</ol>
<p>而在第二部分的<strong>情景4</strong>中，我们看到如果对不同的持续集成环境建立不同的部署脚本和环境维护脚本，这部署过程的维护会非常繁琐。基于第三部分的环境管理，我们可以将部署过程抽象为：</p>
<p><a href="https://coolshell.cn/?attachment_id=7737" rel="attachment wp-att-7737"><img decoding="async" loading="lazy" class="alignnone size-full wp-image-7737 aligncenter" src="https://coolshell.cn/wp-content/uploads/2012/06/deploy.jpg" alt="" width="566" height="351" srcset="https://coolshell.cn/wp-content/uploads/2012/06/deploy.jpg 566w, https://coolshell.cn/wp-content/uploads/2012/06/deploy-300x186.jpg 300w" sizes="(max-width: 566px) 100vw, 566px" /></a></p>
<p>现在回到开头那个文绉绉的描述：<strong>部署其就是按照其目的执行一系列步骤将环境置于其目的所指向的状态中</strong>。</p>
<p>由于我们已经将部署作为环境管理的一部分，而环境又是对外提供服务的最小实体，因此，对环境的部署就是要根据部署的类型，在环境上按一定的步骤执行一系列操作，从而使环境置于部署类型所要的状态，这个过程中可能会生成对应的环境实例。举例来说，我们可能会修改环境相关的一些配置，然后重启环境，显然，这种情况下不需要下载安装软件包（没有改变），因此也就不需要生成环境实例。</p>
<p>对于标准的部署——安装软件包并启动环境，可能的步骤将会是：</p>
<ol>
<li>选择将要部署的软件包的版本</li>
<li>生成新的环境实例（确定环境实例的版本和其依赖包的版本，确定环境配置等）</li>
<li>清理和准备目标机环境</li>
<li>下载包</li>
<li>设置环境配置</li>
<li>环境实例切换</li>
<li>生成部署报告</li>
<li>……</li>
</ol>
<p>好，部署系统和环境管理各就各位，我们可以将各个项目环境纳入我们的环境管理之中，甚至是持续集成环境本身。再补充一句，要让部署系统和环境管理能很好的发挥作用，我们即需要一个简单一致的UI界面（为开发人员），也需要提供一个清晰明了的服务接口（供外部系统调用，如持续部署系统）。<strong>对于与环境管理相关的机器状态管理，网络资源的配置等等，本文不再涉及，大家可以自己思考</strong>。环境管理的实现、编译系统改造以及持续部署的具体实现，另作文章探讨。</p>
<p>就技术而言（不考虑围绕持续部署的过程实践），环境管理、部署系统以及我们没有提及的编译系统改造才是生产线的真正引擎，持续部署不过是水到渠成的传送带而已。</p>
<h4>五、没完</h4>
<p>打通了任督二脉后，事还还没有完，还有很多细节上的问题。你想，这个工具实在是太好用了，于是公司里成百上千的工程师们都在使用这个自动化部署系统，我们又会面对很多很多问题：</p>
<ul>
<li><strong>部署系统的性能问题</strong>。几百号人不停地在把他们的软件部署到自己的机器上，部署到测试环境，部署到生产环境，一天之内一个人可能会要部署N次，回滚N次，不但有大量部署请求，还有大量的文件在网络上传输。你得想想这套部署系统如何解决这些性能问题，还得考虑未来更大规模的性能水平扩展问题。</li>
</ul>
<ul>
<li><strong>目标机环境的管理。</strong>在目标运行机上需要解决几个问题：1）两个环境间如果有一些的一样的包，那就没有必要再下载了，这样可以节约时间。2）每次部署都需要把老的部署环境给保留下来，这样方便在新旧环境下的切换。这两点对于在生产环境下部署非常关键。（这需要环境内所有软件的绿色安装才能更容易达到这个目标，因些，Unix/Linux会比Windows更容易做到这点）</li>
</ul>
<ul>
<li><strong>部署一致性事务问题</strong>。有时候，我们需要同时部署若干台服务器，比如：包A到机器MA，包B到机器MB，包C到机器MC，……（Web Service的SOA架构），这些包之间有运行依赖性和兼容性问题，要么一次性全部完成，要么就全部失败。回滚也是一样的，这是一个部署事务或部署一致性的问题。如何解决呢？</li>
</ul>
<ul>
<li><strong>部署环境的版本控制问题</strong>。前面说过，我们的一个环境就会和若干个包的版本耦合，环境必需管理要部署的包的版本。于是，当你的部署越来越多的时候，各个环境的包的版本开始出现混乱，各种依赖间的版本也会出现不统一的情况，也就是说，就算你有这样的一个工具，在一个高速开发的环境下，我们的部署环境的管理还是会出现很多混乱的情况，需要你不断地统一大家的开发、测试环境。</li>
</ul>
<ul>
<li><strong>部署计划</strong>。我们可能会有很多部署计划，比如：设定定时部署，提升或降低部署优先级，部署事务定义，部署策略（如：先部署10%的机器，如果没有问题，再把剩下的系统部署了），热切计划和策略…… 等等 ，等等 。</li>
</ul>
<ul>
<li><strong>部署的监控和维护</strong>。任何软件和系统都会有这样的问题，当规模上去了以后，我们的自动化部署系统的监控和维护的复杂度并不亚于一个大型的互联网应用。</li>
</ul>
<div>这样的问题会有很多，基本上来说，<strong>这样一个持续集成持续部署的自动化系统并不是那么简单的事，其开发工作量和一个标准的大型互联网业务系统没什么两样</strong>。</div>
<h4>六、总结</h4>
<p>这里只谈一点自己的看法，从传统的持续集成到面向大型软件的持续部署，我们将系统所依赖的软件环境和软件包抽象为一致的实体纳入到管理之中，并将运维人员的工作真正的分摊到开发人员身上。而云计算的出现，使得计算机本身也可以自动化的创建和回收，这样环境管理的范畴将进一步扩充。相应的，部署的能力和灵活性也是一次质的飞跃，将再一次减轻运维人员的工作压力。</p>
<p>说了这么多废话，总结一下自己的观点，对于向大型软件企业推销基于持续集成的持续部署（交付）的哥们：</p>
<ul>
<li><strong>你就是在耍流氓</strong>，如果你不解决环境管理！！！</li>
<li><strong>你就是在耍流氓</strong>，如果你不建立部署系统！！！</li>
<li><strong>你就是在耍流氓</strong>，如果你不扩展编译系统！！！</li>
<li><strong>你就是在耍流氓</strong>，如果你只是推销小团队的实践而不考虑改造大环境！！！</li>
<li><strong>你就是个流氓</strong>，如果你只是不断地告诉别人怎么做，自己却从来不动手写一个测试或建立一个持续集成环境！！！</li>
</ul>
<p>最后，用Linus最经典的话来结束本文——“ Talk is Cheap, Show me the Code！”</p>
<p>（<strong>注：本文由<a href="http://weibo.com/renfake" target="_blank">@常新居士</a>完成初稿，我做了一些编辑，主要写了第五节“没完”</strong> ）<!--



<p align="center"><a href= target=_blank><img decoding="async" src=""></a></p>





<p align="center"><img decoding="async" src="https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg"> <img decoding="async" loading="lazy" src="https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg" width="300" height="300"> <br />关注CoolShell微信公众账号和微信小程序</p>

 

--></p>
<div style="margin-top: 15px; font-size: 16px;color: #cc0000;">
<p align="center"><strong>（转载本站文章请注明作者和出处 <a href="https://coolshell.cn/">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>
</div>

<div class="wp_rp_wrap  wp_rp_vertical_m" id="wp_rp_first"><div class="wp_rp_content"><h3 class="related_post_title">相关文章</h3><ul class="related_post wp_rp"><li ><a href="https://coolshell.cn/articles/8209.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2012/09/fight-150x150.jpg" alt="“单元测试要做多细？”" width="150" height="150" /></a><a href="https://coolshell.cn/articles/8209.html" class="wp_rp_title">“单元测试要做多细？”</a></li><li ><a href="https://coolshell.cn/articles/5531.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg" alt="Test-Driven Development？别逗了" width="150" height="150" /></a><a href="https://coolshell.cn/articles/5531.html" class="wp_rp_title">Test-Driven Development？别逗了</a></li><li ><a href="https://coolshell.cn/articles/5625.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/19.jpg" alt="“品质在于构建过程”吗？" width="150" height="150" /></a><a href="https://coolshell.cn/articles/5625.html" class="wp_rp_title">“品质在于构建过程”吗？</a></li><li ><a href="https://coolshell.cn/articles/5143.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/10.jpg" alt="在新浪微博上关于敏捷的一些讨论" width="150" height="150" /></a><a href="https://coolshell.cn/articles/5143.html" class="wp_rp_title">在新浪微博上关于敏捷的一些讨论</a></li><li ><a href="https://coolshell.cn/articles/5044.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2011/07/hat-150x150.jpeg" alt="为什么Scrum不行？" width="150" height="150" /></a><a href="https://coolshell.cn/articles/5044.html" class="wp_rp_title">为什么Scrum不行？</a></li><li ><a href="https://coolshell.cn/articles/4891.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg" alt="Bob大叔和Jim Coplien对TDD的论战" width="150" height="150" /></a><a href="https://coolshell.cn/articles/4891.html" class="wp_rp_title">Bob大叔和Jim Coplien对TDD的论战</a></li></ul></div></div>The post <a href="https://coolshell.cn/articles/7657.html">持续部署，并不简单！</a> first appeared on <a href="https://coolshell.cn">酷 壳 - CoolShell</a>.]]></content:encoded>
					
					<wfw:commentRss>https://coolshell.cn/articles/7657.html/feed</wfw:commentRss>
			<slash:comments>48</slash:comments>
		
		
			</item>
		<item>
		<title>预发布环境,Tag发布机制和可重复的部署过程</title>
		<link>https://coolshell.cn/articles/3709.html</link>
					<comments>https://coolshell.cn/articles/3709.html#comments</comments>
		
		<dc:creator><![CDATA[陈皓]]></dc:creator>
		<pubDate>Sun, 20 Feb 2011 07:28:59 +0000</pubDate>
				<category><![CDATA[技术读物]]></category>
		<category><![CDATA[流程方法]]></category>
		<category><![CDATA[Tag]]></category>
		<category><![CDATA[投递]]></category>
		<category><![CDATA[部署]]></category>
		<guid isPermaLink="false">http://coolshell.cn/?p=3709</guid>

					<description><![CDATA[<p>下面文章由网友吕毅投递，源文是：http://blog.lvscar.info/?p=427 —————————————————————————————————...</p>
<p class="read-more"><a class="btn btn-default" href="https://coolshell.cn/articles/3709.html"> Read More<span class="screen-reader-text">  Read More</span></a></p>
The post <a href="https://coolshell.cn/articles/3709.html">预发布环境,Tag发布机制和可重复的部署过程</a> first appeared on <a href="https://coolshell.cn">酷 壳 - CoolShell</a>.]]></description>
										<content:encoded><![CDATA[<p><script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158"
     crossorigin="anonymous"></script><span style="font-size: 11pt;"><strong>下面文章由网友<a href="http://blog.lvscar.info/" target="_blank">吕毅</a>投递，源文是：<a href="http://blog.lvscar.info/?p=427" target="_blank">http://blog.lvscar.info/?p=427</a></strong></span></p>
<p>—————————————————————————————————————————————</p>
<p>周末聚会，无意间聊起建筑行业。自己是搞软件开发的，我们的行业从建筑设计/施工过程中借鉴了大量的概念，隐喻，名词。可以说软件就是现实中伴随整个人类历史发展的“建筑”在虚拟空间中的投影。有个两年前问过其他朋友的问题，这次友人又再次提起，“为什么建筑设计过程中没有普遍性的采用版本控制呢？” 瞎扯了一干各种原因后，我们几乎同时想到一个名字”Joel”，建筑设计行业或许缺乏像<a href="http://www.joelonsoftware.com/">Joel Spolsky</a>一样十数年如一日，把自己丰富的经验和深入的思考转化成一篇篇文章以向新人传授软件开发过程中那些容易被忽略的概念。高傲的黑客们会对CMMI之类的认证抱以鄙夷之情，但对Joel整理出的12条写出更好软件的”最佳实践”，大家甚至把此称为审视其他团队开发过程的<a href="http://www.joelonsoftware.com/articles/fog0000000043.html">“Joel TEST”</a>以推崇</p>
<p>这12条测试如下:</p>
<blockquote><p>1. 是否启用版本控制？</p>
<p>2. 是否可以一步构建?</p>
<p>3. 是否进行每日构建？</p>
<p>4. 是否有bug跟踪列表？</p>
<p>5. 是否在修改bug后，才开始写新代码？</p>
<p>6. 是否及时更新工作计划？</p>
<p>7. 是否在开发前编写了大家一致认可的功能文档？</p>
<p>8. 是否有安静的工作环境？</p>
<p>9. 是否在使用最好的软件开发工具？</p>
<p>10.是否有专职测试人员？</p>
<p>11.是否在面试时以实际编写代码来检查求职者？</p>
<p>12.是否利用陌生人进行可用性测试？</p></blockquote>
<p>你所在的团队符合其中的几条呢？  觉得这些条目太一般，软件开发原本就该如此？ Joel Test写于十年前，一个Windows XP，Mac OS X,Ubuntu都还没有面世的年代。 如果你觉得这些条目有些过时了，Google中搜索“Joel  Test”，你可以看到这十年内很多对此进行更新的尝试, 比如这两个页面<a href="http://geekswithblogs.net/btudor/archive/2009/06/16/132842.aspx">“The Joel Test  Update for   2010″</a>,<a href="http://allinthehead.com/retro/228/">“Joel  Test  for  web dev”</a>.</p>
<p><span id="more-3709"></span></p>
<p>我的主要工作集中在”Web/Mobile  Web”领域，在”Joel Test”写就的年代，Web技术仅仅是一些用记事本就能写出的Html页面。但到了今天，到了经历过BS浪潮,后端编程语言井喷涌现，Ajax和HTML5变得人人皆知的今天。Web技术已经变成了一个由N种后端技术*N种开发语言/框架*N种前端技术交织起来的复杂体系。Web 程序员们觉得Joel开出的列表仍然有价值,那是因为我们的大部分工作仍然延续着上一代程序员们开创的轨迹；我们仍然在通过程序代码释创造力同时避免BUG的出现;我们仍然得谨慎的在强大,华丽与高效之间做着权衡. 相比客户端,Web技术最大的优势在于部署成本的节省,我们的程序和Joel年代最大的区别也在于此。这一年来新的工作岗位让我学到了很多,部署过程正是其中我觉得最值得和大家分享的部分.</p>
<p>下面这个列表来自前阵子看到的一篇很好的文章<a href="http://www.kalzumeus.com/2010/12/12/staging-servers-source-control-deploy-workflows-and-other-stuff-nobody-teaches-you/">Staging Servers, Source Control &amp; Deploy Workflows, And Other Stuff Nobody Teaches You</a>,标题中的列出的三项和我的体会高度吻合,下面我会对他们一一做出自己的诠释</p>
<blockquote><p>1.是否采用了预发布环境</p>
<p>2.是否以Tag作为发布单位</p>
<p>3.是否让部署过程是可重复的</p></blockquote>
<div>
<h3>是否采用了预发布环境</h3>
<p>关于测试驱动开发的鼓吹中,”免除对代码修改的恐惧”十分具有诱惑力.我们都不喜欢功能逐渐丰富过程中冷不防出现的各种BUG,这些BUG打乱我们的计划,破坏我们的心情,从而让我们对开发新功能的旅程心存恐惧.TDD的最大魅力也来自于通过测试先行来保证后续的功能扩展相对于预期是可验证的. 不过无论你的WEB开发过程是怎样的,最终的代码和内容还是要通过发布来送达到用户浏览器中,你可以对PK需求,修改BUG,延长加班毫无畏惧,但你不能忽略用户体验.代码一旦部署到正式环境上,对你工作的评判不再是项目组中关心你,体谅你的同事.而是千万对错误零容忍的用户. 在发布前你已经做过周全的测试? 新增的每一项功能已经测试过? 很好.不过是在你的开发环境或某处偏僻的”测试环境”中? 服务器OS不一样,Web Server有差别,缓存服务未启用,APP容器或解释器,数据库版本有差别,没接通第三方API, 这所有的一切都可能会造成发布后,你自己或用户刷新网站后的那声”What The fuck?”, 我想这应该是较之修改BUG,你更不想面对的情景吧.</p>
<p>总的说来,”预发布环境”就等于没有真实用户访问的生产环境, 除了让用户不能访问到外,尽一切可能让这个环境和生产环境一致.每次正式发布时以这个环境为目标,测试流程完成后.把发布内容从这个环境”平移”到生产环境.</p>
</div>
<div>
<h3>是否以Tag作为发布单位</h3>
<p>从业几年来,”所在团队把SVN当FTP用”是几乎每次朋友们互相吐槽时都能听到的话题,”SVN的分支合并太难用;需要更密切和团队伙伴共享工作内容…”我们可以很轻松的找到不创建功能分支然后进行合并的理由,事实上这么做可能也有一定的”合理性”.但发布时打个Tag,对你的现有开发流程几乎不会带来负担.你不需要切换到<a href="http://git-scm.com/">Git</a>或<a href="http://mercurial.selenic.com/">Mercury</a>,唯一要做的只是在提交后,发布前运行一行svn copy命令,然后在发布目标上用svn switch命令代替svn update来更新代码.只有一点需要注意,创建Tag的svn copy命令的目标最好是一个新的SVN仓库地址(新Tag路径),而不是本地目录.这么做的理由是当以仓库路径作为svn copy目标时,不会产生文件拷贝,而以本地路径为目标执行时,会发生文件拷贝,如果项目包含很多文件,这个过程会较为漫长.如果想避免本地打tag时的文件拷贝,你可切换到分布式版本控制系统.</p>
<p>这么做的好处也是明显的,虽然我们已经通过预发布环境规避了大部分发布环境可能引入的问题.但当那”万一”发生时.你能够以最快的速度切换到上一次发布时的状态.通常可以通过”$svn switch [上次发布Tag的SVN路径]“一行命令搞定.</p>
</div>
<div>
<h3>是否让部署过程是可重复的</h3>
<p>如果你所在的团队对开发和运维工作进行了严格切分,这不会是一个问题.但不是所有项目都会到这个规模,如果你是一个幸福的能变更生产环境的Web程序员,请千万小心,你对生产环境的每次调整/优化,都可能让项目部署过程变得不可重复.随着时间的推移,你会忘记当时的配置项.一旦项目需要扩容,恢复,移交.这过程都可能演变成灾难.</p>
<p>上面提到那篇文章中,提倡用部署脚本来管理部署过程.这是很好的解决方法,但如果你暂时缺乏系统脚本编程能力.分门别类把每次环境配置过程记录清楚吧,就当这项工作要在你不在场的情况下被别人重复执行.</p>
</div>
<p>别人说我们是”码农”,我们要把自己当工程师.<!--



<p align="center"><a href= target=_blank><img decoding="async" src=""></a></p>





<p align="center"><img decoding="async" src="https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg"> <img decoding="async" loading="lazy" src="https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg" width="300" height="300"> <br />关注CoolShell微信公众账号和微信小程序</p>

 

--></p>
<div style="margin-top: 15px; font-size: 16px;color: #cc0000;">
<p align="center"><strong>（转载本站文章请注明作者和出处 <a href="https://coolshell.cn/">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>
</div>

<div class="wp_rp_wrap  wp_rp_vertical_m" ><div class="wp_rp_content"><h3 class="related_post_title">相关文章</h3><ul class="related_post wp_rp"><li ><a href="https://coolshell.cn/articles/7657.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2012/06/hudsonCI2-150x150.jpg" alt="持续部署，并不简单！" width="150" height="150" /></a><a href="https://coolshell.cn/articles/7657.html" class="wp_rp_title">持续部署，并不简单！</a></li><li ><a href="https://coolshell.cn/articles/2753.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2010/08/Best-Programming-Quotations-150x150.jpg" alt="最佳编程语录" width="150" height="150" /></a><a href="https://coolshell.cn/articles/2753.html" class="wp_rp_title">最佳编程语录</a></li><li ><a href="https://coolshell.cn/articles/172.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/21.jpg" alt="幽默：程序员的进化" width="150" height="150" /></a><a href="https://coolshell.cn/articles/172.html" class="wp_rp_title">幽默：程序员的进化</a></li><li ><a href="https://coolshell.cn/articles/1532.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg" alt="到处都是Unix的胎记" width="150" height="150" /></a><a href="https://coolshell.cn/articles/1532.html" class="wp_rp_title">到处都是Unix的胎记</a></li><li ><a href="https://coolshell.cn/articles/2672.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg" alt=".NET代码转换器" width="150" height="150" /></a><a href="https://coolshell.cn/articles/2672.html" class="wp_rp_title">.NET代码转换器</a></li><li ><a href="https://coolshell.cn/articles/2109.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/15.jpg" alt="Python处理encoding的小技巧" width="150" height="150" /></a><a href="https://coolshell.cn/articles/2109.html" class="wp_rp_title">Python处理encoding的小技巧</a></li></ul></div></div>The post <a href="https://coolshell.cn/articles/3709.html">预发布环境,Tag发布机制和可重复的部署过程</a> first appeared on <a href="https://coolshell.cn">酷 壳 - CoolShell</a>.]]></content:encoded>
					
					<wfw:commentRss>https://coolshell.cn/articles/3709.html/feed</wfw:commentRss>
			<slash:comments>8</slash:comments>
		
		
			</item>
	</channel>
</rss>
