Greengrassのローカルシャドウがお手軽に確認できるハックをご紹介します

Greengrassのコアデバイス上でローカルシャドウの中身をサクっと確認するためのハックをご紹介します
2019.08.06

CX事業本部の岩田です。

Greengrassのローカルシャドウを操作していたところ、シャドウの状態が意図通りに更新されずどハマりしてしまいました。 原因を切り分けようにもローカルシャドウの中身を確認するために、いちいちLambdaを実行するのが面倒で面倒で...

nsenterを使って、いわゆる「コンテナの中に入る」操作を行ってからGreengrass SDKを使用すればLambdaを使わなくても行けるか?と思ったのですが、普通にGreengrass SDKを使用すると動作に必要な環境変数が不足し正常に動作しませんでした。 Greengrass SDKを正常動作させるにはGreengrass Core経由でLambdaの中で利用する必要があるようです。

色々と調べた結果、ローカルシャドウを管理しているDBを直接参照する方法を発見したのでご紹介します。

Greengrassにおけるローカルシャドウの管理

Greengrass Coreのシャドウ同期に関連するログは/greengrass/ggc/var/log/system/GGShadowSyncManager.logに出力されます。このログファイルを眺めていたところ、以下のような出力を発見しました。

[2019-08-06T04:01:32.367Z][INFO]-Using /state/shadow/shadow.db file for sqlite3

ん?!SQLite?どうもローカルシャドウの管理にはSQLite3を利用しているようです。 ということは普通にsqlite3コマンドから中身を覗けるのでは...

早速やってみましょう

SQLiteの中身を覗いてみる

先ほど確認した通り、ローカルシャドウのDBは/greengrass/ggc/var/state/shadow/shadow.dbに作成されています。Greengrass Coreを稼働させているOSからsqlite3コマンドを使ってDBの中身を確認してみましょう。

DBに接続

まずは接続から...

sqlite3 /greengrass/ggc/var/state/shadow/shadow.db
SQLite version 3.7.17 2013-05-20 00:56:22
Enter ".help" for instructions
Enter SQL statements terminated with a ";"
sqlite>

テーブル一覧表示

テーブル一覧です

sqlite> .tables
doc   sync

docsync2つのテーブルを持っているようです

テーブルのスキーマ表示

docsync2つのテーブルのスキーマを確認してみます

まずはdocテーブルから

sqlite> .schema doc
CREATE TABLE doc
	(arn text not null, name text, state text);  
  • arn
  • name
  • state

3つの列で構成されています。名前から察するにこれがローカルシャドウのシャドウドキュメントを格納しているテーブルでしょう。

続いてsyncテーブル

sqlite> .schema sync
CREATE TABLE sync (arn text not null, local text, cloud text);
  • arn
  • local
  • cloud

3つの列で構成されています。こちらがローカルシャドウとAWS IoT上のシャドウの同期を管理するテーブルなのでしょう

テーブルの中身表示

テーブルの中身を表示してみます。まずはdocテーブルから

sqlite> .headers ON
sqlite> select * from doc;
arn|name|state
thing_hogehoge|thing_hogehoge|{"ggFlv":{"reported":{"hoge":{"version":14}}},"isDeleted":false,"metadata":{"reported":{"hoge":{"timestamp":1565069505}}},"state":{"reported":{"hoge":"fuga"}},"version":20}

state列をフォーマットするとこのような形になります

{
  "ggFlv": {
    "reported": {
      "hoge": {
        "version": 14
      }
    }
  },
  "isDeleted": false,
  "metadata": {
    "reported": {
      "hoge": {
        "timestamp": 1565069505
      }
    }
  },
  "state": {
    "reported": {
      "hoge": "fuga"
    }
  },
  "version": 20
}

続いてsyncテーブルです。

sqlite> select * from sync;
arn|local|cloud
thing_hogehoge|{"ggFlv":{"reported":{"hoge":{"version":14}}},"isDeleted":false,"metadata":{"reported":{"hoge":{"timestamp":1565069505}}},"state":{"reported":{"hoge":"fuga"}},"version":20}|{"metadata":{"reported":{"hoge":{"timestamp":1565069505}}},"state":{"reported":{"hoge":"fuga"}},"version":98}

カラムlocalの値をフォーマットするとこのような形になります

{
  "ggFlv": {
    "reported": {
      "hoge": {
        "version": 14
      }
    }
  },
  "isDeleted": false,
  "metadata": {
    "reported": {
      "hoge": {
        "timestamp": 1565069505
      }
    }
  },
  "state": {
    "reported": {
      "hoge": "fuga"
    }
  },
  "version": 20
}

先ほどのdocテーブルの中身と一致しています。 同様にcloudの値をフォーマットすると以下のようになります

{
  "metadata": {
    "reported": {
      "hoge": {
        "timestamp": 1565069505
      }
    }
  },
  "state": {
    "reported": {
      "hoge": "fuga"
    }
  },
  "version": 98
}

AWS IoT上でシャドウドキュメントを確認すると以下のような状態でした

syncテーブルのcloud列と同期が取れていることが分かります。

最後に

Greengrass Coreの裏側に少し詳しくなれました。 これでデバッグが捗りそうです。

誰かの参考になれば幸いです。