【新機能】AWS IoT Device Management の「ソフトウェアパッケージカタログ」を使ってデバイスアプリケーションのバージョン管理を効率化する
6月の初旬、AWS IoT Management に Software Package Catalog という新機能がリリースされました。
従来も(名前付き)デバイスシャドウにパッケージバージョンの情報を持たせることでアプリのバージョンを管理できましたが、ジョブとの連携やシャドウの更新といった管理作業はユーザー側で実装する必要がありました。
今回リリースされた Software Package Catalog では、デバイスシャドウや IoT ジョブと自動的に連携するので、これまでよりも効率的にデバイスアプリケーションのバージョンを管理できるようになりました。
具体的にどんな機能で、どのように使うのか試してみたので紹介します。
ソフトウェアパッケージカタログの作成
最初に該当アプリケーションを管理する単位である「パッケージ」を作成します。
ソフトウェアパッケージの画面が開いたら Create package
をクリックします。
パッケージ管理の依存関係
初めてマネジメントコンソールからパッケージを作成する際は、次のような 「パッケージ管理の依存関係を有効にする(Enable dependencies for package management)」 というウィンドウが開きます。
ここでは、2つのチェックボックス(オプション)があります。画像では1つだけにチェックが入っていますが、基本的には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
をクリックします。
インデックスの管理画面で、次の2つの設定にチェックを入れて有効化します。
Add named shadows
- 名前付きシャドウをインデックスの対象にします。
Add device software package and versions
- ソフトウェアパッケージカタログのパッケージとバージョンをインデックスの対象にします。
「ジョブによるデバイスシャドウの自動アップデート」 については 「設定」画面の「Auto update device shadows from jobs」 欄にある Settings
を開きます。
設定画面にて、下記の2つにチェックを入れます。ジョブがデバイスシャドウを更新する IAM Role を新規に作成するときは、Create role
ボタンから作成します。
詳細は下記のドキュメントに記載されています。
パッケージ名と属性の設定
「パッケージ管理の依存関係」の設定が終わったら、次の画面でパッケージ名を付けます。ここでは TestPackage
としました。説明欄は記入してませんが、必要に応じて記載してください。
次に最初のバージョンをセットします。セマンティックバージョン形式が推奨されています。今回は 1.0.0
としました。
(先程と同様に説明欄は記入してませんが、必要に応じて記載してください。)
同じ画面の下にある Version attribute
は属性の設定です。
ソフトウェアパッケージのバージョンには、属性を付与することができます。ここでは、OS
という属性に対して Linux
という値をセットしました。
(本記事では、ここで設定した属性について以後使わないので、設定はスキップしても構いません)
Add attribute
ボタンを押すことで属性一覧に反映されます。
最後に Create package
をクリックして完了です。
TestPackage
というパッケージが登録できました。
デバイスとパッケージの関連付けと検索
パッケージができたので、まずは手動でデバイス(モノ)とパッケージを関連付けて、フリートインデックスで検索できるところまで確認してみます。
デバイスとパッケージの関連付け
先程作成したパッケージ TestPackage
のリンクをクリックするとパッケージの詳細画面が開きます。
画面下側にある 「Versions」 に初期設定で指定したバージョン「1.0.0」があるので、クリックします。
バージョンは作成した直後は、Draft
という状態になっています。これはソフトウェアが 「準備中」だったり「デプロイできる前の状態」 ということを示すステータスです。
デバイスにデプロイできる状態は Publish
というステータスと規定されています。デバイスと関連付けるので Publish
に状態を変えておきましょう。
パブリッシュするバージョンの確認画面が出るので、該当バージョンを入力して Publish
をクリックします。
1.0.0
がパブリッシュ状態になりました。
なお、Draft
のままでも関連付けできますが、一般的な運用を考えると Publish
にしておいた方が実際の運用と整合性が取れるかと思います。
各種ステータスについては下記ドキュメントが参考になります。
次に、バージョン「1.0.0」のパッケージをデバイス(モノ)に関連付けます。先程と同じように該当バージョンをクリックします。
バージョン「1.0.0」の詳細画面で、Add things to this version
をクリックします。
(ここでは、まだデバイスが何も関連づいていないので一覧には何も表示されていません)
関連付けたいデバイス(モノ)をプルダウンから選択すると、画面下部の一覧に登録されます。ここでは packageTestThing
というモノを選択しました。(複数選択可能です)
モノを指定できたら、Add thing to version
をクリックします。
関連付けができた直後に、画面常備に下記のようなメッセージが表示されます。これは指定のパッケージバージョンとデバイス(モノ)の関連付けが、フリートインデックスにインデックス中であることを示したものです。
メッセージにあるように10秒程度で完了します。
インデックスに登録が完了すると、先程の「関連づいたデバイス一覧」に該当デバイスが現れます。
パッケージの詳細画面にも、バージョン 1.0.0
とそのデバイス数が円グラフおよび、バージョン一覧に表示(1 thing
)されるようになりました。
同じ作業を繰り返して、バージョン 1.0.0
、1.0.1
、1.2.0
と3バージョン登録してみました。
さらに他のデバイス(モノ)も追加して、合計4つのデバイスを各バージョンに登録してみたのが以下の画面です。各バージョンとそのバージョンに関連づいたデバイス数が、グラフと一覧に現れています。
フリートインデックスによるデバイスの検索
パッケージバージョンとデバイス(モノ)の関連付けが完了したので、フリートインデックスを使ってデバイスを検索してみます。
マネジメントコンソールで Manage
> All devices
> Things
と画面を開いて、Advanced search
をクリックします。
今回は、ソフトウェアパッケージ TestPackage
のバージョン 1.2.0
が紐づいているデバイス(モノ)を検索してみます。
次のクエリを検索バーに入力します。入力したら Enter
でクエリを確定させます。複数クエリを入力して、AND 検索や、OR 検索なども可能です。
検索を実行すると該当する2つのデバイス(モノ)が抽出できました。
このクエリは保存して後から使い回すこともできます。検索時に Save query
をクリックすると保存できます。
保存したクエリを使うには Use saved query
をクリックします。
すると、保存済みのクエリ一覧が表示されるので、使いたいものを選択するだけでOKです。
なお、予約された名前付きシャドウに対する検索には範囲や <
,>
, =
といった比較演算子などは使えません。使うと次のようなエラーメッセージが表示されます。
他のクエリパターンなどクエリに関するドキュメントは以下になります。
IoT ジョブを使ったパッケージ バージョンのデプロイ
次は、IoT ジョブを使ってパッケージをデバイスにデプロイした時に、ソフトウェアパッケージカタログの情報も自動的に追随して更新できることを確認してみます。
モノの動的グループの作成
最初にモノの動的グループを作成します。動的グループを使うと、特定の属性や今回のようなパッケージバージョンの情報を元に、AWS IoT Core のデバイスレジストリに登録されているモノを動的グルーピングすることができます。
例えば、「ソフトウェアのバージョンが 1.0
」という条件の動的グループを作ると、その属性を持つモノが抽出されてグルーピングされます。モノのバージョンが 2.0
になるなど、条件にマッチしないモノはグループから外れます。逆に 0.1
というバージョンを持つモノのバージョンが 1.0
になると条件に合致するので、新たにグループに追加されます。
今回は次のように、「パッケージ TestPackage
のバージョンが 1.0.X
」 であるモノのグループ 「TestPackageV1Group」 を作成しました。
該当するモノとして、packageTestThing
と packageTestThing2
の2つが含まれている状態になっています。
ジョブによるデプロイ
先程作成した動的グループに所属している2つのモノの TestPackage
のバージョンを、 ジョブを使って 1.0.X
(1.0.0
と1.0.1
) から両方ともに 1.2.0
のバージョンに更新してみます。
ジョブはソフトウェアパッケージの画面から作成することができるので、作成済みのパッケージである TestPackage
の画面を開きます。
バージョン 1.2.0
をデプロイするので、該当バージョンを選択して Deploy job version
をクリックします。
確認ウィンドウが出る場合があるので、Acknowledge
をクリックして進めます。
ジョブのプロパティを設定する画面では、ジョブ名と説明を記載します。ジョブ名はデフォルトでは、Deploy_ランダムな数字
となっています。今回はそのまま Deploy_1688534555174
を使うことにしました。
ジョブを作成する前にジョブの発行状態を確認したかったので、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
の証明書類を使います。
AWS コンソールに戻って、ジョブのターゲットを指定します。先程作成した「モノの動的グループ」である TestPackageV1Group
を選択します。
ジョブドキュメントの指定では、S3に適当なジョブドキュメントをアップロードし、そのファイルを S3 URL
の箇所で指定します。
ジョブが成功すると、対象デバイスに紐づく TestPackage
のバージョンが自動的に指定のバージョンに更新されます。Jobs integration with the Package Caralo service
の欄では、更新先のバージョンを指定します。今回は 1.2.0
のパッケージをデプロイするので、1.2.0
を指定します。
(改めて指定しなくても ソフトウェアパッケージの画面からジョブを作成するとそのバージョンがプリセットされています)
ちなみにジョブドキュメントは下記のような簡易的なものを使いました。
{ "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
で作成しておきます。
最後の画面では、ジョブの実行タイプを指定します。今回は動作確認のテストなので Snapshot
を選択しました。
全てのジョブ設定が完了したら Submit
をクリックしてジョブを実行します。
ジョブを実行すると、ジョブがキューに入ったことが分かります。
ジョブがキューに入ると次のような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 するメッセージ(下記)
status
をIN_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になりました。
また「in progress」の数字をクリックすると、ジョブ対象であるデバイス一覧が「in progress」中であるデバイスだけにフィルタリングされます。
さらにメッセージを追加で 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
のジョブが完了したことが分かります。
「ソフトウェアパッケージ」の画面でも、バージョン 1.2.0
のデバイス(モノ)の数が2→3に増えています。一方でバージョン 1.0.0
のデバイス(モノ)の数が1→0になりました。
上記の画面にある 3 things
をクリックすると、packageTestThing
が新たに追加されていることも確認できました。
ジョブの実行によるパッケージ更新が確認できたので、先程と同じようにデバイス packageTestThing2
のジョブも完了させてしまいましょう。
MQTT クライアントで packageTestThing2
のデバイス証明書類を使ってジョブと Pub/Sub 通信してジョブを終了させます。トピックに含まれる「モノ」の名前も packageTestThing2
に変えることを忘れないでください。
次のように2つのジョブ全てが成功しました。
ソフトウェアパッケージのコンソール画面からも2つのデバイスが見えます。
また、ジョブの実行対象であった「モノの動的グループ」では、条件に該当するデバイスが全てバージョンアップして、該当デバイスがなくなったことが分かります。
デバイス(モノ)に紐づくパッケージバージョンは、「モノ」の側(デバイスレジストリ側)からも確認できます。
マネジメントコンソールで、Manage
> All devices
> Things
と辿って該当のデバイスを開きます。
デバイス(モノ)の詳細画面にある package and versions
というタブで紐づいているパッケージとバージョンを確認できます。
パッケージ削除時の注意点
デバイスに複数のパッケージとバージョンが関連づいている状態で、特定のパッケージやバージョンを削除すると、デバイスレジストリ側からは関連づいているパッケージ・バージョンが表示されなくなります。 一方でデバイスシャドウは何も更新されないままの状態で残り続けてしまいます。
もう少し簡単に書くと、Software packages
側で登録されている状態と デバイスシャドウの中身が異なっている場合、デバイスレジストリの thing
側で packages and versions
の欄を見ても何も表示されない、という状態が発生します。
現象が発生する例
想定する状況は下記とします。
- 最初に以下の3つのパッケージソフトウェアを作成
SampleApp
SamplePackage
TestPackage
- 3つのソフトウェアをデバイス(モノ)
packageTestThing2
に紐づけ - ジョブなどを使わず手動でデバイスから
SampleApp
とSamplePackage
を削除 - 次にソフトウェア
sampleApp
とSamplePackage
をコンソールからいきなり削除- ソフトウェアの削除は、バージョンの削除などは行わずソフトウェアパッケージをいきなり削除
sampleApp
(削除)SamplePackage
(削除)TestPackage
(そのまま)
- ソフトウェアの削除は、バージョンの削除などは行わずソフトウェアパッケージをいきなり削除
このような手順でパッケージを削除すると、「ソフトウェアパッケージ側の情報」と「名前付きデバイスシャドウ側」の情報が一致しない状態が発生します。
この時、コンソール上の表示は次のようになります。デバイス packageTestThing2
には、TestPackage
が紐づいているはずですが、何も表示されていません。
デバイスシャドウの中身を確認してみます。ソフトウェアパッケージのバージョンを管理する専用のシャドウ $package
を確認します。
次のように、削除したはずのソフトウェアであるsampleApp
とSamplePackage
の情報を持っており、古いままの状態を保持しています。
この情報の不一致により、デバイスレジストリ側では紐づくパッケージの情報が表示されなくなっています。
対処と予防方法
対処方法
取り急ぎこの現象を解消したい場合は、デバイスシャドウを作り直すのが一番早いです。
まずは現在のシャドウを削除します。
次にシャドウを再作成します。
作成時にシャドウの種類として 「Reserved Shadow for Package Catalog」 を選択します。
シャドウを作成直後は、下記のように中身が空になっているので、Edit
をクリックして正しい状態に更新します。
正しい状態を入力して Update
をクリックします。
改めてデバイス(モノ)の画面からパッケージバージョンを確認すると、正しい内容で表示できました。
予防方法
そもそもこの状況が発生するのは、パッケージを削除するときに正しい順番で行っていないことが原因です。
パッケージを削除するときは次の順番で行います。
- パッケージとそのバージョンがアクティブにデプロイされていないことを確認
- 紐づくデバイスがある場合は、そのバージョンからデバイスをデタッチ
- デバイスが全てデタッチされたバージョンを削除
- バージョンを全て削除してからパッケージを削除
該当するドキュメントは下記の一番下にある 「Deleting a software package and its package versions」 の部分になります。
最後に
今回の「ソフトウェアパッケージカタログ」によって、IoT デバイスで使うアプリケーションのバージョン管理が更に簡単になりました。うまく活用していきたいと思います。
以上です。