【Greengrass V2】Lambda コンポーネントの環境変数を使ってデバイス設定を指定してみた

Greengrass V2 の Lambda コンポーネントで環境変数の使い方を確認してみました。
2021.08.23

今回は Greengrass V2 の Lambda コンポーネントで環境変数を使ってみました。
Lambda の環境変数は Greengrass V1 でも利用できますが、使ったことがなかったので V2 で試してみました。

想定する前提

今回実現したい事は下記の通りとします。

  • Lambda コンポーネントでセンサーデータを取得したい
  • 取得したデータをローカルの指定ファイルに出力したい
  • 「ローカルの指定ファイル」を環境変数で渡したい

なお、利用しているセンサーやデバイスは下記の記事の通りとします。

Lambda 関数の作成

まずは環境変数を利用する Lambda 関数を作成します。今回も AWS SAM を使います。

sam init

sam init \
    --runtime python3.7 \
    --name modbus-sensor-lambda-component \
    --app-template hello-world \
    --package-type Zip

テンプレート

環境変数は Greengrass コンポーネントの設定で指定するので、Lambda 側で環境変数は定義しません。

AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: >
  modbus-sensor-lambda-component

  Sample SAM Template for modbus-sensor-lambda-component

Globals:
  Function:
    Timeout: 3

Resources:
  HelloWorldFunction:
    Type: AWS::Serverless::Function
    Properties:
      CodeUri: hello_world/
      Handler: app.lambda_handler
      Runtime: python3.7
      AutoPublishAlias: dev

Outputs:
  HelloWorldFunction:
    Description: "Hello World Lambda Function ARN"
    Value: !GetAtt HelloWorldFunction.Arn
  HelloWorldFunctionIamRole:
    Description: "Implicit IAM Role created for Hello World function"
    Value: !GetAtt HelloWorldFunctionRole.Arn

Lambda コード

環境変数として「FilePath」を定義して、そこに温湿度データを出力するファイルパスを格納します。
これにより、環境変数のデータを変更するだけでエッジデバイス側の動作を変更できる想定です。

from pymodbus.client.sync import ModbusSerialClient as ModbusClient
import datetime
import time
import os

def run_sync_client():
    client = ModbusClient(baudrate=9600, port="/dev/ttyUSB0", method="rtu")
    client.connect()
    FilePath = os.environ["FilePath"] # Lambda の環境変数からデータ出力ファイルのパスを取得
    rr = client.read_input_registers(address=1, count=2, unit=0x1)
    temperature = rr.registers[0]/10
    humidity  = rr.registers[1]/10

    # Append the message to the log file.
    with open(FilePath, 'a') as f:
        print(f"{str(datetime.datetime.now())}\tTemperatur:\t{temperature} ℃", file=f)
        print(f"{str(datetime.datetime.now())}\tHumidity:\t{humidity} %", file=f)

    client.close()

while True:
    run_sync_client()
    time.sleep(10)


def lambda_handler(event, context):
    return

ビルド

$ sam build \
    --use-container \
    --build-image Function1=amazon/aws-sam-cli-build-image-python3.7

デプロイ

$ sam package \
    --output-template-file packaged.yaml \
    --s3-bucket ichida-aws-sam-artifact
$ sam deploy \
    --template-file packaged.yaml \
    --stack-name modbus-sensor-lambda-component-stack \
    --s3-bucket ichida-aws-sam-artifact \
    --capabilities CAPABILITY_NAMED_IAM \
    --no-fail-on-empty-changeset

Lambda コンポーネントのデプロイ

次に作成した Lambda をデバイスにデプロイします。コンポーネントの画面から「コンポーネントの作成」をクリックします。
(既存のコンポーネントを更新する場合は、コンポーネントの画面から「新しいバージョン」を作成してください。)

200-make-component

先程作成した Lambda 関数を選択して Greengrass サービスにインポートします。

201-import-lambda

オプション設定で「追加のパラメータ」をクリックして設定項目を展開します。

202-add-parameter

「環境変数」の設定箇所で下記の通り、環境変数を設定します。環境変数FilePath の値を/tmp/data_path_default.log としました。

203-add-lambda-env-2

横着になりますが、Lambda コンポーネントのコンテナモードは「コンテナなし」としました。必要に応じて「Greengrassコンテナ」を選択して、アクセスを許可するデバイスやボリュームを指定してください。

204-set-container-mode-and-make-component

コンポーネントの作成ができたら、次の画面で「デプロイ」をクリックします。この画面にある「デフォルト設定」の表示で、先程設定した環境変数の内容を確認することができますね。

205-made-comonent

デプロイ設定に進んで、最初にデプロイ対象を指定します。

207-add-deploy

デプロイのターゲットを指定します。

208-set-target

デプロイするコンポーネントを選択します。先程作成した Lambda コンポーネントを選択します。

209-select-component

スクリーンショットが無いですが、最後にレビュー画面が出るので問題なければデプロイを実行して完了です。

デバイス側の動作確認

デプロイにエラーが出ていないか /greengrass/v2/log/以下のログを確認します。このディレクトリに「コンポート名.log」 というログファイルが作成されます。
/greengrass/v2/log/はデフォルトのログ出力先です。)

下記はエラー発生時のログの一部です。このときはセンサー側の電源が入っていないことが原因のエラーでした。

2021-08-21T04:39:11.351Z [ERROR] (pool-2-thread-19) modbus-sensor-lambda-component--HelloWorldFunction-xxxxxxxxxxxx: FATAL: lambda_runtime.py:427,Failed to initialize Lambda runtime due to exception: 'ModbusIOException' object has no attribute 'registers'. {serviceInstance=0, serviceName=modbus-sensor-lambda-component--HelloWorldFunction-xxxxxxxxxxxx, currentState=RUNNING}

次に温湿度データの出力を確認してみます。 環境変数で指定した/tmp/data_path_default.logに出力されていますね!

$ tail -f /tmp/data_path_default.log

2021-08-21 14:51:20.410572	Temperatur:	27.6 ℃
2021-08-21 14:51:20.410634	Humidity:	15.4 %
2021-08-21 14:51:30.459651	Temperatur:	27.6 ℃
2021-08-21 14:51:30.459746	Humidity:	15.4 %
2021-08-21 14:51:40.635797	Temperatur:	27.6 ℃
2021-08-21 14:51:40.635893	Humidity:	15.4 %
2021-08-21 14:51:50.684847	Temperatur:	27.6 ℃
2021-08-21 14:51:50.684946	Humidity:	15.4 %

デバイス上でデプロイされているコンポーネントを確認しても、正しい設定内容でデプロイできていることが確認できました。

$ sudo /greengrass/v2/bin/greengrass-cli component list

Component Name: modbus-sensor-lambda-component--HelloWorldFunction-xxxxxxxxxxxx
    Version: 1.0.1
    State: RUNNING
    Configuration: {"containerMode":"NoContainer","containerParams":{"devices":{},"memorySize":16000.0,"mountROSysfs":false,"volumes":{}},"inputPayloadEncodingType":"json","lambdaExecutionParameters":{"EnvironmentVariables":{"FilePath":"/tmp/data_path_default.log"}},"maxIdleTimeInSeconds":60.0,"maxInstancesCount":100.0,"maxQueueSize":1000.0,"pinned":true,"pubsubTopics":{},"statusTimeoutInSeconds":60.0,"timeoutInSeconds":3.0}

最後に

Greengrass V2 の Lambda で環境変数を使う方法は簡単に確認できました。環境変数を使うことで Lambda コードを変更することなくデバイス側の動作を変えることができるようになります。

次回は、環境変数を変えてデバイス側の変更に対応する方法を試してみたいと思います。

以上です。