服务
关于
CloudProse博客
无服务器

使用SAM管理您的AWS Greengrass Lambda函数

AWS无服务器应用程序模型(SAM)使您可以将Lambda代码部署到Greengrass。
福雷斯特Brazeal Trek10 191210 171202
阿甘(Forrest Brazeal) | 2018年6月12日

2018年6月12日,星期二

AWS Greengrass使您可以在喜欢的边缘设备(如Raspberry Pi)上运行Lambda函数,同时保持与AWS云中资源的无缝集成。如果您觉得这很复杂,那您就没错。官方的草丛 入门指南 涵盖六个模块,并且需要在设备和云中进行大量手动配置。

一些工具开始出现,这些工具消除了一些Greengrass部署难题。 一位云大师发布了 AWS Greengrass:缺少的手册 和相关的 格林戈 部署工具。我们在IOPipe的朋友刚刚发布了名为Pi的Greengrass图片 草bian。 AWS在其中也提供了一些Lambda部署示例 原始的Greengrass演示应用.

但是所有这些示例都调用Lambda API。那不是我们想要部署Lambda函数的方式!我们希望使用类似的框架,以与处理任何其他无服务器代码相同的方式来管理Greengrass Lambda。 AWS SAM(无服务器应用程序模型).

那么SAM如何帮助我们将代码推送到Greengrass?

使用AWS SAM管理Greengrass Lambda代码

先决条件

  • 您已经创建了Greengrass核心和组
  • 您已根据Greengrass中的说明配置了Lambda函数。 入门指南.

脚步

为了在Greengrass中推出对Lambda代码的更新,我们需要:

  1. 更新Lambda函数
  2. 发布Lambda函数的新版本
  3. 将该版本与我们现有的Greengrass组使用的Lambda别名相关联
  4. 创建我们的Greengrass组的新部署

在AWS控制台中点击了很多。让我们看看AWS SAM如何使用下面的模板使此操作更容易。

AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: Device Lambda
Parameters:
  GroupName:
    Default: my-group
    Type: String
  FunctionAlias:
    Default: prod
    Type: String
Resources:
  LambdaRole:
    Type: AWS::IAM::Role
    Properties:
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/service-role/AWSGreengrassResourceAccessRolePolicy
        - arn:aws:iam::aws:policy/AWSGreengrassFullAccess
      AssumeRolePolicyDocument:
        Statement:
          - Effect: Allow
            Principal:
              Service:
                - lambda.amazonaws.com
            Action: sts:AssumeRole

  DeviceCoreFunction:
    Type: AWS::Serverless::Function
    Properties:
      CodeUri: device/
      Handler: device.function_handler
      Runtime: python2.7
      Role: !GetAtt LambdaRole.Arn
      AutoPublishAlias: !Ref FunctionAlias
  
  CustomFunction:
    Type: AWS::Serverless::Function
    Properties:
      CodeUri: custom/
      Handler: custom.function_handler
      Runtime: python2.7
      Role: !GetAtt LambdaRole.Arn
      Environment:
        Variables:
          GROUP_NAME: !Ref GroupName

  CustomResource:
    Type: Custom::CustomResource
    DependsOn: DeviceCoreFunction
    Properties:
      ServiceToken: !GetAtt 'CustomFunction.Arn'
      ParameterOne: Parameter to pass into Custom Lambda Function

该SAM模板中发生了什么?我们正在创建两个Lambda函数,一个关联的IAM角色和一个自定义资源。让我们分别细分这些部分。

LambdaRole包含您在边缘设备上的功能与AWS Greengrass服务进行交互所需的托管策略。 (如果您的功能需要访问其他AWS资源,您也可以向这些角色添加这些权限。)

  LambdaRole:
    Type: AWS::IAM::Role
    Properties:
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/service-role/AWSGreengrassResourceAccessRolePolicy
        - arn:aws:iam::aws:policy/AWSGreengrassFullAccess
      AssumeRolePolicyDocument:
        Statement:
          - Effect: Allow
            Principal:
              Service:
                - lambda.amazonaws.com
            Action: sts:AssumeRole

DeviceCoreFunction是要在Greengrass上运行的Lambda函数。

  DeviceCoreFunction:
    Type: AWS::Serverless::Function
    Properties:
      CodeUri: device/
      Handler: device.function_handler
      Runtime: python2.7
      Role: !GetAtt LambdaRole.Arn
      AutoPublishAlias: !Ref FunctionAlias

Assuming we have some code in device.py, the magic here is the AutoPublishAlias property. This amazing line of config singlehandedly creates an alias for the function, publishes a new version, points the alias to the version, and points 所有 event sources to the alias, any time your function code changes. (In fact, 这只是SAM出色的Lambda部署功能的表面

That takes care of steps 1 and 2 on our list of Greengrass deployment steps. Now we just need to update the Greengrass deployment itself. Unfortunately, Greengrass does not yet have CloudFormation support. Instead, we can use a Lambda-backed custom CloudFormation resource in our SAM template, here called CustomResource:

  CustomFunction:
    Type: AWS::Serverless::Function
    Properties:
      CodeUri: custom/
      Handler: custom.function_handler
      Runtime: python2.7
      Role: !GetAtt LambdaRole.Arn
      Environment:
        Variables:
          GROUP_NAME: !Ref GroupName

  CustomResource:
    Type: Custom::CustomResource
    DependsOn: DeviceCoreFunction
    Properties:
      ServiceToken: !GetAtt 'CustomFunction.Arn'

The CustomFunction will run our Greengrass deployment code in custom.py:

import boto3
import json
import os
from urllib2 import build_opener, HTTPHandler, Request

client = boto3.client('greengrass')

def deploy_greengrass_group(group_name):
    group = [ group for group in client.list_groups()['Groups'] if group['Name'] == group_name ][0]
    client.create_deployment(
        DeploymentType='NewDeployment',
        GroupId=group['Id'],
        GroupVersionId=group['LatestVersion']
    )

def function_handler(event, context):
    if event['RequestType'] == 'Create' or event['RequestType'] == 'Update':
        deploy_greengrass_group(os.environ['GROUP_NAME'])
        sendResponse(event, context, "SUCCESS", { "Message": "Resource update successful!" })
    else:
        sendResponse(event, context, "FAILED", { "Message": "Unexpected event received from CloudFormation" })

def sendResponse(event, context, responseStatus, responseData):
    responseBody = json.dumps({
        "Status": responseStatus,
        "Reason": "See the details in CloudWatch Log Stream: " + context.log_stream_name,
        "PhysicalResourceId": context.log_stream_name,
        "StackId": event['StackId'],
        "RequestId": event['RequestId'],
        "LogicalResourceId": event['LogicalResourceId'],
        "Data": responseData
    })
    opener = build_opener(HTTPHandler)
    request = Request(event['ResponseURL'], data=responseBody)
    request.add_header('Content-Type', '')
    request.add_header('Content-Length', len(responseBody))
    request.get_method = lambda: 'PUT'
    response = opener.open(request)

Most of the code above is boilerplate that takes care of sending the custom resource response back to CloudFormation. The function of interest is deploy_greengrass_group, which retrieves the group identifiers based on its name and then creates a new deployment using the Greengrass SDK for Python. (Note that urllib2 is used to avoid the extra step of packaging the requests module with our Lambda code.)

All that's left now is to deploy the SAM template, replacing [YOUR_BUCKET] with an S3 bucket in your environment (SAM CLI安装说明):

sam package --template-file device.template --s3-bucket [YOUR_BUCKET] --output-template-file packaged.yaml
sam deploy --template-file ./packaged.yaml --stack-name gg-device --capabilities CAPABILITY_IAM

现在,您可以在更改代码时多次部署SAM模板,并且应该自动将代码推送到Greengrass组中的所有Lambda函数中!说到部署,看起来栅栏SAM上的草确实更绿了。

作者
福雷斯特Brazeal Trek10 191210 171202
阿甘(Forrest Brazeal)