基础设施即代码
基础设施即代码(IaC)是一种通过机器可读的定义文件管理设备和服务器的方法。从根本上说,你要写下你希望基础设施是什么样子,以及应该在该基础设施上运行什么代码。然后,按下一个按钮,说“部署我的基础设施”即可。BAM 是你已经准备投入使用的应用程序,它运行在服务器上,后台是一个通过 API 访问的数据库。你刚刚用 IaC 定义了所有的基础设施。
IaC 是 DEVOPS 团队的一项重要实践,已经集成到 CI/CD 管道。
Terraform 是 HashiCorp 推出的一个很棒的基础设施即代码工具(https://www.terraform.io/)。
我个人使用它来提供和维护 AWS 上的基础设施。我在这方面有很好的经验。
介绍和演示
我将通过一个例子来演示 IaC。我们将在 AWS 上创建一个应用程序。我在 GitLab 上提供了代码:https://gitlab.com/nxtra/codingtips-blog。用户可以输入编码提示并查看其他用户输入的所有编码提示。这些提示存储在一个 NoSQL 数据库 AWS DynamoDB 中。存储和检索这些提示是由 Lambda 函数完成的,这些函数从数据库中获取或存放提示。要使用这个应用程序,用户必须能够调用这些 Lambda 函数。因此,我们通过 AWS API 网关公开 Lambda 函数。以下是该应用程序的总体架构。
你可以将这些函数组合到一个 Web 页面上,用户可以在该页面中输入提示并查看已有的所有提示。下面是最终结果:
让我们深入了解一下。
创建应用程序
现在,我将介绍创建你在上面的演示中所看到的应用程序的步骤。IaC 是重点。我将展示必要的代码和 AWS CLI 命令,但我不会详细解释它们,因为这不是本文的目的。我将重点介绍 Terraform 的定义。欢迎复制我在下文中提供的存储库,并按照本文提供的步骤进行操作。
准备工作
安装 Terraform
安装 AWS CLI
在 GitLab 上检出库:https://gitlab.com/nxtra/codingtips-blog
准备好迎接 IaC 带来的惊喜
Terraform:基础
你将使用 Terraform 配置的主要内容是资源。资源是应用程序基础设施的组件。例如,一个 Lambda 函数、一个 API 网关部署、一个 DynamoDB 数据库……资源是通过在关键字 resource 后面加上类型和名称来定义的。名称可以任意选择。类型是固定的。例如:resource “aws_dynamodb_table” “codingtips-dynamodb-table”。
要按照这篇文章的介绍进行操作,你需要知道以下两个基本的 Terraform 命令。
terraform apply
Terraform apply 将开始准备你定义的所有基础设施,创建你的数据库,设置 Lambda 函数,准备 API 网关。
terraform destroy
Terraform destroy 将删除你在云中配置的所有基础设施。如果你正确地使用了 Terraform,应该就用不到这个命令。但是,如果你想从头重新开始,则可以使用此命令删除所有现有的基础设施。不用担心,你的机器上仍然有所有基础设施的完整描述,因为你使用的是基础设施即代码。
我们将把所有使用 Terraform 定义的基础设施放在同一个文件夹中。文件需要使用.tf 扩展名。
General
让我们从创建一个 general.tf 文件开始。
Provider 块指明我们正在向 AWS 上部署。你还可以在这里提供将用于部署的凭证。如果你已经在自己的机器上正确地设置了 AWS CLI,那么,.aws 文件夹中就会有默认凭证。如果没有指定凭证,Terraform 将使用这些默认凭证。
变量有一个名称,我们可以在 Terraform 配置的任何地方引用它。例如,我们可以使用 ${var.s3_bucket)引用 s3_bucket 变量。当你在多个地方使用相同的变量时,这非常方便。在这篇文章中,我不会使用太多的变量,因为这会使你的 Terraform 配置添加更多的引用,我希望它尽可能清晰。
数据库:DynamoDB
我们从基础开始。我们所有的编码提示将存储在哪里?没错,在数据库中。这个数据库是我们的基础设施的一部分,将在一个名为 dynamo.tf 的文件中定义。
因为 Dynamo 是一个 NoSQL 数据库,我们不需要预先指定所有属性。我们唯一需要提供的是 AWS 用来构建分区键的元素。当你提供一个哈希键和一个排序键时,AWS 会把它们组合成一个惟一的分区键。注意“UNIQUE”这个词。确保这个组合是唯一的。
DynamoDB 使用分区键值作为内部哈希函数的输入。哈希函数的输出决定了项存储在哪个分区(DynamoDB 的内部物理存储)。具有相同分区键值的所有项存储在一起,按键值排序。——来自 AWS 文档:DynamoDB核心组件。
从 dynamo.tf 中的属性定义可以很容易地看出,Author (S)是一个字符串,Date (N)应该是一个数字。
IAM
在指定 Lambda 函数之前,我们必须为要使用的函数创建权限。这确保我们的函数具有访问其他资源(如 DynamoDB)的权限。简单来说,AWS 权限模型的工作原理如下:
提供一个带角色的资源
给该角色添加权限
以下权限允许该角色访问其他资源:
触发另一个资源(如 Lambda 函数将日志转发到 CloudWatch)的权限
被另一个资源(如 Lambda 函数被 API 网关触发)触发的权限
在上面的例子中,定义的第一个资源是 aws_iam_role。我们稍后会把这个角色赋予 Lambda 函数。
然后,我们创建了资源 aws_iam_role_policy,将其关联到到角色 aws_iam_role。第一个 aws_iam_role_policy 允许这个角色调用指定 DynamoDB 资源上的任何操作。第二个 role_policy 允许具有此角色的资源向 CloudWatch 发送日志。
以下是几个注意事项:
aws_iam_role 和 aws_iam_role_policy 通过 role_policy 资源的 role 参数关联;
在 aws_iam_role_policy 的 Statement 属性里,我们赋予(Effect 属性)在特定资源上(Resource 属性)执行某些操作的权限(Action 属性);
资源通过 ARN 或 Amazon Resource Name 引用,这是资源在 AWS 上的唯一标识;
有两种方式可以指定 aws_iam_role_policy:
使用 until EOF 语法(正如我此处的做法)
使用一个单独的 Terraform aws_iam_policy_document 元素,然后耦合到 aws_iam_role_policy
dynamodb-lambda-policy 允许在指定的 DynamoDB 资源上执行所有操作,因为 Action 属性的设置为 dynamodb:* 。你可以像下面这样进行更严格的限制,并指出可以执行的操作:
Lambda 函数
这个应用程序有两个 Lambda 函数。第一个 Lambda 用于从数据库中获取或检索编码提示,引用名是 getLambda。第二个 Lambda 用于将编码提示发布或发送到数据库,引用名是 postlambda。
我不打算在这里复制粘贴 Lambda 函数的代码。你可以通过本文提供的存储库链接查看(GitLab 存储库:https://gitlab.com/nxtra/codingtips-blog)。
这里,我将以 getLambda 函数为例。postLambda 以相同的方式部署,你可以在 Git 存储库中找到 Terraform 定义。Lambda 函数与我们在这里定义的其他基础设施稍有不同。我们不仅需要一个 Lambda 函数作为基础设施。我们还需要指定在 Lambda 函数中运行的代码。但是,在部署 Lambda 函数时,AWS 将在哪里找到特定的代码呢?他们无法访问你的本地机器,是吗?这就是为什么你需要首先将代码发送到 AWS 上的 S3 桶,以便在部署函数时可以在那里找到代码。
这就意味着要创建一个 S3 桶,当你想在 eu-west-1(爱尔兰)地区创建时,可以使用下面的命令:
现在,你需要使用 zip 压缩你的 Lambda 函数代码:
并把那个文件上传到 S3:
注意,我将把它发送到存储桶 codingtips-node-bucket 的文件夹 v1.0.0 下,文件名为 getLambda.zip。
好了,代码已经到了该到的地方。现在让我们看看如何使用 Terraform 来指定这些函数。
注意,我们把 S3 桶和查找代码的目录告知 Terraform;
我们为这个 Lambda 函数指定运行时和内存;
index.handler 指向该文件和进入代码的函数;
aws_lambda_permission 资源是权限,说明该 Lambda 函数可以被我们创建的 API 网关调用。
API 网关
我把最难的内容留到最后一部分介绍。另一方面,它也是最有趣的。我给 Terraform 提供 API 的 Swagger 定义。你也可以不使用 Swagger,但是,你必须指定更多的资源。
Swagger API 定义如下:
如果你还不知道 Swagger,复制上述代码并粘贴到在线编辑器里(Swagger编辑器)。
这将使你对 API 定义有一个直观的了解。
在上述 Swagger 规范中只有一个是 AWS 特定的东西,那就是 x-amazon-apigateway-integration。这指定了 API 与后端集成的细节。
注意,这里永远是 POST,即使资源路径的 HTTP 方式是 GET;
aws_proxy 表示不对请求做任何操作就传递给 Lambda 函数;
当没有为 Content-Type 指定 requestTemplate 时,when_no_match 会将请求体直接传到后台,而不做转换;
uri 引用一个变量,如 ${get_lambda_arn},Terraform 会将其传递给 Swagger 定义。我们一会就会看到。
前面已经提到,使用 Swagger 定义 API 网关有一些优点:
使 Terraform 更为简明;
你可以通过 Swagger 获得 API 的良好表示。
我们首先列出了资源 aws_api_gateway_rest_api resource。该资源就像它的名称一样,提供一个 API 网关 REST API:
body 引用 Swagger 文件;
template_file 数据源允许 Terraform 使用 Terraform(在这里是 Swagger )中未定义的信息:
传递到 template_file 的变量用于填充文件;
要使指定的 REST API 可用,它必须:
通过资源 aws_api_gateway_deployment 部署;
引用该 REST API;
它需要一个 stage,这好比 API 的“版本”或“快照”,stage_name 将用在 URL 中,用于调用这个 API;
最后,可以用来调用这个 API 的 URL 被输出到终端,并在末尾加上/api 以提供正确的资源路径。
尾声
好了,我们现在来看看,这是否真得可行。这里,我在和这篇博文相关的存储库中运行 terraform apply。
不错,它可以工作。我只告诉 Terraform 我想要的基础设施。整个安装过程自动进行!现在,你可以使用输出的 URL 来 GET 和 POST 编码提示。POST 体应该是这样的:
当你需要将 API 端点耦合到自己设计的前端时,你需要正确设置 CORS 头。如果你想迎接这个挑战,在我解决这个问题的存储库中(cors-enabled)还有另一个分支。
快乐编码人,用代码写下基础设施!
查看英文原文:INFRASTRUCTURE AS CODE: TERRAFORM AND AWS SERVERLESS
评论