mirror of
https://github.com/cheetahlou/CategoryResourceRepost.git
synced 2026-05-11 04:04:34 +08:00
del
This commit is contained in:
163
极客时间专栏/geek/持续交付36讲/环境管理/08 | 测试环境要多少?从现实需求说起.md
Normal file
163
极客时间专栏/geek/持续交付36讲/环境管理/08 | 测试环境要多少?从现实需求说起.md
Normal file
@@ -0,0 +1,163 @@
|
||||
<audio id="audio" title="08 | 测试环境要多少?从现实需求说起" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/2b/f5/2baa4e34f51bd22150440461868cb7f5.mp3"></audio>
|
||||
|
||||
在整个持续交付生命周期中,测试环境的易用程度会直接影响软件的交付速度,但因为以下两点,它又是最被容易忽略的一环。
|
||||
|
||||
<li>
|
||||
我们总是把环境理想化,忽略了其管理的难度;
|
||||
</li>
|
||||
<li>
|
||||
我们也很少设立专职的环境管理员,导致环境长期处于混乱状态。
|
||||
</li>
|
||||
|
||||
通常,我们在项目初期并不会关注测试环境的问题,然而在回顾时却发现在环境问题上浪费的时间非常惊人:硬件资源申请困难,测试环境配置繁琐,测试应用更新困难,基础设施稳定性差,服务调用异常,多项目并行造成互相干扰等等问题。
|
||||
|
||||
而不管你是开发人员还是测试人员,相信你都或多或少地碰到过这些问题。
|
||||
|
||||
在接下来的《环境管理》系列文章中,我会和你聊聊构建一整套好的测试环境的关键点以及具体实施方案。今天,我就先跟你说说和测试环境相关的两个问题:
|
||||
|
||||
<li>
|
||||
测试环境的结构一般是怎样的?
|
||||
</li>
|
||||
<li>
|
||||
什么才是好的测试环境?
|
||||
</li>
|
||||
|
||||
## 互联网公司测试环境的结构
|
||||
|
||||
当公司规模较小时,测试环境的维护相对容易。开发和测试共用一套数据库缓存等基础设施,因为应用数量不多,开发环境可以是单机的,无论是手动或半自动化的更新测试环境的应用,花费的时间都还在可接受范围内。
|
||||
|
||||
这时,公司环境的结构很简单,分为开发环境,测试环境,生产环境即可。
|
||||
|
||||
但实际上,我看到的大多数公司的研发过程及配套环境并没有这么简单,一般都会存在5套以上的大环境以及更多的子环境,每个环境的机器数量可能有数十台甚至更多。
|
||||
|
||||
那么为什么会需要这么多套环境呢?我把主要原因概括为了以下两个方面。
|
||||
|
||||
<li>
|
||||
纵向上看,人员的增多提高了项目的并行度,如果这时还使用一套环境的话,就会发生以下问题:
|
||||
<ul>
|
||||
1. 开发同学在debug一个困难问题时,发现下游的应用突然就不可用了;
|
||||
<li>测试同学在跑了10多分钟测试脚本后,发现应用已经被开发更新掉了。<br />
|
||||
这样的体验是让人崩溃的。</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
横向上看,公司的应用架构逐渐转为微服务化,完整的应用数量很容易就达到了几百甚至几千个的量级,建立一套独立而完整的环境变得越来越复杂,往往是研发团队想要构建一套新的环境却构建不出来。
|
||||
</li>
|
||||
|
||||
所以,目前互联网公司常见的环境模型一般分为开发环境,功能测试环境,验收测试环境,预发布环境,生产环境这五个大套环境。
|
||||
|
||||
**第一,开发环境**
|
||||
|
||||
微服务架构下,单机已经无法完整地运行业务应用,这就需要开发环境内包含一套完整的业务应用依赖以及相关的基础设施,以保证业务开发同学能在本地完成开发测试。
|
||||
|
||||
**第二,功能测试环境**
|
||||
|
||||
在开发环境下,每个下游依赖应用都只有一个可用的stable版本。而在实际的开发过程中,由于项目的并行开发,往往会同时存在多个可依赖的版本。而每个项目组的同学在测试时,都希望测试过程中的关键依赖应用是可以被独占的,版本是固定的,不会被其他项目组干扰。
|
||||
|
||||
所以,一套独立的功能测试环境就很有必要了。通常,互联网企业会通过中间件的方式分割出一块隔离区域,在功能测试环境中创建多个子环境来解决这个问题。
|
||||
|
||||
**第三,验收测试环境**
|
||||
|
||||
验收测试环境和功能测试环境是完全隔离的。当功能测试通过后,你可以在验收测试环境进行最终的验收。
|
||||
|
||||
它除了可以用作测试之外,还可以用作产品展示。所以,除了测试和开发人员,产品经理也是验收测试环境的主要使用者。
|
||||
|
||||
**第四,预发布环境**
|
||||
|
||||
到了预发布阶段,应用已经进入了生产网络,和真实的生产应用共享同一套数据库等基础设施。预发布是正式发布前的最后一次测试,在这个环境中往往可以发现线下环境中发现不了的Bug。这个环境的运维标准等同于生产环境,一般不允许开发人员直接登录机器。
|
||||
|
||||
根据不同的业务需求和部署策略,不同公司对预发布环境的实现也有所不同:
|
||||
|
||||
- 一种比较常见的方式是,将金丝雀发布作为预发布,从接入真实流量的集群中挑选一台或一小组机器先进行版本更新,通过手工测试以及自动化测试和监控系统验证,降低新版本发布的风险。
|
||||
- 另一种做法是,独立出一组始终不接入真实流量的机器,调用在预发布环境中形成闭环。
|
||||
|
||||
相对于第一种方式,第二种方式对生产环境的影响更小,但需要额外的资源和维护成本。
|
||||
|
||||
**第五,生产环境**
|
||||
|
||||
生产环境是用户真实使用的环境,对安全性和稳定性的要求最高。
|
||||
|
||||
## 什么是好的测试环境?
|
||||
|
||||
在和你分享什么是好的测试环境前,建议你先思考一下开发环境、功能测试环境、验收测试环境、预发布环境这四种测试环境形成的原因是什么,这样有利于你更好的理解好的测试环境的含义。
|
||||
|
||||
首先,搭建测试环境的目的是保证最终交付的软件质量,但每套测试环境的用户并不完全一样:
|
||||
|
||||
<li>
|
||||
开发环境的用户是开发同学;
|
||||
</li>
|
||||
<li>
|
||||
功能测试环境的主要用户是测试同学;
|
||||
</li>
|
||||
<li>
|
||||
验收测试环境的用户是产品经理和测试同学;
|
||||
</li>
|
||||
<li>
|
||||
预发布环境的使用者是测试同学,但收益者却是运维同学。
|
||||
</li>
|
||||
|
||||
而每种角色对于产品研发流程中的需求也是不同的:
|
||||
|
||||
<li>
|
||||
开发同学关注研发效率;
|
||||
</li>
|
||||
<li>
|
||||
测试同学关注测试的可靠性;
|
||||
</li>
|
||||
<li>
|
||||
产品经理更关注的是真实的用户体验和产品的完整性;
|
||||
</li>
|
||||
<li>
|
||||
预发布环境的需求其实来自于运维同学,他们需要保证生产环境的稳定性,减少生产环境的变更,所以需要将预发布环境与线下环境完全隔离。
|
||||
</li>
|
||||
|
||||
如果你是一位测试环境治理工程师,在规划测试环境以及开发和实施工具的时候,最关键的就是要考虑到不同环境的主要用户是谁,环境要做成什么样才能满足用户在研发流程中的需求。当用户不用发愁环境问题时,研发效率也就自然而然地上去了。
|
||||
|
||||
当然,不论一套环境用户是测试同学还是开发同学,以下几个需求都是必须被做到的。
|
||||
|
||||
<li>
|
||||
可得性,即在开发一个新项目时,能快速获取构建一个环境需要的机器,基础设施。最好的情况是,能随时可得,随时归还。
|
||||
</li>
|
||||
<li>
|
||||
快速部署,即在搭建新环境时,能以最快的速度构建出一整套完整的环境。测试环境的部署很频繁,在代码提交后,能在很短的时间内构建代码,在环境上更新,就能更早开始测试。
|
||||
</li>
|
||||
<li>
|
||||
独立性,即一个环境在使用过程中,可以不受其他项目测试人员的干扰。
|
||||
</li>
|
||||
<li>
|
||||
稳定性,即不会因为下游服务,基础设施的异常,造成测试中断、等待。
|
||||
</li>
|
||||
<li>
|
||||
高仿真,主要分为两个方面:“测试数据真实”,即能在测试环境构建出真实的测试用例;“环境真实”,即基础服务的架构和行为与线上环境保持一致,避免因为环境不一致造成测试结果不一致。
|
||||
</li>
|
||||
|
||||
但是,毕竟各个环境的用户和使用场景不同,它们的需求也是有差别的。 比如,相对于开发环境,验收测试环境对测试数据的仿真性要求会更高,而开发环境的灵活性,决定了不会过于严格的维护测试数据的真实性。
|
||||
|
||||
所以,如何评价一个好的测试环境,就是看它是否最终满足了核心使用者的需求。
|
||||
|
||||
## 总结
|
||||
|
||||
通常,互联网公司的环境会包括:开发环境、功能测试环境、验收测试环境、预发布环境和生产环境这5套。
|
||||
|
||||
测试环境的目的是要保证最终将交付的软件产品的质量,所以好用的测试环境,不能从规模、性能和作用的角度来评判,而应该是从它能否满足用户需求去保证软件质量的角度进行定义,于是得出:
|
||||
|
||||
>
|
||||
当一个环境可以满足其真正核心用户的需求时, 就是一个好用的测试环境。
|
||||
|
||||
|
||||
除此之外,你还需要理解,环境是昂贵的,不仅涉及单一的机器资源成本,环境副本数的增加也意味着更难管理,更复杂的流程,所以仅仅考虑单套使用者的体验是不够的。
|
||||
|
||||
那么,在我的下一篇文章中,将会分享多环境带来的成本问题,以及如何在成本、效率、可管理之间权衡取舍。
|
||||
|
||||
## 思考题
|
||||
|
||||
<li>
|
||||
请你思考一下测试环境中最让你痛苦的一点是什么?
|
||||
</li>
|
||||
<li>
|
||||
如果让你来优化测试环境, 你会如何去改善这最让你痛苦的一点?
|
||||
</li>
|
||||
|
||||
欢迎你给我留言。
|
||||
|
||||
|
||||
121
极客时间专栏/geek/持续交付36讲/环境管理/09 | 测试环境要多少?从成本与效率说起.md
Normal file
121
极客时间专栏/geek/持续交付36讲/环境管理/09 | 测试环境要多少?从成本与效率说起.md
Normal file
@@ -0,0 +1,121 @@
|
||||
<audio id="audio" title="09 | 测试环境要多少?从成本与效率说起" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/fc/e7/fc2eb8c427628f1473fc5da431147ae7.mp3"></audio>
|
||||
|
||||
在上一篇文章中,我分享了互联网公司测试环境的常见结构,以及对用户来说什么样的测试环境才是好用的。然而对测试环境来说,只是高效好用还不够,还要考虑到成本问题。
|
||||
|
||||
效率和成本永远是一对矛盾体。今天,我就从成本和效率出发,和你聊聊构建测试环境时,还需要考虑的其他维度。
|
||||
|
||||
## 测试环境的成本
|
||||
|
||||
谈到环境成本,你很自然地就会想到云计算,《持续交付:发布可靠软件的系统方法》一书出版时,云计算还是一个时髦的概念,而8年后的今天,云技术已经非常成熟了。
|
||||
|
||||
今天,部分传统企业和互联网企业都在选择混合云架构,而创业公司选择公有云已经有了点模式化的意味。公有云非常好地满足了DevOps的“基础设施即代码”的理念,哪怕你完全不使用公有云,那在环境中整合开源的私有云技术依然能为你带来不少便利。
|
||||
|
||||
然而,云计算并非“银弹”,我们上云后,在成本上,还是有很多值得去思考和做的事情。
|
||||
|
||||
尤其是当环境数量增加时,你很容易就可以想到成本会增加,但是你可能并不明白要增加哪些方面的成本,以及会增加多少的问题。那么,接下来,我就跟你聊聊当环境数量增加时,你需要考虑的成本有哪些呢?
|
||||
|
||||
**首先是机器资源成本**
|
||||
|
||||
保证环境的独立性,是你构建更多套环境的一个主要原因。但是,一套独立的、拥有完整链路的环境成本是非常高的。
|
||||
|
||||
那么,以阿里云的价格为例,我来跟你一起算算这笔账。假设一个只有100个应用的微服务架构环境,选取单应用单机2核4G内存的低配置实例方式进行部署,单实例的年价格在2000元左右,100个实例的话,一年的花费就是20万元左右。
|
||||
|
||||
而这只是最保守的计算,随着服务规模的增加,以及更多环境的需要,整体花费上涨两个数量级也是很正常的。
|
||||
|
||||
这样的问题在开发环境和集成环境的表现是最明显的。为了保证这两套环境的独立性,你必然需要有很高的环境副本数。但无论如何,你都不可能让每一个开发和测试人员都拥有一套完整环境的硬件资源。
|
||||
|
||||
可见,每一套环境的机器资源成本都很大,而且随着需求的增加成比例增长。
|
||||
|
||||
**其次是管理成本**
|
||||
|
||||
管理成本,包括维护环境的可用性,配置的管理成本,和测试数据的维护成本三个维度。
|
||||
|
||||
<li>
|
||||
<p>**维护多套环境的第一要点是,维护环境的可用性。**<br />
|
||||
与云时代之前相比,容器技术已经解决了很多问题。比如,服务器操作系统级别的依赖的标准化更容易了;当出现硬件故障时,迁移和恢复服务也更加方便了。<br />
|
||||
但是,容器技术并没有解决故障定位的问题。微服务架构下集群的节点数量多, 调用链复杂,你不再能确定到底是环境问题,还是程序本身的Bug,也就导致定位故障更加困难了。<br />
|
||||
所以,更多套环境就意味着更大的集群规模,出现故障的几率会随之增加,而解决故障也会占用你更长的工作时间。</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>**维护多套环境的另一大成本是,配置的管理成本。**<br />
|
||||
配置是环境管理中最核心的内容,创建一套环境时,为了保证它真正的独立可用,不仅要保证应用可以成功运行,还要保证应用在基础设施的配置是正确的。比如集成测试环境下部署了一个应用的多个平行项目,就需要有办法保证测试人员能访问到正确的应用。<br />
|
||||
如果是Web应用,你就要考虑把应用绑定到不同的域名,这样就会增加域名管理的成本;如果是一个service应用,你就要考虑到这些service不会被其他项目的、无关环境中的应用调用到,同时也不会调用到其他错误的服务。<br />
|
||||
每多一套环境,就会多一套这样的配置,而且这些配置都需要在各类基础设施中生效。</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>**维护多套环境的第三大成本是,测试数据的维护成本。**<br />
|
||||
测试数据也是环境中极为重要的一个组成部分。当并行环境的数量变多后,数据的维护同样是让人头疼的问题。<br />
|
||||
为了保证环境的高仿真,哪些环境共用一套数据库,以及测试数据的更新在多套环境中怎么执行等等,都需要非常高的管理成本。</p>
|
||||
</li>
|
||||
|
||||
**最后是流程成本**
|
||||
|
||||
流程成本主要包括沟通成本和测试成本两个方面。
|
||||
|
||||
<li>
|
||||
<p>沟通成本<br />
|
||||
每增加一套环境,你都需要考虑团队成员如何在新环境上沟通协作。谁在占用,何时退出这些信息,你都需要第一时间告知团队。当环境的数量变得非常多以后,做好这些事的难度就很大了。</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>测试成本<br />
|
||||
在开发环境,集成测试环境,验收测试环境,预发布环境,生产环境这样的结构下,核心功能的测试流程就至少会执行五次。每引入一套新的环境,测试流程都会变得更加复杂。</p>
|
||||
</li>
|
||||
|
||||
## 如何调解效率和成本的矛盾?
|
||||
|
||||
现在你应该已经意识到,因为增加一套环境带来的成本竟然有那么多。但是为了提高持续交付的效率,隔离的多套环境又是必不可少的。
|
||||
|
||||
那么,你究竟应该怎样去规划和设计环境呢?
|
||||
|
||||
**第一,公共与泳道的**
|
||||
|
||||
**第一个关键点是抽象公共环境,而其中的公共服务基本都属于底层服务,相对比较稳定,这是解耦环境的重中之重。** 比如我们经常会将中间件,框架类服务,底层业务公共(账户,登陆,基本信息)服务部署在这套公共环境下。
|
||||
|
||||
**在公共环境的基础上,可以通过泳道的方式隔离相关测试应用**,利用LB和SOA中间件对路由功能的支持,在一个大的公共集成测试环境中隔离出一个个独立的功能测试环境,那么增加的机器成本就仅与被并行的项目多少有关系了。
|
||||
|
||||
为了帮助你理解,我跟你分享一个具体的案例。
|
||||
|
||||
比如,你有一个新的下单流程需要测试。你可以将“下单web 2.0”和“下单service 2.0”抽离出来,如图中的“功能环境1”所示。并保证被剥离出的“下单service 2.0”只能被当前环境内的web服务器调用。而“下单service 2.0”所依赖调用的“支付service 1.0”则放在公共环境中。
|
||||
|
||||
于此同时,如图中所示的“功能环境2”,可以同时支持“下单service 3.0”这个并行版本与一个新版本的“支付service 2.0”进行联调,此环境是不会调用公共环境中的“支付service 1.0”的。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/a4/d0/a4497ca9571f432cf1e82629161872d0.png" alt="" />
|
||||
|
||||
通过这种方式,你就可以解决并行开发和并行测试的问题了。
|
||||
|
||||
**第二,避免产生多套公共环境**
|
||||
|
||||
从原则上讲,集成环境中只要有一套公共环境就足够了。但有时候,你会发现项目的范围特别广,依赖应用或者影响应用特别多,特别是一些底层服务的改动或者重构等。在这种情况下,如果把依赖它的所有服务都部署起来,就很有可能变成第二、第三套公共环境了。
|
||||
|
||||
这种情况下,你可以通过 mock service 来模拟大多数服务,这样就可以达到测试环境的最小集合了。关于mock service 如何做,我会在后续的讲解中,为你详细解释。
|
||||
|
||||
**第三,减轻配置的复杂度**
|
||||
|
||||
而对于减轻环境配置的复杂度来说,你可以从以下两个方面着手:
|
||||
|
||||
<li>
|
||||
制定一套统一配置的解决方案;
|
||||
</li>
|
||||
<li>
|
||||
要让环境自己说话,有效减少配置项。
|
||||
</li>
|
||||
|
||||
比如,对于数据库,对于不同的测试需求,我们可能会在公共的数据库和独立的数据库之间进行频繁切换;又比如,我们可以在启动应用时自动配置正确的数据库。那么无论有多少套环境,配置也不是一件难事。
|
||||
|
||||
关于减轻配置的复杂度,我也会在专栏后续的讲解中,给你详细分析如何实现。
|
||||
|
||||
## 总结
|
||||
|
||||
我们究竟需要多少套环境,这个问题的答案应该是这样的:在大环境(开发、集成、验收)的数量上,你要考虑环境的核心用户是谁,环境的核心价值是什么。在环境的核心价值没有冲突时,尽量减少大环境的数量。
|
||||
|
||||
有些公司就通过功能分支直接上线的分支策略,对每个分支, 都创建一整套的功能测试环境,并在分支上线后快速释放。以这样的方式,精简了验收测试环境,即大环境的产生。但其代价是发布过程分支之间必须是串行的,即一个分支的上线会阻塞其他分支的上线。
|
||||
|
||||
而在每个大环境的子环境上(也就是按照测试需求被剥离出来的功能环境),你必须保证它的副本数可以满足用户测试的隔离需求。比如,在集成测试环境,只产生一套公共环境,并通过工具,支持隔离的功能测试环境的快速建立和销毁,让环境可以按需分配。
|
||||
|
||||
## 思考题
|
||||
|
||||
当你需要一套性能测试环境时,是独立出一套大环境还是作为一个子环境依附于某个大环境比较好?
|
||||
|
||||
欢迎你给我留言。
|
||||
|
||||
|
||||
191
极客时间专栏/geek/持续交付36讲/环境管理/10 | 让环境自己说话,论环境自描述的重要性.md
Normal file
191
极客时间专栏/geek/持续交付36讲/环境管理/10 | 让环境自己说话,论环境自描述的重要性.md
Normal file
@@ -0,0 +1,191 @@
|
||||
<audio id="audio" title="10 | 让环境自己说话,论环境自描述的重要性" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/a9/ed/a90cae813d8e979ad0f1b226de704fed.mp3"></audio>
|
||||
|
||||
在前两篇文章中,我从现实需求、成本与效率的角度,分析了对环境管理者来说最重要的一个问题,即到底需要多少套环境来支撑持续交付。如果你已经从中能掌握了一些环境管理的窍门,那么你基本就可以搞定对环境管理的宏观把控了。
|
||||
|
||||
但是,除了宏观的把控和管理外,即使只有一套环境,你还是有可能陷入无穷无尽的细节工作中。因为在日常的环境管理过程中,环境配置才是工作的重头和难点。那么今天,我就来跟你详细说说有关环境配置的问题。
|
||||
|
||||
**从我的实践经验看,要想把环境配置这件事做好,就是要做到让环境自己能说话。**
|
||||
|
||||
要做到这点,首先需要定义配置的范围。
|
||||
|
||||
从面向的目标来看,环境配置大体上可以分为两大部分:
|
||||
|
||||
<li>
|
||||
以环境中每台服务器为对象的运行时配置;
|
||||
</li>
|
||||
<li>
|
||||
以一个环境为整体目标的独立环境配置。
|
||||
</li>
|
||||
|
||||
## 服务器运行时配置
|
||||
|
||||
以一个 Java Web 应用为例,需要哪些运行时配置呢?
|
||||
|
||||
<li>
|
||||
安装 war 包运行依赖的基础环境,比如 JDK,Tomcat 等。
|
||||
</li>
|
||||
<li>
|
||||
修改 Tomcat 的配置文件,关注点主要包括:应用的日志目录,日志的输出格式,war 包的存放位置。Tomcat的server.xml配置包括:连接数、 端口、线程池等参数。
|
||||
</li>
|
||||
<li>
|
||||
配置Java 参数,包括JVM堆内存的xmx、xmn等参数,GC方式、参数,JMX 监控开启等。
|
||||
</li>
|
||||
<li>
|
||||
考虑操作系统参数,比较常见的一个配置是 Linux 的文件句柄数,如果应用对网络环境有一些特殊要求的话,还需要调整系统的TCP参数等配置。
|
||||
</li>
|
||||
|
||||
经过上面这4步,一个简单的运行时环境的配置就算是完成了, 可以开始运行一个程序了。是不是感觉有点复杂呢?
|
||||
|
||||
而这,对正常的运行时配置管理来说,只不过是冰山一角而已。
|
||||
|
||||
**我们不光要考虑单个实例初始化配置,还要考虑每次JDK、Tomcat等基础软件的版本升级引起的运行时配置的变更,而且这些变更都需要被清晰地记录下来,从而保证扩容出新的服务器时能取到正确的、最新的配置。**
|
||||
|
||||
另外,对于一个集群的服务器组来说,还需要强制保证它们的运行时配置是一致的。
|
||||
|
||||
## 独立环境配置
|
||||
|
||||
**独立环境配置的主要目的是,保证一个环境能够完整运作的同时,又保证足够的隔离性,使其成为一个内聚的整体。**
|
||||
|
||||
所以,要让一个环境能够符合需求的正常运作,你需要考虑的内容包括:
|
||||
|
||||
<li>
|
||||
**这个环境所依赖的数据库该如何配置,缓存服务器又该如何配置。**
|
||||
</li>
|
||||
<li>
|
||||
**如果是分布式系统,或者SOA架构的话,就需要考虑服务中心、配置中心等一系列中间件的配置问题。**
|
||||
</li>
|
||||
|
||||
其中,最为重要的是配置中心的配置。只有先访问到正确的配置中心,才能获取到其他相关的环境配置或者应用配置信息。也就是说,如果配置中心的配置错了,那么环境就会陷入混乱状态。
|
||||
|
||||
<li>
|
||||
<p>**要考虑访问入口问题。** 这套环境的入口在哪里?是一个站点还是一个服务入口?<br />
|
||||
如果是一个站点的话,那这个站点的访问域名就需要被特殊配置。如果这是一个内部环境的话,那么这个内部域名的 DNS 解析也需要被配置。如果这套环境中有多个 Web 应用,那么你就要考虑7层路由的配置问题了。</p>
|
||||
</li>
|
||||
<li>
|
||||
**还要配置环境对应的基础服务,比如监控,短信,搜索等。**
|
||||
</li>
|
||||
|
||||
读到这里,如此多的与环境有关的配置,有没有让你觉得太复杂了。
|
||||
|
||||
再想象一下,如果你的环境要承载多种语言栈,各类应用依赖的基础软件也不同,环境和环境之间有各种关联设置,数据库的连接分配,环境中负载均衡的设置,等等。是不是让你感觉有些焦虑?
|
||||
|
||||
如果每天都要和这样的工作做斗争,那简直就是一场噩梦。更别提在这样的环境下,完成持续交付了,那简直就是难如登天。
|
||||
|
||||
虽然环境配置有这么多糟心的待处理事项,但是环境本身也是一个非常强大的工具,本身包含非常多的信息,如果这些糟心的事情环境能和你一起来解决,那就简单了,也就是我所说的让环境自己来说话,那么接下来就看看怎么做到吧。
|
||||
|
||||
## 环境一定要标准化
|
||||
|
||||
解决复杂问题的办法,无非是先将其分解,再将其简单化,对环境配置这个难题来说也是同样的道理。想要解决它,首先得要想办法分解、简化它。
|
||||
|
||||
最好的简化方法,莫过于标准化了。**所谓标准化,就是为了在一定范围内获得最佳秩序,对实际的或潜在的问题制定共同、可重复使用的规则。**
|
||||
|
||||
**标准化也就是让环境学会了一门统一的语言,是自己说话的前提。**
|
||||
|
||||
按照这个思路,我们首先可以实现对语言栈的使用、运行时配置模板、独立环境配置的方法等的标准化:
|
||||
|
||||
<li>
|
||||
规定公司的主流语言栈;
|
||||
</li>
|
||||
<li>
|
||||
统一服务器安装镜像;
|
||||
</li>
|
||||
<li>
|
||||
提供默认的运行时配置模板;
|
||||
</li>
|
||||
<li>
|
||||
统一基础软件的版本,以及更新方式;
|
||||
</li>
|
||||
<li>
|
||||
在架构层面统一解决环境路由问题;
|
||||
</li>
|
||||
<li>
|
||||
自动化环境产生过程。
|
||||
</li>
|
||||
|
||||
看到这里,你可能感觉需要标准化的内容也是多种多样的,而且每个公司的具体情况也不同,那么标准化实施起来也必定困难重重。
|
||||
|
||||
从我的实践经验来看,建议你在实施持续交付的同时,去推动形成以下几个方面的规范:
|
||||
|
||||
<li>
|
||||
代码及依赖规范;
|
||||
</li>
|
||||
<li>
|
||||
命名规范;
|
||||
</li>
|
||||
<li>
|
||||
开发规范;
|
||||
</li>
|
||||
<li>
|
||||
配置规范;
|
||||
</li>
|
||||
<li>
|
||||
部署规范;
|
||||
</li>
|
||||
<li>
|
||||
安全规范;
|
||||
</li>
|
||||
<li>
|
||||
测试规范。
|
||||
</li>
|
||||
|
||||
其实,不管是持续交付还是架构改造,标准先行都是技术实施的前提条件。
|
||||
|
||||
## 约定大于配置
|
||||
|
||||
讲到这里,你可能也会疑惑了,和环境有关的内容实在是太多了,即使有了标准化,怎么可能都通过配置实现呢?
|
||||
|
||||
举个例子,代码的部署路径,标准化后所有服务器的路径都应该遵循这个标准,但是不可能在每台服务器上都去定义一个配置文件或环境变量来标示它,也没有这个必要。
|
||||
|
||||
实际上,你也从来都没有疑惑过部署路径的问题,因为从你来到公司起,它就已经是约定俗成了。而且,每家公司都是这样的,难道不是吗?
|
||||
|
||||
像代码的部署路径这种情况,我们就把它叫作“约定大于配置”,在实际工作中,还有很多类似的场景,你完全可以利用这套方法,简化环境配置。
|
||||
|
||||
比如,每个环境的域名定义,可以遵循以环境名作为区分的泛域名实现;又比如,可以用FAT,UAT这样的关键词来表示环境的作用;又比如,可以约定单机单应用;再比如,可以约定所有服务的端口都是8080。
|
||||
|
||||
**“约定大于配置”的好处是,除了简化配置工作外,还可以提高沟通效率。** 团队成员一旦对某项内容形成认知,他们的沟通将不再容易产生歧义。
|
||||
|
||||
**“约定大于配置”相当于赋予了环境天生的本能,进一步加强了环境的自我描述能力。**
|
||||
|
||||
## 让环境自己能开口说话
|
||||
|
||||
有了环境标准化,以及约定大于配置的基础,你就可以顺利地让环境自己开口说话了。
|
||||
|
||||
也就是说,**通过环境的自描述文件,让环境能讲清楚自己的作用、依赖,以及状态,而不是由外部配置来解释这些内容。**
|
||||
|
||||
以一台服务器为例,一旦生成,除了不能控制自己的生死外,其他运行过程中的配置,都应该根据它自身的描述来决定。
|
||||
|
||||
那么,如何让服务器自己说话呢?
|
||||
|
||||
**首先,需要定义Server Spec。**
|
||||
|
||||
这是重中之重,在服务器生成时,写入它自己的描述文件。我们通常把这个文件命名为“Server Spec”。在这个文件里,记录了这台服务器的所有身份信息,包括:IDC,型号,归属环境,作用,所属应用,服务类型,访问路径等。
|
||||
|
||||
**其次,解决配置中心寻址。**
|
||||
|
||||
中间件根据Server Spec的描述,寻找到它所在环境对应的配置中心,从而进一步获取其他配置,如数据库连接字符串,短信服务地址等等。
|
||||
|
||||
**最后,完成服务自发现。**
|
||||
|
||||
其实这就是一个服务自发现的过程。根据服务类型,访问路径等,还可以自动生成对应的路由配置,负载均衡配置等。
|
||||
|
||||
总结来说,我们是在尝试把环境配置的方向调个个儿:由原来外部通过配置告知环境应该干什么,转变成环境根据自身的能力和属性,决定自己应该去干什么。
|
||||
|
||||
这种尝试,标志着环境配置能力的质的飞跃。一台服务器可以实现自描述,你同样就可以把这个方法推广到所有服务器中。同理,一个环境可以实现自描述,你就可以把自描述的方式扩展到所有环境中。
|
||||
|
||||
从此,环境配置将变得不再艰难。
|
||||
|
||||
## 总结
|
||||
|
||||
我主要围绕环境配置的问题,讲了它的内容和一些特性,以及简化和优化的一些方案。
|
||||
|
||||
一定要意识到,环境配置是非常复杂的,直接影响你的环境治理能力,而环境治理能力又直接影响着持续交付的能力。但是我们还是可以通过:**标准化、约定、自描述**等方式去简化和优化环境配置工作。
|
||||
|
||||
我们的目标是,让环境自己能说话。
|
||||
|
||||
## 思考题
|
||||
|
||||
在你的公司,这些环境配置相关的工作由谁来完成?又由谁来为他们制造工具和提高工作效率?
|
||||
|
||||
欢迎你给我留言。
|
||||
|
||||
|
||||
216
极客时间专栏/geek/持续交付36讲/环境管理/11 | “配置”是把双刃剑,带你了解各种配置方法.md
Normal file
216
极客时间专栏/geek/持续交付36讲/环境管理/11 | “配置”是把双刃剑,带你了解各种配置方法.md
Normal file
@@ -0,0 +1,216 @@
|
||||
<audio id="audio" title="11 | “配置”是把双刃剑,带你了解各种配置方法" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/4a/b6/4a489134338ffd486d2e11bd2a72b0b6.mp3"></audio>
|
||||
|
||||
很多人分不清配置和配置管理,但其实它们是完全不同的概念。
|
||||
|
||||
**配置管理:** 是通过技术或行政手段对软件产品及其开发过程和生命周期进行控制、规范的一系列措施。 它的目标是记录软件产品的演化过程,确保软件开发者在软件生命周期的各个阶段都能得到精确的产品配置信息。
|
||||
|
||||
**配置:** 是指独立于程序之外,但又对程序产生作用的可配变量。也就是说,同一份代码在不同的配置下,会产生不同的运行结果。
|
||||
|
||||
从上面的定义中,你可以看到配置和配置管理有着本质上的不同:配置管理服务于软件研发过程,而配置则服务于程序本身。
|
||||
|
||||
作为一名程序员,开发时经常要面对不同的运行环境:开发环境、测试环境、生产环境、内网环境、外网环境等等。不同的环境,相关的配置一般不一样,比如数据源配置、日志文件配置,以及一些软件运行过程中的基本配置等。
|
||||
|
||||
另外,你也会遇到一些业务上的,以及逻辑上的配置。比如,针对不同地域采取不同的计费逻辑,计费逻辑又要根据这些地域的需要随时调整。
|
||||
|
||||
**如果我们把这些信息都硬编码在代码里,结果就是:每次发布因为环境不同,或者业务逻辑的调整,都要修改代码。而代码一旦被修改,就需要完整的测试,那么变更的代价将是巨大的。**
|
||||
|
||||
因此,我们往往会通过“配置”来解决这些问题。
|
||||
|
||||
但是,“配置”本身也很讲究。在什么阶段进行配置,采用什么手段进行配置,都将直接影响持续交付的效果。
|
||||
|
||||
那么,接下来我就跟你详细聊聊各种配置方法。
|
||||
|
||||
## 构建时配置
|
||||
|
||||
以 Maven 为例,实现多环境的构建可移植性需要使用 profile。profile 是一组可选的配置,可以用来设置或者覆盖配置默认值。通过不同的环境激活不同的profile,可以实现构建的可移植性。 我们可以看一个简单使用示例:
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/9a/6f/9a95af6dfe491b66076f7085c5a64b6f.png" alt="" />
|
||||
|
||||
这段代码定义了dev和prod两个 profile(没有定义任何其他配置,实际使用中可按需定义任何配置),并且使用了 echo 插件验证 profile 是否生效,通过运行。
|
||||
|
||||
```
|
||||
maven initialize –Pdev
|
||||
|
||||
```
|
||||
|
||||
或
|
||||
|
||||
```
|
||||
maven initialize –Pprod
|
||||
|
||||
```
|
||||
|
||||
然后,可以看到输出:
|
||||
|
||||
```
|
||||
[INFO] profiles.active = dev
|
||||
|
||||
```
|
||||
|
||||
或
|
||||
|
||||
```
|
||||
[INFO] profiles.active = prod
|
||||
|
||||
```
|
||||
|
||||
其中, dev 是默认激活的,也就是说如果不填写任何 –P 参数,或者 –P 参数不为 dev 或者 prod,都会使用 dev 作为默认的 profile。
|
||||
|
||||
这样在代码构建时,你就可以根据具体需要选择对应的profile了。
|
||||
|
||||
这个方案看起来很简单, 但也有两个缺点:
|
||||
|
||||
<li>
|
||||
<p>它依赖于某个特定的构建工具,而且使用方法不统一。<br />
|
||||
什么意思呢?如果你不使用Maven作为构建工具,这个配置功能就失效了;而且对于跨平台、跨语言栈的支持也不友好。</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>每次都要重新编译,浪费计算资源。<br />
|
||||
即使你只是替换一些配置文件,并没有改动任何代码,但为了让配置生效,还是需要完成代码的整个构建过程,这就会在编译上花费大量的计算资源。</p>
|
||||
</li>
|
||||
|
||||
因此,为了解决这两个问题,通常会把“打包”这个过程拆解出来,并将它插入构建之后,接下来我就介绍一下“打包时配置”。
|
||||
|
||||
## 打包时配置
|
||||
|
||||
“打包”,是我在多年持续交付实践中总结出的一个非常重要的概念。我把打包过程与构建过程脱离,也就是说构建成功后,并不立即打包。而是把打包安排在发布之前,打包完成之后立即发布,打包就与发布过程形成了一个整体。
|
||||
|
||||
为什么要独立分离出打包这个步骤呢?你可能会问,Maven 在构建过程中不是已经完成了 package 步骤吗?
|
||||
|
||||
正因为构建时配置,需要针对多个 profile 编译多次,而持续交付有一个核心概念,即:**一次构建多次部署**。打包就是为了解决这个问题而被发明的。
|
||||
|
||||
**打包时配置的基本思想是:构建时完全不清楚程序所要部署的环境,因此只完成最基本的默认配置;而发布时清晰地知晓环境信息,因此可根据环境信息,进行相关配置的替换。**
|
||||
|
||||
在携程,我们开发了一个叫作 ConfigGen 的工具,用以替换配置文件。 这样,你就不需要每次更改配置时,都重新编译整个代码,大幅缩短了整个发布流程的时间, 而且 ConfigGen 完全基于XML,适用于任何语言。
|
||||
|
||||
ConfigGen 的使用也很简单,只要一个 ConfigProfile.xml 文件即可,dev和prd指两个入参,根据这两个入参分别定义了currentENV的具体值,如下图所示。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/6f/b7/6fdcaad49b49c278f3db52d7610284b7.png" alt="" />
|
||||
|
||||
其中,currentENV 节点便是该环境下的变量,然后在项目下面创建一些TPL文件,该文件就是最终生成的配置文件的模板,其中的占位符将根据之前xml中配置的值进行替换。比如,Web.config.tpl,如下图所示。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/10/f2/1028f169ec53723efa71acc680e718f2.png" alt="" />
|
||||
|
||||
运行 ConfigGen后,会在当前项目下生成一个 __ConfigTemp 目录,该目录下包含 dev 和 prd 两个目录,如下图所示。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/df/24/dfc48e1904a7a3d00cbbf33c6e7d1324.png" alt="" />
|
||||
|
||||
所得到的 dev/Web.config 文件就是 Web.config.tpl 生成的最终配置文件。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/95/93/95698caa79f0d9e0ad0043d889d5d393.png" alt="" />
|
||||
|
||||
从图中可以看出,Web.config 已经正确替换了 currentENV 变量。 __ConfigTemp 里面的配置文件目录结构与项目中TPL文件的目录结构是一致的。
|
||||
|
||||
利用类似于 ConfigGen 这样的工具,可以在打包阶段很好地解决不同环境的配置问题。但还是会有解决不了的痛点:
|
||||
|
||||
>
|
||||
打包时配置,需要借助发布的力量使配置生效。而实际场景中,只是修改了配置就要发布代码往往是不被接受的。特别是,如果你还不具备很成熟的持续部署能力,那将会是很头痛的事情。
|
||||
|
||||
|
||||
因此,为了更好地解决配置问题,绝大多数的互联网企业会推荐使用“配置中心”。如果你所在的公司还没有成熟的配置中心,那么我推荐尽快使用开源系统来搭建配置中心。下面,我就分享一下,配置中心是如何工作的。
|
||||
|
||||
## 运行时配置
|
||||
|
||||
随着程序功能的日益复杂,程序的配置日益增多,各种功能的开关,参数的配置,服务器的地址,等等不断增加到系统中。而且应用对程序配置的期望值也越来越高,需要配置系统能够:
|
||||
|
||||
<li>
|
||||
修改后实时生效;
|
||||
</li>
|
||||
<li>
|
||||
支持灰度发布;
|
||||
</li>
|
||||
<li>
|
||||
能分环境、分集群管理配置;
|
||||
</li>
|
||||
<li>
|
||||
有完善的权限、审核机制。
|
||||
</li>
|
||||
|
||||
在这样的大环境下,传统的配置文件、数据库等方式已经越来越无法满足开发人员对配置的管理需求;另外,对于数据库连接串,各个服务之间的 API Key 等机密配置,如果放在代码里也会引起安全的问题。
|
||||
|
||||
针对以上的种种需求和问题,我们采用系统化、服务化的思想,引入了配置中心,尝试彻底解决配置问题。
|
||||
|
||||
以携程为例,我们自研了 Apollo 配置中心,(目前[该项目已经在GitHub开源](https://github.com/ctripcorp/apollo))用以满足上述需求。
|
||||
|
||||
如下图所示,即是Apollo的基础模型:
|
||||
|
||||
<li>
|
||||
用户在配置中心对配置进行修改并发布;
|
||||
</li>
|
||||
<li>
|
||||
配置中心通知Apollo客户端有配置更新;
|
||||
</li>
|
||||
<li>
|
||||
Apollo 客户端从配置中心拉取最新的配置,更新到本地配置并通知应用重新载入配置。
|
||||
</li>
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/b6/d7/b61c6a2db1612d3a78e79f2c822ec2d7.png" alt="" />
|
||||
|
||||
Apollo系统有几个特别突出的能力,能够很好地解决配置的问题:
|
||||
|
||||
<li>
|
||||
统一管理不同环境、不同集群的配置,通过一个管理平台可以达到管理多个环境或集群配置的效果,省时省力;
|
||||
</li>
|
||||
<li>
|
||||
配置修改实时生效(热发布),不再需要重启服务,与应用运行生命周期完全解耦;
|
||||
</li>
|
||||
<li>
|
||||
版本发布管理,方便配置变更后的比对和追溯,配置变更有迹可循;
|
||||
</li>
|
||||
<li>
|
||||
支持配置的灰度生效,减少配置错误所带来的故障影响;
|
||||
</li>
|
||||
<li>
|
||||
客户端配置信息监控,这样有利于管理员进行集中式管理;
|
||||
</li>
|
||||
<li>
|
||||
提供 Java 和 .Net 原生客户端。
|
||||
</li>
|
||||
|
||||
使用配置中心的运行时配置,应该说是现在绝大多数企业选择的解决方案。而且,面对微服务的技术趋势,它也有一定的技术优势。比如,Apollo就是Spring Cloud推荐使用的开源配置中心解决方案。
|
||||
|
||||
Apollo有详尽的文档,其功能基本可以覆盖绝大多数业务对配置的需求,因此,我建议你也可以基于这套开源系统来搭建一套自己的配置中心,解决配置难题。
|
||||
|
||||
## 回滚是配置永远的痛
|
||||
|
||||
虽然配置中心已经很强大了,但是你也要清晰地认识到,配置中心不是万金油,比如对于版本回滚的情况:
|
||||
|
||||
>
|
||||
当你使用构建配置和打包配置时,配置是随着代码的一起发布的。这样的话,如果代码回滚了,配置自然而然的也会跟着一起回滚,旧版本的代码和旧版本的配置在绝大多数情况下是兼容的。但如果你用了配置中心,配置就不会随着代码回滚,就可能引发意想不到的问题。
|
||||
|
||||
|
||||
此时,**先回滚配置还是先回滚代码就成了一个死循环的问题。最好的办法是保证配置与代码的兼容性,这有点类似于数据库的 schema 变更。** 比如,只增加配置不删减配置、不改变配置的数据类型而是新增一个配置等方法。同时,也要做好代码版本与配置版本的对应管理。
|
||||
|
||||
那你可能会问,是不是只要做到代码和配置一起回滚就行了呢?其实不是,配置是一个很复杂的问题,像之前所说,绝大多数情况下,回滚配置能够兼容,但也有不行的时候。
|
||||
|
||||
比如,修改了数据库连接串的配置,代码回滚后还是要用最新的配置,如果配置也一起回滚了,反而会出现错误。
|
||||
|
||||
所以,对于配置回滚这个复杂问题,没有一劳永逸的办法, 只能根据实际情况选择最适合自己的方案。
|
||||
|
||||
但是,**我有一个推荐做法就是,每次回滚时,将可能发生变化的配置进行diff操作,由负责回滚的具体人根据结果去做最后的判断。**
|
||||
|
||||
## 总结
|
||||
|
||||
在这篇文章中,我和你讨论了三种配置方案:
|
||||
|
||||
<li>
|
||||
构建时配置:会增加构建成本;
|
||||
</li>
|
||||
<li>
|
||||
打包时配置:依赖发布生效;
|
||||
</li>
|
||||
<li>
|
||||
运行时配置:配置中心,便于管理和维护。
|
||||
</li>
|
||||
|
||||
我的建议是:业务相关的配置尽量放在运行时的配置中心服务里。
|
||||
|
||||
同时,一定要注意配置的回滚问题。因为,无论是回滚还是不回滚,它没有标准答案,这个复杂问题必须按当时情况作出相对应的处理。
|
||||
|
||||
## 思考题
|
||||
|
||||
在日常开发或者维护的系统中,你还遇到过哪些配置需要管理?你又是如何管理这些配置的呢?
|
||||
|
||||
欢迎你给我留言。
|
||||
|
||||
|
||||
126
极客时间专栏/geek/持续交付36讲/环境管理/12 | 极限挑战,如何做到分钟级搭建环境?.md
Normal file
126
极客时间专栏/geek/持续交付36讲/环境管理/12 | 极限挑战,如何做到分钟级搭建环境?.md
Normal file
@@ -0,0 +1,126 @@
|
||||
<audio id="audio" title="12 | 极限挑战,如何做到分钟级搭建环境?" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/d9/0d/d9f7afdcc3232a4c6c699ce74ab9990d.mp3"></audio>
|
||||
|
||||
在上两篇文章中,我介绍了环境管理中最关键的几个概念,环境的标准化,让环境自己说话以及环境配置的几种方法。
|
||||
|
||||
今天,我分享的主题就是,如何从零出发, 实现一套完整的环境创建。并且尝试挑战一下,如何做到分钟级交付。毕竟,天下武功,无坚不摧,唯快不破。
|
||||
|
||||
## 环境构建流水线
|
||||
|
||||
当开发人员向你申请一套新环境时,作为测试环境的维护者,你首先需要明确打造环境构建流水线需要关注的三大内容:
|
||||
|
||||
<li>
|
||||
**虚拟机环境准备**,根据环境的应用数、每个应用需要的硬件配置,准备好环境的硬件资源。
|
||||
</li>
|
||||
<li>
|
||||
**应用部署流水线**,在标准化的虚拟机上进行应用部署,当出现问题时如何容错。
|
||||
</li>
|
||||
<li>
|
||||
**环境变更**,在SOA或微服务的架构体系下,常常会因为测试的需求,将几套环境合并或拆分,创建环境时,你需要考虑如何高效地完成这些操作。
|
||||
</li>
|
||||
</<!-- [[[read_end]]] -->ol>
|
||||
接下来,我会针对这三大内容进行展开,带你快速搭建一套环境。
|
||||
<h2>虚拟机环境准备</h2>
|
||||
在部署应用之前,我们首先需要创建应用部署的虚拟机环境。目前在携程,我们使用OpenStack做物理机和虚拟机的初始化的工作。
|
||||
<ol>
|
||||
<li>
|
||||
当物理机接到机架上以后,打开交换机端口,等待机器被发现后,调用Nova进行物理机基本的硬件配置。
|
||||
</li>
|
||||
<li>
|
||||
<p>物理机环境准备完毕后,从OpenStack获取虚机所需的镜像、网络等信息,调用OpenStack API进行虚拟机部署。虚拟机配置的一个关键点是,如何对网络进行配置。<br />
|
||||
携程的测试环境使用的是大二层的网络架构,配置简单。但如果你对测试环境的网络规划是,需要做每个测试环境的独立的网段切分的话,配置会更复杂。</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>虚拟机初始化后,需要在虚拟机上进行一些基础软件比如JDK,Tomcat的安装和配置。业界一般采用的方式是,通过自动化的配置管理工具来进行操作。<br />
|
||||
目前,市场上主流的开源配置管理工具有Puppet、Chef、Ansible、SaltStack等。这几款工具都能帮助你很好地处理配置问题,当然它们也有自己独特的设计思想,实现语言也不同,你可以根据自己的技术背景和要管理的环境情况挑选适合自己的工具。</p>
|
||||
</li>
|
||||
|
||||
讲到这里,你肯定会有疑问。虚拟机的初始化流程已经这么复杂了,这个过程已经远远不是分钟级了,那我在文章开始部分说的分钟级是如何实现的呢?
|
||||
|
||||
我的建议是,采用资源池的方案。你可以根据用户平时使用虚拟机的情况,统计每天虚拟机申请和销毁的具体数量,预先初始化一定量的虚拟机。 这样用户从上层的PaaS平台创建环境时,就不用等待初始化了,可以直接从资源池中获取虚拟机,这部分的时间就被节省下来了。
|
||||
|
||||
但是,采用资源池的方式也有一定的复杂性,比如机型多、资源使用率难以预先估计等问题,当然这些问题对云计算来说,可以被轻松搞定。
|
||||
|
||||
## 应用部署流水线
|
||||
|
||||
由于不同公司的中间件和运维标准不同,部署流水线的差异也会很大,所以这里我只会从单应用部署标准化、应用部署的并行度,以及流水线的容错机制,这三个关键的角度,分享如何提速环境的搭建。
|
||||
|
||||
<li>
|
||||
**单应用部署标准化**,这是整个环境部署的基础。对一套测试环境而言,每个应用就像是环境上的一个零件,如果单个应用无法自动发布或者发布失败率很高,那么整个环境就更难以构建起来。而如何实现一个好的发布系统,提升单应用部署速度,我会在后面的文章中详细介绍。
|
||||
</li>
|
||||
<li>
|
||||
<p>**应用部署的并行度**,为了提高环境的部署速度,需要尽可能得最大化应用部署的并行度。理想的情况下,环境中的所有应用都可以一次性地并行部署。<br />
|
||||
然而,做到一次性并行部署并不容易,需要保证:应用都是无状态的,并且可以不依赖别的应用进行启动,或者仅仅依赖于基础环境中的应用就可以启动,且可以随时通过中间件进行调用链的切换。<br />
|
||||
**在携程,我们力求做到所有应用都可以一次性并行部署,但这条运维标准并不通用。**<br />
|
||||
当我们需要更复杂的应用部署调度规则时,一个原则是将应用部署的次序、并行方式的描述交给开发人员去实现,并基于DevOps的理念,即调度策略和规则可以通过工具代码化,保证同一套环境反复创建的流水线是一致的。</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>**流水线的容错机制**。对于环境构建工具,通常的做法是力求做到全面的标准化、代码化。但是因为环境的创建本身是一个非常复杂的工作流,在创建过程总会有一些异常中断整个流程。比如,某个应用启动失败了。<br />
|
||||
而对于这些工作流中的异常,我们应该如何处理呢?</p>
|
||||
<ul>
|
||||
1. **第一种方法是,错误中断法。** 创建环境过程中,各种资源申请、应用部署出现问题时,我们将工作流快照下来,然后收集所有的异常信息,返回给用户。由用户判断当前的情况,等用户确认问题已经得到解决后,可以触发一次快照重试,继续被中断的流程。
|
||||
<li>**第二种方法是,优先完成法。** 创建环境过程中发生错误时,先进行几次重试。如果重试依然发生错误的话,就忽略当前错误,先走完剩余的流程,等所有的流程都走完了,再一次性将错误返回给用户。<br />
|
||||
从整体速度上来看,第二种优先完成的处理方式是更优的,而且也会更少地打断用户。只是方式二需要保证的关键原则是:所有的部署脚本的操作都是幂等的,即两次操作达成的效果是一致的,并不会带来更多的问题。</li>
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
## 环境变更
|
||||
|
||||
实现了应用部署流水线后,创建环境的主流程,即虚机准备和应用部署已经完成,环境已经可以工作了。但还是不能忽略了后续环境变更的需求和工作。一般情况下,研发人员变更环境主要有以下4种场景。
|
||||
|
||||
<li>
|
||||
已经有一套新环境,当有新项目时,开发人员会挑选部分应用,组成一个独立的子环境。这里的重点是,要保证子环境和完整环境的调用是互相隔离的。
|
||||
</li>
|
||||
<li>
|
||||
当存在多个子环境时,可能在某个时间点需要做多个项目的集成,这时开发人员需要合并多个环境。
|
||||
</li>
|
||||
<li>
|
||||
和合并的情况相反,有些情况下,开发人员需要将一个子环境中的应用切分开来,分为两个或者多个环境分别进行隔离测试。
|
||||
</li>
|
||||
<li>
|
||||
已经存在一个子环境,当多个并行项目时,开发人员会克隆一套完整的子环境做测试。
|
||||
</li>
|
||||
|
||||
**对于这4个场景,我们需要关注的是在多并行环境的情况下应用拓扑图,包括用户访问应用的入口、应用之间调用链的管理,以及应用对数据库之类的基础设施的访问。**
|
||||
|
||||
<li>
|
||||
<p>**用户访问应用的入口管理。** 以最常用的访问入口(域名)为例,我推荐的做法是根据约定大于配置的原则,当环境管理平台识别到这是一个Web应用时,通过应用在生产环境中的域名、路由,环境名等参数,自动生产一个域名并在域名服务上注册。<br />
|
||||
这里需要注意的是,域名的维护尽量是在SLB(负载均衡,Server Load Balancer)类似的软负载中间件上实现,而不要在DNS上实现。因为域名变更时,通过泛域名的指向,SLB二次解析可以做到域名访问的实时切换。而如果配置在DNS上,域名的变更就无法做到瞬时生效了。</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>**应用之间调用链的管理。** 对于service的调用关系,我在《“配置”是把双刃剑,带你了解各种配置方法》这篇文章中,提到了携程开源的配置中心Apollo的实现策略,所有的服务调用的路由都是通过环境描述文件server.spec自发现的,你只要保证文件的环境号、IDC等属性是正确的,整个调用链就不会被混淆。<br />
|
||||
同时,服务调用中间件需要可以做到自动判断,被隔离的环境内是否有需要被调用的服务,并在当前环境以及基础环境中间进行自动选择,以保证服务被正确调用到。</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>**对数据库的访问。** 一是,数据库连接串的维护问题,与SOA调用链(即服务之间的调用关系)的维护类似,完全可以借鉴;二是,数据库的快速创建策略。<br />
|
||||
对于数据库中的表结构和数据,我们采取的方式是根据生产中实际的数据库结构,产生一个基准库,由用户自己来维护这个基准库的数据,保证数据的有效性。并在环境创建时,提供数据库脚本变更的接口,根据之前的基准库创建一个新的实例,由此保证环境中的数据符合预期。</p>
|
||||
</li>
|
||||
|
||||
**对于环境的创建和拆分**,最主要的问题就是如何复制和重新配置环境中的各个零件。环境创建,就是不断提高虚拟机准备和应用部署两个流水线的速度和稳定性;环境拆分,则需要关注以上所说的三个最重要的配置内容。
|
||||
|
||||
**而环境的合并需要注意的问题是,合并后的环境冲突。** 比如,两套环境中都存在同一个服务应用,而两者的版本是不一致的;又或者,两个环境各自配置了一套数据库。此时该如何处理呢。
|
||||
|
||||
因为环境的描述已经被代码化了,所以我们解决这些问题的方式类似于解决代码合并的冲突问题。在环境合并前,先进行一次环境的冲突检测,如果环境中存在不可自动解决的冲突,就将这些冲突罗列出来,由用户选择合适的服务版本。
|
||||
|
||||
如何高效、自动化地实现环境变更的关键点还是在于,我在前面几篇文章中提到的如何管理和实现应用配置和环境配置,以及如何配合环境管理在速度上的需求。
|
||||
|
||||
## 总结
|
||||
|
||||
对于如何快速搭建一套环境,我从虚拟机环境准备、应用部署流水线和环境变更,这三个方面给你总结了一些常见问题和原则:
|
||||
|
||||
<li>
|
||||
可以使用虚拟机资源池,提升获取机器资源的速度;
|
||||
</li>
|
||||
<li>
|
||||
合理打造并行的应用部署流水线,是进一步提升环境创建速度的方法;
|
||||
</li>
|
||||
<li>
|
||||
利用配置等方式快速达到环境变更需求,可以再次有效地提升整个环境部署的效率。
|
||||
</li>
|
||||
|
||||
## 思考题
|
||||
|
||||
你所在的公司,新环境应用部署的流水线是怎样的?如果要进一步提速的话,还有哪些优化空间呢?
|
||||
|
||||
欢迎你给我留言。
|
||||
|
||||
|
||||
170
极客时间专栏/geek/持续交付36讲/环境管理/13 | 容器技术真的是环境管理的救星吗?.md
Normal file
170
极客时间专栏/geek/持续交付36讲/环境管理/13 | 容器技术真的是环境管理的救星吗?.md
Normal file
@@ -0,0 +1,170 @@
|
||||
<audio id="audio" title="13 | 容器技术真的是环境管理的救星吗?" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/3e/2d/3e7079f9b0b399526053970a71400b2d.mp3"></audio>
|
||||
|
||||
在上一篇文章中,我分享了基于虚拟机打造自动化流水线中的一些常见问题和原则。随着计算机技术的发展,交付方式也在不断地演进和变更。而基于虚拟机的交付方式,正在被基于容器的交付方式所替代。
|
||||
|
||||
今天,如果你在一个较大的科技公司,你必定会遇到的如下的场景:
|
||||
|
||||
<li>
|
||||
多个技术栈;
|
||||
</li>
|
||||
<li>
|
||||
多个不同类型的应用;
|
||||
</li>
|
||||
<li>
|
||||
不同的开发环境和运行环境。
|
||||
</li>
|
||||
|
||||
因此,你所面对的交付场景也会变得越来越复杂,带来的挑战也会越来越大。
|
||||
|
||||
此外,敏捷研发的流行,使得低成本、高效率的解决研发问题的方式成为主流,因此复杂的交付的场景,显然就会成为拖油瓶。
|
||||
|
||||
加之,传统交付方法,已经很难满足这样快速迭代的交付需求,服务交付方式、快速部署、环境隔离、环境一致性等诸多问题亟待解决。
|
||||
|
||||
因此,在过去很长一段时间内,持续交付本身也陷入一个发展瓶颈。各规模的团队、企业都承认持续交付是一个好方案,但却都不敢试。其实,主要原因还是,持续交付在技术上没有得到突破性的发展。
|
||||
|
||||
但是,容器的出现和兴起,为微服务、CI/CD、DevOps 带来了新的可能性,使得持续交付又有了向前发展的动力,同时也带来了新的挑战。
|
||||
|
||||
那么,容器的出现到底为持续交付带来了哪些契机和挑战呢?我在这篇文章中,将和你讨论:为什么说容器是持续交付最重要的利器之一,是环境管理的将来式,这个问题,助你借助容器构建自己的持续交付体系。
|
||||
|
||||
## 什么是容器
|
||||
|
||||
在传统模式下的开发到部署流程是这样的:
|
||||
|
||||
<li>
|
||||
在本地电脑上安装开发应用所需要的库文件、扩展包、开发工具和开发框架,完成开发工作;
|
||||
</li>
|
||||
<li>
|
||||
本地开发完成后,将开发好的应用部署到测试环境进行测试;
|
||||
</li>
|
||||
<li>
|
||||
一切就绪后,再把应用部署到生产环境。
|
||||
</li>
|
||||
|
||||
但问题是,你该如何保证开发、测试和生产这三套环境,甚至更多套环境是完全一致的呢?再有就是,环境的变更问题,虽说“百分之九十九的故障是由变更导致的”是一句废话,但也是一句实话,你又该如何确保每套环境的变更是一致的呢?
|
||||
|
||||
而容器的出现,似乎解决了这些问题。
|
||||
|
||||
正如 Docker 官网解释的:
|
||||
|
||||
>
|
||||
容器镜像是软件的一个轻量的、独立的、可执行的包,包括了执行它所需要的所有内容:代码、运行环境、系统工具、系统库、设置。
|
||||
|
||||
|
||||
这代表着,一旦一个应用被封装成容器,那么它所依赖的下层环境就不再重要了。
|
||||
|
||||
那么,容器和虚拟机到底有什么区别呢?
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/39/72/394eb166b74653303f5eb1064c0df772.png" alt="" />
|
||||
|
||||
容器是一个在App层的抽象,整合了运行的应用软件代码以及它的依赖、环境。许多个这样的容器可以运行在同一台宿主机上,并与其他容器共享这台宿主机的系统内核。而且,每一个容器实例,都运行在自己独立的进程中,与其他实例隔离。
|
||||
|
||||
虚拟机是一种将一台服务器转变成多台服务器的物理硬件设备的抽象。Hypervisor软件是虚拟机的主要部分,它使得一台物理设备上可以运行多个虚拟机。
|
||||
|
||||
每个虚拟机都是一个完整操作系统的拷贝,再搭建一层 runtime,最后供应用程序运行。通常一个虚拟机大小都要超过10 GB。
|
||||
|
||||
**容器和虚拟机的主要差异,包括三个方面:**
|
||||
|
||||
- 首先,多个容器可以共享同一个宿主机的内核,所以容器的体积要比虚拟机小很多,这就使得容器在分发和存储上比较有优势;
|
||||
- 其次,启动容器不需要启动整个操作系统,所以容器部署和启动速度更快、开销更小,也更容易迁移,这使得容器拥有更强的恢复能力;
|
||||
- 最后,容器连带代码和环境一起部署的方式,保证了它所包含的程序的运行依赖不会被变更,这就使得容器有效解决了不同环境不同结果的问题。
|
||||
|
||||
了解了容器的基本概念,我接下来跟你说说,容器可以对持续交付产生什么影响。
|
||||
|
||||
## 重新定义交付标准
|
||||
|
||||
没有容器之前,交付标准包括软件环境(也就所谓的机器)和软件代码两部分。交付系统更关注的是软件代码,环境一旦产生后,我们就不再关心或者很难再干预用户后期是如何对其做变更的了。
|
||||
|
||||
也就是说,环境的变更没有版本,没有记录,甚至当事人也会忘记当时变更了什么, 不言而喻,这会带来很多未知的安全隐患。
|
||||
|
||||
**而,容器技术统一了软件环境和软件代码,交付产物中既包括了软件环境,又包括了软件代码。也就是说,容器帮我们重新定义了交付标准。**
|
||||
|
||||
那么,容器技术到底是如何做到的呢?被重新定义后的交付,又有哪些特点呢?
|
||||
|
||||
**第一,交付结果一致**
|
||||
|
||||
容器镜像可以把软件的运行环境以及代码打包在一起,因此可以基于同一个镜像,在不同的地方生成一模一样的运行环境,也就是说**单个镜像的交付结果不可变**。
|
||||
|
||||
当然,单个容器只能提供一个服务,而实际场景下,应用都是跑在SOA或微服务的框架下的。所以,还需要利用如 Mesos 或 Kubernetes 这样的编排系统,将多个容器组织起来,并**固化编排过程**。
|
||||
|
||||
基于这两个特性,一旦形成了固定的容器镜像和对应的编排(也成为应用模板),那在不同的环境下,一定可以重复部署,且部署结果保持一致。
|
||||
|
||||
**第二,交付自动化**
|
||||
|
||||
容器镜像及容器编排技术很好地解决了CI和CD问题:
|
||||
|
||||
- CI方面,与传统方式的不同只在于,原先交付的是安装包或软件包,而容器交付的则是镜像;
|
||||
<li>CD方面,与传统方式相比则有了长足的进步。<br />
|
||||
对传统方式而言,部署和安装方式与软件类型、开发方式有直接关系,存在多种多样的可能。<br />
|
||||
而容器技术则没有这样的问题,唯一的方式就是拉起容器镜像。这就大大简化了部署的复杂度,而且在编排系统的支持下,完成CD越来越容易了。</li>
|
||||
|
||||
**第三,交付个性化**
|
||||
|
||||
传统的交付模式,往往因为环境的初始化问题,只能完成有限种类的交付。运维部门很难为所有的应用做出统一的环境模板,比如需要哪些软件依赖、需要哪些系统配置、部署的步骤是怎样的等等,要统一这些模板,就需要协调多个部门共同完成,难度可想而知。
|
||||
|
||||
对于一些受众比较少的程序语言,或者一个仅仅想部署一套开源软件的需求是很难满足的,大多数情况下,需要用户自己去申请虚拟机,然后按照官方提供的文档一步一步安装环境。这样操作,非常麻烦,更别提后续的更新了。
|
||||
|
||||
但是,有了容器之后,我们可以使用统一的接口完成任何应用的部署,几乎可以很好地满足所有的个性化需求。
|
||||
|
||||
**第四,交付版本控制**
|
||||
|
||||
对于容器来说,遵循的是不可变基础设施(Immutable Infrastructure)的理念,也就是说任何变化,包括代码、环境、配置的变更,都需要重新制作镜像,产生一个新的版本。这与版本往往只和代码变更有关的传统方式有所不同。
|
||||
|
||||
那么,这样的变化到底是好是坏呢?
|
||||
|
||||
## 变还是不变,这是个问题
|
||||
|
||||
不可变基础设施(Immutable Infrastructure),是 Chad Fowler 在2013年提出的一个很有前瞻性的构想:
|
||||
|
||||
>
|
||||
在这种模式中,任何基础设施的实例(包括服务器、容器等各种软硬件)一旦创建之后便成为一种只读状态,不可对其进行任何更改。如果需要修改或升级某些实例,唯一的方式就是创建一批新的实例来替换它。
|
||||
|
||||
|
||||
这种思想与不可变对象的概念完全相同。
|
||||
|
||||
而容器相比于虚拟机体积小和启动快的优势,正好符合了不可变基础设施这一模式的核心思想。
|
||||
|
||||
不可变基础设施模式的好处显而易见,主要包括以下三个方面:
|
||||
|
||||
<li>
|
||||
很多与runtime相关的配置工作都可以被简化,这让持续集成与持续部署过程变得更流畅。
|
||||
</li>
|
||||
<li>
|
||||
它也更易于应对部署环境间的差异及版本,进行更有效、全面的管理。
|
||||
</li>
|
||||
<li>
|
||||
对回滚来说,更是得到了充分的保证,只要原先版本的镜像存在,它就一定能被恢复。
|
||||
</li>
|
||||
|
||||
虽然不可变基础设施模式能够带来非常多的好处,但是其实现的难度也很高,你需要一套完全不同的版本管理系统,纳入所有的变更,重新定义版本、变更和发布。如何做到这些,我会在后续的文章中为你详细介绍。
|
||||
|
||||
但是,这种模式在我看来也是略微违反人性的(人们往往是想怎么简单怎么来)。试想如果你仅有一台机器,只是想升级一下 cURL 的版本,你觉得是直接在容器里更新方便,还是更改 Dockerfile 重打镜像走完一整套发布流程更方便呢?
|
||||
|
||||
## 容器不是银弹
|
||||
|
||||
正如上面所说,不可变基础设施模式对运维人员来说绝对是福音,为企业实现持续交付保驾护航。但是,对普通用户来说,这种模式有时候却是一种折磨,不可能有完美的标准化容纳所有的个性化,我们必须为个性化需求做准备。
|
||||
|
||||
目前,很多业务开发人员的观念还停留在使用虚拟机的阶段,从虚拟机迁移到容器时,我们也是拼了命地把容器的使用体验向虚拟机靠近,尽量让用户感觉就是在用虚拟机。
|
||||
|
||||
初衷是好的,但是这种做法却不能让用户真正认识并理解容器。
|
||||
|
||||
在迁移前期,我们经常会遇到这样的案例:由于个别应用对环境的个性化需求,用户需要登录虚拟机安装一些软件,或者更新一些配置。迁到容器后,他们依然这么做,但是结果让他们失望,因为每次应用部署后,之前的环境变更就都消失了。这无疑让他们非常沮丧,就好比写了几个小时的代码忘记了保存。
|
||||
|
||||
我们虔诚地遵循了不可变基础设施模式,但是又没有很好地告知用户这一原则。因此,我们不得不提供各种各样的方式让用户完成 “不可变中的可变” 与 “标准化中的个性化”,甚至我们必须在不同的环境使用不同的镜像。
|
||||
|
||||
而这,与我们认为的容器交付的理想状态是有差距的。虽然如此,但如何达成这样的目的,我也会在之后的文章中为你具体介绍。
|
||||
|
||||
## 总结
|
||||
|
||||
在这篇文章中,我介绍了容器如何代替虚拟机帮助我们应对持续交付的新挑战,但也阐述了使用容器技术实施持续交付的一些不足。
|
||||
|
||||
首先,容器是一种轻量级、可移植、自包含的软件打包技术,使应用程序几乎可以在任何地方以相同的方式运行。
|
||||
|
||||
然后,我分别从交付结果一致、交付自动化、交付个性化和交付版本控制这4个方面重新定义了基于容器的交付标准。
|
||||
|
||||
最后,我又从变和不变两个方向,阐述了容器能解决一些已有的问题,但它并不是银弹,它同样会带来问题,而这些问题,则需要改造和重新设计既有的持续交付模式来解决。
|
||||
|
||||
## 思考题
|
||||
|
||||
你所在的公司是否已经容器化了?如果已经容器化了,是如何平衡应用标准化与个性化的?对于有状态应用,又该如何使用容器进行交付呢?
|
||||
|
||||
欢迎你给我留言。
|
||||
Reference in New Issue
Block a user