【Greengrass V2】Lambda コンポーネントを更新して環境変数を使ってエッジデバイスの変更に対応する

Greengrass V2 で Lambda コンポーネントの更新方法を確認してみました。
2021.08.23

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

前回は Lambda コンポーネントの環境変数を試しました。今回は、デバイス側の変更に対応するため環境変数を変更する方法を検証してみました。

単純に環境変数の中身を変えるだけでは駄目でした…

解決したい課題

まず最初にコンポーネントを更新したいユースケースを考えてみます。

IoT デバイスを運用していると、途中でデバイス側の環境に変更があった際にデバイスの設定も変更したい場合があります。例えば、エッジデバイスから接続する現場システムの IP アドレスが変わったり、接続しているセンサーモジュールの交換で接続インターフェースが変わる、などです。

解決方法の検討

デバイスに直接アクセスして設定変更することも方法の一つですが、僻地など遠隔にデバイスがある場合はリモートから設定を変更できると嬉しいです。パッと思いつく範囲では下記のような方法が考えられます。

  • SSH 接続したり、AWS IoT セキュアトンネリングを使って直接アクセスして変更する
  • AWS IoT ジョブを使う
  • Greengrass V2 の場合は、コンポーネントを更新する

今回は、対象デバイスが Greengrass Core(V2) デバイスなので、Greengrass V2 で解決できる方法を試してみたいと思います。

想定する前提

今回の前提は下記のとおりです。

  • Greengrass Core デバイスには既に Lambda コンポーネントが動いている
  • この Lambda コンポーネントでセンサーデータを取得している
  • 取得したデータをローカルの指定ファイルに出力している

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

想定する課題

上記の想定のもと、今回解決したい課題を「現場の何らかの理由により出力ファイルのパスを変更したい」とします。

例えば、センサーデータの出力先を現地にあるファイルサーバにしているようなとき、ファイルサーバのIPが変わると、そのファイルパスも変わると思います。そういう現場を想定しています。

対応方針

Lambda コンポーネントの場合、次の2つのパラメーターで今回の制御ができるかと思います。

  • プロセス引数
    • Lambda 関数実行時に関数に渡すコマンドライン引数
  • 環境変数
    • Lambda 関数実行時に関数が利用できる環境変数を指定します。
    • クラウド上で利用される Lambda の場合とほぼ同じものです。

今回は、タイトルにある通り Lambda コンポーネントの環境変数を使います。

Lambda 関数の作成

まずは、環境変数を利用する Lambda 関数を作成します。Lambda コンポーネントは前回の記事で作ったものと同じなので、「Lambda の作成〜コンポーネントの作成」までは下記の記事を参考にしていただければと思います。

設定の変更

Lambda コンポーネントが正常に動作していれば、現在のデータ出力先は /tmp/data_path_default.log になっているはずです。

次に、このデータ出力先のパスを変えてみたいと思います。ファイルパスは環境変数で渡しているので、環境変数の中身を変更すればいいだけです。理屈は非常に単純です。

Greengrass V2 のコンポーネント設定の更新

Greengrass V2 でコンポーネント設定の更新を行うには、大きく2通りあります。

  • デフォルト設定の更新
  • デフォルト設定は変えずに(一部の)設定のみ更新

デフォルト設定を更新せずに既存コンポーネントを更新する

普通はデフォルト設定は変えずに、必要に応じて(対応が必要なデバイスに応じて)一部の設定だけ変えてデプロイしたいと思います。
こうすることで他のデバイスに新規にデプロイしたい場合は、変わらずデプロイすることができます。

コンポーネントの設定は下記の通りとします。
(現在は/tmp/data_path_default.logにデータが出力されています。)

220-component-config

それでは更新のためのデプロイを開始します。

221-deploy-start

「ステップ3」 まではこれまでと同じ手順で進めてください。「ステップ3」の「コンポーネントを設定」 で、デプロイするコンポーネント設定を更新します。
対象のコンポーネントを選択して右上の「コンポーネントを設定」 をクリックしてください。

222-step3-config-component

ここで更新の内容を編集する画面が開くので、下記のように「マージする設定」 に更新内容を JSON で記載します。記入が終わったら画面右下の「確認」をクリックして編集画面を終了します。

223-merge-config

記載内容は下記のとおりです。データ出力先のパスを/tmp/data_path_default_2.logに変更しています。

{
     "lambdaExecutionParameters": {
        "EnvironmentVariables": {
          "FilePath": "/tmp/data_path_default_2.log"
        }
    }
}

設定を変更できたら、「変更済み?」 の列が「Yes」に変わります。後はそのままデプロイまで進めましょう。(以降は同様の手順になるので割愛します)

224-modify-ok-2

デプロイが正常終了したら、デバイス側を確認してみます。下記のとおり新しいファイル(/tmp/data_path_default_2.log)にデータが出力されていることを確認しました。既存の /tmp/data_path_default.log は出力が止まっていました。

$ tail -f /tmp/data_path_default_2.log 

2021-08-21 17:40:58.415185	Temperatur:	27.4 ℃
2021-08-21 17:40:58.415248	Humidity:	46.4 %
2021-08-21 17:41:08.461597	Temperatur:	27.5 ℃
2021-08-21 17:41:08.461697	Humidity:	46.4 %

デプロイされたコンポーネント一覧を確認すると、"FilePath":"/tmp/data_path_default_2.log"という設定でデプロイされていることが分かります。

$ 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_2.log"}},"maxIdleTimeInSeconds":60.0,"maxInstancesCount":100.0,"maxQueueSize":1000.0,"pinned":true,"pubsubTopics":{},"statusTimeoutInSeconds":60.0,"timeoutInSeconds":3.0}

デプロイ内容の確認(コンソール)

デバイスにデプロイした際の「更新されたコンフィグ」はコンソール上でも確認することができます。
対象のデプロイ画面の「コンポーネント」一覧にある「設定項目の表示」で見ることが可能です。

225-display-config-item

クリックすると更新内容を表示するウィンドウが開きます。

226-window-for-deploy-config

デフォルト設定を更新する場合

先程はデフォルト設定を更新しないケースを確認しました。運用の中ではデフォルト設定を更新したい場合もあると思います。その場合も基本的には同じ手順で対応することができます。

手順としては次のようになります。

  • 新しいバージョンでコンポーネントを作成
    • そのコンポーネントの環境変数の設定を変更する
  • 環境変数の値を変更した「新バージョン」のコンポーネントをデプロイする
    • デプロイ設定の中でマージ更新を設定する

先程見たように、既存デプロイに対して設定を更新したい場合は「マージ更新」を行う点がポイントになります。単純に、「コンポーネントの新バージョン作成の中で環境変数を更新してデプロイ」するだけでは、デバイス上の動作を変更できないので注意 が必要です。

具体的な手順

やることはほぼ同じですが、具体的な手順も見ておきましょう。下記画面のように対象のコンポーネントに対して「バージョンを作成」をクリックして、新しいバージョンを作ります。
なお、現在の設定は先程の続きとして下記の様になっているものとします。

  • デプロイのデフォルト設定でデータファイルのパスは /tmp/data_path_default.log になっている。
  • 先程のデプロイでデータ出力先は /tmp/data_path_default_2.log になっている。

227-make-new-version

コンポーネントバージョンをアップデートしておきます。今回は1.0.1から1.0.2に更新しました。

228-version-increment

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

229-add-parameters

今回は、環境変数 FilePath の値を /tmp/data_path_new_default.log に変更しました。

230-new-env

最後に分離モードを指定してコンポーネントを作成します。(相変わらず「コンテナなし」に設定していますが、適宜変更してください。)

231-make-component

新しいバージョン1.0.2のコンポーネントが作成できました。デフォルト設定も更新されていることが分かります。

232-new-version-component

今後「新規に」このバージョンのコンポーネントをデプロイされるデバイスは、全てこのデフォルト設定が適用されることになります。

一方で、既存のコンポーネントがデプロイされたデバイスにも、このデフォルト設定を適用したいケースもあります。先程と同様の手順で更新していきましょう。

新しいバージョンのコンポーネントをデプロイします。下記のように「ステップ3」マージ更新したいコンポーネントを選択して「コンポーネントを設定」をクリックします。

233-config-component-option

編集画面を開いた直後は下記のように先程の更新内容が残っています。(以前の内容が残っているのはデプロイターゲットが前回と同じためです)
左側の「デフォルト設定」は変更前のデフォルト設定が表示されています。

234-before-config-marge

更新したい内容に合わせて、次のように入力して「確認」をクリックします。

235-after-update-merge-config

{
	"lambdaExecutionParameters": {
		"EnvironmentVariables": {
			"FilePath": "/tmp/data_path_new_default.log"
		}
	}
}

編集画面を閉じると元の画面に戻るので、後は同じ手順でデプロイします。

236-next-deploy-step

デプロイが完了したらデバイスを確認してみます。下記のように新しいファイル /tmp/data_path_new_default.log にデータが出力されています!

$ tail -f /tmp/data_path_new_default.log 

2021-08-21 18:22:46.249172	Temperatur:	27.7 ℃
2021-08-21 18:22:46.249233	Humidity:	46.4 %
2021-08-21 18:22:56.298060	Temperatur:	27.7 ℃
2021-08-21 18:22:56.298156	Humidity:	46.4 %
2021-08-21 18:23:06.472046	Temperatur:	27.7 ℃
2021-08-21 18:23:06.472145	Humidity:	46.4 %

コンポーネントの一覧にも正しいコンフィグ {"FilePath":"/tmp/data_path_new_default.log"} でデプロイされていることが確認できました。

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

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

設定の更新でセットする内容

先程の更新内容の入力では下記の内容を記入しました。これは変更前の内容に対する変更内容を指定しています。

{
	"lambdaExecutionParameters": {
		"EnvironmentVariables": {
			"FilePath": "/tmp/data_path_new_default.log"
		}
	}
}

今回の場合、変更前の直前バージョンのデフォルト設定は下記のとおりでした。

{
  "lambdaExecutionParameters": {
    "EnvironmentVariables": {
      "FilePath": "/tmp/data_path_new_default.log"
    }
  },
  "containerParams": {
    "memorySize": 16000,
    "mountROSysfs": false,
    "volumes": {},
    "devices": {}
  },
  "containerMode": "NoContainer",
  "timeoutInSeconds": 3,
  "maxInstancesCount": 100,
  "inputPayloadEncodingType": "json",
  "maxQueueSize": 1000,
  "pinned": true,
  "maxIdleTimeInSeconds": 60,
  "statusTimeoutInSeconds": 60,
  "pubsubTopics": {}
}

上記のうち、FilePathの設定を更新したいので、その部分を指定する形で「マージ更新」の内容を設定しています。

{
  "lambdaExecutionParameters": {
    "EnvironmentVariables": {
      "FilePath": "/tmp/data_path_new_default.log"
    }
  }
}

「パスのリセット」について

また、先程見た「設定の更新」画面では、いずれも「パスのリセット」[]の設定のみで何も変更しませんでした。この「リセット」の意味は「コンポーネントの設定をリセット」するという意味になります。

今回リセットに設定した[](何も指定なし)は「コンポーネントの設定全体をデフォルト値にリセットする」という意味になります。
個人的には、「毎回[]で全体をリセットして、更新したい箇所だけマージ更新で指定する」という対応が設定ミスに繋がりにくい運用になるのではないかと感じました。

AWS のドキュメントにあるように、デバイスを運用していく中で「前バージョンの更新の一部のみリセットしつつ更新する」といったケースもありえるかと思いますが、この場合は更新内容に誤りが無いか事前によく確認するなど、慎重な運用が必要になりそうだと感じました。

個人的には、この「コンポーネント設定の更新」は少々分かりづらい部分があったので、改めて説明する記事を書ければと思っています。

最後に

コンポーネントを更新する方法を検証する中で、「コンポーネント設定の更新」について理解を深めることができました。
今回は Lambda コンポーネントが対象でしたがカスタムコンポーネントでも方法は同じだと思います。カスタムコンポーネントについても改めて試してみたいと思います。

また、一部の「AWSが管理するマネージドコンポーネント」の中には、今回のマージ設定を行う必要がありそうなので、事前に学習できたことは良かったと思います。
次はマネージドコンポーネントについてご紹介していきたいと思います。

以上です。