Help us understand the problem. What is going on with this article?

【AWS】ポイントをおさえてAWS IoT Jobsを理解する #1/2【IoT】

はじめに

AWS IoT Jobsについての記事が少ないこと、あってもAWS IoTのREST APIを利用した検証でMQTTを利用した内容でなかったりと、情報が少ないので書きました。
分量が多くなったので2部構成にしています。

【AWS】ポイントをおさえてAWS IoT Jobsを理解する #1/2【IoT】 - Qiita ←いまココ
【AWS】ポイントをおさえてAWS IoT Jobsを理解する #2/2【IoT】 - Qiita

幾つかのIoT SDK(JavaScript用やPython用)では、AWS IoT Jobsを抽象的に扱える機能が実装されています。しかしながら中身を理解しないままブラックボックスとして扱うと、いざプロダクションレベルのIoTシステムを作るときには大きなリスクになります。
堅牢性の高いシステムを作るためには、結局のところ、如何にイレギュラーケースを想定して網羅し、これに備えた仕組みを実装できるか、ということに尽きます。
この意味においては、AWS IoT Jobsを抽象的に扱える機能を利用する/しない如何に関わらず、AWS IoT Jobsの原理的な仕組みを理解しておく必要があります。

本稿の方針

  • AWS IoT Jobsがカバーする領域を理解し、その特徴・制約を理解することを第一の目的とする。
  • そのため、シーケンス図と、授受されるデータを中心に説明する。
  • デバイス側はMQTTによる操作を行い、クラウド側はAWS Management Consoleで操作を行うことを想定する。
  • MQTT通信するコード・個別具体的なAPI仕様の記載は極力避け、最低限必要なものだけを記載する。
  • MQTTの基本的な概念(以下の項目)は理解していることを前提とする。
    • MQTT Broker
    • Connect
    • Subscribe
    • Publish
    • QoS0/1/2

TL;DR

  • AWS IoT Jobsの本質は、デバイス側とクラウド側で一定のルールに従ってJSONドキュメントを共有・更新すること、といえる。
  • MQTTでのJobs操作は少し面倒くさいが、通信の途絶・デバイスの長時間の停止を考慮した、よく考えられた仕組みであると言える。
  • QoS0(0回以上メッセージが送信される=メッセージがロストする可能性がある)だけで通信しても問題がない仕組みであると言える。

AWS IoT Jobsに基づく処理の大まかな流れ、雰囲気をつかむため、シーケンス図だけ貼っておきます。
詳細はそれぞれ後述します。
1-1.RetrieveJobsReactively.png
1-2.RetrieveJobsProactively.png
2.PerformAJob.png

用語の定義

用語 説明 備考
AWS IoT Jobs AWS IoTの機能の一つとして提供されるもの。遠隔地で稼働しているIoTデバイスに対してなんらか実行させたい処理を配信し、その実行状況を管理することができる。本稿では、「IoT Jobs」と表現することもある。 Jobs - AWS IoT
デバイス MQTTでAWS IoT MQTT Brokerと通信し、AWS IoT Jobsに関するデータをやり取りする。いわゆるIoTデバイスを想定。
クラウド AWS側全般を指す。
JobDocument デバイスに実行させたい内容を記述したJSONドキュメント。記載内容をもとにどのような処理を行うかについては、完全にユーザ側に任されている。

環境・事前準備

AWSアカウントは既に取得済みであるものとします。
本稿では以下のAWSリソースを使います。S3 Bucket/IAM Roleは作成済みであるものとします。

  • S3 Bucket
    • investigate-iot-jobs
  • IAM Role
    • Name: role-investigate-iot-jobs
    • Trust Relationship: 「iot.amazonaws.com」からAssumeRole出来るよう設定
    • Permissions: S3 BucketのファイルをGetObject出来る権限を付与

検証作業の中で、以下のThing/Jobを作成します。

  • IoTデバイス名(Thing Name)
    • device01
  • ジョブ名:
    • iot-job01
    • iot-job02

S3 Bucketを作成する

手順は省略します。AWS Management ConsoleやAWS CLIなどでS3 Bucketを作成しておきます。
Bucket Policyなどは特に設定しません。

IAM Roleを作成する

詳細は後述しますが、AWS IoTサービスがAssumeRoleする必要がありますので、この用途で用いるIAM Roleをあらかじめ作成しておきます。
デバイスで何らかファイルを取得する必要がある場合、配信対象となるファイルは通常S3 Bucketに格納しておきます。このIAM Roleの権限を利用して、各デバイスはS3上のファイルの取得します。

Permissionとしては、最低限、当該S3 Bucket上のファイルに対するGetObjectが必要となります。
Trust Relationshipsは「iot.amazonaws.com」を指定します。
具体的な設定は以下のようになります。

Permissions

最低限、下記のPolicyを設定します。今回はinline policyとして付与しました。

inlinepolicy
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": "s3:GetObject",
            "Resource": "arn:aws:s3:::investigate-iot-jobs/*"
        }
    ]
}

Trust Relationship

「iot.amazonaws.com」からAssumeRole出来るように、Trust Relationshipを設定します。

Trust Relationship
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "",
      "Effect": "Allow",
      "Principal": {
        "Service": "iot.amazonaws.com"
      },
      "Action": "sts:AssumeRole"
    }
  ]
}

Thingの作成

Thingをdevice01という名前で作成する

AWS CLIでThingを作成するには以下のように実行します。

### Thingを作成する
$ aws iot create-thing --thing-name device01
{
    "thingArn": "arn:aws:iot:ap-northeast-1:4**********8:thing/device01",
    "thingName": "device01",
    "thingId": "f2******-****-****-****-**********43"
}

### Thingの存在を確認する
$ aws iot list-things
{
    "things": [
        {
            "thingArn": "arn:aws:iot:ap-northeast-1:4**********8:thing/device01",
            "version": 1,
            "thingName": "device01",
            "attributes": {}
        }
    ]
}

AWS IoT Jobsとは

AWS IoT Jobsとは、遠隔地の無数のデバイスに対して何らかのオペレーションを送信し、実行させることを目的とした機能です。
ただし、AWS IoT Jobsはリモートデバイスで実行させる処理の内容そのものは定義しません。この機能は、既定のルールに従ってクラウド側とデバイス側でJSONドキュメントをやり取りすること、MQTTメッセージがロストしても最終的には必要な情報を確実に相互に更新・参照できる仕組みであること、が本質といえます。

デバイス上で具体的に何を実行するかについては、予め利用者が設計する必要があります。
授受されるJSONドキュメント(≒Job Document)の内容に従い、デバイス上での処理内容を制御する、という形になります。

重要な用語の定義は下記公式ドキュメントにまとめられています。

Jobs - AWS IoT
https://docs.aws.amazon.com/iot/latest/developerguide/iot-jobs.html

配信するファイルの準備

AWS IoT Jobsで必要となるファイルをあらかじめ作成し、S3 Bucketに格納しておきます。
詳細は後述します。

iot-job01/iot-job02用のJobDocumentを作成し、S3 Bucketに格納する

JobDocumentを「JobDocument-iot-job01.txt」及び「JobDocument-iot-job02.txt」というファイル名で作成します。中身は単純なJSONドキュメントです。このJSONドキュメントの構造・内容は、AWS利用者が自由に設定できます。
ここでは、「jobType」と「fileUrl」というオブジェクトを持つJSONドキュメントとします。「fileUrl」項目で配信ファイルのURLを指定することとなります。
それぞれ下記のようになります。

JobDocument-iot-job01.txt
{
    "jobType": "fetchFile",
    "fileUrl": "${aws:iot:s3-presigned-url:https://s3.amazonaws.com/investigate-iot-jobs/iot-job01-files/distfile.bin}"
}
JobDocument-iot-job02.txt
{
    "jobType": "fetchFile",
    "fileUrl": "${aws:iot:s3-presigned-url:https://s3.amazonaws.com/investigate-iot-jobs/iot-job02-files/distfile.bin}"
}

「${}」でくくられた文字列は特殊な値です。この値は、デバイスに渡される際にS3 Presigned URLが生成されて置き換えられます。Presigned URLとは一定時間のみ有効なURLのことで、生成からxx分間を超えてアクセスするとエラーとなります。
具体的な説明は下記ドキュメントを参照ください。

Managing Jobs - AWS IoT
https://docs.aws.amazon.com/iot/latest/developerguide/create-manage-jobs.html

Your job document can contain a presigned Amazon S3 URL that points to your code file (or other file). Presigned Amazon S3 URLs are valid for a limited amount of time and so are not generated until a device requests a job document. Because the presigned URL has not been created when you are creating the job document, you put a placeholder URL in your job document instead. A placeholder URL looks like the following: ${aws:iot:s3-presigned-url:https://s3.region.amazonaws.com/\/<code file>} where bucket is the Amazon S3 bucket that contains the code file and code file is the Amazon S3 key of the code file.

また、S3 Presinged URLについての仕様・詳細な仕組みについて下記記事にまとめましたので併せて参考にしてください。

【AWS IoT Jobs】有効期限付きのURLでファイルを配信する【S3 Presigned URL】
【AWS S3】S3 Presigned URLの仕組みを調べてみた

作成した「JobDocument-iot-job01.txt」及び「JobDocument-iot-job02.txt」ファイルをS3 Bucketにアップロードします。

配布用ファイルをアップロードする

配信用ファイルとして適当なバイナリファイルを作り、これをS3 Bucketに格納しておきます。JobDocumentの「fileUrl」に一致するファイル名でアップロードする必要があります。
本稿では10MBのファイルを作成してS3 Bucketに格納しています。

S3 Bucketに格納されたファイルの確認

最終的には、JobDocument・配信ファイルは下記のように格納されます。

iot-job02用のファイル
$ aws s3 ls --recursive s3://investigate-iot-jobs/
2019-04-14 17:13:30        150 JobDocument-iot-job01.txt
2019-04-14 17:13:30        150 JobDocument-iot-job02.txt
2019-04-14 16:20:21   10485760 iot-job01-files/distfile.bin
2019-04-14 16:20:44   10485760 iot-job02-files/distfile.bin

AWS IoT Jobsのフロー

Jobの配信から完了までの時間軸を考えた場合、一連の処理はまず大きく「Jobの取得」と「Jobの実行」という2つに分けられます。
「Jobの取得」処理はさらに、「能動的な取得」と「受動的な取得」に分けることができます。
整理すると、以下の3つのフローになります。

  • 1. Jobの取得:クラウド側で作成されたJobを、デバイスが認知する
    • 1-1. 受動的なJob取得:クラウド側でJobを作成したことをトリガーとし、その内容がデバイス側に通知される。
    • 1-2. 能動的なJob取得:デバイス側から、自分宛に割り当てられたJobを取得しにいく。
  • 2. Jobの実行:デバイス側で、認知したJobを実行し、完了させる。

1. Jobの取得

1-1. 受動的なJob取得

まずは、受動的にJobを取得するフローを見ていきます。
1-1.RetrieveJobsReactively.png

通常、デバイスの電源投入後、必要なプログラムが初期化・実行されていきますが、一般的には常駐してなんらか処理を行うプログラムが実行されます。
このプログラムの初期化処理の中で、MQTT Connectionの確立・TopicのSubscribeが行われることで、いつでもMQTTメッセージを受け取れる状態になります。
また必要に応じて、MQTTメッセージを任意のTopicにPublishする、ということも行います。

まず最初に、以下の2つのTopicをSubscribeします。「{{Thing Name}}」は、各デバイスのThing Nameが入りますので、実際に動作検証する際には適宜読み替えてください。

  • $aws/things/{{Thing Name}}/jobs/notify
  • $aws/things/{{Thing Name}}/jobs/notify-next

Management ConsoleなりAWS CLIなりでJobを追加すると、その情報が上記TopicにPublishされます。Topicの詳細については下記も併せてごらんください。

Devices and Jobs - AWS IoT -> Jobs Notifications
https://docs.aws.amazon.com/iot/latest/developerguide/jobs-devices.html

Jobを作成する際に必要な情報は以下の項目があります。(※データ定義1-1-1)

  • JobId: 当該Jobを識別するためのUniqueな名前。
  • Targets: Job実行対象のThingを指定する。Thing名/Thing Group名で指定が可能。
  • DocumentSource: JobDocumentが格納されているS3パス。
  • JobDocument: デバイスに実行させたい処理内容を記載したJSONドキュメント。JSONドキュメントでさえあれば、データ構造・内容は自由に設定できます。

詳細は下記ドキュメントを合わせて参照ください。

Creating and Managing Jobs (CLI) - AWS IoT -> Create Jobs
https://docs.aws.amazon.com/iot/latest/developerguide/manage-job-cli.html#create-job

MQTT Topic「\$aws/things/{{Thing Name}}/jobs/notify」および「\$aws/things/{{Thing Name}}/jobs/notify-next」をSubscribeした状態で、AWS Management Console等からJobを作成(「iot-job01」という名称で作成)すると、下記のようなメッセージが当該TopicにPublishされます。

「\$aws/things/{{Thing Name}}/jobs/notify」にはjobIdなどの情報しかPublishされませんが、「\$aws/things/{{Thing Name}}/jobs/notify-next」には次に実行するべきJobの詳細情報がPublishされます。
デバイスは基本的に「\$aws/things/{{Thing Name}}/jobs/notify-next」にPublishされた内容をもとに、必要な処理を実行していきます。

「\$aws/things/{{Thing Name}}/jobs/notify」には、対象デバイスで実行するべきJob一覧が、最大10個まで表示されます。StatusがIN_PROGRESS/QUEUEDのものが対象となり、Status・timestampの順にソートされています。

\$aws/things/device01/jobs/notify(※データ定義1-1-2)
{
  "timestamp": 1555262670,
  "jobs": {
    "QUEUED": [
      {
        "jobId": "iot-job01",
        "queuedAt": 1555262670,
        "lastUpdatedAt": 1555262670,
        "executionNumber": 1,
        "versionNumber": 1
      }
    ]
  }
}

下記JSONドキュメントで重要な情報は「execution.jobDocument」です。これは先ほど作成してS3に格納したJobDocumentoファイルと同等の内容になります。ただし、S3 Presinged URLを利用する場合は、下記のように実際のURLに置き換わります。

\$aws/things/device01/jobs/notify-next(※データ定義1-1-3)
{
  "timestamp": 1555262670,
  "execution": {
    "jobId": "iot-job01",
    "status": "QUEUED",
    "queuedAt": 1555262670,
    "lastUpdatedAt": 1555262670,
    "versionNumber": 1,
    "executionNumber": 1,
    "jobDocument": {
      "jobType": "fetchFile",
      "fileUrl": "https://investigate-iot-jobs.s3.ap-northeast-1.amazonaws.com/iot-job01-files/distfile.bin?X-Amz-Security-Token=AgoGb3JpZ2luEGUaDmFwLW5vcnRoZWFzdC0xIoACFZBtrnE5T6QTP7%2FElU9iW06MYGK%2FmRVNuOZWGSV8WjY63nnm37IYbREWtVNkqSZ7gMf%2BSIf7o9fFW4r6IIWA7YDdfpYUCSBJWSt8zcr2hQTlXwtMc4FJlZztw2D%2F%2F5XBK32%2FJH22LuH0Tleac6a1f6hdGqxPPh8S5pysWz8XOzG4Qsa9qtaet4rM8iPMVH3fXfttMvYPYSHn7hNUsJyWnAyIBXiF920wspUDA%2BxFdV%2B%2FoQNkzusn%2F36mOgsToee9pgko3YgrDVm%2B3pica2E5vArQN1aW5SLFSAyE%2BPjNOF6PKBVjfPVa8UehzUrQ7CiVmEwPYe5M2YAVFRgpp6MWoiqPAgjb%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F8BEAAaDDQ5NzMzOTA2MzAwOCIM28PN%2F0N3kY5OS2XdKuMBKnMV7KWUxHpw5CuLGWCgi9rce6A6%2Fyq3Y%2Fmiz6AfqWECjivGPzYiaN77u7u7z3kOKdQJJ7Jd8OQU%2B%2Fm9vF09JuVJe6II5IDvSorw%2FXUDafZj78bayCIApioHbjVDt8jrMgEeBiW%2Bxj8OcwDA1rYVhElA8qIdT6snzQRnKXAjhBt3nqNTQKU2N6PqLLiLRuZT51ENF1kfiY0FOLb6Hqyimmt1za2Pk9vrNo9poQeVCIAIZnKuhqzQzdp%2B1KRrYhTzhGEA3r%2FSvMiTDwcY%2FexRYWxIVOZGo418jLakBuUi6z%2B6PkMwztnN5QU%3D&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Date=20190414T172430Z&X-Amz-SignedHeaders=host&X-Amz-Expires=300&X-Amz-Credential=ASIAXHS5X3LQDCAJRFN4%2F20190414%2Fap-northeast-1%2Fs3%2Faws4_request&X-Amz-Signature=636cc1afc7156948b49d6c8eff86af4f4d055117d9a0a7dda9fc4e394a4b8218"
    }
  }
}

今はJobが一つだけ存在する状態ですが、さらに新たなJobを作成するとどうなるでしょうか。「iot-job02」を作成してみます。

「\$aws/things/device01/jobs/notify」には下記のようなJSONドキュメントがPublishされます。

\$aws/things/device01/jobs/notify(※データ定義1-1-2)
{
  "timestamp": 1555263139,
  "jobs": {
    "QUEUED": [
      {
        "jobId": "iot-job01",
        "queuedAt": 1555262670,
        "lastUpdatedAt": 1555262670,
        "executionNumber": 1,
        "versionNumber": 1
      },
      {
        "jobId": "iot-job02",
        "queuedAt": 1555263139,
        "lastUpdatedAt": 1555263139,
        "executionNumber": 1,
        "versionNumber": 1
      }
    ]
  }
}

上記Topicには、最大10個までのpending jobの情報がPublishされます。先ほどの公式ドキュメントの「Jobs Notifications」に記載の内容を引用します。

  • A ListNotification contains a list of no more than 10 pending job executions. The job executions in this list have status values of either IN_PROGRESS or QUEUED. They are sorted by status (IN_PROGRESS job executions before QUEUED job executions) and then by the times when they were queued.

「\$aws/things/device01/jobs/notify-next」には何もPublishされません。このTopicにメッセージがPublishされるタイミングは下記のとおりです。先ほどの公式ドキュメントの「Jobs Notifications」に記載の内容を引用します。

  • A NextNotification contains summary information about the one job execution that is next in the queue.

A NextNotification is published whenever the first job execution in the list changes.

  • A new job execution is added to the list as QUEUED, and it is the first one in the list.
  • The status of an existing job execution that was not the first one in the list changes from QUEUED to IN_PROGRESS and becomes the first one in the list. (This happens when there are no other IN_PROGRESS job executions in the list or when the job execution whose status changes from QUEUED to IN_PROGRESS was queued earlier than any other IN_PROGRESS job execution in the list.)
  • The status of the job execution that is first in the list changes to a terminal status and is removed from the list.

1-2. 能動的なJob取得

次に、デバイス側から能動的にJobを取得するフローを見ていきます。
1-2.RetrieveJobsProactively.png

通常、長時間のネットワーク切断・電源停止などの最中にJobが作成された場合、デバイスはこのタイミングではJobの情報を取得できません。そこでデバイスの起動時に、自分宛に登録されたJobがあるかどうか確認し、あればこれを実行するという処理が必要になります。
AWS IoT Jobsにはこの目的のために「DescribeJobExecution」というAPIが用意されています。

Using the AWS IoT Jobs APIs - AWS IoT -> DescribeJobExecution
https://docs.aws.amazon.com/iot/latest/developerguide/jobs-api.html#mqtt-describejobexecution

まず、下記2つのMQTT TopicをSubscribeします。「+」はワイルドカードを意味し、任意の文字列にマッチします。

  • $aws/things/{{Thing Name}}/jobs/+/get/accepted
  • $aws/things/{{Thing Name}}/jobs/+/get/rejected

次に、下記MQTT Topicに空のメッセージをPublishします。実際には任意の文字列を指定することができますが、内容は無視されます。
「jobId」の箇所には「$next」という特殊な文字列を指定することが可能です。これは、当該デバイスが次に実行するべきJob(statusがIN_PROGRESSかQUEUEDのもの)を意味します。

  • $aws/things/{{Thing Name}}/jobs/{{jobId}}/get

上記MQTT TopicにメッセージをPublishすると、「$aws/things/{{Thing Name}}/jobs/+/get/accepted」にJobの内容がPublishされます。

$aws/things/device01/jobs/+/get/accepted(※データ定義1-2-1)
{
  "timestamp": 1555263695,
  "execution": {
    "jobId": "iot-job01",
    "status": "QUEUED",
    "queuedAt": 1555262670,
    "lastUpdatedAt": 1555262670,
    "versionNumber": 1,
    "executionNumber": 1,
    "jobDocument": {
      "jobType": "fetchFile",
      "fileUrl": "https://investigate-iot-jobs.s3.ap-northeast-1.amazonaws.com/iot-job01-files/distfile.bin?X-Amz-Security-Token=AgoGb3JpZ2luEGUaDmFwLW5vcnRoZWFzdC0xIoACQqsC5krx0S6azrc4h%2BGxErc2Bn56FRw3Wv1HWRY3REaruyiGNHA7iW3zyc9eBmY1%2FyGNNzPJSxaSsnOrWtgS0hfRChjB9Kp7rPgi9pRAxd7ZeSspaXH1s5mTJnBSzzbChCUbpsX1x5WdMp97Gi5LcbYff8rM8JirZr6kXxhOuVLtXSSvPgtGnzjdpvP1ERcRtFCj2e%2FchTmOtUCSakYlT04Qeek%2BKuRdkstdQh9w25p6NtOgFWsUqbMlGHVKuW7xalXecx0XZ1bOZ1NRChv1vj6qHt%2FNPX4XEvhprK47I0t1PyjsAA%2Fl8UR31uhBffAjVFsmvrhVwtE24QD2zfZgViqPAgjb%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F8BEAAaDDQ5NzMzOTA2MzAwOCIMDgPv2Nu%2FenWUH1EOKuMBbpGvp77yjc7XjEwgybl9SSgDVzKhJIp9vuGsRp95ly9yMUJbpYSNI6DTpseq1W1LIeDs%2FACxlvLl%2B5COdHGurn0IHP42w4b6RL0UxtB1goo36225Jk9jLSdMfYGXVPvM%2F5DtT4BRlC81PNuzlwvBQmfdZavjZD1%2Fp6fkQnIbjD8PlB9MC17UPi%2FlZGUB2LhYl0o3XDSp8NmQ2qJyHwJXsV3BaCLWztmqj8JALGQlqqVVtvc%2FbCWlNZ1vM5czj%2Fb7Llbsr9N3lqTuuThTEnfgCKF67DoumQ7sQ4dmK2efktoVvVswz%2BHN5QU%3D&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Date=20190414T174135Z&X-Amz-SignedHeaders=host&X-Amz-Expires=300&X-Amz-Credential=ASIAXHS5X3LQCRWMEZ7L%2F20190414%2Fap-northeast-1%2Fs3%2Faws4_request&X-Amz-Signature=71f4b9575a89fba54315441b306f915c6231cdb5bf20f7f8c43752f5f9900edb"
    }
  }
}

2. Jobの実行

上記までのフローにより、デバイス側でJobの内容を取得することができました。デバイスはJobDocumentに記載の内容をもとに必要な処理を実行していきます。
分量が多くなったので、Job実行時のフローは下記記事にまとめます。

【AWS】ポイントをおさえてAWS IoT Jobsを理解する #2/2【IoT】 - Qiita

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away