AWS Greengrass を Raspberry Pi で動かしてみる

 AWS re:Invent 2017 では今年も多数の新サービスが発表されましたね。その中には IoT や AI 関連のものも多く、 エッジデバイス上で Machine Learning の推論が実行できる AWS Greengrass ML Inference などはとても興味深いです。が、そもそも Greengrass に今まで触ったことがなかったので、今更ではありますが Raspberry Pi で Greengrass を動かしてみました。基本的に下記の公式ドキュメントのチュートリアルの実行です。

docs.aws.amazon.com

Raspberry Pi での環境設定

 Raspberry Pi の基本的な環境構築は終わっているものとして、 Greengrass を動かすための設定を行います。まずは Greengrass Core 用の Linux ユーザとグループを作成し、 sqlite3 をインストールします。

pi@raspberrypi:~ $ sudo adduser --system ggc_user
Adding system user `ggc_user' (UID 117) ...
Adding new user `ggc_user' (UID 117) with group `nogroup' ...
Creating home directory `/home/ggc_user' ...
pi@raspberrypi:~ $ 
pi@raspberrypi:~ $ sudo addgroup --system ggc_group
Adding group `ggc_group' (GID 122) ...
Done.
pi@raspberrypi:~ $ 
pi@raspberrypi:~ $ sudo apt-get install sqlite3
Reading package lists... Done
Building dependency tree       
Reading state information... Done
Suggested packages:
  sqlite3-doc
The following NEW packages will be installed:
  sqlite3
0 upgraded, 1 newly installed, 0 to remove and 0 not upgraded.
Need to get 99.6 kB of archives.
After this operation, 139 kB of additional disk space will be used.
Get:1 http://mirrordirector.raspbian.org/raspbian/ jessie/main sqlite3 armhf 3.8.7.1-1+deb8u2 [99.6 kB]
Fetched 99.6 kB in 1s (91.6 kB/s)
Selecting previously unselected package sqlite3.
(Reading database ... 104790 files and directories currently installed.)
Preparing to unpack .../sqlite3_3.8.7.1-1+deb8u2_armhf.deb ...
Unpacking sqlite3 (3.8.7.1-1+deb8u2) ...
Processing triggers for man-db (2.7.5-1~bpo8+1) ...
Setting up sqlite3 (3.8.7.1-1+deb8u2) ...
pi@raspberrypi:~ $ 

 Greengrass Core では起動時にOSでハードリンク/ソフトリンクの保護が有効か確認しているため、この保護を有効にしておきます。 /etc/sysctl.d/98-rpi.conf に設定を追加します。追加前後の差分は下記の通りです。

pi@raspberrypi:~ $ diff /etc/sysctl.d/98-rpi.conf.20171220 /etc/sysctl.d/98-rpi.conf
2a3,4
> fs.protected_hardlinks = 1
> fs.protected_symlinks = 1
pi@raspberrypi:~ $ 

 設定を追加したら一度再起動します。

pi@raspberrypi:~ $ sudo reboot

 再起動したら下記のように確認するとハードリンク/ソフトリンクの保護が有効になっていることがわかります。

pi@raspberrypi:~ $ sudo sysctl -a | grep 'fs.protected'
fs.protected_hardlinks = 1
fs.protected_symlinks = 1
sysctl: reading key "net.ipv6.conf.all.stable_secret"
sysctl: reading key "net.ipv6.conf.default.stable_secret"
sysctl: reading key "net.ipv6.conf.eth0.stable_secret"
sysctl: reading key "net.ipv6.conf.lo.stable_secret"
sysctl: reading key "net.ipv6.conf.wlan0.stable_secret"

Greengrass Group と Greengrass Core の作成

 Greengrass のデバイスには Core デバイスと、 Core デバイスに接続するそれ以外のデバイスがあり、Core デバイスが AWS IoT や Greengrass のクラウドサービスと通信します。また、それらのデバイスと設定情報をひとまとまりにしたものが Greengrass Group になります。まずはコンソールから Greengrass Group を作成しますが、その前にコンソールから Greengrass を操作するための権限を追加しておきます。今回はお試しということでとりあえず Greengrass へのフルアクセス権限を追加してしまいます。

f:id:akanuma-hiroaki:20171221081929p:plain

 それでは Greengrass Group を作成します。AWS IoT のコンソールのメニューから Greengrass を選択します。

f:id:akanuma-hiroaki:20171221082356p:plain

 ようこそ画面が表示されるので、「Greengrass グループの定義」の 今すぐ始める をクリックします。

f:id:akanuma-hiroaki:20171221082445p:plain

 グループの作成方法の選択画面が表示されますので、 簡単な作成の使用 を選択します。この方法だと Core デバイスがクラウドサービスにアクセスするために必要な証明書やキーペアの作成なども自動で行ってくれます。

f:id:akanuma-hiroaki:20171221082740p:plain

 グループ名の入力画面で任意のグループ名を設定します。

f:id:akanuma-hiroaki:20171221083155p:plain

 続けて Core デバイスの名前も決めます。デフォルトだとグループ名に _Core がついたものになっているので今回はそのまま使用します。

f:id:akanuma-hiroaki:20171221083252p:plain

 実行する内容が表示されるので、 グループとコアの作成 をクリックします。

f:id:akanuma-hiroaki:20171221083411p:plain

 Group と Core が作成され、証明書のダウンロード画面が表示されますので、ダウンロードしておきます。

f:id:akanuma-hiroaki:20171221083632p:plain

 また、同じ画面で Greengrass ソフトウェアパッケージもダウンロードできますので、CPUアーキテクチャとして ARMv7l を選択し、ダウンロードして、 完了 をクリックします。

f:id:akanuma-hiroaki:20171221083804p:plain

 これで Greengrass Group と Greengrass Core の作成は完了です。

Greengrass Core のインストール

 Raspberry Pi に先ほどダウンロードした Greengrass ソフトウェアパッケージをインストールします。ソフトウェアパッケージを Raspberry Pi 上に配置したら展開します。

pi@raspberrypi:~ $ sudo tar zxf greengrass-linux-armv7l-1.3.0.tar.gz -C /
pi@raspberrypi:~ $ 
pi@raspberrypi:~ $ ls -l /greengrass/
total 16
drwxr-xr-x 2 nobody 99 4096 Nov 21 08:09 certs
drwxr-xr-x 2 nobody 99 4096 Nov 21 08:09 config
drwxr-xr-x 5 nobody 99 4096 Nov 21 08:09 ggc
drwxr-xr-x 3 nobody 99 4096 Nov 21 08:09 ota

 Lambda の cgroup を自動的に設定するために、 /etc/fstab に下記設定を追加します。

cgroup /sys/fs/cgroup cgroup defaults 0 0

 追加後の fstab は下記のようになります。設定を追加したら一度再起動しておきます。

pi@raspberrypi:~ $ tail /etc/fstab 
proc            /proc           proc    defaults          0       0
/dev/mmcblk0p1  /boot           vfat    defaults          0       2
/dev/mmcblk0p2  /               ext4    defaults,noatime  0       1
# a swapfile is not a swap partition, no line here
#   use  dphys-swapfile swap[on|off]  for that
cgroup /sys/fs/cgroup cgroup defaults 0 0

 続いて証明書を配置します。先ほど展開したディレクトリ内の certs ディレクトリに、Versign からルートCA証明書をダウンロードして配置します。

pi@raspberrypi:/greengrass/certs $ pwd
/greengrass/certs
pi@raspberrypi:/greengrass/certs $ 
pi@raspberrypi:/greengrass/certs $ sudo wget http://www.symantec.com/content/en/us/enterprise/verisign/roots/VeriSign-Class%203-Public-Primary-Certification-Authority-G5.pem -O root-ca-cert.pem                                             
--2017-12-20 15:30:39--  http://www.symantec.com/content/en/us/enterprise/verisign/roots/VeriSign-Class%203-Public-Primary-Certification-Authority-G5.pem
Resolving www.symantec.com (www.symantec.com)... 72.247.61.29
Connecting to www.symantec.com (www.symantec.com)|72.247.61.29|:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 1758 (1.7K) [text/plain]
Saving to: ‘root-ca-cert.pem’

root-ca-cert.pem                                            100%[===========================================================================================================================================>]   1.72K  --.-KB/s   in 0s     

2017-12-20 15:30:39 (37.7 MB/s) - ‘root-ca-cert.pem’ saved [1758/1758]

 続けて、 Group と Core 作成時にダウンロードした証明書の圧縮ファイルに含まれる証明書とプライベートキーを同じディレクトリに配置します。

pi@raspberrypi:~ $ tar zxf dbb10e0817-setup.tar.gz 
pi@raspberrypi:~ $ ls certs/
dbb10e0817.cert.pem  dbb10e0817.private.key  dbb10e0817.public.key
pi@raspberrypi:~ $ 
pi@raspberrypi:~ $ sudo cp certs/dbb10e0817.private.key /greengrass/certs/.                                                                                                                                                                   
pi@raspberrypi:~ $ sudo cp certs/dbb10e0817.cert.pem /greengrass/certs/.

 次に、コンソールから Greengrass 用のサービスロールを作成します。 IAM のコンソールから ロールの作成 をクリックします。

f:id:akanuma-hiroaki:20171221085246p:plain

 サービスのリストから Greengrass を選択して 次のステップ をクリックします。

f:id:akanuma-hiroaki:20171221085325p:plain

 ポリシーの選択画面で AWSGreengrassResourceAccessRolePolicy を選択して 次のステップ をクリックします。

f:id:akanuma-hiroaki:20171221085446p:plain

 任意のロール名を入力して ロールの作成 をクリックします。

f:id:akanuma-hiroaki:20171221085631p:plain

 ロールが作成されたら ARN を記録しておきます。

f:id:akanuma-hiroaki:20171221085839p:plain

 続いて今作成したサービスロールをアカウントに紐づけます。 Mac 上から AWS CLI で下記のようにコマンドを実行します。

Greengrass  $ aws greengrass associate-service-role-to-account --role-arn arn:aws:iam::365361468908:role/Greengrass
2017-12-20T15:53:26Z

 最後に Raspberry Pi 上の Greengrass 設定ファイルを下記のように編集します。各証明書などへのパスはフルパスではなくファイル名だけでOKです。

pi@raspberrypi:/greengrass/ggc/packages/1.3.0 $ cat /greengrass/config/config.json 
{
    "coreThing": {
        "caPath": "root-ca-cert.pem",
        "certPath": "dbb10e0817.cert.pem",
        "keyPath": "dbb10e0817.private.key",
        "thingArn": "arn:aws:iot:ap-northeast-1:365361468908:thing/MyGroup_Core",
        "iotHost": "xxxxxxxxxxxxxx.iot.ap-northeast-1.amazonaws.com",
        "ggHost": "greengrass.iot.ap-northeast-1.amazonaws.com"
    },
    "runtime": {
        "cgroup": {
            "useSystemd": "yes"
        }
    },
    "managedRespawn": false
}

Greengrass Core の起動

 それでは Raspberry Pi 上の Greengrass Core を起動します。 Greengrass のソフトウェアパッケージのディレクトリ(今回は /greengrass/ggc/packages/1.3.0 )で下記のようにコマンドを実行します。

pi@raspberrypi:/greengrass/ggc/packages/1.3.0 $ sudo ./greengrassd start
Setting up greengrass daemon
Validating hardlink/softlink protection
Validating execution environment
Found cgroup subsystem: cpu
Found cgroup subsystem: cpuacct
Found cgroup subsystem: blkio
Found cgroup subsystem: memory
Found cgroup subsystem: devices
Found cgroup subsystem: freezer
Found cgroup subsystem: net_cls

Starting greengrass daemon
Greengrass successfully started with PID: 1917

 成功するとデーモンが起動します。

pi@raspberrypi:/greengrass/ggc/packages/1.3.0 $ ps aux | grep greengrass | grep -v grep
root      1304  0.7  1.4 940632 13900 pts/0    Sl   Dec20   0:38 /greengrass/ggc/packages/1.3.0/bin/daemon -core-dir=/greengrass/ggc/packages/1.3.0 -port=8000 -connectionManager=true -router=true -shadow=true -shadowSync=true -tes=true -deviceCertificateManager=true
ggc_user  1472  0.8  1.1  31308 10796 ?        Ssl  Dec20   0:45 python2.7 /runtime/python2.7/lambda_runtime.py --handler=greengrassHelloWorld.function_handler

Lambda Function の作成

 Greengrass Core デバイスで動作させるための Lambda Function を作成します。 Lambda のコンソールから 関数の作成 をクリックします。

f:id:akanuma-hiroaki:20171221090835p:plain

 今回はあらかじめ用意されているサンプルを使用しますので、「設計図」から「greengrass-hello-world」を選択して 設定 をクリックします。

f:id:akanuma-hiroaki:20171221090927p:plain

 任意の Function 名とロールを選択します。今回は以前作成していたロールを使用していますが、作成していなかった場合は新たに作成します。

f:id:akanuma-hiroaki:20171221091049p:plain

 Core デバイスで Lambda Function を実行するには新しいバージョンを発行しておく必要があるので、「アクション」メニューから 新しいバージョンを発行 をクリックします。

f:id:akanuma-hiroaki:20171221091156p:plain

 バージョンの説明を入力して 発行 をクリックします。

f:id:akanuma-hiroaki:20171221091339p:plain

Greengrass Group に Lambda Function を追加

 作成した Lambda Function を Greengrass Group に追加します。 Greengrass Group のコンソールの Lambda メニューから、 最初の Lambda を追加する をクリックします。

f:id:akanuma-hiroaki:20171221091712p:plain

 Lambda の追加方法の選択画面が表示されますので、 既存の Lambda の使用 を選択します。

f:id:akanuma-hiroaki:20171221092014p:plain

 Lambda Function の選択画面が表示されますので、先ほど作成した Lambda Function を選択して 次へ をクリックします。

f:id:akanuma-hiroaki:20171221092126p:plain

 先ほど発行したバージョンを選択して 完了 をクリックします。

f:id:akanuma-hiroaki:20171221092229p:plain

 Lambda Function が追加されたら、設定を変更するために 設定の編集 をクリックします。

f:id:akanuma-hiroaki:20171221092414p:plain

 設定項目の中の「Lambda のライフサイクル」の項目で「存続期間が長く無制限に稼働する関数にする」を選択して 更新 をクリックします。

f:id:akanuma-hiroaki:20171221092505p:plain

Greengrass Group にサブスクリプションを追加

 Greengrass Core が MQTT プロトコルでメッセージをやりとりするためのサブスクリプションを追加します。サブスクリプションは、メッセージの送信元であるソース、メッセージの送信先であるターゲット、それとトピックから構成されます。 Greengrass のサブスクリプションメニューから、 最初のサブスクリプションを追加 をクリックします。

f:id:akanuma-hiroaki:20171221232433p:plain

 ソースの選択で先ほど作成した Lambda Function を選択し、ターゲットの選択では IoT Cloud を選択して 次へ をクリックします。

f:id:akanuma-hiroaki:20171221232652p:plain

 今回は対象のトピックを hello/world に限定するため、「オプションのトピックフィルター」に hello/world を入力して 次へ をクリックします。

f:id:akanuma-hiroaki:20171221232849p:plain

 最後に 完了 をクリックしてサブスクリプションの追加は完了です。

f:id:akanuma-hiroaki:20171221233139p:plain

Greengrass Group のデプロイ

 ここまででクラウド上での Greengrass Group の設定は完了したので、これを Greengrass Core デバイスにコピーします。 Greengrass Group のデプロイのコンソールのアクションから デプロイ を選択します。

f:id:akanuma-hiroaki:20171221233546p:plain

 検出方法の設定画面になりますので、 自動検出 を選択します。

f:id:akanuma-hiroaki:20171221233654p:plain

 これまでの設定が正しく行われ、 Core デバイス上でデーモンが正しく稼働していれば、少し経つとデプロイが完了します。

f:id:akanuma-hiroaki:20171221233809p:plain

動作確認

 それでは Greengrass Core デバイス上で Lambda Function が正しく実行されているか確認します。AWS IoT コンソールから「テスト」を選択し、 hello/world トピックにサブスクライブします。

f:id:akanuma-hiroaki:20171221234017p:plain

 Lambda Function が正しく実行されていれば、下記のようにトピックにメッセージが発行されていきます。

f:id:akanuma-hiroaki:20171221234034p:plain

まとめ

 今回はチュートリアルの内容をそのまま追っただけになってしまいましたが、 Greengrass Core デバイス(今回は Raspberry Pi)上で Lambda Function を動かせることが確認できました。今回はここまでで長くなってしまいましたが、まだ Core デバイスに接続している他のデバイスとの連携は試せていないので、次回以降で試してみたいと思います。