Jetson Nano Developer KitでAWS IoT Greengrassを動かしてみる

NvidiaのJetson Nanoを手に入れたので、AWS IoT Greengrassが使えるか実際に試してみました。

Jetson Nanoのセットアップは[Getting Started With Jetson Nano Developer Kit](https://developer.nvidia.com/embedded/learn/get-started-jetson-nano-devkit)を参考にしましたが、SDカードにイメージを焼いて、起動するだけで、特にハマることなくあっさりと動きます。

アーキテクチャを確認してみると`aarch64`だというのが確認できます。

```
$ uname -a
Linux nvidia-desktop 4.9.140-tegra #1 SMP PREEMPT Wed Mar 13 00:32:22 PDT 2019 aarch64 aarch64 aarch64 GNU/Linux
```

ここから、Greengrassのセットアップを進めていきます。
基本的な手順は、[AWS IoT Greengrass の使用開始](https://docs.aws.amazon.com/ja_jp/greengrass/latest/developerguide/gg-gs.html)を参考に進めます。

## 1.Greengrassが動かせる環境かを確認

Greengrassを動かす際に、対象のデバイスが要件を満たしているかを調べるスクリプトが有るので、それを使って確認します。
ここからは、Jetson NanoにSSHして、ターミナルで操作して行きます

### Greengrass用のuser、Groupの作成

```
sudo adduser --system ggc_user
sudo groupadd --system ggc_group
```

### スクリプトの実行

ダウンロード

```
wget https://github.com/aws-samples/aws-greengrass-samples/raw/master/greengrass-dependency-checker-GGCv1.8.0.zip
unzip greengrass-dependency-checker-GGCv1.8.0.zip
```

実行

```
cd greengrass-dependency-checker-GGCv1.8.0/
sudo ./check_ggc_dependencies

==========================Checking script dependencies==============================
The device has all commands required for the script to run.

========================Dependency check report for GGC v1.8.0=========================
System configuration:
Kernel architecture: aarch64
Init process: /lib/systemd/systemd
Kernel version: 4.9
C library: Ubuntu GLIBC 2.27-3ubuntu1
C library version: 2.27
Directory /var/run: Present
/dev/stdin: Found
/dev/stdout: Found
/dev/stderr: Found
----------------------------Commands and software packages--------------------------
Python version: 2.7.15rc1
NodeJS 6.10: Not found
Java 8: Not found
wget: Present
realpath: Present
tar: Present
readlink: Present
basename: Present
dirname: Present
pidof: Present
df: Present
grep: Present
umount: Present

---------------------------------Platform security----------------------------------
Hardlinks_protection: Enabled
Symlinks protection: Enabled

-----------------------------------User and group-----------------------------------
ggc_user: Present
ggc_group: Present

----------------(Optional) Greengrass container dependency check----------------

--------------------------------Kernel configuration--------------------------------
Kernel config file: /proc/config.gz

Namespace configs:
CONFIG_IPC_NS: Enabled
CONFIG_UTS_NS: Enabled
CONFIG_USER_NS: Enabled
CONFIG_PID_NS: Enabled

Cgroup configs:
CONFIG_CGROUP_DEVICE: Enabled
CONFIG_CGROUPS: Enabled
CONFIG_MEMCG: Enabled

Other required configs:
CONFIG_POSIX_MQUEUE: Enabled
CONFIG_OVERLAY_FS: Enabled
CONFIG_HAVE_ARCH_SECCOMP_FILTER: Enabled
CONFIG_SECCOMP_FILTER: Enabled
CONFIG_KEYS: Enabled
CONFIG_SECCOMP: Enabled
CONFIG_SHMEM: Enabled

------------------------------------Cgroups check-----------------------------------
Cgroups mount directory: /sys/fs/cgroup

Devices cgroup: Enabled and Mounted
Memory cgroup: Enabled and Mounted


------------------------------------Results-----------------------------------------
Note:
1. It looks like the kernel uses 'systemd' as the init process. Be sure to set the
'useSystemd' field in the file 'config.json' to 'yes' when configuring Greengrass core.

Missing optional dependencies:
1. Could not find the binary 'nodejs6.10'.

If NodeJS 6.10 or later is installed on the device, name the binary 'nodejs6.10' and
add its parent directory to the PATH environment variable. NodeJS 6.10 or later is
required to execute NodeJS lambdas on Greengrass core.

2. Could not find the binary 'java8'.

If Java 8 or later is installed on the device name the binary 'java8' and add its
parent directory to the PATH environment variable. Java 8 or later is required to
execute Java lambdas on Greengrass core.

Supported lambda isolation modes:
No Container: Supported
Greengrass Container: Supported

----------------------------------Exit status---------------------------------------
You can now proceed to installing the Greengrass core 1.8.0 software on the device.
Please reach out to the AWS Greengrass support if issues arise.
```

環境は問題ないですね！(Java, nodejsがないと出ますが、今回はpythonで試すので、インストールされていなくても大丈夫）
[Jetson TX2を利用した場合はセットアップ](https://docs.aws.amazon.com/ja_jp/greengrass/latest/developerguide/module1.html#setup-filter.other)が大変ですが、Nanoは特に設定を変更することなく、利用可能でした。

## 2.Greengrassの準備

動作できることが確認できたので、AWSのマネージメントコンソール(MC)からGreengrassの設定をして、ソフトウエアのダウンロードを行います。
2019/3/31時点の手順ですので、最新の情報は[AWS IoT Greengrass の使用開始](https://docs.aws.amazon.com/ja_jp/greengrass/latest/developerguide/gg-gs.html)を参照してください

### Greengrassグループの作成

[AWS IoTのMC](https://console.aws.amazon.com/iot/home)を開きます。

最初に、グループを作成
<img width="1154" alt="スクリーンショット 2019-03-31 6.47.57.png" src="https://qiita-image-store.s3.amazonaws.com/0/36184/66f20727-fbf7-4a30-4048-b9adaf4db6a5.png">

`Use easy creation`を選択
<img width="994" alt="スクリーンショット 2019-03-31 6.48.32.png" src="https://qiita-image-store.s3.amazonaws.com/0/36184/472d08ad-3756-3557-d7a1-2e986273bfb4.png">

Greengrassのグループ名を入力して`Next`を選択
<img width="981" alt="スクリーンショット 2019-03-31 9.04.19.png" src="https://qiita-image-store.s3.amazonaws.com/0/36184/f1bcb058-d943-90b5-39ab-9d1a624ecf68.png">

Greengrass Coreの名称を入力（今回は自動補完される名前をそのまま利用）して、`Next`を選択
このCoreがAWS IoTのThinｇとなる
<img width="985" alt="スクリーンショット 2019-03-31 6.48.40.png" src="https://qiita-image-store.s3.amazonaws.com/0/36184/8072c925-31ab-07c8-5ed6-967f716851af.png">

Greengrass作成の際に行われる処理が表示されるので、確認したら`Create Group and Core`を選択
<img width="985" alt="スクリーンショット 2019-03-31 6.48.59.png" src="https://qiita-image-store.s3.amazonaws.com/0/36184/7a351cd7-a932-7b7e-f9ca-2dbcc6c8bf15.png">

Greengrass Coreように作成された証明書と設定情報を含んだファイルをダウンロードするので、`Download these resources as a tar.gz`を選択
<img width="988" alt="スクリーンショット 2019-03-31 6.49.20.png" src="https://qiita-image-store.s3.amazonaws.com/0/36184/2f379ffa-56f6-4574-d24e-9c9ff43fb8fe.png">

Greengrassのソフトウエアをダウンロードするので、`Choose your platform`を選択
<img width="991" alt="スクリーンショット 2019-03-31 6.50.49.png" src="https://qiita-image-store.s3.amazonaws.com/0/36184/17fb00ad-268e-bcc6-f390-c431c34976cb.png">

[ダウンロード画面](https://docs.aws.amazon.com/ja_jp/greengrass/latest/developerguide/what-is-gg.html#gg-downloads)から、Jetson Nano用のファイルを選んで`ダウンロード`を選択
（先の手順でuname -aで調べたアーキテクチャ`aarch64`）
<img width="478" alt="スクリーンショット 2019-03-31 6.50.06.png" src="https://qiita-image-store.s3.amazonaws.com/0/36184/71ea79eb-4c5a-8d17-36ff-b8f85289dacd.png">

ダウンロードが終わったら、前の画面に戻って`Finish`を選択することを忘れずに

## 3.Greengrassのインストール

AWS IoT側の設定が終わったので、Jetson Nanoにダウンロードしたファイルをセットアップします。
まずは、自分のPCからSCPでファイルをコピーします。IPアドレス、ユーザー名、ファイル名は実際の環境に合わせてください

```
scp ~/11111111-setup.tar.gz nvidia@192.168.1.11:~/

scp ~/greengrass-linux-aarch64-1.8.0.tar.gz nvidia@192.168.1.11:~/
```

Jetson NanoにSSHし、以下のコマンドでコピーしたファイルを配置します

```
sudo tar -zxvf greengrass-linux-aarch64-1.8.0.tar.gz -C /
sudo tar -zxvf 11111111-setup.tar.gz -C /greengrass
```

AWS IoTのエンドポイントの検証で利用するRoot CAの証明書もダウンロードします

```
sudo wget https://www.amazontrust.com/repository/AmazonRootCA1.pem -O /greengrass/certs/root.ca.pem
```

Greengrassで利用する証明書や、エンドポイントの設定は`/greengrass/config/config.json`に書かれています

```
cat /greengrass/config/config.json
{
  "coreThing" : {
    "caPath" : "root.ca.pem",
    "certPath" : "11111111.cert.pem",
    "keyPath" : "11111111.private.key",
    "thingArn" : "arn:aws:iot:us-east-1:1234567890:thing/JetsonNano_Core",
    "iotHost" : "1234567890-ats.iot.us-east-1.amazonaws.com",
    "ggHost" : "greengrass-ats.iot.us-east-1.amazonaws.com",
    "keepAlive" : 600
  },
  "runtime" : {
    "cgroup" : {
      "useSystemd" : "yes"
    }
  },
  "managedRespawn" : false,
  "crypto" : {
    "principals" : {
      "SecretsManager" : {
        "privateKeyPath" : "file:///greengrass/certs/11111111.private.key"
      },
      "IoTCertificate" : {
        "privateKeyPath" : "file:///greengrass/certs/11111111.private.key",
        "certificatePath" : "file:///greengrass/certs/11111111.cert.pem"
      }
    },
    "caPath" : "file:///greengrass/certs/root.ca.pem"
  }
}
```

これで、起動する準備ができたので、実際に起動します。

```
sudo /greengrass/ggc/core/greengrassd start
Setting up greengrass daemon
Validating hardlink/softlink protection
Waiting for up to 40s for Daemon to start

Greengrass successfully started with PID: 21862
```

起動しているかプロセスを見てみましょう

```
ps aux | grep greengrass
root     21862  1.5  0.4 908468 17372 pts/1    Sl   06:58   0:00 /greengrass/ggc/packages/1.8.0/bin/daemon -core-dir /greengrass/ggc/packages/1.8.0 -greengrassdPid 21857
```

ログの状態も確認してみます

```
sudo cat /greengrass/ggc/var/log/system/runtime.log

[2019-03-31T06:58:30.946+09:00][INFO]-===========================================
[2019-03-31T06:58:30.946+09:00][INFO]-Greengrass Root: /greengrass
[2019-03-31T06:58:30.946+09:00][INFO]-Greengrass Write Directory: /greengrass/ggc
[2019-03-31T06:58:30.946+09:00][INFO]-Group File Directory: /greengrass/ggc/deployment/group
[2019-03-31T06:58:30.947+09:00][INFO]-Default Lambda UID: 125
[2019-03-31T06:58:30.947+09:00][INFO]-Default Lambda GID: 999
[2019-03-31T06:58:30.947+09:00][INFO]-===========================================
[2019-03-31T06:58:30.947+09:00][INFO]-The current core is using the AWS IoT certificates with fingerprint: 746042da68b1380ca7c07471
[2019-03-31T06:58:30.948+09:00][WARN]-worker process info: /greengrass/ggc/packages/1.8.0/var/worker/processes
[2019-03-31T06:58:30.948+09:00][WARN]-worker process info: /greengrass/ggc/packages/1.8.0/var/worker/processes
[2019-03-31T06:58:30.95+09:00][INFO]-Reloading registry
[2019-03-31T06:58:30.95+09:00][INFO]-The current core is using the AWS IoT certificates with fingerprint: 746042da68b1380ca7c07471
[2019-03-31T06:58:30.954+09:00][INFO]-Connecting with MQTT: endpoint: 1234567890-ats.iot.us-east-1.amazonaws.com:8883: clientId JetsonNano_Core
[2019-03-31T06:58:30.954+09:00][INFO]-MQTT connection attempt: attemptId: mZHY, clientId: JetsonNano_Core
[2019-03-31T06:58:31.9+09:00][INFO]-MQTT connection successful: attemptId: mZHY, clientId: JetsonNano_Core
[2019-03-31T06:58:31.9+09:00][INFO]-Started Deployment Agent and listening for updates
[2019-03-31T06:58:31.9+09:00][INFO]-Started Deployment Agent and listening for updates
[2019-03-31T06:58:31.901+09:00][INFO]-MQTT connection connected. Start subscribing: clientId: JetsonNano_Core
[2019-03-31T06:58:31.901+09:00][INFO]-Deployment agent connected to cloud
[2019-03-31T06:58:31.901+09:00][INFO]-Start subscribing 2 topics, clientId: JetsonNano_Core
[2019-03-31T06:58:31.902+09:00][INFO]-Trying to subscribe to topic $aws/things/JetsonNano_Core-gda/shadow/update/delta
[2019-03-31T06:58:32.116+09:00][INFO]-Subscribed to : $aws/things/JetsonNano_Core-gda/shadow/update/delta
[2019-03-31T06:58:32.116+09:00][INFO]-Trying to subscribe to topic $aws/things/JetsonNano_Core-gda/shadow/get/accepted
[2019-03-31T06:58:32.36+09:00][INFO]-Subscribed to : $aws/things/JetsonNano_Core-gda/shadow/get/accepted
[2019-03-31T06:58:32.565+09:00][INFO]-All topics subscribed, clientId: JetsonNano_Core
```

Subscribeもされているので、正しく起動できたのが確認できました

つぎに、MCからデプロイをしてみて、GreengrassとAWS IoTが連携できるかを確認します

<img width="1001" alt="スクリーンショット 2019-03-31 7.00.06.png" src="https://qiita-image-store.s3.amazonaws.com/0/36184/b3154d41-e236-1ae8-0db8-db6cc38d6afb.png">

`Automatic detection`を選択
初めてデプロイするときは、Roleを作成しますみたいなダイアログが表示されるので、作成してください。
<img width="995" alt="スクリーンショット 2019-03-31 7.00.14.png" src="https://qiita-image-store.s3.amazonaws.com/0/36184/b989041e-df91-3493-8a9b-5a2f29db0596.png">

何も設定を追加していないので、すぐに正常にデプロイと表示されます。
（前の手順でRoleを作成したときは反映までに少し時間がかかるためデプロイに失敗します。数分待ってから再度デプロイしてください）
<img width="990" alt="スクリーンショット 2019-03-31 7.01.10.png" src="https://qiita-image-store.s3.amazonaws.com/0/36184/34c22ae9-c51b-42ff-8bf7-00bd6c8b95a6.png">

## 4.Lambdaをデプロイしてメッセージを送信する

ここまでで、Greengrassが動くことが確認できたので、実際にLambdaをデプロイしてデバイスからAWS IoTにメッセージを送れるかを確認します。

作成したGreengrass Groupを選び、`Lambdas`を選択し、`Add Lambda`を選択する
<img width="996" alt="スクリーンショット 2019-03-31 7.14.37.png" src="https://qiita-image-store.s3.amazonaws.com/0/36184/aeac63e7-20cc-ad4b-7122-761643b8bda9.png">

`Create new Lambda`を選択
<img width="987" alt="スクリーンショット 2019-03-31 7.14.47.png" src="https://qiita-image-store.s3.amazonaws.com/0/36184/b7d67999-32d7-f88e-22ff-e4f8041feef1.png">

Lambdaのコンソールが開くので、以下を指定し、`Create Function`を選択

- Lambda名
- Runtimeは、Python2.7 ([Greengrassの対応言語](https://docs.aws.amazon.com/ja_jp/greengrass/latest/developerguide/create-lambda.html))
- Roleは、実際にはGreengrassの実行Roleが適用されるので、ここではbasicなのを作成してます

<img width="1142" alt="スクリーンショット 2019-03-31 7.16.22.png" src="https://qiita-image-store.s3.amazonaws.com/0/36184/48e6898e-8442-7b32-6046-74022dabc7c6.png">

[Lambda 関数の作成とパッケージ化](https://docs.aws.amazon.com/ja_jp/greengrass/latest/developerguide/create-lambda.html)に有るサンプルソースをzip化して、`Upload`から指定します。Handlerのところは、サンプルソースのファイル名に合わせます。最後に`Save`を選択し作成を完了します。
<img width="1146" alt="スクリーンショット 2019-03-31 7.19.01.png" src="https://qiita-image-store.s3.amazonaws.com/0/36184/9c6c73d5-062d-eb55-d27b-a746917c8249.png">

Lambdaのバージョンを作成するので、`Actions` > `Publish new version`を選択します
<img width="1141" alt="スクリーンショット 2019-03-31 7.19.22.png" src="https://qiita-image-store.s3.amazonaws.com/0/36184/85b93a59-e80f-7c4c-c420-4abdc78eb84a.png">

`Publish`を選択
<img width="824" alt="スクリーンショット 2019-03-31 7.19.31.png" src="https://qiita-image-store.s3.amazonaws.com/0/36184/89bb435d-e912-8927-6630-8e2d0f1af386.png">

次にAliasを作成したいので、`Actions` > `Create alias`を選択
<img width="831" alt="スクリーンショット 2019-03-31 7.19.45.png" src="https://qiita-image-store.s3.amazonaws.com/0/36184/e37f480a-c385-f8a5-7442-dc6690a76ca2.png">

名前をつけて、先ほど作成したversionを選び、`Create`を選択
<img width="822" alt="スクリーンショット 2019-03-31 7.20.02.png" src="https://qiita-image-store.s3.amazonaws.com/0/36184/2a7cfa91-49c4-8846-86ae-da844d32e968.png">

Greengrassの画面に戻って`Use existing Lambda`を選択
<img width="993" alt="スクリーンショット 2019-03-31 9.44.46.png" src="https://qiita-image-store.s3.amazonaws.com/0/36184/a32c546f-9620-9773-5b91-f76393b6538c.png">

先ほど作成したLambdaを選択
<img width="988" alt="スクリーンショット 2019-03-31 7.20.36.png" src="https://qiita-image-store.s3.amazonaws.com/0/36184/e6d24e6f-4735-9ecc-05b3-09f0c00f604a.png">

利用するバージョンで、作成したAliasを選択して、`Finish`を選択
<img width="990" alt="スクリーンショット 2019-03-31 7.20.53.png" src="https://qiita-image-store.s3.amazonaws.com/0/36184/2ffec0a0-044e-b421-fc0b-e2bae47f8774.png">

Lambdaが追加されるので「…」を選び、`Edit configuration`を選択
<img width="994" alt="スクリーンショット 2019-03-31 7.21.11.png" src="https://qiita-image-store.s3.amazonaws.com/0/36184/bdd149ae-79d2-ef72-a188-bd3cb5c941b7.png">

`Make this function long-lived and keep it running indefinitely`を選択します。
今回のLambdaはイベントドリブンで実行されるlambdaではなく、long lived lambdaと言う、常時実行しているタイプにするためです。
<img width="522" alt="スクリーンショット 2019-03-31 7.21.37.png" src="https://qiita-image-store.s3.amazonaws.com/0/36184/3bb7b9a8-168e-2766-fc37-eafe51283b2a.png">

最後に一番下の`Update`を選択
<img width="993" alt="スクリーンショット 2019-03-31 7.22.13.png" src="https://qiita-image-store.s3.amazonaws.com/0/36184/8b9eed25-03cf-63b4-a056-9841800aeb0d.png">

次にサブスクリプションの設定を行うので、`Subscriptions`を選択。
Greengrassは自身と、下にぶら下がるthingといった構成も出来、どういったデータをどこに送るかや、特定のデータが来たらLambdaを実行するといった事ができます。それらのルーティングの設定をここのサブスクリプションで行います。
<img width="997" alt="スクリーンショット 2019-03-31 7.22.33.png" src="https://qiita-image-store.s3.amazonaws.com/0/36184/973d5228-159f-f14d-891c-7d312f4fb141.png">

LambdaからAWS IoTにデータをpublishしたいので、sourceにLambdaを指定します
<img width="986" alt="スクリーンショット 2019-03-31 7.22.49.png" src="https://qiita-image-store.s3.amazonaws.com/0/36184/be888244-a6ac-dde7-c3d9-81e8c30a218e.png">

そして、TargetにIoT Cloudを指定
<img width="973" alt="スクリーンショット 2019-03-31 7.22.58.png" src="https://qiita-image-store.s3.amazonaws.com/0/36184/cb49ae58-f317-3be6-be66-996e56872e95.png">

確認して`Next`を選択
<img width="986" alt="スクリーンショット 2019-03-31 7.23.10.png" src="https://qiita-image-store.s3.amazonaws.com/0/36184/09edbd3b-0b5c-ece3-91e9-9177b4b404a0.png">

どのデータに対してこのサブスクリプションを適用するか指定するので、サンプルプログラムは`hello/world`というtopicにデータをpublishするので、Topic filterに`hello/world`を入力して、`Next`を選択
<img width="994" alt="スクリーンショット 2019-03-31 7.23.29.png" src="https://qiita-image-store.s3.amazonaws.com/0/36184/92afffc5-5112-9a2b-f09e-1f453e54c939.png">

最後に確認して、`Finish`を選択
<img width="984" alt="スクリーンショット 2019-03-31 7.23.38.png" src="https://qiita-image-store.s3.amazonaws.com/0/36184/7c2269dd-3f23-7798-eca5-758d597440dd.png">

これで、Greengrassにデプロイをします
<img width="1001" alt="スクリーンショット 2019-03-31 7.23.55.png" src="https://qiita-image-store.s3.amazonaws.com/0/36184/c64dceb5-0627-2fff-5159-3b5c42984826.png">

実際にデータが届いているか確認するには、MCにあるMQTTのテストクライアントで、`hellow/world`topicをsubscribeしてみます
<img width="1212" alt="スクリーンショット 2019-03-31 7.24.17.png" src="https://qiita-image-store.s3.amazonaws.com/0/36184/14993ec6-f736-b721-5b65-f1641d96940a.png">

ちゃんとデータが届くのが確認できました！
<img width="743" alt="スクリーンショット 2019-03-31 7.25.21.png" src="https://qiita-image-store.s3.amazonaws.com/0/36184/ac75247a-18e6-bbdb-9128-8f80a9c9fd8c.png">

## まとめ

Jetson Nanoをセットアップして、Greengrassを動かしてみました。Jetson TX2よりも楽に動かせたので、これは色々試すのには良さそうですね！

次の回では、Greengrass上で推論を行っていみたいと思います
**4/13追記**
[Jetson Nano Developer KitでAWS IoT GreengrassのML Inferenceを試す](https://qiita.com/sparkgene/items/425d310c1d6c9158f896)

## 免責

発言内容は個人的な意見であり、所属する企業を代表するものではありません。
掲載しているソースコードは、サンプルレベルの物ですので動作を保証するものではありません。