【AWS IoT】Greengrass Coreデバイス上でLambdaを起動し、ローカルファイルをS3にアップロードする際の9個のハマりどころをまとめてみた。

#はじめに
AIカメラ等のプロジェクトでほぼ必須（だと思います）の「Greengrass Coreデバイス上でLambda(boto3)を動かしローカルファイルをS3にアップロードする」手順と注意点をまとめました。エッジコンピューティングの基礎でもある機能で、一見すごく簡単そうですが、筆者自身かなりハマってしまいました。ラズパイの初期設定から最後の画像のアップロードまでハマりどころが9つあるのでそれを実際の手順にそって解説しています。
（ちなみにGreengrassはversion1を使っています）

#概略図
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1001699/c8c852f3-690f-1c49-25c8-42e5eeda2878.png)


#今回やることの大まかな流れ

**初期設定（OSインストール、ラズパイの初期設定、PCからラズパイへSSH接続、Greengrass Coreのインストール＆Greengrass初期設定、Filezilla等FTPソフトウェアでラズパイに接続）
↓
Lambdaの作成
↓
Greengrass Groupの設定
↓
ラズパイの追加設定
↓
動作確認（IoT CoreからGreengrass CoreデバイスにMQTTメッセージを送ってLambdaを起動）**


#9のハマりどころ
Greengrass Core LambdaでS3にローカルファイルをアップロードしたい場合に気をつけたい注意点は以下の10個です。

###初期設定時
**① ssh接続のときsudo sshとしない**

###Lambda作成時
**② Lambda作成時に必ずGreengrass SDKとまとめてZIPファイルにしてアップロードする
③ Lambdaハンドラーを「Pythonファイル名.関数名」と指定する
④ Lambdaエイリアスには「＄LATEST」を指定しない
⑤ Lambdaの新しいバージョンを発行したら必ずエイリアスのバージョンも変える**

###Greengrass Groupの設定
**⑥ 「作成したLambdaの登録」、「リソースの追加」、「サブスクリプションの追加」、「IAMロール＆ポリシーの追加」の４つを忘れない
⑦ GreengrassのシステムlogとLambdaのlogを有効にする**

###ラズパイの追加設定
**⑧ リソースパスは実際にラズパイのLinuxOS上に存在しなくてはいけない
⑨ リソースパスで指定したパスに「chmod 775」でアクセス(readとwrite)ができるようにする**


これらの注意点は実際に手順を説明していく上で詳しく説明していきます。
※初期設定に関しては色々なところで説明があるので、ポイントの箇条書きにとどめて、内容はかなり省かせていただきます。

#1. 初期設定
##1-1. ディスクイメージのダウンロードとフォーマッティング
Raspbian OS GUI版が２GBとファイルサイズもそれほど大きくなく十分な機能があるのでおすすめです。
[RaspbberyPi OSダウンロード](https://www.raspberrypi.org/software/)

また慣れないうちはCLI版じゃなくGUI版を落としておいて、スクリーンとキーボード＆マウスを用意しておくのが無難です。

ラズパイを初期化したいときが大抵くるのでディスクイメージを複数保存しておくと便利です。フォーマッターはBalena Etcherを使っていますが早くて快適です。

##1-2. ラズパイの初期設定
まずデスクトップ右上のアイコンからwifiの設定（SSID、パスワード）をします。LANケーブルの方はケーブルをラズパイに繋ぎます。

ラズパイのターミナルを開いて

```bash

raspi-config
```

この画面になるので、「Interface Options」からSSH接続をONにします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1001699/49f62c28-d366-9c13-bb41-30158bdaa004.png)



##1-3. PCからSSH接続
###注意点①ssh接続のときsudo sshとしない

```
sudo ssh pi@xxx.xxx.xxx.xxx
```

とやると一応

```
password?
|
```

といった感じでパスワードを聞いて来ますが、何を入れてもaccess deniedと表示されます。SSHの設定かな？とsshの設定ファイルに飛んでいじってみたり、沼にハマることがあるので注意です。

ラズパイのIPアドレスはラズパイのターミナル上で

```
hostname -I
```

で表示します。

##1-4. Greengrass CoreのインストールとGGG(Greengrass Group), GGC(Greengrass Core)の作成

[こちら](https://qiita.com/hdmn54321/items/8ea22734faf7ab4ec930#4-2-2-%E6%89%8B%E9%A0%86)にAWS公式と同じ手順をまとめています。ここで作成する
Greengrass Group名、Greengrass Core名等を入力します。


#2. Lambdaの作成

##2-1. Lambdaに上げるPythonファイルの作成とSDKのダウンロード
今回使用するコードはこちら。Chromiumでダウンロードしてきたマリオの画像をアップロードする想定です。このコードをコピペして``test.py``という名前で保存してください。

```python
import boto3

s3= boto3.resource('s3')

#S3にあげたいファイルが存在するローカルのパス(絶対参照のみ)
filePath='/home/pi/Downloads/mario.jpeg'
#ファイルをあげたいS3のバケットの名前
bucketName = 'BUCKET_NAME'
#S3バケットの入れたいフォルダとファイル名（Key）を指定
s3Key = 'Pictures/GameCharacter/mario.jpeg'

def function_handler(event, context):    
    
    # 画像ファイルの場合#をとる
    #data = open(filePath, mode='rb')
    #s3.Bucket(bucketName).put_object(Key = s3Key, Body = data)
    
    # テキストファイルの場合#をとる
    #data = open(filePath, mode='r')
    #s3.Bucket(bucketName).put_object(Key = s3Key, Body = data.read())
    
    return
```

Pythonファイルができたら次はSDKをダウンロードしていきます。
[AWSのGithub](https://github.com/aws/aws-greengrass-core-sdk-python)からGreengrass Core SDK Pythonをダウンロードします。ダウンロードしたら先程のPythonファイルとこのSDKをFinderで選択して圧縮し、``.zip``ファイルにします（[こちらの記事](https://qiita.com/kt-yt/items/d8d44c7649699303a727#1-4lambda%E9%96%A2%E6%95%B0%E3%81%AE%E4%BD%9C%E6%88%90lambda%E3%81%A7%E3%81%AE%E4%BD%9C%E6%A5%AD)を参照してください）。


##2-2. ZIPファイルをLambdaに上げる
AWSコンソールからLambdaに飛びます。画像のようにお気に入りを登録しておくと便利です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1001699/7122e0e7-fade-d8c9-2072-089c5c50ef9a.png)

左側から「関数」を選んで右側のオレンジの「作成」のボタンを押します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1001699/2d05b947-5137-40e6-4290-5a79563a1fb8.png)
１から作成を選び適当に関数名を入力します。ランタイムには「Python3.7」を選び、「作成」を押します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1001699/01e8c490-01df-64df-152c-855770617281.png)
次にコードソースの「アップロード」から「zipファイル」を選び先程圧縮したPythonファイルとSDKのZIPファイルをアップロードします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1001699/1ba84466-2c03-5ea1-2ef6-5474eb5c9ec4.png)

###注意点② Lambda作成時に必ずGreengrass SDKとまとめてZIPファイルにしてアップロードする
これが意外と忘れがちなので注意です。これを忘れるとlogに
「ローカルのサーバーから認証情報が取れませんでした」
といった内容のlogが吐かれ、一見SDKの上げ忘れだと気づかないので注意が必要です。

※S3にあらかじめGreengrass SDKをZIPファイルであげておくとそこからLambdaにアップロードができるので便利です。

その後、画像のように情報を入力し、「ランタイムの設定」から「ハンドラー」を、
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1001699/bdd5e43f-f231-df23-cfda-084c704afd19.png)

``test.function_handler``

とします。

###注意点③ Lambdaハンドラーを「Pythonファイル名.関数名」と指定する
ハンドラーとはLambdaの中のどのPythonファイルを開くかを「.」の右側で指定して、Pythonファイルの中のどの関数を扱うかを「.」の右側で指定しています。一文字でも間違うとエラーになるので注意してください。

##2-2.の続き
その後右上の「新しいバージョンを発行する」を押します。Description（メモのようなもの）を必要なら書いて「パブリッシュ」を押します。その後「エイリアスの作成」を押します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1001699/0b4cc90a-63b8-1b1e-b646-565ad8f8dcea.png)
エイリアスの名前は全て大文字にするのをお勧めします（AWS公式でも色々派閥があるみたいですが）。``TEST``とか``DEV``とかわかりやすい名前をつけます。ここでは``TEST``とします。「バージョン」は一番新しい（今は作成したばかりなので1しかないはずですが）ものを選びますが、ここで注意が必要です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1001699/7958e6cc-93ef-8afe-59a3-5dc18d967ecc.png)


###注意点④ Lambdaエイリアスには「＄LATEST」を指定しない
Greengrassは設計思想上、`$LATEST`を採用していません（2021年3月現在）。`＄LATEST`はimmutabilityの設計思想にもとるとのことです（AWSの公式ドキュメントに書いてありました)。`$LATEST`を選ぶとGreengrass Groupのデプロイでエラーが出ますが、これまたlogをよくみないと原因がわからないので初めから気をつけましょう。

###注意点⑤ Lambdaの新しいバージョンを発行したら必ずエイリアスのバージョンも変える

これも意外と忘れがちです。新しいバージョンパブリッシュ後、画面上部の作成した関数名をクリックし、
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1001699/1240f082-81f3-3307-156a-77e1c943d4ff.png)
今度は画面下部にある「エイリアス」タブから自分の関数名の左にある○を選択し「編集」をクリック。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1001699/3b493b7e-1595-18fe-4ef9-7a772ad15cab.png)
バージョンを最新にして「保存」を押します。これでエイリアスの紐づけもできました。これをしておくとGreengrass Groupのデプロイの際にLambdaのバージョンを変えてあげる必要がなくなります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1001699/ce6cedac-1470-acbc-c70e-9b0a4831fe06.png)
これでLambdaの作成は完了です。

#3. Greengrass Groupの設定
AWSコンソールからAWS IoTに飛び、「グリーングラス」「クラシック（V1）」「グループ」に行き、先程作成したGreengrass Groupをクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1001699/942fe1e2-30cd-78a5-e367-8e15c1e72ea0.png)

###注意点⑥ 「作成したLambdaの登録」、「リソースの追加」、「サブスクリプションの追加」、「ポリシーの追加」の４つを忘れない

Greengrassの設定は主にGroupの設定からおこないます。今回はLambdaの登録に加えてLambdaにローカルファイルへのアクセスを許可するための**リソースの追加**、エッジデバイスとのMQTT通信に許可を与える**サブスクリプションの追加**、Greengrass Group自体にIAM権限を与えてやる**ポリシーの追加**を忘れずにやっていきます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1001699/5b32ab0f-efb7-c129-63bb-f360f52c5229.png)

##3-1. Lambdaの追加
Lambdaタブから「Lambdaの追加」→「既存のLambdaを追加」で先程作成したLambdaを選択します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1001699/c1945457-71e3-751f-9492-9ec7d18d85cc.png)
作成したエイリアスを選択して「確定」をクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1001699/baef7306-9353-0c77-42aa-0a647bc0a2ba.png)


##3-2. リソースの追加
「リソース」から「ローカルリソースの追加」をクリック。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1001699/4aa239eb-a9de-186f-d1f0-198d195a5e3e.png)
「リソース名」を入力し、「リソースタイプ」はボリュームを選択（カメラ等を追加する場合はデバイスを選択します）。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1001699/624c5134-adba-2316-4353-8e061986d9e9.png)
￼ソースパス（Lambdaにアクセスを許可するフォルダ）とデスティネーションパス（Lambdaコンテナ上仮想フォルダ）は今回は画像のように

```
#Lambdaにアクセスを許可したいフォルダ
/home/pi/Downloads

#Lambdaコンテナが使用するためのフォルダ（今回は使いませんが、必須です）
/Lambdadirectory
```

としました。グループオーナーのファイルアクセス許可は「自動的に追加」を選択（詳しい説明は[こちら](https://docs.aws.amazon.com/ja_jp/greengrass/v1/developerguide/access-local-resources.html#lra-group-owner)）。

最後に画像のようにLambdaをアタッチして最後に「アップデート」をクリックしてリソースの追加は完了です。

##3-3. サブスクリプションの追加
今度はサブスクリプションの追加をしていきます。GreengrassのLambdaは通常のLambdaを少し振る舞いが異なります。**常時実行Lambda**と**オンデマンドLambda**の2種類があって（デフォルトではオンデマンドLambdaです）、オンデマンドLambdaはLambdaにMQTT通信でメッセージが届いたらinvokeされます。

「サブスクリプション」から「サブスクリプションの追加」をクリック。ソースには「IoT Cloud」を選びます。ターゲットには先程追加したLambdaを追加します。その後「次へ」をクリック。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1001699/6f9848b5-4aba-e933-b30c-935efa5f68e0.png)
トピックフィルターはなんでも良いですが、覚えやすい``invoke/test``にしました。ワイルドカード（``#``）を使うこともできますが、そうすると大抵Lambdaが意図せず頻繁にキックされてしまうので注意が必要です。「次へ」から「完了」をクリック。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1001699/dbd89f86-d0cd-170d-9f36-39138f8e1c11.png)
これでサブスクリプションの追加は完了です。

##3-4. IAMロール＆ポリシーの追加
最後にポリシーを追加していきます。「設定」から「グループロール」の「・・・」をクリックして「IAMロールを設定」をクリック。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1001699/27ac1b3c-35cb-8370-920c-f80e986a797e.png)
「Greengrass_ServiceRole」を選択し「保存」をクリック（このロールはGreengrassのクイックスタートインストール時に作成されるロールです）。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1001699/20603ce8-3b12-45e9-b15a-5257e7983ee5.png)
「IAM」サービスから今指定したロールに**AdministratorAccess**ポリシーを付与しておくと楽です（実験段階では権限をゆるく設定しておきます）。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1001699/38319e71-4985-ac19-6df3-c6a47f670e43.png)

###注意点⑦ GreengrassのシステムlogとLambdaのlogを有効にする
この設定タブでlogファイルの設定ができます。デフォルトだとlogをはかない設定になっていますが、オススメはデバッグレベルのlogです。GreengrassとLambdaどちらもデバッグレベルのlogに設定しましょう。CloudWatch、ローカル（ラズパイ）どちらもにlogがはけます。自分はデバッグはラズパイでやって、記録用のlogはCloudWatchにはかせるため以下のように設定しています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1001699/51905541-6b98-e1e5-8f0b-87d5a0edc82d.png)

ちなみにローカルのlogは

```bash

#ルートユーザーに切り替える（/varへのアクセスはルート権限が必要なため）
sudo su

cat /greengrass/ggc/var/log/user/[リージョン]/[アカウントID]/[関数名].log
```

で見られます。

###注意点⑧ リソースパスは実際にラズパイのLinuxOS上に存在しなくてはいけない

デスティネーションパスはLambdaが仮想的に使うフォルダなのでラズパイのOS上に存在している必要はありませんが、リソースパスは存在している必要があります。

#4. ラズパイの追加設定

そしてここで最後の注意点があります。

###注意点⑨ リソースパスで指定したパスに「chmod 777」でアクセス(readとwrite)ができるようにする

Linuxのデフォルト設定だとGreengrassがフォルダに対してアクセスができないのでLinux側でも特定フォルダに対してアクセスを許可してやる必要があります。

```bash
chmod 777 /home/pi/Downloads
```

でDownloadsフォルダに対してのreadとwriteを許可します。
これでラズパイの追加設定も完了したので、AWSコンソールに戻ってGreengrass Groupのデプロイをしていきます。
先ほどと同様にGreengrass Groupの「アクション」から「デプロイ」を選択します。ステータスが**in progress**、その後**successfully completed**に変わったらデプロイ完了です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1001699/3eeeae57-4af7-4d46-3f0b-e4991465577f.png)

##4-1. アップロードしたいファイルをラズパイの所定のフォルダに置く
[3-2](https://qiita.com/drafts/e10990014620d674d3ec/edit#3-2-%E3%83%AA%E3%82%BD%E3%83%BC%E3%82%B9%E3%81%AE%E8%BF%BD%E5%8A%A0)で指定したフォルダにアップロードしたいファイルを置きます。自分の場合は**mario.jpeg**を**Downloads**フォルダに置きます。

#5. 動作確認（IoT CoreからGreengrass CoreデバイスにMQTTメッセージを送ってLambdaを起動）

最後に動作確認です。IoT Coreの「テスト」タブからMQTTテストクライアントを開きます。そしてワイルドカード(``#``)にサブスクライブします（これでシャドウの更新等もみられるので便利です）。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1001699/602280cb-4910-fc62-de70-d257373e2b26.png)
シャドウに関するやりとりが流れた後、
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1001699/7b92f5be-052d-d968-20e7-185a74c3fbf6.png)
デフォルトでGreengrass Groupに付属しているHello World Lambdaが機能しているはずなので、下のように5秒おきにメッセージが届いています（Greengrassのクイックスタートインストール時の「Hello World Lambdaをインストールしますか？」に「Yes」と答えている必要あり）。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1001699/4abd5ba2-97c7-0c40-af3d-11e747cd1d60.png)
次にGreengrass CoreデバイスのLambdaをキックするためにMQTTメッセージを送ります。トピックは[3-3](https://qiita.com/drafts/e10990014620d674d3ec/edit#3-3-%E3%82%B5%E3%83%96%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%97%E3%82%B7%E3%83%A7%E3%83%B3%E3%81%AE%E8%BF%BD%E5%8A%A0)で指定した``invoke/test``です。最初に「トピックにパブリッシュ」を選んで``invoke/test``を入力し「パブリッシュ」を押します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1001699/da83c6b3-a0ea-4256-f51f-2a3afa333623.png)

これまでのプロセスが全て成功しているとS3内にファイルがアップロードされているはずです。
**無事指定したS3バケットの**``Pictures/GameCharacter``**配下にアップロードできていました。**
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1001699/ddb59f13-bc76-3b58-fb37-5afe4ad2f1e6.png)


**無事ファイルも開けました。**
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1001699/01b66d24-f432-4aee-60d6-c66d7af5f28b.png)

#6. もしアップロードができていなかったら
・注意点の①〜⑨を見直す

・リソースパスの指定など、手打ちの箇所で誤字が許されない箇所が多いので、その打ち間違えがないか確認する

・[注意点⑦](https://qiita.com/drafts/e10990014620d674d3ec/edit#%E6%B3%A8%E6%84%8F%E7%82%B9-greengrass%E3%81%AE%E3%82%B7%E3%82%B9%E3%83%86%E3%83%A0log%E3%81%A8lambda%E3%81%AElog%E3%82%92%E6%9C%89%E5%8A%B9%E3%81%AB%E3%81%99%E3%82%8B)で設定したlogを確認する

を試してみてください。


#さいごに
AIカメラ等のプロジェクトではほぼ使うであろう「Greengrass LambdaでローカルファイルをS3にアップロード」とその注意点をまとめました。軽い画像ファイルならIoT Coreに直接MQTTで送ってRuleで処理というのも簡単だし良さそうですが、大きめのファイルやRuleをかませないファイルは今回のやり方が良さそうです。Lambdaにもっと処理を足したり新しいLambdaを足していけばエッジコンピューティングも実装できます。