互联网发展至今,瞬息万变的市场要求我们的产品具有更快的响应速度。技术的快速进步,软件的快速迭代升级,各种软件基础设施也不得不随之进行频繁的修改。为更好地适应各各式各样各样的需求,将基础设施、工具、服务以及对基础设施的管理作为一个软件系统,使用软件工程实践中的方式来处理各种基础设施的变化。本文作者在文中详细讲解了软件设施代码背后的思想,带领我们详细了解背后的原理。
作者 | Tylor Borgeson,已获作者翻译授权
译者 | 罗昭成
原文 | What shape is your software infrastructure?
本文首发于 CSDN 微信(ID:CSDNnews)
这是我「流行软件开发实践」系列文章中的第六部分,在本系列文章中,我计划包含软件工程师通过提升开发流程和实践来改善软件开发的一系列方法。我曾在 ThoughtWorks 担任软件顾问,现在我在德国一家大型的零售公司工作,这些方法都是我在职业生涯中学习并实践验证过的。
我们从小就知道,三角形具有稳定性,当今社会,众多建筑物(如大桥)都使用了这个基础的结构,来保证建筑物的稳定性。
我在 ThoughtWorks 任职期间,我花费了大量的时间来帮助他们处理基础设施的问题,增强他们基础设施的稳定性。包括如何将他们”圆形“的基础设施改成”三角形“ 的基础设施。
改善基础设施有很多步骤,这个会和公司当前的基础设施进行结合,进行调整,但不管最终需要做哪些事情,都有一个都是必须要做的,那就是基础设施代码化。
基础设施代码化是一种实践,它处理了基础架构团队所面临的许多挑战。它关注于使用配置来管理可配置的系统。
我们的软件可以随时根据需求,随时进行变更与修改。那我们的基础设施是否也可以这样进行灵活的扩展和升级?基础设施代码化的想法正是来源于此。现在,最常见的实现方式是用代码来控制服务器、云实例、管道、以及”扩展基础设施“的接口,在需要的时候,通过代码来实现更改,不在通过用户界面进行配置。当然,实现自动化更改之前,都必须经过必要的检查和验证。
因为基础设施资源由代码定义,在软件开发中使用的那些工具(CI/CD, TDD),也能很好地在这上面使用,使系统受益。
基础设施代码化致力于实现以下目标:
随着公司增长,业务变大,基础设施不会成为短板;
像软件升级一样,基础设施的更改或升级能够非常地容易;
冗余的自动化任务和基础任务由公司/部门的基础架构团队负责;
基础设施的使用者可以简单的进行修改并应用于他们的基础设施;
团队在基础设施上的故障恢复时间大大地降低。
越来越多的团队将他们的应用迁移到云上,并在虚拟机中进行运行。团队频繁增加服务器的可能性正在增加。这就意味着,我们需要保证所有的机器都运行最新版本的程序,并在相互之间保持一致。不一致的问题会越来越频繁的给我们带来问题。如果不同的机器之间出现了不同,那么就可能会出现同一个脚本在不同的机器上,跑出不同的结果。
想像一下,我们软件的更新,只在某一台机器上能够正常运行。在负载均衡的情况下,只有”非常幸运“的用户才能将请求落在那台正常运行的服务器上。
因为雪花是不能被复制,所以没有任何两个雪花是相同的。因此我们认为每一片雪花都是独特的。在基础设施上也存在这个问题。
许多系统管理员都尝试手动检查来纠正配置不一致的服务器。这样做带来的后果是一些针对特定问题的特定更新,并不能留下历史记录(当然,有时候,在云服务器上也是有历史记录的。但是,在问题解决之前,还有 100 条是记录的之前的操作,那么这个历史记录也没有太大的用处),只有更新服务器的人,才知道服务器更新了什么东西。所以,有些时候,软件能正确运行,有些时候,又无法正确的运行,像魔术一样,很难解释清楚原因。
这些问题都不是故意造成的。但我们都是人,人都会犯错。例如有时候我们更新了编号为 1、2、3、4 的服务器,但因为一些原因却忘记了更新编号为 5 的机器。
经过更改的服务器,最终能将软件按照预期的运行起来,但是,它去像一个纸房子一样,非常脆弱。所以,请”不要随意地动它“。
我们很不愿意对系统进行升级和改造,而自动化会让系统管理员更加担心。因为他们不知道,这些更改,是否会造成服务器停止服务。
如果,你已经实现了基础设施代码化,能够通过代码配置来创建基础设施;而不是通过 Amazon 的 Web 控制台或者是 Jenkins 来创建。那么你新建的步骤就简单多了。所有的创建步骤都记录在脚本中,任何步骤都不会被遗忘。
当一个其它团队的同事咨询你们是如何在 AWS 中配置 Kubernetes 集群的自动扩展时,你不需要去控制台找 3 个月前编辑的设置,相反,他们可以直接查看创建设置的代码,甚至是直接将你们的代码拷贝到他们的实例中去运行。
当你将你们的数据中心迁移到云上,或者是从一个云服务提供商迁移到另一个云服务提供商时,创建资源的过程会重复多次,代码化将会非常的有用。
由于基础设施的配置步骤是可以通过代码的方式进行复制,所有不需要担心由于机器损坏需要更换而带来的维护成本。
云服务的一个主要优点就是”动态的基础设施“,它可以快速的移动、更新、创建、替换、调整大小甚至是删除。你不需要担心,在你删除掉某台机器而导致你漏掉某些你不知道的设置,我们可以充分利用动态的特性。
在更新代码的,如果我们是通过代码而非 UI 控制台来配置基础设施,那么使用代码创建的所有基础设施也会被同步更新。
这就意味着,我们所有的基础设施(如服务器)都能够保持一致,就像是精确的副本一样。我们无法预测到,随着软件的增长和变化,基础设施将要如何变化。我们能做的是,就是尽可能的积极的、快速的升级更改我们的系统。
当基础设施代码化,每一个小的更改都可以用过持续集成的方式进行应用。通过每一个小的更改来提高系统的弹性。
要实现基础设施代码化,你需要将你所有的基础设施配置转换成配置文件,用代码进行定义。这些定义文件将作为你基础设施的真实来源。
推荐一个有用的工具:Terraform
Terraform 可以使用 HCL(Hashicorp 配置语言)定义和配置你的基础设施。可以像在 UI 控制台中使用的关键字和变量那样,提供各种资源。在运行代码时, 将以编程方式供应代码中定义的资源。
Terraform 设计中最棒的部分在于,它也可以对已经存在的资源进行编码管理。下面是一个在 Microsoft Azure 中配置 Linux 并分配虚拟网络的例子:
resource "azurerm_resource_group" "main" {
name = "${var.prefix}-resources"
location = var.location
}
resource "azurerm_virtual_network" "main" {
name = "${var.prefix}-network"
address_space = ["10.0.0.0/16"]
location = azurerm_resource_group.main.location
resource_group_name = azurerm_resource_group.main.name
}
resource "azurerm_subnet" "internal" {
name = "internal"
resource_group_name = azurerm_resource_group.main.name
virtual_network_name = azurerm_virtual_network.main.name
address_prefix = "10.0.2.0/24"
}
resource "azurerm_network_interface" "main" {
name = "${var.prefix}-nic"
resource_group_name = azurerm_resource_group.main.name
location = azurerm_resource_group.main.location
ip_configuration {
name = "internal"
subnet_id = azurerm_subnet.internal.id
private_ip_address_allocation = "Dynamic"
}
}
resource "azurerm_linux_virtual_machine" "main" {
name = "${var.prefix}-vm"
resource_group_name = azurerm_resource_group.main.name
location = azurerm_resource_group.main.location
size = "Standard_F2"
admin_username = "adminuser"
admin_password = "P@ssw0rd1234!"
disable_password_authentication = false
network_interface_ids = [
azurerm_network_interface.main.id,
]
source_image_reference {
publisher = "Canonical"
offer = "UbuntuServer"
sku = "16.04-LTS"
version = "latest"
}
os_disk {
storage_account_type = "Standard_LRS"
caching = "ReadWrite"
}
}
一但你的资源在代码中进行了定义,你的基础设施也将受到版本管理带来的好处。
历史记录将会被保存下来,是谁在什么时候更新了它,这些都将变得有据可查。当出现问题,需要调试甚至是回滚时,这些信息能够提供非常大的帮助。
我们还能从版本记录中看到更改的日志,轻松的看到已经更改的内容。当提交被更新到代码库中时,我们可以触发一些编译任务或一些自定义事件,这让基础设施在 CI/CD 中进行测试变成可能。
需要记住的是,与其它代码一样,尽可能小的提交更改。小的更改比大的更改具有更多的好处。较小的更改更容易测试和调试,并且容易修复问题。在基础设施中进行的修改与之相同。
如上面的描述,Terraform 是一个好用的工具,它可以用于各种基础设施的定义、管理及部署。在它的官网上是如下描述的:
Terraform 是一种用于安全,高效地构建、更改和版本化基础设施的工具。Terraform 可以管理现有流行的服务提供商以及定制的内部解决方案。配置文件向 Terraform 描述了运行单个应用程序或整个数据中心所需的组件。Terraform 将生成一个能够达到描述中预期状态的执行计划,然后执行该计划,以构建描述中基础设施。随着配置文件的更改,Terraform 能够确定更改的内容并创建可以执行的增量执行计划。
Packer 也是基于 Hashicorp 的工具,它允许快速自动创建机器镜像。过去创建镜像通常是通过创建虚拟机,手动配置(安装所需的软件包和依赖项)然后复制这个虚拟机来完成的。现在可以单个文件来定义该镜像,并通过 Packer 程序进行构建。它甚至可以使用 Chef 和 Puppet 这类的工具将应用安装到镜像上。
一个具有用户界面的构建管道,但用户只能在上面查看状态和日志。整个管道必须通过配置文件进行配置。我认为它是市场上最酷的管道工具之一,并且它是开源的
如果你想了解有关基础设施代码化的更多细节,我强烈建议阅读以下内容:
https://www.hashicorp.com/resources/what-is-infrastructure-as-code — Hashicorp
https://docs.microsoft.com/en-us/azure/devops/learn/what-is-infrastructure-as-code — Microsoft
https://www.thoughtworks.com/de/insights/blog/infrastructure-code-reason-smile — Thoughtworks
本文仅涉及基础设施代码化背后的思想。如果您还有其他问题和意见,请随时与我联系!感谢您的阅读!
1. 为什么持续集成和部署在开发中非常重要?
2. 被高估了的测试驱动开发?
3. 为什么程序员如此“嫌弃”主干开发模式?
4. 程序员为什么千万不要瞎努力?
5. 为什么许多程序员讨厌结对编程?
6. 程序员如何用代码彻底终结系统那些事儿?