🐟

GreengrassV2のコンポーネントとしてLambda関数をエッジデバイスにデプロイするためシェルスクリプト

2022/11/09に公開

エッジデバイス上のプログラムをLambda関数(Node.js)+GreengrassV2で作っています。
実機での動作確認の際、Lambda 関数をデプロイして、Greengrassのコンポーネントを設定して、デプロイを設定していますが、この一連の作業が非常に面倒なのでシェルスクリプトを作成しました。

経緯

  • Lambda関数はServerlessFramework(v2)で開発
  • GreengrassのコンポーネントのソースにLambda関数を使用する際の諸々の設定をコンソールから実施している
    • Lambda関数のバージョンを明示したりSubscribeしたいIoTCoreのトピックを書いたり、環境変数書いたり、、、、
  • CLIで設定できるので自動化したい

コンソールでの設定方法
https://docs.aws.amazon.com/ja_jp/greengrass/v2/developerguide/import-lambda-function-console.html

CLIでの設定方法
https://docs.aws.amazon.com/ja_jp/greengrass/v2/developerguide/import-lambda-function-cli.html

Before/After

Before

  • ServerlessFrameworkでデプロイ
    • sls deploy ...
  • IoTCoreのコンソールに移動
  • コンソールからGreengrassのコンポーネントを設定
    • Lambda関数のバージョンを指定
    • コンポーネントのバージョンを更新
    • Lambda関数のEventとしてSubscribeしたいIoTCoreのトピックを指定
    • 環境変数設定
  • デプロイの設定
    • 数回ポチポチするだけ

After

  • スクリプト実行
    • sh ./scripts/deploy.sh

作ったもの

Lambda関数をデプロイしてバージョンを取得してコンポーネントに設定するだけのスクリプトを作りました。
以前書いた記事をLambda関数向けにアレンジしただけです。
https://zenn.dev/nkmrkz/articles/greengrass-deployment

フォルダ構成

scripts
|- template
|  |- component_template.json
|  |- deployment_template.json
|- deployment.sh

特定のトピックをSubscribeしてメッセージをEventとして受け取るLambda関数をコンポーネント化する例

template/component.json

{
  "lambdaFunction":{
    "lambdaArn": "LambdaFunctionのARN:$LAMBDA_LATEST_VERSION",
    "componentName": "カスタムコンポーネント名",
    "componentVersion": "$CUSTOM_COMPONENT_VERSION",
    "componentDependencies": {
      "aws.greengrass.LambdaLauncher": {
        "versionRequirement": ">=2.0.0 <3.0.0",
        "dependencyType": "HARD"
      },
      "aws.greengrass.TokenExchangeService": {
        "versionRequirement": ">=2.0.0 <3.0.0",
        "dependencyType": "HARD"
      },
      "aws.greengrass.LambdaRuntimes": {
        "versionRequirement": ">=2.0.0 <3.0.0",
        "dependencyType": "SOFT"
      }
    },
    "componentLambdaParameters": {
      "eventSources": [
        {
          "topic": "LambdaのEventとしてSubscribeしたいトピック、ワイルドカード(#)も使用可能",
          "type": "IOT_CORE"
        }
      ],
      "maxQueueSize": 1000,
      "maxInstancesCount": 100,
      "maxIdleTimeInSeconds": 60,
      "timeoutInSeconds": 3,
      "statusTimeoutInSeconds": 60,
      "pinned": true,
      "inputPayloadEncodingType": "json",
      "environmentVariables": {
        "STAGE": "$STAGE_NAME",
        "ENV1": "$ENV1",
        "ENV2": "$ENV2"
      },
      "linuxProcessParams": {
        "isolationMode": "NoContainer",
        "containerParams": {}
      }
    }
  }
}

templates/deployment.json

{
  "targetArn": "arn:aws:iot:ap-northeast-1:$ACCOUNT_ID:thinggroup/デプロイ対象のターゲットグループ",
  "deploymentName": "デプロイ設定名",
  "components": {
      "aws.greengrass.Cli": {
          "componentVersion": "2.7.0"
      },
      "aws.greengrass.LegacySubscriptionRouter": {
          "componentVersion": "2.1.6",
          "configurationUpdate": {
              "merge": "{\"subscriptions\":{\"設定名(Lambda関数名と同じものを設定しています)\":{\"id\":\"識別子(Lambda関数名と同じものを設定しています)\",\"source\":\"component:Lambda関数名\",\"subject\":\"#\",\"target\":\"cloud\"}}}"
          },
          "runWith": {}
      },
      "aws.greengrass.Nucleus": {
          "componentVersion": "2.7.0",
          "runWith": {}
      },
      "カスタムコンポーネント名": {
          "componentVersion": "$CUSTOM_COMPONENT_VERSION",
          "runWith": {}
      }
  },
  "deploymentPolicies": {
      "failureHandlingPolicy": "DO_NOTHING",
      "componentUpdatePolicy": {
          "timeoutInSeconds": 60,
          "action": "NOTIFY_COMPONENTS"
      },
      "configurationValidationPolicy": {
          "timeoutInSeconds": 60
      }
  },
  "iotJobConfiguration": {}
}

deploy.sh

#!/bin/bash
echo "StageName(dev/stg) : "
read stage_name

echo "start lambda function deployment"

./node_modules/.bin/sls deploy --stage $stage_name --aws-profile AWSCLIで使用するプロファイル名

env1=`grep "ENV1=" ./scripts/.secrets | awk -F "=" '{print$2}'`
env2=`grep "ENV2=" ./scripts/.secrets | awk -F "=" '{print$2}'`
account_id=`aws sts get-caller-identity --profile AWSCLIで使用するプロファイル名 --query "Account" |sed -e 's/"//g'`

# component version: $LambdaLtestVersion.0.0
lambda_latest_version=`aws lambda  list-versions-by-function --function-name 関数名 --query 'Versions[-1].Version' --profile AWSCLIで使用するプロファイル名| sed -e 's/"//g'`
custom_component_version=$lambda_latest_version.0.0

echo "create component.json"
cat ./scripts/templates/component_template.json | sed -e "s/\$ENV1/$env1/g"| sed -e "s/\$ENV2/$env2/g"| sed -e "s/\$CUSTOM_COMPONENT_VERSION/$custom_component_version/g" | sed -e "s/\$LAMBDA_LATEST_VERSION/$lambda_latest_version/g" | sed -e "s/\$STAGE_NAME/$stage_name/g" | sed -e "s/\$ACCOUNT_ID/$account_id/g" > ./scripts/component.json

echo "create deployment.json"
cat ./scripts/templates/deployment_template.json | sed -e "s/\$CUSTOM_COMPONENT_VERSION/$custom_component_version/g" | sed -e "s/\$STAGE_NAME/$stage_name/g" | sed -e "s/\$ACCOUNT_ID/$account_id/g" > ./scripts/deployment.json

echo "start create greengrass component"
aws greengrassv2 create-component-version --cli-input-json fileb://scripts/component.json --profile AWSCLIで使用するプロファイル名

echo "start create greengrass deployment"
aws greengrassv2 create-deployment --cli-input-json fileb://scripts/deployment.json --profile AWSCLIで使用するプロファイル名
  • デプロイ時に設定が必要な値はスクリプト実行時に取得、置換しています。
  • 値を公開したくないもの(環境変数等、↑の場合はENV1ENV2)は.secretに書いて.gitignoreさせています。
    grep "ENV2=" ./scripts/.secrets | awk -F "=" '{print$2}'
  • AWSのアカウントIDはAWS CLIから取得できます。AWSCLIで使用するプロファイル名も.secretから取得しても良いかもです。
    aws sts get-caller-identity --profile AWSCLIで使用するプロファイル名 --query "Account" |sed -e 's/"//g'
  • Lambdaの最新バージョンもAWS CLIから取得できます。
    aws lambda list-versions-by-function --function-name 関数名 --query 'Versions[-1].Version' --profile AWSCLIで使用するプロファイル名| sed -e 's/"//g'
  • 一時ファイルは削除したほうが良いかもですね。一応、.gitignoreには書いています。
    • deployment.json
    • component.json
  • エラーハンドリングしていません。ちゃんと書きましょう。

.secrets

ENV1=xxxxx
ENV2=yyyyy

.gitignore(抜粋)

.secrets
component.json
deployment.json

実機での確認が楽になりました。
以上です。

GitHubで編集を提案

Discussion