【新機能】AWS IoT Device Management の「ソフトウェアパッケージカタログ」を使ってデバイスアプリケーションのバージョン管理を効率化する

AWS IoT の新機能「ソフトウェアパッケージカタログ」で IoT アプリケーションのバージョン管理が簡単にできるようになりました!
2023.07.07

6月の初旬、AWS IoT Management に Software Package Catalog という新機能がリリースされました。

従来も(名前付き)デバイスシャドウにパッケージバージョンの情報を持たせることでアプリのバージョンを管理できましたが、ジョブとの連携やシャドウの更新といった管理作業はユーザー側で実装する必要がありました。

今回リリースされた Software Package Catalog では、デバイスシャドウや IoT ジョブと自動的に連携するので、これまでよりも効率的にデバイスアプリケーションのバージョンを管理できるようになりました。

具体的にどんな機能で、どのように使うのか試してみたので紹介します。

ソフトウェアパッケージカタログの作成

最初に該当アプリケーションを管理する単位である「パッケージ」を作成します。 01-iot-console

ソフトウェアパッケージの画面が開いたら Create package をクリックします。

02-create-package

パッケージ管理の依存関係

初めてマネジメントコンソールからパッケージを作成する際は、次のような 「パッケージ管理の依存関係を有効にする(Enable dependencies for package management)」 というウィンドウが開きます。

ここでは、2つのチェックボックス(オプション)があります。画像では1つだけにチェックが入っていますが、基本的には2つともチェックを入れておくのが良いかと思います。

03-enable-fleet-indexing-2

下記に記載した2つのオプションの意味を確認して、有効にするかどうか検討してください。

  • Fleet Indexing
    • Add device software packages and version
      • ソフトウェアパッケージとバージョンをデータソースとして、フリート インデックス作成に追加するオプションです。
      • 対象のソフトウェアのパッケージバージョンに基づいて、デバイスのインデックス付けが行われます。
      • インデックスが作成されると、ソフトウェアパッケージカタログ専用の(予約された)名前付きシャドウによりデータを検索したり集計することができます。
  • Auto update shadows from jobs
    • Auto update shadows from jobs
      • ジョブが正常に完了したときに、ジョブが「モノ」の「予約された名前付きシャドウ」を更新できるようにします。
      • ジョブを用いてデバイス上のパッケージバージョンをデプロイ・更新した際に、専用の「名前付きデバイスシャドウ」を更新します。
      • Create role で専用の IAM Role を作成し、その IAM Role を用いてジョブがデバイスのシャドウを更新します。

なお、ここで出てくる「専用の名前付きデバイスシャドウ」とは、今回リリースされたソフトウェアパッケージカタログ専用のもので、各デバイス(モノ)に対してソフトウェアのバージョンを管理します。

後から「パッケージ管理の依存関係」を変更する場合

先程のウィンドウは、初めて「パッケージ」を作成する時に出るもので、2回目以降は現れません。後で設定を変えたいときは、マネジメントコンソールの settings から「設定」画面を開いて変更できます。

フリートインデックスの設定は、 「Fleet indexing」 欄にある Manage indexing をクリックします。

271-manage-indexing

インデックスの管理画面で、次の2つの設定にチェックを入れて有効化します。

  • Add named shadows
    • 名前付きシャドウをインデックスの対象にします。
  • Add device software package and versions
    • ソフトウェアパッケージカタログのパッケージとバージョンをインデックスの対象にします。

272-add-namaed-shadow-and-add-package-versions

「ジョブによるデバイスシャドウの自動アップデート」 については 「設定」画面の「Auto update device shadows from jobs」 欄にある Settings を開きます。

270-iam-update-shadow-from-jobs

設定画面にて、下記の2つにチェックを入れます。ジョブがデバイスシャドウを更新する IAM Role を新規に作成するときは、Create role ボタンから作成します。

273-auto-update-shadows-from-job-settings

詳細は下記のドキュメントに記載されています。

300-document-update-shadow-from-jobs

パッケージ名と属性の設定

「パッケージ管理の依存関係」の設定が終わったら、次の画面でパッケージ名を付けます。ここでは TestPackage としました。説明欄は記入してませんが、必要に応じて記載してください。

101-create-new-package-2

次に最初のバージョンをセットします。セマンティックバージョン形式が推奨されています。今回は 1.0.0 としました。
(先程と同様に説明欄は記入してませんが、必要に応じて記載してください。)

102-version-attribute-2

同じ画面の下にある Version attribute は属性の設定です。
ソフトウェアパッケージのバージョンには、属性を付与することができます。ここでは、OS という属性に対して Linux という値をセットしました。
(本記事では、ここで設定した属性について以後使わないので、設定はスキップしても構いません)

04-add-attributes

Add attribute ボタンを押すことで属性一覧に反映されます。

103-set-attribute-and-create-package

最後に Create package をクリックして完了です。

06-create-packege

TestPackage というパッケージが登録できました。

104-list-package

デバイスとパッケージの関連付けと検索

パッケージができたので、まずは手動でデバイス(モノ)とパッケージを関連付けて、フリートインデックスで検索できるところまで確認してみます。

デバイスとパッケージの関連付け

先程作成したパッケージ TestPackage のリンクをクリックするとパッケージの詳細画面が開きます。

105-open-package

画面下側にある 「Versions」 に初期設定で指定したバージョン「1.0.0」があるので、クリックします。

106-click-version

バージョンは作成した直後は、Draft という状態になっています。これはソフトウェアが 「準備中」だったり「デプロイできる前の状態」 ということを示すステータスです。

デバイスにデプロイできる状態は Publish というステータスと規定されています。デバイスと関連付けるので Publish に状態を変えておきましょう。

107-publish-version

パブリッシュするバージョンの確認画面が出るので、該当バージョンを入力して Publish をクリックします。

108-publish

1.0.0 がパブリッシュ状態になりました。

109-status-published

なお、Draft のままでも関連付けできますが、一般的な運用を考えると Publish にしておいた方が実際の運用と整合性が取れるかと思います。

各種ステータスについては下記ドキュメントが参考になります。

次に、バージョン「1.0.0」のパッケージをデバイス(モノ)に関連付けます。先程と同じように該当バージョンをクリックします。

110-click-version

バージョン「1.0.0」の詳細画面で、Add things to this version をクリックします。
(ここでは、まだデバイスが何も関連づいていないので一覧には何も表示されていません)

111-add-things

関連付けたいデバイス(モノ)をプルダウンから選択すると、画面下部の一覧に登録されます。ここでは packageTestThing というモノを選択しました。(複数選択可能です)

モノを指定できたら、Add thing to version をクリックします。

112-add-things

関連付けができた直後に、画面常備に下記のようなメッセージが表示されます。これは指定のパッケージバージョンとデバイス(モノ)の関連付けが、フリートインデックスにインデックス中であることを示したものです。
メッセージにあるように10秒程度で完了します。

113-generate-fleet-index

インデックスに登録が完了すると、先程の「関連づいたデバイス一覧」に該当デバイスが現れます。

114-list-device

パッケージの詳細画面にも、バージョン 1.0.0 とそのデバイス数が円グラフおよび、バージョン一覧に表示(1 thing)されるようになりました。

115-discovery-package-version

同じ作業を繰り返して、バージョン 1.0.01.0.11.2.0 と3バージョン登録してみました。

116-three-versions

さらに他のデバイス(モノ)も追加して、合計4つのデバイスを各バージョンに登録してみたのが以下の画面です。各バージョンとそのバージョンに関連づいたデバイス数が、グラフと一覧に現れています。

117-four-devices

フリートインデックスによるデバイスの検索

パッケージバージョンとデバイス(モノ)の関連付けが完了したので、フリートインデックスを使ってデバイスを検索してみます。

マネジメントコンソールで ManageAll devicesThings と画面を開いて、Advanced search をクリックします。

122-1-advanced-search

今回は、ソフトウェアパッケージ TestPackage のバージョン 1.2.0 が紐づいているデバイス(モノ)を検索してみます。

次のクエリを検索バーに入力します。入力したら Enter でクエリを確定させます。複数クエリを入力して、AND 検索や、OR 検索なども可能です。

122-2-query-devices

検索を実行すると該当する2つのデバイス(モノ)が抽出できました。

123-searched-two-devices

このクエリは保存して後から使い回すこともできます。検索時に Save query をクリックすると保存できます。

124-save-query

保存したクエリを使うには Use saved query をクリックします。

125-use-seved-query

すると、保存済みのクエリ一覧が表示されるので、使いたいものを選択するだけでOKです。

126-select-queey

なお、予約された名前付きシャドウに対する検索には範囲や <,>, = といった比較演算子などは使えません。使うと次のようなエラーメッセージが表示されます。

129-error-comparison-query

他のクエリパターンなどクエリに関するドキュメントは以下になります。

IoT ジョブを使ったパッケージ バージョンのデプロイ

次は、IoT ジョブを使ってパッケージをデバイスにデプロイした時に、ソフトウェアパッケージカタログの情報も自動的に追随して更新できることを確認してみます。

モノの動的グループの作成

最初にモノの動的グループを作成します。動的グループを使うと、特定の属性や今回のようなパッケージバージョンの情報を元に、AWS IoT Core のデバイスレジストリに登録されているモノを動的グルーピングすることができます。

例えば、「ソフトウェアのバージョンが 1.0という条件の動的グループを作ると、その属性を持つモノが抽出されてグルーピングされます。モノのバージョンが 2.0 になるなど、条件にマッチしないモノはグループから外れます。逆に 0.1 というバージョンを持つモノのバージョンが 1.0 になると条件に合致するので、新たにグループに追加されます。

今回は次のように、「パッケージ TestPackage のバージョンが 1.0.X であるモノのグループ 「TestPackageV1Group」 を作成しました。

該当するモノとして、packageTestThingpackageTestThing2 の2つが含まれている状態になっています。

150-create-dynamic-group-sample

ジョブによるデプロイ

先程作成した動的グループに所属している2つのモノの TestPackage のバージョンを、 ジョブを使って 1.0.X1.0.01.0.1) から両方ともに 1.2.0 のバージョンに更新してみます。

ジョブはソフトウェアパッケージの画面から作成することができるので、作成済みのパッケージである TestPackage の画面を開きます。

151-open-sw-package

バージョン 1.2.0 をデプロイするので、該当バージョンを選択して Deploy job version をクリックします。

152-select-software-package-version

確認ウィンドウが出る場合があるので、Acknowledge をクリックして進めます。

153-acknowledge-deploy

ジョブのプロパティを設定する画面では、ジョブ名と説明を記載します。ジョブ名はデフォルトでは、Deploy_ランダムな数字 となっています。今回はそのまま Deploy_1688534555174 を使うことにしました。

154-deploy-job-next

ジョブを作成する前にジョブの発行状態を確認したかったので、MQTTクライアント(MQTT X)で AWS IoT Core と接続して以下のトピックを Subscribe しておきます。(MQTT クライアントの種類はなんでも構いません。)
3つ目のトピックにある「ジョブID」の部分はジョブ作成時に指定したジョブ名になるので、適宜ご利用の環境に応じて変更してください。

  • $aws/things/packageTestThing/jobs/notify
    • 書式:$aws/things/[YOUR-THING-NAME]/jobs/notify
  • $aws/things/packageTestThing/jobs/notify-next
    • 書式:$aws/things/[YOUR-THING-NAME]/jobs/notify-next
  • $aws/things/packageTestThing/jobs/+/update/accepted
    • 書式:$aws/things/[YOUR-THING-NAME]/jobs/[Job-ID]/update/accepted

なお、今回は実際の IoTデバイスは用意していないので、MQTT クライアントを仮想のデバイスとして使います。そのため、MQTT クライアントで使うデバイス証明書には、作成済みの「モノ」である packageTestThing の証明書類を使います。

155-0-mqtt-subscribe

AWS コンソールに戻って、ジョブのターゲットを指定します。先程作成した「モノの動的グループ」である TestPackageV1Group を選択します。

155-1-select-group

ジョブドキュメントの指定では、S3に適当なジョブドキュメントをアップロードし、そのファイルを S3 URL の箇所で指定します。

ジョブが成功すると、対象デバイスに紐づく TestPackageのバージョンが自動的に指定のバージョンに更新されます。Jobs integration with the Package Caralo service の欄では、更新先のバージョンを指定します。今回は 1.2.0 のパッケージをデプロイするので、1.2.0 を指定します。
(改めて指定しなくても ソフトウェアパッケージの画面からジョブを作成するとそのバージョンがプリセットされています)

156-1-job-document

ちなみにジョブドキュメントは下記のような簡易的なものを使いました。

{
    "app_url": "${aws:iot:s3-presigned-url:https://s3-ap-northeast-1.amazonaws.com/[YOUR-S3-BUCKET]/app.sh}",
    "app_version": "1.2.0"
}

上記のジョブドキュメントの内容の通り、このジョブはS3に別途配置したデプロイ対象のアプリケーションである app.sh に対して、Pre-sigin URL でアクセスしてデバイスにダウンロードする想定です。

ジョブドキュメントの設定画面の下側では、その Pre-sign URL にアクセスするための IAM Role を指定しています。既存のものがなければ Create role で作成しておきます。

156-2-pre-signed-url-2

最後の画面では、ジョブの実行タイプを指定します。今回は動作確認のテストなので Snapshot を選択しました。

全てのジョブ設定が完了したら Submit をクリックしてジョブを実行します。

157-job-submit

ジョブを実行すると、ジョブがキューに入ったことが分かります。

160-job-queued

ジョブがキューに入ると次のような2つのメッセージを MQTT クライアントで受信します。

  • 受信トピック:$aws/things/packageTestThing/jobs/notify
  • メッセージ:下記
{
  "timestamp": 1688534759,
  "jobs": {
    "QUEUED": [
      {
        "jobId": "Deploy_1688534555174",
        "queuedAt": 1688534758,
        "lastUpdatedAt": 1688534758,
        "executionNumber": 1,
        "versionNumber": 1
      }
    ]
  }
}
  • 受信トピック:$aws/things/packageTestThing/jobs/notify-next
  • メッセージ:下記

S3 の pre-sign URL が発行されて MQTT メッセージとして受け取れています。実際にはデバイス側はこの URL からアプリケーションをダウンロードして然るべき処理を実行します。(アプリのバージョンアップなど)

{
  "timestamp": 1688534759,
  "execution": {
    "jobId": "Deploy_1688534555174",
    "status": "QUEUED",
    "queuedAt": 1688534758,
    "lastUpdatedAt": 1688534758,
    "versionNumber": 1,
    "executionNumber": 1,
    "jobDocument": {
      "app_url": "https://[YOUR-S3-BUCKEET].s3.ap-northeast-1.amazonaws.com/app.sh?X-Amz-Security-Token=IQoJb...(中略)...81721",
      "app_version": "1.2.0"
    }
  }
}

ジョブがキューに入ったので、次のトピックに指定のメッセージを publish します。
このMQTTクライアントは packageTestThing というデバイスを想定しているので、トピックには packageTestThing を指定します。
実際にはジョブ通知を受け取ったら、デバイス側でアプリのダウンロード、バージョンアップなどの処理を開始します。処理を開始したことをジョブ側に伝えるために、ここでは "status": "IN_PROGRESS" として Publish しています。

  • トピック
    • $aws/things/packageTestThing/jobs/Deploy_1688534555174/update
      • トピックの書式:$aws/things/[YOUR-THING-NAME]/jobs/[Job-ID]/update
  • publish するメッセージ(下記)
    • statusIN_PROGRESS にしてジョブのステータスを更新させます。
    • clientToken は任意の文字列を指定します。
    • どのジョブリクエストに対するレスポンスなのか AWS 側が認識するために利用するものなので、ジョブが終了するまで同じトークンを使います。
{
    "status": "IN_PROGRESS",
    "statusDetails": { 
        "downloading": "in-progress",
        "extracting": "awaiting",
        "updating": "awaiting"
    },
    "expectedVersion": "1",
    "stepTimeoutInMinutes": 300,
    "clientToken": "91FB98F8-A439-4570-912B-E9FB4E5F2B27"
}

メッセージを publish できたらジョブの状態が in progress に変わります。「Queued」の数が2→1に減って、「in progress」が0→1になりました。

162-job-status-in-progress-1-2

また「in progress」の数字をクリックすると、ジョブ対象であるデバイス一覧が「in progress」中であるデバイスだけにフィルタリングされます。

163-detail-job-status-in-progress-1

さらにメッセージを追加で publish してデバイス側の処理状態を進めましょう。次のメッセージを publish します。

  • トピック:$aws/things/packageTestThing/jobs/Deploy_1688534555174/update
    • 先程と同じ
  • メッセージ:下記
    • clientToken は先程と同じトークンを使います。
    • ジョブステータスは IN_PROGRESS のままですが、アプリのダウンロードが完了したことを通知するために "downloading": "done" というメッセージを含めます。
{
    "status": "IN_PROGRESS",
    "statusDetails": {
        "downloading": "done",
        "extracting": "awaiting",
        "updating": "awaiting"
    },
    "expectedVersion": "2",
    "stepTimeoutInMinutes": 300,
    "clientToken": "91FB98F8-A439-4570-912B-E9FB4E5F2B27"
}

次に、デバイス側でアプリのバージョンアップが正常終了したと想定して、処理の終了を通知します。

  • トピック:$aws/things/packageTestThing/jobs/Deploy_1688534555174/update
    • 先程と同じ
  • メッセージ:下記
    • "status": "SUCCEEDED" としてジョブステータスを SUCCEEDED に更新させます。
    • 他の statusDetails も全て完了したことを通知します。
    • clientToken は先程と同じトークンを使います。
{
    "status": "SUCCEEDED",
    "statusDetails": {
        "downloading": "done",
        "extracting": "done",
        "updating": "done"
    },
    "expectedVersion": "3",
    "stepTimeoutInMinutes": 300,
    "clientToken": "91FB98F8-A439-4570-912B-E9FB4E5F2B27"
}

マネジメントコンソール側では、packageTestThing のジョブが完了したことが分かります。

164-succeeded-job-device01

「ソフトウェアパッケージ」の画面でも、バージョン 1.2.0 のデバイス(モノ)の数が2→3に増えています。一方でバージョン 1.0.0 のデバイス(モノ)の数が1→0になりました。

165-software-package-update

上記の画面にある 3 things をクリックすると、packageTestThing が新たに追加されていることも確認できました。

166-add-packageTestThing

ジョブの実行によるパッケージ更新が確認できたので、先程と同じようにデバイス packageTestThing2 のジョブも完了させてしまいましょう。

MQTT クライアントで packageTestThing2 のデバイス証明書類を使ってジョブと Pub/Sub 通信してジョブを終了させます。トピックに含まれる「モノ」の名前も packageTestThing2 に変えることを忘れないでください。

次のように2つのジョブ全てが成功しました。

168-succeeded-job-device02

ソフトウェアパッケージのコンソール画面からも2つのデバイスが見えます。

169-software-package-all-device-complete

また、ジョブの実行対象であった「モノの動的グループ」では、条件に該当するデバイスが全てバージョンアップして、該当デバイスがなくなったことが分かります。

170-group-device-nothing

デバイス(モノ)に紐づくパッケージバージョンは、「モノ」の側(デバイスレジストリ側)からも確認できます。

マネジメントコンソールで、ManageAll devicesThings と辿って該当のデバイスを開きます。
デバイス(モノ)の詳細画面にある package and versions というタブで紐づいているパッケージとバージョンを確認できます。

171-thing-list-package-and-versions-updated

パッケージ削除時の注意点

デバイスに複数のパッケージとバージョンが関連づいている状態で、特定のパッケージやバージョンを削除すると、デバイスレジストリ側からは関連づいているパッケージ・バージョンが表示されなくなります。 一方でデバイスシャドウは何も更新されないままの状態で残り続けてしまいます。

もう少し簡単に書くと、Software packages 側で登録されている状態と デバイスシャドウの中身が異なっている場合、デバイスレジストリの thing 側で packages and versions の欄を見ても何も表示されない、という状態が発生します。

現象が発生する例

想定する状況は下記とします。

  1. 最初に以下の3つのパッケージソフトウェアを作成
    • SampleApp
    • SamplePackage
    • TestPackage
  2. 3つのソフトウェアをデバイス(モノ)packageTestThing2 に紐づけ
  3. ジョブなどを使わず手動でデバイスから SampleAppSamplePackage を削除
  4. 次にソフトウェア sampleAppSamplePackage をコンソールからいきなり削除
    • ソフトウェアの削除は、バージョンの削除などは行わずソフトウェアパッケージをいきなり削除
      • sampleApp(削除)
      • SamplePackage(削除)
      • TestPackage(そのまま)

このような手順でパッケージを削除すると、「ソフトウェアパッケージ側の情報」と「名前付きデバイスシャドウ側」の情報が一致しない状態が発生します。

この時、コンソール上の表示は次のようになります。デバイス packageTestThing2 には、TestPackage が紐づいているはずですが、何も表示されていません。

201-no-package-thing2

デバイスシャドウの中身を確認してみます。ソフトウェアパッケージのバージョンを管理する専用のシャドウ $package を確認します。

202-thing2-named-shadow

次のように、削除したはずのソフトウェアであるsampleAppSamplePackage の情報を持っており、古いままの状態を保持しています。

203-thing2-shadow-state

この情報の不一致により、デバイスレジストリ側では紐づくパッケージの情報が表示されなくなっています。

対処と予防方法

対処方法

取り急ぎこの現象を解消したい場合は、デバイスシャドウを作り直すのが一番早いです。
まずは現在のシャドウを削除します。

204-delete-thing2-shadow

次にシャドウを再作成します。

205-create-thing2-shadow

作成時にシャドウの種類として 「Reserved Shadow for Package Catalog」 を選択します。

206-create-reserved-shadows-package-catalog

シャドウを作成直後は、下記のように中身が空になっているので、Edit をクリックして正しい状態に更新します。

206-edit-thing2-shadow

正しい状態を入力して Update をクリックします。

207-modified-thing2-shadow-state

改めてデバイス(モノ)の画面からパッケージバージョンを確認すると、正しい内容で表示できました。

208-list-thing2-package

予防方法

そもそもこの状況が発生するのは、パッケージを削除するときに正しい順番で行っていないことが原因です。

パッケージを削除するときは次の順番で行います。

  1. パッケージとそのバージョンがアクティブにデプロイされていないことを確認
  2. 紐づくデバイスがある場合は、そのバージョンからデバイスをデタッチ
  3. デバイスが全てデタッチされたバージョンを削除
  4. バージョンを全て削除してからパッケージを削除

該当するドキュメントは下記の一番下にある 「Deleting a software package and its package versions」 の部分になります。

最後に

今回の「ソフトウェアパッケージカタログ」によって、IoT デバイスで使うアプリケーションのバージョン管理が更に簡単になりました。うまく活用していきたいと思います。

以上です。