在此模块中,您将应用程序代码部署到 AWS Lambda,这是一项无服务器计算服务。利用 AWS Lambda,您无需担心服务器管理或容量规划方面的问题。您只需上传想要运行的代码,AWS 会在已配置事件触发条件出现时运行该代码。

您可以结合 Lambda 使用的事件触发条件之一是 Amazon API Gateway。您可以利用 Amazon API Gateway 配置 HTTP 终端节点,将请求转发至 AWS Lambda 函数。在 Lambda 和 API Gateway 之间,您可以构建功能完整的 Web 应用程序,无需配置或管理任何服务器。此外,Lambda 和 API Gateway 按照使用量付费,这表示您只需为您的实际使用量付费。

在以下步骤中,您将了解如何使用 AWS 命令行界面 (AWS CLI) 设置 Lambda 函数以及通过 API Gateway 配置 HTTP 路由。如果您使用 Lambda 和 API Gateway 构建应用程序,请使用 AWS Serverless Application Model (AWS SAM)Serverless Framework 等工具,使用基础设施即代码管理您的无服务器应用程序。

完成模块所需时间:20 分钟


  • 第 1 步:创建您的网络资源

    在较早的模块中,您已配置了 ElastiCache 实例,使其可从您的 Cloud9 部署环境中访问。ElastiCache 实例必须从 VPC 访问,而不是通过公共互联网访问。

    在此模块中,您将部署要在 AWS Lambda 上运行的应用程序代码。要让您的 AWS Lambda 函数能够访问您的 ElastiCache 实例,该函数必须位于您的 VPC 中的子网内,并配置有能够访问您的 ElastiCache 实例的安全组。

    您的 Lambda 函数还需要能够使用 Data API 访问您的 Amazon Aurora Serverless 数据库。Data API 是公开发布的适用于您的数据库的终端节点。要让位于私有子网中的 Lambda 函数能够访问公共互联网,您需要配置一个 NAT 网关,将专用网络流量转换为公共互联网流量。

    本教程未涵盖所需网络资源的内部详细信息。现在,您可以使用 scripts/ 目录中的 create-networking.sh 脚本预置所需资源。

    您可以使用以下命令执行此脚本:

    bash scripts/create-networking.sh

    您应当看到以下输出(请注意这些可能需要一会儿):

    Fetching VPC Id
    Fetching subnet Id
    Creating Elastic IP address
    Creating NAT Gateway
    Waiting for NAT Gateway to be ready...
    Creating private subnet
    Creating route table
    Creating route
    Associating route table with subnet
    Networking resources created!
    
  • 第 2 步:打包您的代码并将其部署到 Lambda

    现在,您需要打包您的函数代码并将其部署到 AWS Lambda。Lambda 需要您上传包含所有应用程序代码的 ZIP 文件。另外,您指定要使用的运行时,以及用作代码入口点的文件和函数。此入口点称为处理程序,会在传入事件触发您的代码时进行调用。

    除了函数代码外,您还需要为 Lambda 函数提供 AWS Identity and Access Management (IAM) 角色。此角色由您的函数在执行时承担,以便它具有访问 AWS 资源的权限,例如从数据库读写、向队列发送消息或将输出记录到 Amazon CloudWatch

    scripts/ 目录中,有一个名为 create-lambda.sh 的文件。此脚本执行以下四项操作:

    1. 压缩 application/ 目录中的内容以在 Lambda 函数中使用。
    2. 创建由您的 Lambda 函数承担的 IAM 角色。
    3. 向您的 IAM 角色添加一个 IAM 策略,允许您的 Lambda 函数访问 Amazon Aurora Serverless 数据库中的 Data API。
    4. 通过上传 Zip 文件创建 Lambda 函数。

    如果您想要查看用于运行这些步骤的 AWS CLI 命令,请在文件资源管理器中打开 scripts/create-lambda.sh 文件。

    要执行脚本,在终端中运行以下命令:

    bash scripts/create-lambda.sh

    您应该能看到以下输出:

    Building zip file
    Creating IAM role
    Adding policy to IAM role
    Sleeping for IAM role propagation
    Creating Lambda function
    Lambda function created with ARN <functionArn>
    
  • 第 3 步:创建并配置 API Gateway REST API

    在您部署了 Lambda 函数后,您可以使用 Amazon API Gateway 将其设置为可通过 HTTP 访问。API Gateway 为您的后端服务提供强大的访问层。它具有超高可配置性,并提供了身份验证、验证、速率限制等功能。

    在您的 scripts/ 目录中,一个名为 create-rest-api.sh 的文件可配置 API Gateway REST API 并将其连接至您的 Lambda 函数。此脚本处理各种操作,包括:

    1. 在 API Gateway 中创建 REST API。
    2. 在 REST API 中配置代理资源和方法,以将所有的传入请求路径和方法发送至一个 Lambda 函数。
    3. 添加 REST API 的权限以触发您的 Lambda 函数。

    API Gateway 有许多配置选项,本教程无法涵盖全部详细信息。如需了解更多详情,请参阅 Amazon API Gateway 概念

    在您的终端中运行以下命令,以执行脚本并配置 REST API:

    bash scripts/create-rest-api.sh

    您应该能在终端中看到以下输出:

    Creating REST API
    Fetching root resource
    Creating proxy resource
    Creating method
    Adding integration
    Creating deployment
    Fetching account ID
    Adding lambda permission
    REST API created
    
    Your API is available at: https://<id>.execute-api.<region>.amazonaws.com/prod

    现在,您已经配置了 REST API 来连接到 Lambda 函数。输出的最后包含了访问 REST API 的基本 URL。

    您的基本 URL 的值已添加到 env.sh 文件。现在获取该文件的源代码,以在终端中设置 BASE_URL 环境变量。

    source env.sh
  • 第 4 步:测试您的 API 终端节点

    现在您的 API 已上线,我们来测试一下吧。在测试其中一个终端节点之前,您将快速浏览一下代码的解释。

    Web 应用程序使用 Express.js 构建,后者是一个热门的 Node.js Web 应用程序框架。在 AWS Lambda 上构建应用程序时,您不需要使用现有的 Web 框架(如 Express.js),但是如果您有使用 Express 的经验,那么学习过程会变得简单。

    Express 应用程序的核心位于 application/app.js。在文件资源管理器中打开文件。

    // application/app.js
    const express = require('express')
    const bodyParser = require('body-parser')
    const { fetchUserScores, addUserScore, fetchTopScores } = require('./data')
    const { createCognitoUser, login, verifyToken } = require('./auth')
    const { validateCreateScore } = require('./validate')
    
    const app = express()
    app.use(bodyParser.json())
    
    function wrapAsync(fn) {
      return function(req, res, next) {
        fn(req, res, next).catch(next);
      };
    }
    // Login
    app.post('/login', wrapAsync(async (req, res) => {
      const idToken = await login(req.body.username, req.body.password)
      res.json({ idToken })
    }))
    
    // Create user
    app.post('/users', wrapAsync(async (req, res) => {
      await createCognitoUser(req.body.username, req.body.password, req.body.email)
      res.json({ username: req.body.username })
    }))
    
    // Fetch user scores
    app.get('/users/:username', wrapAsync(async (req, res) => {
      const limit = req.query.limit || 10;
      const scores = await fetchUserScores(req.params.username, limit)
      res.json(scores)
    }))
    
    // Add new score
    app.post('/users/:username', wrapAsync(async (req, res) => {
      const validated = validateCreateScore(req.body)
      if (!validated.valid) {
        throw new Error(validated.message)
      }
      const token = await verifyToken(req.header('Authorization'))
      if (token['cognito:username'] != req.params.username) {
        throw new Error('Unauthorized')
      }
      const score = await addUserScore(req.params.username, req.body.level, req.body.score)
      res.json(score)
    }))
    
    // Fetch top scores
    app.get('/scores/:date', wrapAsync(async (req, res) => {
      const scores = await fetchTopScores(req.params.date)
      res.json(scores)
    }))
    
    app.use(function(error, req, res, next) {
      res.status(400).json({ message: error.message });
    });
    
    module.exports = app

    在此文件的顶部,您导入了 Express 和其他依赖项,包括您在前面的模块中查看过的身份验证帮助程序函数和数据访问函数。然后,您可以在函数中配置所需的各种路由,例如用户用于登录并获取 ID 令牌的 /login,或者用于获取特定用户的最高分数的 /users/:username。最后,在文件的底部,脚本导出 Express 应用程序。

    然后,查看 application/handler.js。这是带有入口点方法的文件,您告知 Lambda 在收到传入请求时调用该方法。此文件的内容如下:

    // application/handler.js
    const awsServerlessExpress = require('aws-serverless-express')
    const app = require('./app')
    const server = awsServerlessExpress.createServer(app)
    
    exports.handler = (event, context) => { awsServerlessExpress.proxy(server, event, context) }
    

    此处理程序正在使用 aws-serverless-express 程序包。此程序包将传入的 API Gateway 请求转换为 Express.js 框架期望所需要的标准请求。这让使用 Lambda 和 API Gateway 运行 Express.js 代码变得简单。

    我们来使用您在前几面的模块中看到的 fetchUser 函数,对您的终端节点进行测试。这一次,您将全面了解 Web 应用程序,而不是直接调用函数。

    在终端中运行以下命令:

    curl -X GET ${BASE_URL}/users/ubecker

    您应该能在终端中看到以下输出:

    [{"game_id":101,"username":"ubecker","gamedate":"2019-11-06 09:00:37","score":9090,"level":84},{"game_id":148,"username":"ubecker","gamedate":"2019-11-10 07:53:27","score":8428,"level":30},{"game_id":146,"username":"ubecker","gamedate":"2019-11-06 13:28:49","score":8052,"level":86},{"game_id":33,"username":"ubecker","gamedate":"2019-11-08 08:05:11","score":7218,"level":18},{"game_id":5,"username":"ubecker","gamedate":"2019-11-07 17:56:25","score":6983,"level":91},{"game_id":252,"username":"ubecker","gamedate":"2019-11-05 18:57:45","score":5403,"level":8},{"game_id":245,"username":"ubecker","gamedate":"2019-11-10 06:16:58","score":5230,"level":75},{"game_id":51,"username":"ubecker","gamedate":"2019-11-07 10:44:46","score":5043,"level":2},{"game_id":282,"username":"ubecker","gamedate":"2019-11-07 02:58:57","score":4884,"level":17},{"game_id":106,"username":"ubecker","gamedate":"2019-11-07 07:56:40","score":4394,"level":17}]

    成功! 您命中了 API 终端节点。这触发了 Lambda 函数,该函数使用 Data API 从 Amazon Aurora Serverless 数据库中获取用户的最高分数。


在此模块中,您部署了 Lambda 函数并使用 API Gateway 配置了 REST API。然后,您看到了应用程序的运作原理,并调用了终端节点来测试该应用程序。

在下一个模块中,您将了解所有应用程序功能。您将创建一个新用户并使用您的凭证登录。然后,您记录用户的一些游戏分数,检查用户的最高分数,并检查总体最高分数。