Skip to main content
Rapsberry Pi

冬休みの自由研究: EdgeX Foundry (2) 導入とデータの確認

EdgeX Foundry 関連エントリ

動かしてみる

EdgeX Foundry、動かしてみましょう。

公式の developer-scripts リポジトリ で、リリースごとに Docker Compose ファイルが提供されているので、動かすだけならとても簡単です。

現時点の最新は Fuji リリースです。ファイルの詳細はリポジトリの README.md に書かれていますが、簡単に試すのであれば、ひとまずはセキュリティ機能を省いた *-no-secty.yml を使うとよいでしょう。

が、今日時点の状態だと、エクスポートサービス層がコメントアウトされていたので、このエントリの内容をそのまま試したい場合は、ぼくのリポジトリに置いてあるヤツ を使ってください。

前提

Git と Docker、Docker Compose が動く環境を用意済みであるものとします。ぼくの家では Ubuntu 上の Docker ですが、もちろん Windows 上の Docker でも大丈夫です。

ファイルの用意と起動

流れを追えるように GitHub にもろもろ置いてあります。これをクローンして、イメージをプルします。

$ git clone https://github.com/kurokobo/edgex-lab-handson.git
$ cd lab01

$ docker-compose pull
Pulling volume            ... done
Pulling consul            ... done
Pulling config-seed       ... done
Pulling mongo             ... done
Pulling logging           ... done
Pulling system            ... done
Pulling notifications     ... done
Pulling metadata          ... done
Pulling data              ... done
Pulling command           ... done
Pulling scheduler         ... done
Pulling app-service-rules ... done
Pulling rulesengine       ... done
Pulling export-client     ... done
Pulling export-distro     ... done
Pulling device-virtual    ... done
Pulling ui                ... done
Pulling portainer         ... done

おもむろに起動させます。

$ docker-compose up -d
Creating network "lab01_edgex-network" with driver "bridge"
Creating network "lab01_default" with the default driver
Creating edgex-files ... done
Creating lab01_portainer_1 ... done
Creating edgex-core-consul ... done
Creating edgex-mongo       ... done
Creating edgex-config-seed ... done
Creating edgex-support-logging ... done
Creating edgex-core-metadata         ... done
Creating edgex-sys-mgmt-agent                 ... done
Creating edgex-support-notifications          ... done
Creating edgex-core-data             ... done
Creating edgex-support-scheduler              ... done
Creating edgex-core-command                   ... done
Creating edgex-app-service-configurable-rules ... done
Creating edgex-export-client                  ... done
Creating edgex-ui-go                          ... done
Creating edgex-device-virtual                 ... done
Creating edgex-export-distro                  ... done
Creating edgex-support-rulesengine            ... done

なお、Windows の場合、edgex-device-virtual がポート 49990(デフォルトのダイナミックポート範囲である 49152 以上)を使っている関係で、運が悪いと以下のようなエラーが出ることがあります。

$ docker-compose up -d
...
Starting edgex-device-virtual ... error

ERROR: for edgex-device-virtual  Cannot start service device-virtual: driver failed programming external connectivity on endpoint edgex-device-virtual (70409888653ac0ce83c1ee9f7df04feb181b35590fc06f0d6fd4e6b511e27cfc): Error starting userland proxy: listen tcp 0.0.0.0:49990: bind: An attempt was made to access a socket in a way forbidden by its access permissions.

ERROR: for device-virtual  Cannot start service device-virtual: driver failed programming external connectivity on endpoint edgex-device-virtual (70409888653ac0ce83c1ee9f7df04feb181b35590fc06f0d6fd4e6b511e27cfc): Error starting userland proxy: listen tcp 0.0.0.0:49990: bind: An attempt was made to access a socket in a way forbidden by its access permissions.
ERROR: Encountered errors while bringing up the project.

この場合、何らかのプロセスが予約したポートと被っている(netsh int ip show excludedportrange protocol=tcp で確認できる)ことが原因なので、いちど OS を再起動するとおそらく治ります。

docker-compose up -d がエラーなく完了したら、docker-compose psedgex-config-seed 以外の StatusUp になっていることを確認します。

$ docker-compose ps
                Name                              Command               State                                                           Ports
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
edgex-app-service-configurable-rules   /app-service-configurable  ...   Up       48095/tcp, 0.0.0.0:48100->48100/tcp
edgex-config-seed                      /edgex/cmd/config-seed/con ...   Exit 0
edgex-core-command                     /core-command --registry - ...   Up       0.0.0.0:48082->48082/tcp
edgex-core-consul                      docker-entrypoint.sh agent ...   Up       8300/tcp, 8301/tcp, 8301/udp, 8302/tcp, 8302/udp, 0.0.0.0:8400->8400/tcp, 0.0.0.0:8500->8500/tcp, 8600/tcp, 8600/udp
edgex-core-data                        /core-data --registry --pr ...   Up       0.0.0.0:48080->48080/tcp, 0.0.0.0:5563->5563/tcp
edgex-core-metadata                    /core-metadata --registry  ...   Up       0.0.0.0:48081->48081/tcp, 48082/tcp
edgex-device-virtual                   /device-virtual --profile= ...   Up       0.0.0.0:49990->49990/tcp
edgex-export-client                    /export-client --registry  ...   Up       0.0.0.0:48071->48071/tcp
edgex-export-distro                    /export-distro --registry  ...   Up       0.0.0.0:48070->48070/tcp, 0.0.0.0:5566->5566/tcp
edgex-files                            /bin/sh -c /usr/bin/tail - ...   Up
edgex-mongo                            /edgex-mongo/bin/edgex-mon ...   Up       0.0.0.0:27017->27017/tcp
edgex-support-logging                  /support-logging --registr ...   Up       0.0.0.0:48061->48061/tcp
edgex-support-notifications            /support-notifications --r ...   Up       0.0.0.0:48060->48060/tcp
edgex-support-rulesengine              /bin/sh -c java -jar -Djav ...   Up       0.0.0.0:48075->48075/tcp
edgex-support-scheduler                /support-scheduler --regis ...   Up       0.0.0.0:48085->48085/tcp
edgex-sys-mgmt-agent                   /sys-mgmt-agent --registry ...   Up       0.0.0.0:48090->48090/tcp
edgex-ui-go                            ./edgex-ui-server                Up       0.0.0.0:4000->4000/tcp
lab01_portainer_1                      /portainer -H unix:///var/ ...   Up       0.0.0.0:9000->9000/tcp

この段階でできているモノ

この段階で、最低限必要な一通りのモノはすでに稼働しています。

この段階でできているモノ

EdgeX Foundry で制御したいデバイスは、この時点では物理的には存在していませんが、テスト用の仮想デバイスが初期状態でいくつか用意されています。これが図中に赤枠で示したもので、

  • デバイス
    • ランダムな値を生成し続けるだけのデバイス
    • ひとつのデバイスが複数のリソースを持つ
      • ここでいうリソースは、デバイスとやり取りできる情報の種類のこと
      • 例えば、今回のデバイスのひとつ Random-Float-Device であれば、Float32Float64 という二種類のリソースを持っていて、別々に制御できる
      • これは具体的には、例えば『空調センサ』という一つのデバイスが『温度』と『湿度』の二つの情報を持っているような状態
    • 外部から、指定したリソースの現在のランダム値を読み取れる
    • 外部から、指定したリソースの数値の生成ロジックを設定できる
  • デバイスサービス
    • 前述のデバイス群(4 つ)を管理するインタフェイス
    • デバイスごとに一定の間隔で所定のリソースの現在の値を取得し、コアサービス層に蓄積する
    • コアサービス層からあるリソースへの GET コマンドを受け取ると、デバイスからそのリソースの現在の値を取得して返す
    • コアサービス層からあるリソースへの POST コマンドを受け取ると、デバイスのそのリソースの数値の生成ロジックを変更する
  • コアサービス
    • デバイスサービスから送られた値を蓄積する
    • (GUI や API を通じて依頼された)デバイスに対する GET や POST のコマンド実行をデバイスサービスに指示する

のように構成されています。

すなわち、最終的に行いたい、デバイスからのセンシングとその値の蓄積、デバイスに対するアクチュエーションが、物理デバイスではないものの、仮想的なデバイスとしてすでに動いていることになります。

実世界でいえば、デバイスは何らかの装置(スイッチとかセンサとかカメラとかいろいろ)であり、外部からはそのデバイスごとにさまざまな手順で情報を取得したり制御したりする必要があります。

デバイスサービスは、そうしたデバイスごとのプロトコルの差異を抽象化したインタフェイスとして振舞い、デバイス種別ごとに単一のコントロールポイントを提供するわけです。

操作手段

操作は GUI、CLI、REST API の三種類で行えます。

といっても、日常的に何らかの操作をすべきものではなく、最初の構築時点でデバイスの登録だったりエクスポートの設定だったりルールエンジンの構成だったりをしてしまったあとは、あとは粛々と運用し続けることになるでしょう。

現段階では、GUI がリッチではありませんが、そういう利用実態を考えればそういうものかもしれません。原則、すべての操作が REST API を介して行えるようになっているので、基本的には Postman などのツールを使って API を叩くものだと思っておいたほうがよさそうです。

GUI での操作

GUI は OSS の範囲では二種類あります。

  • edgex-ui-go
    • 標準の Web GUI
    • 4000 番ポートで待ち受け
  • edgex-ui-closure
    • 多少リッチになった GUI
    • でもところどころ動かない
    • 8080 番ポートで待ち受け
    • 商用の Edge Xpert の GUI はこれがベースの模様

edgex-ui-go が標準の Web GUI です。ブラウザで 4000 番ポートにアクセスして、デフォルトのユーザ admin(パスワードも admin)でログインできます。

ローカルホストで起動させている場合は、http://localhost:4000/ でアクセスできます。

標準 UI のログイン画面

この GUI を通じてもろもろを操作したい場合は、まずはこの GUI で操作する対象を登録する必要があります。[Gateway] の [Add] ボタンで、コンテナをホストしているマシンの IP アドレスを登録します。

登録後の状態

登録したゲートウェイを選択した状態で [Device Service] に遷移すると、先述の device-virtual が登録されている様子が見えるはずです。

device-virtual の登録状況

さらにデバイスサービス device-virtual の [Devices] アイコンを展開すると、このデバイスサービス経由で管理している、先の図で赤枠で示したデバイス群が確認できます。

device-virtual デバイスサービス経由で制御されるデバイス群

さらにデバイスごとの [Commands] を展開すると、デバイスからの情報の取得や、制御命令の実行ができるようになっています。

別の GUI が欲しい場合、edgex-ui-closure というモジュールも用意されています。標準の Docker Compose ファイルには含まれていませんが、今回の Docker Compose ファイルには以下を追記済みなので、起動できているはずです。

  clojure:
    image: edgexfoundry/docker-edgex-ui-clojure:1.1.0
    ports:
      - "8080:8080"
    container_name: edgex-ui-clojure
    hostname: edgex-ui-clojure
    networks:
      - edgex-network
    volumes:
      - db-data:/data/db
      - log-data:/edgex/logs
      - consul-config:/consul/config
      - consul-data:/consul/data
    depends_on:
      - data
      - command

ブラウザで 8080 番ポートにアクセスして、パスワード admin でログインできます。

Clousre UI のログイン画面

この GUI の [DEVICES] 画面でも、先述の device-virtual の配下のデバイスとして制御されている 4 つのデバイスが確認できます。

登録されているデバイス群

また、デバイスごとの右端のアイコン群から、現在値の確認やコマンドの実行が行えます。

CLI での操作

CLI として、現時点ではオフィシャルなリリースではありませんが、edgex-cli が存在しています。

Go 言語で書かれていて、自分で make しないといけないため、環境をつくる必要がありますが、一時的に動かすならコンテナの中で触れます。

以下、コンテナの中で動かす場合の手順です。golang:latest をそのまま使うとページャ(less とか)がなくて部分的にうまく動かなくなるので、途中で less を突っ込んでいます。

$ docker run --rm -it golang
Unable to find image 'golang:latest' locally
latest: Pulling from library/golang
...
Status: Downloaded newer image for golang:latest

# apt update && apt install -y less
Get:1 http://security.debian.org/debian-security buster/updates InRelease [65.4 kB]
...
Processing triggers for mime-support (3.62) ...

# git clone https://github.com/kurokobo/edgex-cli
Cloning into 'edgex-cli'...
...
Resolving deltas: 100% (402/402), done.

# cd edgex-cli

# make install
...
go: finding github.com/BurntSushi/toml v0.3.1

make install がエラーなく完了すれば、コマンドが使える状態です。

ただし、デフォルトだとホスト名 localhost に接続しにいくので、CLI 自体をコンテナ内で動作させている場合は接続できません。

コマンドを一度でも実行すると、接続先の情報が ~/.edgex-cli/config.yaml に保存されるので、コマンドを空打ちしてファイルを作らせたあとに、これを修正します。

# edgex-cli
 _____    _           __  __  _____                     _            
| ____|__| | __ _  ___\ \/ / |  ___|__  _   _ _ __   __| |_ __ _   _ 
|  _| / _` |/ _` |/ _ \\  /  | |_ / _ \| | | | '_ \ / _` | '__| | | |
| |__| (_| | (_| |  __//  \  |  _| (_) | |_| | | | | (_| | |  | |_| |
|_____\__,_|\__, |\___/_/\_\ |_|  \___/ \__,_|_| |_|\__,_|_|   \__, |
            |___/                                              |___/ 
...
Use "edgex [command] --help" for more information about a command.

# ls -la ~
...
drwxr-xr-x 2 root root 4096 Jan 19 04:01 .edgex-cli
...

# grep host ~/.edgex-cli/config.yaml
host: localhost

# sed -i 's/localhost/192.168.0.100/g' ~/.edgex-cli/config.yaml

# grep host ~/.edgex-cli/config.yaml
host: 192.168.0.100

これで準備が完了です。この実装はヘルプが親切なので雰囲気でだいたい触れますが、例えば GUI で確認したようなデバイスサービスやデバイス群は以下のように確認できます。

# edgex-cli deviceservice list
Service ID                              Service Name    Created Operating State 
f2dae296-0313-45e5-a13d-377fadc85276    device-virtual  4 hours ENABLED

# edgex-cli device list
Device ID                               Device Name                     Operating State Device Service  Device Profile  
c085d64c-0da7-4ce3-aa31-53fa2474c38d    Random-Boolean-Device           ENABLED         device-virtual  Random-Boolean-Device
187804e7-62b2-4a9d-95d5-0f723b763048    Random-Integer-Device           ENABLED         device-virtual  Random-Integer-Device
c81a829c-f5d7-4335-987c-0c483d8c7826    Random-UnsignedInteger-Device   ENABLED         device-virtual  Random-UnsignedInteger-Device
cfd1477e-0e84-4993-9e9f-ef89b8f07c92    Random-Float-Device             ENABLED         device-virtual  Random-Float-Device

API での操作

GUI も CLI も結局は REST API にリクエストを投げたレスポンスを整形して表示しているだけなので、これがいちばん生々しく触れる手段です。これでできない操作は基本的にはないはずなので、できなかったらあきらめましょう……。

ただの REST API なので、細かくは 公式のリファレンス を参照してもらえればわかると思います。

例えば、デバイスサービスやデバイスの一覧は以下のエンドポイントに GET を投げると JSON で返ってきます。

  • デバイスサービスの一覧
    • http://localhost:48081/api/v1/deviceservice
  • デバイスサービスの詳細
    • http://localhost:48081/api/v1/deviceservice/name/<デバイスサービス名>
      • デバイスサービス名は一覧で取得できた name の値
    • http://localhost:48081/api/v1/deviceservice/<デバイスサービス ID>
      • デバイスサービス ID は一覧で取得できた id の値
  • デバイスの一覧
    • http://localhost:48081/api/v1/device
  • デバイスの詳細
    • http://localhost:48081/api/v1/device/name/<デバイス名>
      • デバイス名は一覧で取得できた name の値
    • http://localhost:48081/api/v1/device/<デバイス ID>
      • デバイス ID は一覧で取得できた id の値

蓄積データの確認

先述の通り、末端の仮想デバイスで生成されたランダムな値は、デバイスサービス virtual-device を通じてコアサービス層に蓄積されています。実際に動いていることを確認するため、蓄積された値を覗きに行きます。

GUI でのデータの確認

いちばん簡単な手段は、先の Closure UI の [READINGS] 画面です。ここでは以下のように、各デバイスから取得したデータの履歴をサクッと確認できます。また、例えば Random-Float-Device を選択すると、Float32Float64 という二種類のリソースの値が別々に蓄積されていることも読み取れます。

取得した値の確認

蓄積データの構造と API での取得

もっとも詳細な情報が得られる手段は、もちろん API です。

デバイスサービスは、一つ以上の Reading を含む Event オブジェクトを POST することで、コアサービスにデータを送ります。

  • Reading
    • 単一のデバイスの単一のリソースから取得した値の情報
  • Event
    • あるデバイスサービスが一回の処理で単一のデバイスから取得した一つ以上の Reading の集合
    • 例えば『温度と湿度はセットで GET する』ような構成を組んだ場合は、ひとつの Event に二つの Reading が含まれる

Event を取得するエンドポイントに、URL にデバイス名や取得するイベント数を指定して GET すると、Reading を含む情報が得られます。今回の仮想デバイスでは、ひとつの Event にはひとつの Reading が含まれます。

$ curl -s http://localhost:48080/api/v1/event/device/Random-Float-Device/2 | jq
[
  {
    "id": "4949fc7f-069b-49b1-bc52-f905c5109692",
    "device": "Random-Float-Device",
    "created": 1579419158139,
    "modified": 1579419158139,
    "origin": 1579419158137800200,
    "readings": [
      {
        "id": "6dcac272-28ad-4755-972f-ad58b02aa000",
        "created": 1579419158138,
        "origin": 1579419158124917000,
        "modified": 1579419158138,
        "device": "Random-Float-Device",
        "name": "Float32",
        "value": "fvU+Nw=="
      }
    ]
  },
  {
    "id": "f4f62305-ce8c-4f19-960b-ba2977ab0b2b",
    "device": "Random-Float-Device",
    "created": 1579419157643,
    "modified": 1579419157643,
    "origin": 1579419157642273800,
    "readings": [
      {
        "id": "227aa868-6dbe-4e53-af27-c773075eac85",
        "created": 1579419157642,
        "origin": 1579419157630999000,
        "modified": 1579419157642,
        "device": "Random-Float-Device",
        "name": "Float64",
        "value": "1.258607e+308"
      }
    ]
  }
]

特定のリソースの情報に絞りたい場合は、Reading のエンドポイントにリソース名を指定して GET します。

$ curl -s http://localhost:48080/api/v1/reading/name/Float64/2 | jq
[
  {
    "id": "e1e950c0-7533-4664-a606-91c97e8dbb85",
    "created": 1579419397777,
    "origin": 1579419397752820200,
    "modified": 1579419397777,
    "device": "Random-Float-Device",
    "name": "Float64",
    "value": "8.073392e+305"
  },
  {
    "id": "2b30bdf3-cb3e-4bac-bd20-87653e9259f2",
    "created": 1579419367748,
    "origin": 1579419367735956200,
    "modified": 1579419367748,
    "device": "Random-Float-Device",
    "name": "Float64",
    "value": "-3.105079e+307"
  }
]

CLI でのデータの取得

先述の CLI でも、EventReading は取得できます。引数でデバイス名を与える必要があるので、edgex-cli device list でデバイス名を確認してから実行するとよいでしょう。また、-l で数を絞らないと死にます。

# edgex-cli device list                    
Device ID                               Device Name                     Operating State Device Service  Device Profile  

c085d64c-0da7-4ce3-aa31-53fa2474c38d    Random-Boolean-Device           ENABLED         device-virtual  Random-Boolean-Device
187804e7-62b2-4a9d-95d5-0f723b763048    Random-Integer-Device           ENABLED         device-virtual  Random-Integer-Device
c81a829c-f5d7-4335-987c-0c483d8c7826    Random-UnsignedInteger-Device   ENABLED         device-virtual  Random-UnsignedInteger-Device
cfd1477e-0e84-4993-9e9f-ef89b8f07c92    Random-Float-Device             ENABLED         device-virtual  Random-Float-Device

# edgex-cli event list Random-Float-Device -l 5
Event ID                                Device                  Origin                  Created         Modified        
eb8e4094-f090-4326-be23-98babe42485f    Random-Float-Device     1579437080034563100     7 seconds       7 seconds       
a71959a3-99a8-4cba-aecc-e2c4f90f83ce    Random-Float-Device     1579437079284713500     8 seconds       8 seconds        
8d5ca1d7-9e34-4a59-9140-7e07f1fcc00d    Random-Float-Device     1579437050012350100     37 seconds      37 seconds       
cd08a429-1b0d-4639-b62d-0d7e651219d8    Random-Float-Device     1579437049270313900     38 seconds      38 seconds       
04ab9a44-a6bc-409c-b328-6af41b9ab3f7    Random-Float-Device     1579437019994592700     About a minute  About a minute   

# edgex-cli reading list Random-Float-Device -l 5
Reading ID                              Name    Device                  Origin                  Value           Created 
        Modified        Pushed
9153fa3a-12ad-42fa-980f-867e3b5bd750    Float32 Random-Float-Device     1579437080017207500     /oyN8A==        23 seconds      23 seconds      50 years
ce144555-6652-4514-b28e-647e9fcc3dc9    Float64 Random-Float-Device     1579437079271713200     -7.071508e+307  24 seconds      24 seconds      50 years
91f8e6e8-74b7-435c-acf1-f3a40174a7b1    Float32 Random-Float-Device     1579437049996532600     /RPlrg==        53 seconds      53 seconds      50 years
be52e1ff-707a-4826-8013-6c4111db0263    Float64 Random-Float-Device     1579437049255317200     2.928376e+307   54 seconds      54 seconds      50 years
68fa0b12-c248-43ea-9dae-6497f6c343d1    Float32 Random-Float-Device     1579437019973019800     /eOKAw==        About a minute  About a minute  50 years

エンコードされた数値のデコード

API の場合も CLI の場合も、Float32 の値が /oyN8A== のように文字列で表示されていますが、これは内部的にはこの型の数値(を表すバイト列)を Base64 でエンコードして保持しているからです。

数値を保持する際のエンコード有無は、デバイスを追加するときに作成するプロファイルの中で指定できますが、今回は事前構成済みのデバイスを利用しているため、デフォルトの設定に従ってこのようになっています。

エンコードされた数値はデコードすると数値に戻せます。もっといいやり方がありそうな気はしますが、よくわからないので適当なデコーダを作りました。Go が動く環境(CLI での操作で使ったコンテナ内など)で利用できます。

# git clone https://github.com/kurokobo/edgex-decode-base64.git
# cd edgex-decode-base64/

# go run main.go /eOKAw==
8.163255e-37

生のデータベースの探索

ところで、API で生データが取得できると書きましたが、これらのデータは MongoDB に保管されています。MongoDB に直接アクセスすると、実際のデータを確認できます。

外部から MongoDB Compass などでアクセスする場合は、ポート 27017 にユーザ名 core、パスワード password で認証できますが、ここではコンテナのシェルに入って確認します。

edgex-mongo が、MongoDB の実体のコンテナです。これのシェルを取って、MongoDB のプロンプトに入ります。複数のデータベースが存在していることがわかります。

$ docker exec -it edgex-mongo bash

# mongo
MongoDB shell version v4.2.0
...

> show databases
admin                0.000GB
application-service  0.000GB
config               0.000GB
coredata             0.011GB
exportclient         0.000GB
local                0.000GB
logging              0.006GB
metadata             0.000GB
notifications        0.000GB
scheduler            0.000GB

EventReading の値は、coredata 内のコレクションに保持されています。

> use coredata
switched to db coredata

> show collections
event
reading
valueDescriptor

あとは MongoDB のお作法に従って find() などを叩くのみです。遊んだら exit で抜けます。

> db.reading.find().limit(5)
{ "_id" : ObjectId("5e2471bb0e360800014be635"), "created" : NumberLong("1579446715833"), "modified" : NumberLong("1579446715833"), "origin" : NumberLong("1579446715806322700"), "uuid" : "e0af3c99-be57-4cec-a59a-2af2da976d41", "pushed" : NumberLong(0), "device" : "Random-Boolean-Device", "name" : "Bool", "value" : "true" }
{ "_id" : ObjectId("5e2471bb0e360800014be637"), "created" : NumberLong("1579446715857"), "modified" : NumberLong("1579446715857"), "origin" : NumberLong("1579446715831613700"), "uuid" : "c336b11e-8e91-4007-bd3f-1ee63d80e45b", "pushed" : NumberLong(0), "device" : "Random-Boolean-Device", "name" : "Bool", "value" : "false" }
{ "_id" : ObjectId("5e2471c00e360800014be639"), "created" : NumberLong("1579446720829"), "modified" : NumberLong("1579446720829"), "origin" : NumberLong("1579446720815333700"), "uuid" : "2ae4befc-1fee-48ab-b621-b4d56747a446", "pushed" : NumberLong(0), "device" : "Random-Integer-Device", "name" : "Int16", "value" : "9761" }
{ "_id" : ObjectId("5e2471c00e360800014be63b"), "created" : NumberLong("1579446720843"), "modified" : NumberLong("1579446720843"), "origin" : NumberLong("1579446720830168500"), "uuid" : "317ea1f1-0c36-4e6d-a489-b48711ff5dd6", "pushed" : NumberLong(0), "device" : "Random-Integer-Device", "name" : "Int64", "value" : "-9069744988884413669" }
{ "_id" : ObjectId("5e2471c00e360800014be63d"), "created" : NumberLong("1579446720857"), "modified" : NumberLong("1579446720857"), "origin" : NumberLong("1579446720841898700"), "uuid" : "5be3634e-b171-4849-9bac-f13de720c11e", "pushed" : NumberLong(0), "device" : "Random-Integer-Device", "name" : "Int32", "value" : "-913901160" }

> exit
# exit

環境の停止

ひとしきり遊んだら、クリーンアップします。方法がいくつかあります。

  • また起動させたいから、コンテナの停止だけしたいとき
    • docker-compose stop
  • データボリュームは残してコンテナの停止と削除だけしたいとき
    • docker-compose down
  • コンテナの削除だけでなく、ボリュームも Pull したイメージも何もかもを消し去りたいとき
    • docker-compose down --rmi all --volumes

以下は完全消去の例です。

$ docker-compose down --rmi all --volumes
Stopping edgex-device-virtual                 ... done
...
Removing image portainer/portainer

まとめ

EdgeX Foundry を起動させて、仮想のデバイスを利用したデータの取得や蓄積っぷりが、GUI や CLI、API などで確認できました。

次のエントリでは、エクスポート部分を触ります。

EdgeX Foundry 関連エントリ


Rapsberry Pi

冬休みの自由研究: EdgeX Foundry (1) 概要

EdgeX Foundry 関連エントリ

きっかけ

本業が IT やさんではあるもの、IoT とその周辺とは疎遠な日々を過ごしていました。

そんなある日、あるイベントで EdgeX Foundry の存在を教えてもらい、手元に Raspberry Pi もあるものだから、いろいろ調べて実際に触ってみたわけです。IoT 関連のトレンドはまったく追えている気がしていなかったので、良い機会だしお勉強しましょうということで。

その結果、最新の Fuji リリースがすでに家で動いているのですが、技術的な諸々は長くなったので別のエントリにします。今回は概要編的なヤツです。

EdgeX Foundry とは

公式サイト によれば、

The World’s First Plug and Play Ecosystem-Enabled Open Platform for the IoT Edge.

A highly flexible and scalable open software framework that facilitates interoperability between devices and applications at the IoT Edge, along with a consistent foundation for security and manageability regardless of use case.  

https://www.edgexfoundry.org/

とされています。

ひとまずは、

  • オープンソースで
  • IoT のエッジコンピューティングを実現する
  • フレームワークである

と思って向き合えば、大外れではないでしょう。

これは Linux Foundation が立ち上げた LF Edge のプロジェクトのひとつで、LF Edge には 現時点で 80 社以上 がメンバとして掲載されています。

つまり、素性のわからない謎の OSS ということではまったくなく、 実際のコントリビュータとしても Dell Technologies や Intel などの面々が精力的に活動しています。後ろ盾があるのは安心感がありますね。

家のお手軽空調センサ群。
Raspberry Pi 経由で EdgeX Foundry に値を集約している。

EdgeX Foundry エッジコンピューティング

クラウド、というキーワードが盛り上がり始めたころは、『とにかく全部クラウドに持っていけ! 何でもクラウドに投げこめ!』みたいな風潮も少なからずあったと思います。

が、その後、ハイブリッドクラウドやマルチクラウドなど諸々の議論を経て、最近は、いやいや、何をするにもやっぱり使い分けだよね、という共通認識が形成されつつあり、どちらかといえばふたたび分散型に(前向きに)回帰しています。

IoT の文脈では、処理すべきデータが生まれる場所、あるいは操作すべきデバイスが存在する場所は、少なくともクラウドの中ではないわけで、つまり工場やら家やらオフィスやら屋外やら、目の前にある現実の身近などこかであるわけです。

であれば、

  • 何万もあるセンサが毎秒クラウドに生データを送り続けるのも非効率だ
  • クラウド上で分析にかけるにしても、大量の生データである必要はなく、分析に適した形式にフィルタ(または変換)されたデータだけあればよい
  • クラウドに送って、クラウドで処理して、クラウドから命令が来るとなると、端的に遅延がありすぎてリアルタイムに制御できない
  • 機微データなのでそのままはクラウドに送りたくない

など、コストやら応答速度やらセキュリティやらの諸々で、

  • だったらエッジ側に閉じて処理してしまったほうがよくない?
  • 最終的にクラウドに集めるにしても、必要なデータを必要な形にエッジ側で加工してから送ればよくない?

ということになり、ここからいわゆる『エッジコンピューティング』の概念が導出されてきます。5G も最近話題ですが、これもエッジコンピューティングの文脈でもよく出てきますね。

Rapsberry Pi
Raspberry Pi。
センサの値を MQTT トピックに定期的に投げ込んでいる。

とはいえ、こうしたエッジコンピューティングを実現しようとしても、いろいろなデバイスがいろいろなプロトコルを使っていることもあり、 特に産業分野での IoT の文脈では、いわゆる鉄板のアーキテクチャ的なモノが成熟しておらず、デバイスと上位の分析基盤やアプリケーションとの円滑な連携にはまだまだ課題もあるようです。

EdgeX Foundry は、こうした IoT の世界におけるエッジコンピューティングのためのプラットフォームとしてフレームワークを提供するものであり、具体的には、デバイス群の制御やそれらからのデータの収集、加工、分析、外部への送出など、必要な一連の機能それ自体とそのデザイン手段を一元的に提供するものといえる、のではないでしょうか。

このフレームワークを介することで、データへのアクセスやコントロールの手段が抽象化されるため、デバイスと分析基盤やアプリケーションの相互の運用性が高まる、のだと思います。

EdgeX Foundry のアーキテクチャ

で、実装面をもう少し調べていくわけですが、公式のドキュメント がけっこう充実しているので、これを紐解くのがよさそうです。

見ていくと、EdgeX Foundry が多数のマイクロサービスの集合体として実装されていることがわかります。 以下、アーキテクチャの画像を引用します。

EdgeX Foundry Architecture
EdgeX Foundry Architecture
https://docs.edgexfoundry.org/2.0/general/EdgeX_architecture.png

データフローにかかわる部分だと、全体で大きく 4 つのレイヤで構成されています。レイヤごとに役割をざっくり押さえておくとよさそうです。

触ってみた感触も踏まえて、大まかにぼくなりの書き方をすると、実デバイスに近い(サウスサイド)側から、

  • デバイスサービス層
    • 実デバイスからデータを受け取って、コアサービス層に送る
    • コアサービス層から命令を受け取って、実デバイスを操作する
  • コアサービス層
    • デバイスサービス層からのデータを蓄積する
    • サポートサービス層(のルールエンジン)から命令を受け取って、デバイスサービス層にコマンドを発行する
    • エクスポートサービス層にデータを送る
    • EdgeX Foundry 内のサービス群やオブジェクトのメタデータを管理する
  • サポートサービス層
    • いわゆる分析機能(今のところは簡単なルールエンジンのみ)を提供する
    • アラートやロギングの機能を提供する
    • エクスポート済みのデータの削除を行う
  • エクスポートサービス層
    • EdgeX Foundry の外にデータを送出する

というところでしょうか。

実装は Go 言語が主のようです。動作させるためのハードウェア要件も厳しくなく、Raspberry Pi 上でも動かせるみたいですね。また、起動させるだけなら Docker Compose でさくっとできます。

家では、vSphere 上の Ubuntu 仮想マシン上に Docker Compose で動作させています。

EdgeX Foundry でできそうなこと

さて、で、つまり何ができるの、というところですが、ざっくり技術的な目線では、

  • 様々なデバイスからの情報取得やそれらの操作が EdgeX Foundry に集約できる
    • デバイスサービスが実デバイスに対するインタフェイスとして機能する
    • デバイスごとの機種やプロトコルの差異をデバイスサービスが吸収するので、実デバイスの実装を意識しないで統一された手法(REST API や GUI)でデバイスにアクセスできる
    • デバイスサービスはさまざまなプロトコル(REST、Modbus、MQTT、ZigBee、……)に対応しており、参考実装が GitHub にある
    • SDK が提供され、比較的容易に自製できる
    • デバイスサービスは製品への組み込みも視野に入っている
  • ルールエンジンにより、イベントドリブンなデバイスの制御ロジックを EdgeX Foundry で完結する形で組み込める
    • あるデバイスのセンサの値が閾値を越えたら別のデバイスのスイッチを入れる、など
    • EdgeX Foundry 内の通信も REST API と ZeroMQ なので、他の分析基盤やアプリケーションとも連携させられそう
  • 集約したデータは外部へ送り出せる
    • MQTT での発行や REST エンドポイントへの POST などができる
    • いわゆるストア&フォワード、常時接続でない環境でいったん自分の中に貯めておいて接続できたらまとめて送る、ができる模様
  • マイクロサービス化されており、フォグコンピューティング的な階層化やサービス単位での機能追加、入れ替えにも柔軟に対応できる
    • どこに何をおいてもよい
    • エッジに全部おいてもいいし、クラウドに全部おいてもいい
    • 例えば、デバイスサービスは Raspberry Pi、コアサービスは普通のサーバに置いて、分析系はつよいマシンに置くとか
    • エコシステムが成熟するとアドオンできるサービスも増えそうだ

などは挙げられそうです。

関連しそうなプロジェクト

EdgeX Foundry は OSS ですが、これを商用製品としたのが IOTech の Edge Xpert だそうです。

このドキュメントは追い切れていませんが、少なくともデバイスサービスとエクスポートサービスのバリエーションは、OSS 版に加えてバンドルされたテンプレートがたくさんありそうでした。便利そうです。

また、IOTech は組み込み系などリソースや時間の制約に厳しい環境のための軽量・高速版ともいえる Edge XRT も発表しています。

家の加湿器。
EdgeX Foundry から AWS などいろいろ経由して制御される。

ほか、競合する製品というと、そもそも知っている範囲が狭いのですが、オープンソース系だと KubeEdge、パブリッククラウド系だと Google Cloud IoT や Azure IoT Edge、AWS IoT Greengrass あたりになるでしょうか。

とはいえ、パブリッククラウド系の IoT サービスだと、どうしても中心はクラウド側で、最終的にはクラウドありきになってくるので、言い方が『クラウドで行う処理をエッジにオフロードする』というニュアンスになってきますね。トップダウン的な。

EdgeX Foundry は、その文脈でいうとボトムアップ的なアプローチっぽさがあります。この見方では、クラウドは必ずしも登場はしません。

動かしてみる

冒頭で書いたとおり、最新リリースの Fuji がいま家で動いています。

せっかくなので、導入部分、デバイスから値を貯める部分、デバイスを動かす部分、エクスポートする部分、ルールエンジンを使う部分、あたりに分けて、今後エントリを書いてみようかという気持ちでいます。先走ってタイトルに (1) って付けてしまった……。

いろいろリンク

EdgeX Foundry 関連エントリ