连续云基础架构

使用Packer和Terraform等开源工具向用户喜欢的云环境不断交付基础架构更改的演示。


该材料基于Paul Stack在我们2017年秋季DevOops会议上的演讲。Paul是一名基础架构开发人员,曾在HashiCorp工作,并参与了数百万人使用的工具(例如Terraform)的开发。 他经常在会议上发表演讲,并从CI / CD实施的最前沿,实践部分的正确组织原则来传达实践,并且能够清楚地解释为什么管理员要这么做。 本文其余部分以第一人称叙述。

因此,让我们立即从一些关键发现开始。

长时间运行的服务器很烂




我以前曾在一个组织中工作过,该组织早在2008年就已部署Windows Server 2003,而今天它们仍在生产中。 这样的公司并不孤单。 他们使用这些服务器上的远程桌面,手动安装软件,从Internet下载二进制文件。 这是一个非常糟糕的主意,因为服务器不是典型的服务器。 您不能保证在生产中会发生与在开发环境,中间环境,QA环境中相同的事情。

不变的基础设施


2013年,Chad Foiler的博客上发表了一篇题为“扔掉服务器并刻录代码:不可变的基础结构和一次性组件”的文章(Chad Foiler 乱扔服务器并刻录代码:不可变的基础结构和一次性组件” )。 这主要是关于不变的基础设施是前进之路的讨论。 我们已经创建了基础架构,并且如果需要更改它,那么我们正在创建新的基础架构。 这种方法在云中非常普遍,因为它既快速又便宜。 如果您有物理数据中心,这将有些麻烦。 显然,如果您运行数据中心虚拟化,事情将会变得更加容易。 但是,如果仍然每次都启动物理服务器,则输入新的物理服务器要比修改现有的物理服务器花费更长的时间。

一次性基础设施


根据函数式程序员的说法,“不变”实际上是此现象的错误术语。 因为要真正实现不可变,您的基础架构需要一个只读文件系统:不会在本地写入任何文件,没有人能够使用SSH或RDP等。 因此,实际上看来基础架构并不是一成不变的。

几个人在Twitter上讨论了六到八天的术语。 最后,他们同意“一次性基础设施”是更合适的表述。 当“一次性基础设施”生命周期结束时,可以轻松销毁它。 您无需坚持。

我会打个比方。 农场牛一般不被视为宠物。



农场里有牛时,不要给他们起个单独的名字。 每个人都有一个数字和一个标签。 服务器也是如此。 如果您仍在2006年在生产环境中手动创建服务器,则它们具有重要名称,例如,“ SQL Server on Production 01”。 它们具有非常具体的含义。 如果其中一台服务器崩溃,地狱就会开始。

如果畜群中的一种动物死亡,农民只需购买新的一种。 这就是“一次性基础设施”。

持续交付


那么,如何将其与持续交付结合起来?
我现在谈论的一切都已经存在了一段时间。 我只是想将基础架构开发和软件开发的思想结合起来。

长期以来,软件开发人员一直致力于持续交付和持续集成。 例如,马丁·福勒(Martin Fowler)早在2000年代初就在自己的博客中撰写了有关持续集成的文章。 Jez Humble长期以来一直提倡持续交付。

如果您仔细看一看,并没有为软件的源代码专门创建任何内容。 Wikipedia有一个标准定义: 持续交付是一组旨在尽快创建,测试和发布软件的实践和原则

该定义并不意味着Web应用程序或API,而是一般而言的软件。 创建拼图软件需要很多拼图。 这样,您可以以相同的方式练习连续交付基础结构代码。

基础设施和应用程序的开发是非常接近的方向。 编写应用程序代码的人也会编写基础结构代码(反之亦然)。 这些世界开始团结起来。 每个世界都不再存在这种分离和特定的陷阱。

持续交付原则和实践


持续交付有许多原则:

  • 软件发布/部署过程必须可重复且可靠。
  • 自动化一切!
  • 如果手术困难或痛苦,请多做几次。
  • 将所有内容保留在源代码管理中。
  • 完成-表示“未发布”。
  • 将工作与质量融为一体!
  • 每个人都对发布过程负责。
  • 增加连续性。

但更重要的是,连续交付有四种做法。 拿走它们并直接转移到基础架构:

  • 只能创建一次二进制文件。 一次构建您的服务器。 在这里,我们从一开始就谈论“一次性性”。
  • 在每个环境中使用相同的部署机制。 在开发和生产中请勿进行其他部署。 您必须在每个环境中使用相同的路径。 这很重要。
  • 测试您的部署。 我创建了许多应用程序。 我没有遵循部署机制,因此造成了很多问题。 您应该始终检查会发生什么。 我并不是说您应该花五六个小时进行大规模测试。 足够的“烟雾测试”。 您拥有系统的关键部分,如您所知,它可以使您和您的公司赚钱。 不要太懒惰开始测试。 如果您不这样做,则可能会有中断,这将使您的公司损失金钱。
  • 最后,最重要的是。 如果发生故障,请立即停止并修复! 您不能让问题变得越来越严重。 您必须修复它。 这真的很重要。

有人读过《 持续交付》这本书吗?



我确信您的公司会付给您一份副本,您可以在团队内部转让。 我并不是说您应该坐下来花一天的时间阅读它。 如果这样做,您可能想退出IT。 但是,我建议您定期精读本书的小片段,进行消化,并考虑如何将其转移到您的环境,文化和过程中。 一次一小块。 因为持续供应是关于持续改进的话题。 与同事和老板坐在办公室坐下来讨论以下问题并非易事:“我们将如何实现持续交付?”,然后在董事会上写下10件事,并在十天后就知道您已经实现了。 这需要很多时间,引起很多抗议,因为随着文化的引入而变化。

今天,我们将使用两种工具:Terraform和Packer(均为Hashicorp开发)。 我们将进一步讨论为什么我们应该使用Terraform以及如何将其集成到我们的环境中。 我谈论这两个工具并非偶然。 直到最近,我还在Hashicorp工作。 但是,即使我离开了Hashicorp,我仍然为这些工具的代码做贡献,因为我发现它们非常有用。



Terraform支持与提供程序的交互。 提供者是云,Saas服务等。

在每个云服务提供商中,都有多个资源,例如子网,VPC,负载均衡器等。使用DSL(特定于域的语言),您可以告诉Terraform基础架构的外观。

Terraform使用图论。



您可能知道图论。 节点是我们基础架构的一部分,例如负载平衡器,子网或VPC。 肋骨是这些系统之间的关系。 这是我个人认为对使用Terraform的图论所必需的全部。 我们将其余的留给专家。



Terraform实际上使用有向图,因为它不仅知道关系,而且知道它们的顺序:必须将A(假设A是VPC)设置为B,B是子网。 并且必须在C(实例)之前创建B,因为存在在Amazon或任何其他云中创建抽象的规定过程。
Paul Hinze仍是Hashicorp基础架构总监,可在YouTube上获得有关此主题的更多信息。 通过引用-有关基础架构和图论的精彩讨论。

练习


编写代码比讨论理论要好得多。

我以前创建了AMI(Amazon机器映像)。 我使用Packer来创建它们,并向您展示如何做。

AMI是Amazon中虚拟服务器的一个实例,它是预定义的(根据配置,应用程序等),并且是从映像创建的。 我喜欢我可以创建新的AMI。 本质上,AMI是我的Docker容器。

所以,我有AMI,他们有ID。 转到Amazon界面,我们看到只有一个AMI,仅此而已:



我可以告诉您此AMI中的内容。 一切都非常简单。

我有一个JSON文件模板:

{ "variables": { "source_ami": "", "region": "", "version": "" }, "builders": [{ "type": "amazon-ebs", "region": "{{user 'region'}}", "source_ami": "{{user 'source_ami'}}", "ssh_pty": true, "instance_type": "t2.micro", "ssh_username": "ubuntu", "ssh_timeout": "5m", "associate_public_ip_address": true, "ami_virtualization_type": "hvm", "ami_name": "application_instance-{{isotime \"2006-01-02-1504\"}}", "tags": { "Version": "{{user 'version'}}" } }], "provisioners": [ { "type": "shell", "start_retry_timeout": "10m", "inline": [ "sudo apt-get update -y", "sudo apt-get install -y ntp nginx" ] }, { "type": "file", "source": "application-files/nginx.conf", "destination": "/tmp/nginx.conf" }, { "type": "file", "source": "application-files/index.html", "destination": "/tmp/index.html" }, { "type": "shell", "start_retry_timeout": "5m", "inline": [ "sudo mkdir -p /usr/share/nginx/html", "sudo mv /tmp/index.html /usr/share/nginx/html/index.html", "sudo mv /tmp/nginx.conf /etc/nginx/nginx.conf", "sudo systemctl enable nginx.service" ] } ] } 

我们有传递的变量,Packer有一个针对不同区域的所谓的Builders列表。 有很多。 Builder使用特殊的AMI源,我将其输入AMI标识符。 我给他提供了SSH用户名和密码,还指出了他是否需要公共IP地址,以便人们可以从外部访问它。 对于我们而言,这并不重要,因为它是Packer的AWS实例。
我们还设置了AMI名称和标签。

您不必解析此代码。 他在这里只是向您展示他的工作方式。 这里最重要的部分是版本。 稍后我们进入Terraform时,它将变得有意义。

构建器调用实例后,将在其上启动预配置代理。 我实际上安装了NCP和nginx,向您展示了我在这里可以做什么。 我复制一些文件,然后设置nginx配置。 一切都非常简单。 然后,我激活nginx,以便它在实例启动时启动。

因此,我有一个应用程序服务器,它可以工作。 我将来可以使用。 但是,我总是检查我的Packer模板。 因为它是JSON配置,所以您可能会遇到一些问题。
为此,我运行命令:

make validate

我得到了Packer模板已成功验证的答案:



这只是一个命令,因此我可以将其连接到CI工具(任何人)。 实际上,这将是一个过程:如果开发人员更改模板,则生成拉取请求,CI工具将检查请求,执行等同于检查模板的操作,并在成功验证的情况下发布模板。 所有这些都可以在“主站”中合并。
我们获得了AMI模板的流-您只需要提高版本即可。

假设开发人员创建了新版本的AMI。



我将修复从1.0.0到1.0.1的文件中的版本,以向您显示差异:

 <html> <head> <tittle>Welcome to DevOops!</tittle> </head> <body> <h1>Welcome!</h1> <p>Welcome to DevOops!</p> <p>Version: 1.0.1</p> </body> </html> 

我将返回命令行并开始创建AMI。
我不喜欢跑同样的球队。 我喜欢快速创建AMI,因此我使用makefile。 让我们看看我的makefile中的cat

cat Makefile




这是我的makefile。 我什至提供了帮助:我键入make并单击选项卡,它向我显示了所有目标。



因此,我们将创建一个新的AMI版本1.0.1。

make ami




返回Terraform。

我强调这不是生产代码。 这是一个示范。 有很多方法可以使同一件事做得更好。

我到处都使用Terraform模块。 由于我不再从事Hashicorp的工作,因此我可以对模块发表意见。 对我而言,模块处于封装级别。 例如,我喜欢封装与VPC相关的所有内容:网络,子网,路由表等。

里面到底是怎么回事 从事此工作的开发人员可能并不在意。 他们需要对云的工作原理,VPC是什么有基本的了解。 但是没有必要去研究细节。 只有真正需要更改模块的人员才能理解它。




在这里,我将创建一个AWS资源和一个VPC模块。 这是怎么回事 使用顶级cidr_block并创建三个私有子网和三个公共子网。 以下是acailability_zones的列表。 但是我们不知道这些可访问区域是什么。



我们将创建一个VPN。 只是不要使用此VPN模块。 这是openVPN,它将创建一个没有证书的AWS实例。 它仅使用公共IP地址,此处仅提及它是为了告诉您我们可以连接到VPN。 有更多用于创建VPN的便捷工具。 我花了大约20分钟的时间写了两本啤酒。





然后,我们创建一个application_tier ,它是一个自动扩展组-一个负载平衡器。 一些启动配置是基于AMI-ID的,它结合了多个子网和可用性区域,并且还使用SSH密钥。
让我们再回到这一点。

我已经提到了可用区。 对于不同的AWS账户,它们有所不同。 我在美国东部的帐户可以访问区域A,B和D。您的AWS帐户可以访问区域B,C和E。因此,在代码中固定这些值时,我们会遇到问题。 Hashicorp的我们建议我们可以创建此类数据源,以便我们可以询问Amazon可用的资源。 在后台,我们要求对可用区域进行描述,然后返回您帐户的所有可用区域的列表。 因此,我们可以将数据源用于AMI。

现在,我们深入我的演示。 我创建了一个自动伸缩组,其中正在运行三个实例。 默认情况下,它们都具有版本1.0.0。

当我们部署新版本的AMI时,我将再次启动Terraform配置,这将更改启动配置,新服务将接收下一个版本的代码,依此类推。我们可以对其进行控制。



我们看到Packer完成,并且有了新的AMI。
我回到亚马逊,刷新页面,然后看到第二个AMI。



返回Terraform。

从版本0.10开始,Terraform将提供程序拆分为单独的存储库。 并且init terraform命令获取运行所需的提供程序的副本。



提供程序已加载。 我们准备前进。
接下来,我们必须执行terraform get加载必要的模块。 它们现在在我的本地计算机上。 因此Terraform将在本地获取所有模块。 通常,模块可以存储在GitHub或其他地方的自己的存储库中。 这就是为什么我谈论VPC模块的原因。 您可以授予网络团队访问权限以进行更改。 这是开发团队与他们一起使用的API。 真的很有帮助。

下一步是建立图形。

从开始

terraform plan




Terraform将采用当前的本地状态并与AWS账户进行比较,以表明差异。 在我们的案例中,他将创建35个新资源。



现在我们应用更改:

terraform apply


您不必从本地计算机执行所有这些操作。 这些只是命令,将变量传递给Terraform。 您可以将此过程移植到CI工具。
如果要将其移至CI,则必须使用远程状态。 我希望所有曾经使用Terraform来处理远程状态的人。 请不要使用本地状态。

我的一位朋友指出,即使在Terraform工作了多年之后,他仍然发现了一些新东西。 例如,如果您正在创建一个AWS实例,则需要为其提供密码,然后它将其保存在您的状态。 当我在Hashicorp工作时,我们假设会有一个协作过程来更改此密码。 因此,请勿尝试将所有内容存储在本地。 然后,您可以将所有这些内容放入CI工具中。

因此,基础架构是为我创建的。



Terraform可以构建图形:

terraform graph


就像我说的,他正在建造一棵树。 实际上,它使您有机会评估基础架构中正在发生的事情。 他将向您展示所有不同部分之间的关​​系-所有节点和边。 由于连接具有方向,因此我们正在谈论有向图。

该图将是一个JSON列表,可以保存在PNG或DOC文件中。

返回Terraform。 我们实际上是在创建一个自动伸缩组。



自动伸缩组的容量为3。



一个有趣的问题:我们可以使用保险柜管理Terraform中的机密吗? las,不。 没有Vault数据源可用于读取Terraform中的机密。 还有其他方法,例如环境变量。 在他们的帮助下,您无需在代码中输入机密;您可以将它们作为环境变量读取。

因此,我们有一些基础设施:



我输入了非常秘密的VPN(请勿破解我的VPN)。

这里最重要的是,我们有三个应用程序实例。 没错,我应该注意到他们正在运行哪个版本的应用程序。 这很重要。


一切实际上都在VPN背后:



如果我将此文件( application-elb-1069500747.eu-west-1.elb.amazonaws.com )粘贴到浏览器的地址栏中,则会得到以下信息:



让我提醒您,我已连接到VPN。 如果我注销,则指定的地址将不可用。
我们看到版本1.0.0。 而且无论刷新页面多少,我们都会得到1.0.0。
如果将代码中的版本从1.0.0更改为1.0.1,会发生什么情况?

 filter { name = "tag:Version" values = ["1.0.1"] } 

显然,CI工具将确保您创建正确的版本。
我注意到没有手动更新! 我们并不完美,我们会犯错误,并且在手动更新时可以放置1.0.6版本而不是1.0.1版本。

 filter { name = "tag:Version" values = ["1.0.6"] } 

但是,让我们继续我们的版本(1.0.1)。

terraform plan


Terraform更新状态:





因此,此刻他告诉我他将在启动配置中更改版本。 由于标识符的更改,它将强制重新启动配置,并且自动缩放组也将更改(这对于启用新的启动配置是必需的)。

这不会更改正在运行的实例。 这真的很重要。 您可以按照此过程进行测试,而无需更改生产中的实例。

注意:必须始终创建新的启动配置,然后再销毁旧的启动配置,否则会出现错误。

让我们应用更改:

terraform apply


现在回到AWS。 应用所有更改后,我们进入自动缩放组。
让我们继续进行AWS配置。 我们看到有一个启动配置的三个实例。 他们是一样的。



亚马逊保证,如果我们要运行该服务的三个实例,则它们确实会启动。 这就是为什么我们付钱给他们。

让我们继续进行实验。

新的启动配置已创建。 因此,如果删除其中一个实例,其余实例将不会受到损坏。 这很重要。 但是,如果在更改用户数据时直接使用实例,则会破坏“活动”实例。 请不要这样做。

因此,删除实例之一:





自动缩放组关闭时会发生什么? 一个新实例将出现在其位置。



在这里,您发现自己处于一种有趣的情况。 实例将使用新配置启动。 也就是说,在系统中,您可能有几个不同的映像(具有不同的配置)。 有时最好不要立即删除旧的启动配置,以便根据需要进行连接。

在这里,一切变得更加有趣。 如我所展示的,为什么不使用CI脚本和工具而不是手动进行呢? 有一些工具可以做到这一点,例如GitHub上出色的AWS-missing-tools。



这个工具做什么? 这是一个bash脚本,它遍历负载均衡器中的所有实例,一次销毁一个实例,以确保在其位置上创建新实例。
如果我丢失了一个版本1.0.0的实例,并且出现了一个新实例-1.1.1,我想杀死所有1.0.0,并将所有内容转移到新版本。 因为我总是前进。 让我提醒您,我不喜欢应用程序服务器的使用寿命很长。

在其中一个项目中,每隔7天,我就有一个控制脚本,该脚本销毁了帐户中的所有实例。 因此,服务器的使用期限不超过7天。 另一件事(我的最爱)是在一个盒子中使用SSH将服务器标记为“已染色”,并使用脚本每小时对其进行销毁-我们不希望人们手动进行此操作。

此类控制脚本使您始终可以获取具有固定错误和安全更新的最新版本。

您可以通过运行以下命令来使用脚本:

aws-ha-relesae.sh -a my-scaling-group


-a是您的自动缩放组。 该脚本将遍历自动缩放组的所有实例,并将其替换。 您不仅可以手动运行它,还可以从CI工具运行它。
您可以在质量检查或生产中执行此操作。 您甚至可以在本地AWS账户中执行此操作。 您每次都使用相同的机制来做您想做的任何事情。

回到亚马逊。 我们有一个新实例:



在浏览器中更新了页面(之前是1.0.0版)后,我们得到:



有趣的是,由于我们创建了AMI创建脚本,因此我们可以测试AMI的创建。

有一些很棒的工具,例如ServerScript或Serverspec。

Serverspec允许您创建Ruby样式的规范,以测试应用程序服务器的外观。 例如,下面我给出一个测试,检查服务器上是否安装了nginx。

 require 'spec_helper' describe package('nginx') do it { should be_installed } end describe service('nginx') do it { sould be_enabled } it { sould be_running } end describe port(80) do it { should be_listening } end 

Nginx必须在服务器上安装并运行,并且侦听端口80。 您可以说用户X必须在服务器上可用。 您可以将所有这些测试放在他们的位置。 因此,当您创建AMI时,CI工具可以检查此AMI是否适合于给定目的。 您将知道AMI已准备好进行生产。

而不是结论


玛丽·波彭迪克(Mary Poppendieck)可能是我所听说过的最神奇的女人之一。 她一次谈到了这些年来精益软件开发的发展情况。 以及60年代公司真正从事精益开发时,她与3M的关系。

她问了一个问题:您的组织需要多长时间才能部署与一行代码相关的更改? 您可以使此过程可靠且可重复吗?

通常,此问题始终与软件代码有关。 部署到生产环境后,我需要花多长时间修复此应用程序中的一个错误? 但是没有理由为什么我们不能对基础架构或数据库使用相同的问题。

我曾在一家名为OpenTable的公司工作。 在其中,我们将其称为周期的持续时间。 在OpenTable中,她只有七个星期大。 这是比较好的。 我知道公司需要花费数月的时间才能将代码发送到生产环境。 在OpenTable,我们回顾了这一过程四年。 这花了很多时间,因为组织很大-200人。 而且我们将周期时间减少到三分钟。 由于对我们转换效果的测量,这才有可能。

现在,所有内容均已编写脚本。 我们有很多工具和示例,有GitHub。 因此,请从DevOops之类的会议中汲取想法,并在您的组织中实施它们。 不要尝试实现所有内容。 拿一件小东西卖掉。 给别人看。 微小变化的影响可以衡量,衡量并继续前进!
Paul Stack将在DevOops 2018会议上到达圣彼得堡,并发表报告“使用Chaos进行可持续系统测试” 。 保罗将讨论混沌工程方法论,并展示如何在实际项目中使用该方法论。

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


All Articles