我通过测试200,000行基础架构代码中学到了什么


IaC (基础结构即代码)方法不仅包括存储在存储库中的代码,还包括围绕该代码的人员和流程。 是否可以重用从软件开发到基础架构管理和描述的方法? 在阅读本文时,牢记这个想法并不是多余的。


俄语版


这是我在DevopsConf 2019-05-28上的 表现成绩单



基础设施作为重击历史



假设您来到一个新项目,他们对您说:“我们将基础架构作为代码 。” 实际上,事实证明, 基础架构是bash历史记录,或者例如是Documentation作为bash历史记录 。 这是一个非常现实的情况,例如,丹尼斯·利森科(Denis Lysenko)在演讲中描述了一个类似的案例,“ 如何更换整个基础设施并开始安然入睡” ,他告诉bash从历史上他们如何在该项目上获得了一个细长的基础设施。


如果愿意,可以说将基础结构作为bash历史记录类似于代码:


  1. 重现性 :您可以获取bash历史记录,从那里执行命令,也许,顺便说一句,您将在输出中获得有效的配置。
  2. 版本控制 :您再次知道是谁进来的,做了什么,而不是事实,这将导致您在输出端进行有效的配置。
  3. 历史 :谁做了什么的历史。 只有当服务器丢失时,您才能使用它。

怎么办


基础架构即代码



即使像bash历史记录这样的基础结构这样的奇怪情况也可以被基础结构代码的代码所吸引,但是当我们想做比旧的LAMP服务器更复杂的事情时,我们会得出结论,需要以某种方式修改,修改和修改此代码。 。 此外,我们想考虑基础设施即代码和软件开发之间的相似之处。


干燥



在用于存储系统开发的项目中,有一个子任务用于定期配置SDS :我们正在发布一个新版本-需要将其推出以进行进一步测试。 任务非常简单:


  • 通过ssh来到这里并执行命令。
  • 将文件复制到那里。
  • 这是对配置的调整。
  • 在那开始服务
  • ...
  • 赢利!

对于描述的逻辑,Bash绰绰有余,尤其是在项目刚开始时的早期阶段。 使用bash并不坏 ,但是随着时间的推移,系统会提示您部署类似但略有不同的东西。 首先想到的是:复制粘贴。 现在,我们有两个非常相似的脚本,它们执行几乎相同的操作。 随着时间的流逝,脚本的数量在增加,而且我们面对这样一个事实,那就是用于部署安装的业务逻辑必须在不同脚本之间进行同步,这是非常困难的。



事实证明,有一种这样的练习DRY(不要重复自己)。 这个想法是重用现有代码。 听起来很简单,但并没有马上解决。 在我们的案例中,这是一个很普遍的想法:将配置与脚本分开。 即 业务逻辑如何分别部署安装和分别配置。


SOLID for CFM



随着时间的推移,该项目不断发展,Ansible的出现自然而然地扩展了。 他出现的主要原因是团队中存在专业知识,而bash并非旨在用于复杂的逻辑。 Ansible也开始包含复杂的逻辑。 为了使复杂的逻辑不会变得混乱,有一些在软件开发中组织SOLID代码的原则,例如,格里戈里·彼得罗夫(Grigory Petrov)在他的报告“ IT为什么需要个人品牌”中提出了一个问题,即人的设计如此之好,以至于某些人更容易操作社会实体,在软件开发中,这些都是对象。 如果您将这两个想法结合在一起并继续发展,您会注意到您还可以在基础结构的描述中使用SOLID ,以便将来更容易维护和修改此逻辑。


单一责任原则



每个班级仅执行一项任务。


无需混合代码即可制作整体的神圣面食怪物。 基础设施应由简单的砖块组成。 事实证明,如果将Ansible剧本分成小块,阅读Ansible角色,那么它们将更易于维护。


开放封闭原则



开放/紧密的原则。


  • 开放进行扩展:意味着可以通过创建新类型的实体来扩展实体的行为。
  • 禁止更改:由于扩展了实体的行为,因此不应更改使用这些实体的代码。

最初,我们在虚拟机上部署了测试基础架构,但是由于业务部署逻辑与实现是分开的,因此我们很容易在裸机上添加了内容。


里斯科夫替代原则



芭芭拉·李斯科夫(Barbara Liskov)的替代原则。 程序中的对象必须可以用其子类型的实例替换,而无需更改程序的正确性


如果从更广泛的角度看,它不是可以应用SOLID的任何特定项目的功能,通常是关于CFM的,例如,在另一个项目上,您需要在各种Java,应用程序服务器,数据库,操作系统等之上部署盒装Java应用程序。 对于此示例,我将考虑其他SOLID原则。


在我们的案例中,作为基础架构团队的一部分,达成了一项协议,即如果我们安装了imbjava或oraclejava角色,那么我们将拥有一个二进制Java可执行文件。 这是必要的,因为 较高的角色取决于此行为;他们希望Java存在。 同时,这允许我们在不更改应用程序部署逻辑的情况下,用另一种实现/版本的Java替换Java。


这里的问题在于,在Ansible中无法实现这样的事实,因此,团队中出现了一些协议。


接口隔离原理



接口分离的原理“许多专门为客户设计的接口比单个通用接口要好。


最初,我们尝试将将应用程序部署的所有变体放入一个Ansible剧本中,但是很难支持,并且指定了当我们有一个接口(客户端期望端口443)时使用的方法,然后对于特定的实现,您可以使用单独的模块来构建基础架构。


依赖倒置原则



依赖倒置的原理。 上级模块不应依赖于下级模块。 两种模块都应依赖抽象。 抽象不应依赖细节。 细节应取决于抽象。


这里的例子将基于反模式。


  1. 其中一位客户拥有私有云。
  2. 在云内部,我们订购了虚拟机。
  3. 但是鉴于云的功能,应用程序的部署与VM所在的虚拟机监控程序有关。

即 在高层应用程序部署逻辑中,依赖关系流到了虚拟机管理程序的较低层,这意味着在重用此逻辑时会出现问题。 不要那样做


互动互动



基础架构作为代码不仅与代码有关,而且还与代码与人之间的关系有关,与基础架构开发人员之间的交互有关。


总线系数



假设您在该项目上有Vasya。 Vasya了解您的基础架构的所有内容,如果Vasya突然消失会发生什么? 这是一个非常真实的情况,因为它可能会被公共汽车撞到。 有时会发生这种情况。 如果发生这种情况,并且有关代码,其结构,其工作方式,外观和密码的知识没有在团队中分发,那么您可能会遇到许多不愉快的情况。 可以使用各种方法来最小化这些风险,并在团队中分发知识。


对发展



管理员喝啤酒,更改密码不是开玩笑 ,而是配对编程的模拟。 即 两名工程师坐在一台计算机,一个键盘上,开始一起配置您的基础结构:配置服务器,编写Ansible角色等。 听起来不错,但对我们没有用。 但是这种做法的特殊情况有效。 新员工来了,他的导师与他一起完成了一项真正的任务,工作,传播知识。


另一个特殊情况是事件呼叫。 在问题期间,一群值班人员和相关人员聚集在一起,任命了一位领导人,他分享了他的画面并表达了思路。 其他参与者遵循领导者的想法,从控制台中监视技巧,检查他们是否没有错过日志行,了解有关系统的新知识。 这种方法行之有效而不是无效。


代码审查



从主观上讲,更有效地使用代码审查来传播有关基础结构及其组织方式的知识:


  • 基础结构由存储库中的代码描述。
  • 更改发生在单独的分支中。
  • 通过合并请求,您可以看到基础架构变更的增量。

这里的重点是根据时间表,即依次选择了审阅者。 您很有可能会涉足新的基础架构。


代码风格



随着时间的流逝,在审查期间开始出现争吵,因为 审阅者有自己的风格,审阅者可旋转性使它们具有不同的样式:2个空格或4个,camelCase或snake_case。 实施此操作无法立即生效。


  • 第一个想法是建议使用linter,因为所有相同的工程师都非常聪明。 但是不同的编辑器,OS,不方便
  • 这演变成一个机器人程序,该程序针对每次提交以松懈状态写入有问题的程序,并应用了linter的输出。 但是在大多数情况下,发现了更重要的问题,并且代码仍未固定。

绿色建筑大师



时间过去了,我们得出的结论是,您不应允许未通过某些测试的提交进入主服务器。 瞧! 我们发明了Green Build Master,该软件早已在软件开发中实践过:


  • 开发位于单独的分支中。
  • 测试在此线程上运行。
  • 如果测试失败,则代码不会进入向导。

做出这个决定非常痛苦,因为 引起了很多争议,但这是值得的,因为 在没有风格分歧的情况下,对合并的要求开始接受审查,随着时间的流逝,问题领域的数量开始减少。


IaC测试



除了样式检查之外,您还可以使用其他方式,例如,验证您的基础架构是否可以真正部署。 或检查基础架构的变化不会导致金钱损失。 为什么可能需要这样做? 这个问题是复杂且具有哲学性的,最好用这样的故事来回答:Powershell上有一个自动缩放器,它没有检查边界条件=>创建的VM超出了必要的数量=>客户花费的资金超过了计划。 这还不够令人愉快,但是在早期阶段捕获此错误将是很现实的。


有人可能会问,为什么使复杂的基础架构变得更加困难? 对基础结构以及代码的测试不是关于简化,而是关于了解基础结构应如何工作。


IaC测试金字塔



IaC测试:静态分析


如果立即部署整个基础结构并验证其是否有效,则可能会花费大量时间并且需要大量时间。 因此,基础应该是快速起作用的东西,它很多,并且涵盖了许多原始的地方。


重击很棘手


这是一个简单的例子。 选择当前目录中的所有文件,然后复制到另一个位置。 首先想到的是:


for i in * ; do cp $i /some/path/$i.bak done 

但是,如果文件名中有空格怎么办? 好吧,好的,我们很聪明,我们可以使用引号:


 for i in * ; do cp "$i" "/some/path/$i.bak" ; done 

干得好吗 不行 如果目录中没有任何内容,即 乱搞是行不通的。


 find . -type f -exec mv -v {} dst/{}.bak \; 

现在做得好吗? 不是...忘记文件名可能是\n


 touch x mv x "$(printf "foo\nbar")" find . -type f -print0 | xargs -0 mv -t /path/to/target-dir 

静态分析工具


当我们忘记了引号时,可能会遇到上一步的问题,因为实际上这是许多Shellcheck工具 ,它们很多,而且很可能在IDE下可以找到用于堆栈的衬板。


语言能力工具工具
重击Shellcheck
红宝石鲁科科普
蟒蛇皮林特
AnsibleAnsible皮棉

IaC测试:单元测试



从前面的示例中可以看到,lint并不是万能的,并且不能指向所有问题区域。 此外,通过类似于软件开发中的测试,我们可以回顾单元测试。 然后立即想到shunitjunitrspecpytest 。 但是与ansible,厨师,saltstack和其他类似的人怎么办?


在开始时,我们谈到了SOLID,以及我们的基础架构应由小砖块组成的事实。 他们的时间到了。


  1. 基础设施被粉碎成小块,例如Ansible角色。
  2. 正在开发某种环境,无论是docker还是VM。
  3. 我们将Ansible角色应用于此测试环境。
  4. 我们检查一切是否按预期进行(运行测试)。
  5. 我们决定可以还是不可以。

IaC测试:单元测试工具


问题是,什么是CFM测试? 您可以运行脚本corny,也可以为此使用现成的解决方案:


CFM工具工具
AnsibleTestinfra
主厨检验
主厨服务器规格
盐堆高斯

以testinfra为例,我们验证用户test1test2存在并且在sshusers组中:


 def test_default_users(host): users = ['test1', 'test2' ] for login in users: assert host.user(login).exists assert 'sshusers' in host.user(login).groups 

选择什么? 这个问题既复杂又模棱两可,以下是GitHub 2018-2019年项目变更的一个示例:



IaC测试框架


有如何将它们放在一起运行? 如果您有足够的工程师,则可以自己采取一切措施 。 您可以采用现成的解决方案,尽管其中没有很多:


CFM工具工具
Ansible分子
主厨测试厨房
地貌Terratest

GitHub上2018-2019年项目变更的示例:



分子与 Testkitchen



最初,我们尝试使用testkitchen


  1. 并行创建虚拟机。
  2. 应用Ansible角色。
  3. 赶走检查。

对于25-35个角色,这需要40-70分钟,这是很长的时间。



下一步是切换到jenkins / docker / ansible / molecular。 在意识形态上,一切都一样


  1. 皮棉剧本。
  2. 溢出角色。
  3. 运行容器
  4. 应用Ansible角色。
  5. 赶走testinfra。
  6. 检查幂等性。


一个负责40个角色的标尺和十多个角色的测试开始大约需要15分钟。



选择什么取决于许多因素,例如使用的堆栈,团队中的专业知识等。 在这里,每个人都决定如何结束单元测试的问题


IaC测试:集成测试



在基础架构测试金字塔的下一阶段,将出现集成测试。 它们类似于单元测试:


  1. 基础设施被粉碎成小块,例如Ansible角色。
  2. 正在开发某种环境,无论是docker还是VM。
  3. 许多 Ansible角色应用于此测试环境。
  4. 我们检查一切是否按预期进行(运行测试)。
  5. 我们决定可以还是不可以。

粗略地说,我们不像在单元测试中那样检查系统中各个元素的可操作性,而是检查服务器的整体配置方式。


IaC测试:端到端测试



在金字塔的顶端,我们通过端到端测试遇到了。 即 我们不会检查单独的服务器,单独的脚本,单独的基础架构的运行情况。 我们检查了许多服务器是否组合在一起,我们的基础架构是否按预期工作。 不幸的是,我没有看到任何现成的盒子解决方案,可能是因为 基础架构通常是唯一的,很难模板化并创建测试框架。 结果,每个人都创建自己的解决方案。 有需求,但没有答案。 因此,我将告诉您什么可以促使其他人发表想法或戳我的鼻子,因为这一切都早于我们而已。



历史悠久的项目。 用在大型组织中,可能每个人都间接相交。 该应用程序支持许多数据库,集成等,等等。 知道基础架构的样子是很多docker-compose文件,并且知道在哪种环境下运行哪些测试是詹金斯。



该方案已经工作了很长时间,直到作为研究的一部分,我们试图将其转移到Openshift上。 容器保持不变,但是启动环境已更改(再次DRY,您好)。



研究的想法更进一步,在openshift中,出现了APB(Ansible Playbook Bundle)这样的东西,它允许您在容器中打包如何部署基础结构的知识。 即 关于如何部署基础架构,存在可重现,可测试的知识点。



所有这些听起来都不错,直到我们将自己埋在异构的基础架构中:我们需要Windows进行测试。 结果,jenkins掌握了有关在哪里部署以及如何进行测试的知识。


结论



基础架构即代码


  • 存储库中的代码。
  • 人与人之间的互动。
  • 基础架构测试。

Source: https://habr.com/ru/post/zh-CN467171/


All Articles