将 Lambda 与基础设施即代码(IaC)结合使用 - Amazon Lambda
Amazon Web Services 文档中描述的 Amazon Web Services 服务或功能可能因区域而异。要查看适用于中国区域的差异,请参阅 中国的 Amazon Web Services 服务入门 (PDF)

将 Lambda 与基础设施即代码(IaC)结合使用

Lambda 提供了多种部署代码和创建函数的方法。例如,您可以使用 Lambda 控制台或 Amazon Command Line Interface(Amazon CLI)来手动创建或更新 Lambda 函数。除了这些手动选项外,Amazon 还提供了许多解决方案,用于使用基础设施即代码(IaC)部署 Lambda 函数和无服务器应用程序。借助 IaC,您可以使用代码来配置和维护 Lambda 函数和其他 Amazon 资源,而不必使用手动流程和设置。

大多数情况下,Lambda 函数不是在隔离环境中运行的。相反,它们与数据库、队列和存储等其他资源一起都是无服务器应用程序的组成部分。借助 IaC,您可以自动执行部署流程,从而快速、可重复地部署和更新整个无服务器应用程序,涵盖许多独立的 Amazon 资源。这种方法可以加快开发周期,简化配置管理,并确保每次都以相同的方式部署资源。

适用于 Lambda 的 IaC 工具

为了使用 IaC 部署 Lambda 函数和无服务器应用程序,Amazon 提供了许多不同的工具和服务。

Amazon CloudFormation 是 Amazon 提供的第一个用于创建和配置云资源的服务。凭借 Amazon CloudFormation,您可以创建文本模板来定义基础架构和代码。随着 Amazon 推出更多新服务以及创建 Amazon CloudFormation 模板的复杂性增加,又发布了两个工具。Amazon SAM 是另一个用于定义无服务器应用程序的基于模板的框架。Amazon Cloud Development Kit (Amazon CDK) 是一种代码优先的方法,使用许多流行的编程语言中的代码结构来定义和配置基础架构。

借助 Amazon SAM 以及 Amazon CDK,Amazon CloudFormation 可在幕后运行以构建和部署您的基础架构。下图展示了这些工具之间的关系,图示之后的段落说明了它们的主要功能。

该图显示了 Amazon SAM 和 Amazon CDK 如何使用 Amazon CloudFormation 创建应用程序堆栈,从而部署 Amazon 资源和代码。
  • Amazon CloudFormation-使用 CloudFormation 描述您的Amazon资源及其属性的 YAML 或 JSON 模板对资源进行建模和设置。 CloudFormation 以安全、可重复的方式配置资源,使您无需手动步骤即可频繁构建基础架构和应用程序。更改配置时,会 CloudFormation 确定要执行的正确操作来更新堆栈。 CloudFormation 甚至可以回滚更改。

  • Amazon Serverless Application Model(Amazon SAM)- Amazon SAM 是一个用于定义无服务器应用程序的开源框架。Amazon SAM 模板使用速记语法来定义函数、API、数据库和事件源映射,每个资源只需几行文本(YAML)。在部署期间,Amazon SAM 将 Amazon SAM 语法转换并扩展为 Amazon CloudFormation 语法。因此,任何 CloudFormation 语法都可以添加到Amazon SAM模板中。这提供了Amazon SAM所有功能 CloudFormation,但配置行更少。

  • Amazon Cloud Development Kit (Amazon CDK)-使用Amazon CDK,您可以使用代码结构定义基础架构,并通过Amazon CloudFormation进行配置。 Amazon CDK允许您使用现有 IDE TypeScript、测试工具和工作流程模式,使用 Python、Java、.NET 和 Go(开发者预览版)对应用程序基础架构进行建模。您可以获得 Amazon CloudFormation 的所有益处,包括可重复部署、轻松回滚和偏差检测。

Amazon 还提供了一项名为 Amazon 应用程序编辑器 的服务,可使用简单图形界面开发 IaC 模板。凭借应用程序编辑器,您可以通过在可视画布中拖动、分组和连接 Amazon Web Services 来设计应用程序架构。然后,应用程序编辑器会根据您的设计创建 Amazon SAM 模板或 Amazon CloudFormation 模板,供您用于部署应用程序。

在以下 IaC for Lambda 入门 部分中,您将使用应用程序编辑器基于现有 Lambda 函数为无服务器应用程序开发模板。

IaC for Lambda 入门

在本教程中,您可以通过使用现有 Lambda 函数创建 Amazon SAM 模板,然后通过添加其他 Amazon 资源在应用程序编辑器中构建无服务器应用程序,从而开始将 IaC 与 Lambda 结合使用。

如果您想先学习 Amazon SAM 或 Amazon CloudFormation 教程来了解如何在不使用应用程序编辑器的情况下使用模板,则可以在本页末尾的 后续步骤 部分找到指向其他资源的链接。

在学习本教程时,您将学习一些基本概念,例如如何在 Amazon SAM 中指定 Amazon 资源。您还将了解如何使用应用程序编辑器来构建可使用 Amazon SAM 或 Amazon CloudFormation 部署的无服务器应用程序。

要完成本教程,请执行以下步骤:

  • 创建示例 Lambda 函数

  • 使用 Lambda 控制台查看函数的 Amazon SAM 模板

  • 将函数的配置导出到 Amazon 应用程序编辑器 并根据函数的配置设计一个简单的无服务器应用程序

  • 保存更新后的 Amazon SAM 模板,可作为部署无服务器应用程序的基础

后续步骤 部分,您将找到可用于详细了解 Amazon SAM 和应用程序编辑器的资源。这些资源包括指向更高级教程的链接,这些教程将教您如何使用 Amazon SAM 部署无服务器应用程序。

先决条件

在本教程中,您将使用应用程序编辑器的本地同步功能将模板和代码文件保存到本地生成计算机。要使用此功能,您需要一个支持文件系统访问 API 的浏览器,它允许 Web 应用程序在本地文件系统中读取、写入和保存文件。我们建议使用 Google Chrome 或 Microsoft Edge。有关文件系统访问 API 的更多信息,请参阅 What is the File System Access API?

创建 Lambda 函数

在第一步中,您会创建 Lambda 函数以用于完成本教程的其余部分。为了简单起见,您可以使用 Lambda 控制台通过 Python 3.11 运行时系统创建基本的“Hello world”函数。

使用控制台创建“Hello world”Lambda 函数
  1. 打开 Lambda 控制台

  2. 选择创建函数

  3. 选择从头开始创作,然后在基本信息中输入 LambdaIaCDemo 作为函数名称

  4. 对于运行时系统,选择 Python 3.11

  5. 选择创建函数

查看函数的 Amazon SAM 模板

在将函数配置导出到应用程序编辑器之前,请使用 Lambda 控制台将函数的当前配置作为 Amazon SAM 模板查看。按照本节中的步骤操作,您将了解 Amazon SAM 模板的剖析以及如何定义诸如 Lambda 函数之类的资源以开始指定无服务器应用程序。

查看函数的 Amazon SAM 模板
  1. 打开 Lamba 控制台的函数页面

  2. 选择您刚刚创建的函数(LambdaIaCDemo)。

  3. 函数概述窗格中,选择模板

    您将看到的不是表示函数配置的图表,而是函数的 Amazon SAM 模板。该模板应该如下所示。

    # This AWS SAM template has been generated from your function's # configuration. If your function has one or more triggers, note # that the AWS resources associated with these triggers aren't fully # specified in this template and include placeholder values.Open this template # in AWS Application Composer or your favorite IDE and modify # it to specify a serverless application with other AWS resources. AWSTemplateFormatVersion: '2010-09-09' Transform: AWS::Serverless-2016-10-31 Description: An AWS Serverless Specification template describing your function. Resources: LambdaIaCDemo: Type: AWS::Serverless::Function Properties: CodeUri: . Description: '' MemorySize: 128 Timeout: 3 Handler: lambda_function.lambda_handler Runtime: python3.11 Architectures: - x86_64 EventInvokeConfig: MaximumEventAgeInSeconds: 21600 MaximumRetryAttempts: 2 EphemeralStorage: Size: 512 RuntimeManagementConfig: UpdateRuntimeOn: Auto SnapStart: ApplyOn: None PackageType: Zip Policies: Statement: - Effect: Allow Action: - logs:CreateLogGroup Resource: arn:aws:logs:us-east-1:123456789012:* - Effect: Allow Action: - logs:CreateLogStream - logs:PutLogEvents Resource: - >- arn:aws:logs:us-east-1:123456789012:log-group:/aws/lambda/LambdaIaCDemo:*

我们需要花些时间查看函数的 YAML 模板并理解一些关键概念。

模板以声明 Transform: AWS::Serverless-2016-10-31 开头。此声明是必需的,因为 Amazon SAM 模板在幕后要通过 Amazon CloudFormation 部署。使用 Transform 语句将模板标识为 Amazon SAM 模板文件。

Transform 声明之后是 Resources 部分。在这里可以定义要使用 Amazon SAM 模板部署的 Amazon 资源。Amazon SAM 模板可以包含 Amazon SAM 资源和 Amazon CloudFormation 资源的组合。这是因为在部署过程中,Amazon SAM 模板会扩展为 Amazon CloudFormation 模板,因此任何有效的 Amazon CloudFormation 语法都可以添加到 Amazon SAM 模板中。

目前,模板的 Resources 部分中只定义了一个资源,即您的 Lambda 函数 LambdaIaCDemo。要向 Amazon SAM 模板添加 Lambda 函数,请使用 AWS::Serverless::Function 资源类型。Lambda 函数资源的 Properties 定义函数的运行时系统、函数处理程序和其他配置选项。此处还定义了 Amazon SAM 应该用于部署函数的函数源代码的路径。要了解有关 Lambda 函数资源的更多信息Amazon SAM,请参阅Amazon SAM开发人员AWS::Serverless::Function指南中的。

除了函数属性和配置外,该模板还指定了函数的 Amazon Identity and Access Management(IAM)policy。此策略允许您的函数将日志写入 Amazon L CloudWatch ogs。当您在 Lambda 控制台中创建函数时,Lambda 会自动将此策略附加到您的函数。要了解有关为Amazon SAM模板中的函数指定 IAM 策略的更多信息,请参阅Amazon SAM开发人员指南AWS::Serverless::Function页面上的policies属性。

要了解有关 Amazon SAM 模板结构的更多信息,请参阅Amazon SAM 模板剖析

使用 Amazon 应用程序编辑器 设计无服务器应用程序

要从函数的 Amazon SAM 模板开始构建简单的无服务器应用程序,您可以将函数配置导出到应用程序编辑器,并激活应用程序编辑器的本地同步模式。本地同步会自动将您的函数代码和 Amazon SAM 模板保存到本地生成计算机,并在您在应用程序编辑器中添加其他 Amazon 资源时让保存的模板保持同步。

将您的函数导出到应用程序编辑器
  1. 函数概述窗格中,选择导出到应用程序编辑器

    为了将函数的配置和代码导出到应用程序编辑器,Lambda 在您的账户中创建了一个 Amazon S3 存储桶来临时存储此数据。

  2. 在对话框中,选择确认并创建项目以接受此存储桶的默认名称,并将函数的配置和代码导出到应用程序编辑器。

  3. (可选)要为 Lambda 创建的 Amazon S3 存储桶选择其他名称,请输入新名称并选择确认并创建项目。Amazon S3 存储桶的名称必须全局唯一,并遵守存储桶命名规则

    选择确认并创建项目将打开应用程序编辑器控制台。在画布上,您将看到 Lambda 函数。

  4. 菜单下拉列表中,选择激活本地同步

  5. 在打开的对话框中选择选择文件夹,然后在本地生成计算机上选择一个文件夹。

  6. 选择激活以激活本地同步。

要将函数导出到应用程序编辑器,您需要拥有使用特定 API 操作的权限。如果您无法导出函数,请查看 所需的权限 并确保您拥有所需的权限。

注意

标准 Amazon S3 定价适用于您将函数导出到应用程序编辑器时由 Lambda 创建的存储桶。由 Lambda 放入存储桶的对象会在 10 天后自动删除,但是 Lambda 不会删除存储桶本身。

为避免给您的 Amazon Web Services 账户 增加额外费用,请在将函数导出到应用程序编辑器后,按照删除存储桶中的说明进行操作。有关 Lambda 创建的 Amazon S3 存储桶的更多信息,请参阅 配合使用 Amazon Lambda和 Amazon 应用程序编辑器

在应用程序编辑器中设计您的无服务器应用程序

激活本地同步后,您在应用程序编辑器中所做的更改将反映在本地生成计算机上保存的 Amazon SAM 模板中。现在,您可以将其他 Amazon 资源拖放到应用程序编辑器画布上来构建您的应用程序。在此示例中,您添加了一个 Amazon SQS 简单队列作为 Lambda 函数的触发器,并添加了一个 DynamoDB 表供该函数写入数据。

  1. 通过执行以下操作,将 Amazon SQS 触发器添加到 Lambda 函数:

    1. 资源选项板的搜索字段中输入 SQS

    2. SQS 队列资源拖到画布上,然后将其放置在 Lambda 函数的左侧。

    3. 选择详细信息,然后在逻辑 ID 中输入 LambdaIaCQueue

    4. 选择保存

    5. 单击 SQS 队列卡上的订阅端口,然后将其拖到 Lambda 函数卡上的左侧端口,即可连接您的 Amazon SQS 和 Lambda 资源。两个资源之间出现一条线,表示连接成功。应用程序编辑器还会在画布底部显示一条消息,表明两个资源已连接成功。

  2. 通过执行以下操作,为您的 Lambda 函数添加一个 Amazon DynamoDB 表,供其写入数据:

    1. 资源选项板的搜索字段中输入 DynamoDB

    2. DynamoDB 表资源拖到画布上,然后将其放置在 Lambda 函数的右侧。

    3. 选择详细信息,然后在逻辑 ID 中输入 LambdaIaCTable

    4. 选择保存

    5. 单击 Lambda 函数卡的右侧端口,然后将其拖动到 DynamoDB 卡的左侧端口,即可将 DynamoDB 表连接到您的 Lambda 函数。

现在,您已经添加了这些额外资源,让我们看看应用程序编辑器创建的更新后的 Amazon SAM 模板。

查看更新后的 Amazon SAM 模板
  • 在应用程序编辑器画布上,选择模板以从画布视图切换到模板视图。

现在,您的 Amazon SAM 模板应包含以下额外资源和属性:

  • 具有标识​符 LambdaIaCQueue 的 Amazon SQS 队列

    LambdaIaCQueue: Type: AWS::SQS::Queue Properties: MessageRetentionPeriod: 345600

    当您使用应用程序编辑器添加 Amazon SQS 队列时,应用程序编辑器会设置 MessageRetentionPeriod 属性。您也可以通过在 SQS 队列卡上选择详细信息并选中或取消选中 Fifo 队列来设置 FifoQueue 属性。

    要为队列设置其他属性,您可以手动编辑模板以添加这些属性。要了解有关 AWS::SQS::Queue 资源及其可用属性的更多信息,请参阅《Amazon CloudFormation 用户指南》中的 Amazon:: SQS::Queue

  • Lambda 函数定义中的一个 Events 属性,用于将 Amazon SQS 队列指定为函数的触发器

    Events: LambdaIaCQueue: Type: SQS Properties: Queue: !GetAtt LambdaIaCQueue.Arn BatchSize: 1

    Events 属性由一个事件类型和一组取决于类型的属性组成。要了解Amazon Web Services您可以配置以触发 Lambda 函数的不同配置以及可以设置的属性,请参阅Amazon SAM开发人员EventSource指南中的。

  • 带有标识符 LambdaIaCTable 的 DynamoDB 表

    LambdaIaCTable: Type: AWS::DynamoDB::Table Properties: AttributeDefinitions: - AttributeName: id AttributeType: S BillingMode: PAY_PER_REQUEST KeySchema: - AttributeName: id KeyType: HASH StreamSpecification: StreamViewType: NEW_AND_OLD_IMAGES

    使用应用程序编辑器添加 DynamoDB 表时,您可以通过在 DynamoDB 表卡上选择详细信息并编辑键值来设置表的键。应用程序编辑器还为许多其他属性设置默认值,包括 BillingModeStreamViewType

    要详细了解这些属性以及可以添加到 Amazon SAM 模板中的其他属性,请参阅《Amazon CloudFormation 用户指南》中的 Amazon::DynamoDB::Table

  • 一项新的 IAM policy,允许您的函数对您添加的 DynamoDB 表执行 CRUD 操作。

    Policies: ... - DynamoDBCrudPolicy: TableName: !Ref LambdaIaCTable

最终的 Amazon SAM 模板应该如下所示。

AWSTemplateFormatVersion: '2010-09-09' Transform: AWS::Serverless-2016-10-31 Description: An AWS Serverless Specification template describing your function. Resources: LambdaIaCDemo: Type: AWS::Serverless::Function Properties: CodeUri: . Description: '' MemorySize: 128 Timeout: 3 Handler: lambda_function.lambda_handler Runtime: python3.11 Architectures: - x86_64 EventInvokeConfig: MaximumEventAgeInSeconds: 21600 MaximumRetryAttempts: 2 EphemeralStorage: Size: 512 RuntimeManagementConfig: UpdateRuntimeOn: Auto SnapStart: ApplyOn: None PackageType: Zip Policies: - Statement: - Effect: Allow Action: - logs:CreateLogGroup Resource: arn:aws:logs:us-east-1:594035263019:* - Effect: Allow Action: - logs:CreateLogStream - logs:PutLogEvents Resource: - arn:aws:logs:us-east-1:594035263019:log-group:/aws/lambda/LambdaIaCDemo:* - DynamoDBCrudPolicy: TableName: !Ref LambdaIaCTable Events: LambdaIaCQueue: Type: SQS Properties: Queue: !GetAtt LambdaIaCQueue.Arn BatchSize: 1 Environment: Variables: LAMBDAIACTABLE_TABLE_NAME: !Ref LambdaIaCTable LAMBDAIACTABLE_TABLE_ARN: !GetAtt LambdaIaCTable.Arn LambdaIaCQueue: Type: AWS::SQS::Queue Properties: MessageRetentionPeriod: 345600 LambdaIaCTable: Type: AWS::DynamoDB::Table Properties: AttributeDefinitions: - AttributeName: id AttributeType: S BillingMode: PAY_PER_REQUEST KeySchema: - AttributeName: id KeyType: HASH StreamSpecification: StreamViewType: NEW_AND_OLD_IMAGES

使用 Amazon SAM(可选)部署您的无服务器应用程序

如果要使用 Amazon SAM 来利用刚刚在应用程序编辑器中创建的模板部署无服务器应用程序,则需要先安装 Amazon SAM CLI。为此,请遵循安装 Amazon SAM CLI 中的说明。

在部署应用程序之前,您还需要更新应用程序编辑器随模板一起保存的函数代码。目前,应用程序编辑器保存的 lambda_function.py 文件仅包含 Lambda 在创建函数时提供的基本“Hello world”代码。

要更新您的函数代码,请复制以下代码并将其粘贴到由应用程序编辑器保存到本地生成的计算机的 lambda_function.py 文件中。激活“本地同步”模式时,您指定了应用程序编辑器要将此文件保存到的目录。

此代码接受的键值对来自您在应用程序编辑器中创建的 Amazon SQS 队列的消息。如果键和值都是字符串,则代码会使用它们向您的模板中定义的 DynamoDB 表写入项目。

import boto3 import os import json # define the DynamoDB table that Lambda will connect to tablename = os.environ['LAMBDAIACTABLE_TABLE_NAME'] # create the DynamoDB resource dynamo = boto3.client('dynamodb') def lambda_handler(event, context): # get the message out of the SQS event message = event['Records'][0]['body'] data = json.loads(message) # write event data to DDB table if check_message_format(data): key = next(iter(data)) value = data[key] dynamo.put_item( TableName=tablename, Item={ 'id': {'S': key}, 'Value': {'S': value} } ) else: raise ValueError("Input data not in the correct format") # check that the event object contains a single key value # pair that can be written to the database def check_message_format(message): if len(message) != 1: return False key, value = next(iter(message.items())) if not (isinstance(key, str) and isinstance(value, str)): return False else: return True
部署无服务器应用程序

要使用 Amazon SAM CLI 部署应用程序,请执行以下步骤。为了正确构建和部署函数,必须在您的生成计算机和 PATH 上安装 Python 3.11 版本。

  1. 在应用程序编辑器保存 template.yamllambda_function.py 文件的目录运行以下命令。

    sam build

    此命令会收集应用程序的构建构件,并将其以适当的格式放置在适当的位置进行部署。

  2. 要部署您的应用程序并创建 Amazon SAM 模板中指定的 Lambda、Amazon SQS 和 DynamoDB 资源,请运行以下命令。

    sam deploy --guided

    使用 --guided 标志意味着 Amazon SAM 将向您显示提示,以指导您完成部署过程。对于此部署,请按 Enter 接受默认选项。

在部署过程中,Amazon SAM 将在 Amazon Web Services 账户 中创建以下资源:

  • 一个名为 sam-app 的 Amazon CloudFormation 堆栈

  • 名称格式为 sam-app-LambdaIaCDemo-99VXPpYQVv1M 的 Lambda 函数

  • 名称格式为 sam-app-LambdaIaCQueue-xL87VeKsGiIo 的 Amazon SQS 队列

  • 名称格式为 sam-app-LambdaIaCTable-CN0S66C0VLNV 的 DynamoDB 表

Amazon SAM 还会创建必要的 IAM 角色和策略,以便您的 Lambda 函数可以读取来自 Amazon SQS 队列的消息并对 DynamoDB 表执行 CRUD 操作。

要了解有关使用 Amazon SAM 部署无服务器应用程序的更多信息,请参阅 后续步骤 部分中的资源。

测试已部署的应用程序(可选)

要确认您的无服务器应用程序已正确部署,请向您的 Amazon SQS 队列发送一条包含密钥值对的消息,并检查 Lambda 是否使用这些值将项目写入您的 DynamoDB 表。

测试您的无服务器应用程序
  1. 打开 Amazon SQS 控制台的队列页面,然后选择 Amazon SAM 通过您的模板创建的队列。名称的格式为 sam-app-LambdaIaCQueue-xL87VeKsGiIo

  2. 选择发送和接收消息,然后将以下 JSON 粘贴到发送消息部分的消息正文中。

    { "myKey": "myValue" }
  3. 选择 Send message(发送消息)。

    将您的消息发送到队列会导致 Lambda 通过 Amazon SAM 模板中定义的事件源映射调用您的函数。要确认 Lambda 已按预期调用您的函数,请确认项目已被添加到您的 DynamoDB 表。

  4. 打开 DynamoDB 控制台中的页面,然后选择您的表。名称的格式为 sam-app-LambdaIaCTable-CN0S66C0VLNV

  5. 选择 Explore table items(浏览表项目)。在返回的项目窗格中,您会看到一个带 id myKey myValue 的项目。

后续步骤

要详细了解如何将应用程序编辑器与 Amazon SAM 和 Amazon CloudFormation 一起使用,请先从将应用程序编辑器与 Amazon CloudFormation 和 Amazon SAM 一起使用

对于使用 Amazon SAM 部署在应用程序编辑器中设计的无服务器应用程序的指导教程,我们还建议您在Amazon 无服务器模式研讨会上学习 Amazon 应用程序编辑器 教程

Amazon SAM 提供了一个命令行界面(CLI),您可以将其与 Amazon SAM 模板和支持的第三方集成一起使用,以构建和运行您的无服务器应用程序。借助 Amazon SAM CLI,您可以构建和部署应用程序、执行本地测试和调试、配置 CI/CD 管道等。要了解有关使用 Amazon SAM CLI 的更多信息,请参阅《Amazon Serverless Application Model 开发者指南》中的 Amazon SAM 入门

要了解如何使用 Amazon CloudFormation 控制台部署带有 Amazon SAM 模板的无服务器应用程序,请从 Amazon CloudFormation 用户指南中的使用 Amazon CloudFormation 控制台开始。

支持 Lambda 与应用程序编辑器集成的区域

以下 Amazon Web Services 区域 支持 Lambda 与应用程序编辑器的集成:

  • 美国东部(弗吉尼亚州北部)

  • 美国东部(俄亥俄州)

  • 美国西部(北加利福尼亚)

  • 美国西部(俄勒冈州)

  • 非洲(开普敦)

  • 亚太地区(香港)

  • 亚太地区(海得拉巴)

  • 亚太地区(雅加达)

  • 亚太地区(墨尔本)

  • 亚太地区(孟买)

  • 亚太地区(大阪)

  • 亚太地区(首尔)

  • 亚太地区(新加坡)

  • 亚太地区(悉尼)

  • 亚太地区(东京)

  • 加拿大(中部)

  • 欧洲地区(法兰克福)

  • 欧洲(苏黎世)

  • 欧洲地区(爱尔兰)

  • Europe (London)

  • 欧洲地区(斯德哥尔摩)

  • 中东(阿联酋)