AWS IoT CoreにローカルLinuxコンテナから接続してみた

AWS IoT CoreにローカルLinuxコンテナから接続してみた

AWS IoT Coreを触ってみたいと思い、Linuxコンテナから接続キットを使ってThingへの接続を試してみました。Fedoraコンテナでセットアップする際のハマりポイントと、実行成功までの流れを紹介します。
2026.08.26

はじめに

AWS IoT Coreを触ってみたいと思いまして、まずは「手元のLinuxコンテナから接続できるのか?」を試してみることにしました。

今回はAWS IoT CoreでThingを作成し、ダウンロードした接続キットをFedoraコンテナで動かして、MQTT5のPublish/Subscribeまで試してみます。

検証環境

  • macOS Tahoe 26.6.2
  • Podman 5.7.0
  • コンテナ: Fedora Linux 44(fedora:latest
  • AWS IoT Core(ap-northeast-1

AWS IoT Thingを作成する

今回はAWS IoT Coreコンソールのクイック接続を使いました。公式手順はTry the AWS IoT Core quick connect tutorialにまとまっています。

  1. Connect > Connect one device を開く
  2. Create a new thing を選択し、Thing名に lab_iot-machine_01 と入力する
  3. Platformに Linux/OSX、SDKに Node.js を選択する
  4. 接続キットの connect_device_package.zip をダウンロードする

try-out-iot-core_1
try-out-iot-core_2
try-out-iot-core_3

接続キットには、主に次のファイルが含まれています。

  • デバイス証明書
  • 公開鍵と秘密鍵
  • AWS IoT Policyのコピー
  • サンプルを実行する start.sh

ローカルコンテナ側を準備する

接続キットを展開する

connect_device_package.zip は、コンテナから見える /workspace に置いてそのまま展開しました。ファイル名の変更や別フォルダーへの移動はしていません。

cd /workspace
unzip connect_device_package.zip

Fedoraに不足していたパッケージを入れる

fedora:latest には必要なコマンドが揃っていなかったため、次のパッケージをインストールしました。

  • unzip
  • curl
  • openssl
  • git
  • nodejs
  • iputils
dnf install -y unzip curl openssl
dnf install -y git
dnf install nodejs
dnf install -y iputils

iputils は、後からAWS IoTのendpointへ ping を試すために追加しました。

try-out-iot-core_0-0

try-out-iot-core_0-1

start.sh を実行する前に必要だったこと

try-out-iot-core_4

AWS IoT Coreの画面では、接続キットを展開して start.sh を実行するよう案内されます。ただ、今回のFedoraコンテナでは、必要なパッケージのインストールとSDK・サンプルのbuildを先に行う必要がありました。

状況 原因 対応
uzip: command not found unzip のタイプミス コマンドを修正
git: command not found FedoraにGitがなかった dnf install -y git
npm: command not found Node.jsとnpmがなかった dnf install nodejs
Cannot find module .../dist/index.js clone済みのSDKフォルダーが残り、start.sh がインストール処理をskipした SDKとサンプルを手動でbuild
tsc: command not found SDK側の依存関係が未インストールのまま、サンプル側からSDKの prepare が呼ばれた SDK側から先にdevDependenciesを含めてinstall

start.sh は、SDKフォルダーが存在しない場合だけ、cloneと npm install を実行する作りになっています。

if [ ! -d ./aws-iot-device-sdk-js-v2 ]; then
  git clone https://github.com/aws/aws-iot-device-sdk-js-v2.git --recursive
  cd aws-iot-device-sdk-js-v2
  npm install
  cd samples/node/mqtt/mqtt5_x509
  npm install
fi

最初の実行では、SDKをcloneした直後に npm: command not found で止まりました。しかしSDKフォルダーは残ったため、再実行時は条件に入らず、インストールとbuildが未完了でも npm install がskipされました。その結果、まだ存在しない dist/index.js を実行しようとしていました。そこで、SDK側から順番に依存関係を入れてbuildしました。

cd /workspace/aws-iot-device-sdk-js-v2
rm -rf node_modules
npm install --include=dev
npm run build

cd samples/node/mqtt/mqtt5_x509
rm -rf node_modules dist
npm install --include=dev

cd /workspace
bash start.sh

実行結果

実際の実行ログです。

[root@5a13d8c9d00e workspace]# bash start.sh

Running pub/sub sample application...

Starting MQTT5 X509 PubSub Sample

==== Creating MQTT5 Client ====

==== Starting client ====
Lifecycle Connection Attempt
Connecting to endpoint: 'atczphvn9h1th-ats.iot.ap-northeast-1.amazonaws.com' with client ID 'sdk-nodejs-v2'
Lifecycle Connection Success with reason code: 0

==== Subscribing to topic 'sdk/test/js' ====
Suback received with reason code: 1

==== Sending 5 message(s) ====

Publishing message to topic 'sdk/test/js': Hello from mqtt5 sample [1]
PubAck received with 0

==== Received message from topic 'sdk/test/js': Hello from mqtt5 sample [1] ====

Publishing message to topic 'sdk/test/js': Hello from mqtt5 sample [2]
PubAck received with 0

==== Received message from topic 'sdk/test/js': Hello from mqtt5 sample [2] ====

Publishing message to topic 'sdk/test/js': Hello from mqtt5 sample [3]
PubAck received with 0

==== Received message from topic 'sdk/test/js': Hello from mqtt5 sample [3] ====

Publishing message to topic 'sdk/test/js': Hello from mqtt5 sample [4]
PubAck received with 0

==== Received message from topic 'sdk/test/js': Hello from mqtt5 sample [4] ====

Publishing message to topic 'sdk/test/js': Hello from mqtt5 sample [5]
PubAck received with 0

==== Received message from topic 'sdk/test/js': Hello from mqtt5 sample [5] ====

5 message(s) received.

==== Unsubscribing from topic 'sdk/test/js' ====
Unsubscribed with 0

==== Stopping Client ====
Lifecycle Disconnected with reason code: None
Lifecycle Stopped

==== Client Stopped! ====

try-out-iot-core_6

try-out-iot-core_7

テスト後にサンプルの中身を確認する

ここまでで接続テストは成功しましたが、実行中は証明書やPolicy、MQTT5サンプルの中身まで詳しく見ていませんでした。そこでテスト後に、接続キットとサンプルコードが実際に何をしていたのか確認してみました。

実行後のファイル構成

今回関係したファイルだけ抜き出すと、 /workspace は次のような構成になっていました。

try-out-iot-core_8

/workspace/
├── connect_device_package.zip
├── start.sh
├── lab_iot-machine_01.cert.pem
├── lab_iot-machine_01.public.key
├── lab_iot-machine_01.private.key
├── lab_iot-machine_01-Policy
├── root-CA.crt
└── aws-iot-device-sdk-js-v2/
    └── samples/node/mqtt/mqtt5_x509/
        ├── package.json
        ├── index.ts
        └── dist/index.js

接続キットを展開した時点で入っていたのは、証明書、公開鍵、秘密鍵、Policyのコピー、start.sh です。root-CA.crtaws-iot-device-sdk-js-v2 は、start.sh の実行によってダウンロード・cloneされました。

各ファイルの役割は次のとおりです。

  • lab_iot-machine_01.cert.pem: start.sh--cert に指定するX.509デバイス証明書
  • lab_iot-machine_01.private.key: start.sh--key に指定する秘密鍵
  • lab_iot-machine_01.public.key: 接続キットに含まれる公開鍵。今回のサンプルからは直接読み込まれない
  • lab_iot-machine_01-Policy: 接続キットに含まれていたIoT PolicyのJSON
  • index.ts: MQTT5 X.509サンプルのTypeScriptソース
  • dist/index.js: index.ts をbuildした、start.sh が実際に実行するJavaScript

root-CA.crtstart.sh がダウンロードしますが、今回のNode.jsサンプルにはファイルパスが渡されておらず、index.ts からも参照されていません。

start.sh は何をサンプルへ渡していたのか

接続キットの start.sh は、最後に次のコマンドを実行しています。

node aws-iot-device-sdk-js-v2/samples/node/mqtt/mqtt5_x509/dist/index.js \
  --endpoint atczphvn9h1th-ats.iot.ap-northeast-1.amazonaws.com \
  --key lab_iot-machine_01.private.key \
  --cert lab_iot-machine_01.cert.pem \
  --client_id sdk-nodejs-v2 \
  --topic sdk/test/js

ここで渡しているのは、AWS IoT endpoint、秘密鍵、証明書、MQTT Client ID、Topicです。Thing名の lab_iot-machine_01 自体は接続パラメータとして渡しておらず、Client IDには sdk-nodejs-v2 を使っています。

メッセージ本文と送信回数は指定していないため、サンプル側のデフォルトである Hello from mqtt5 sample と5回が使われました。

MQTT5のサンプルは何をしていたのか

確認したソースコードは、AWS公式GitHubリポジトリのmqtt5_x509サンプルでも参照できます。

QoS(Quality of Service)は、MQTTの配信品質を表すレベルです。今回使われたQoS 1(At Least Once)では、送信側は相手からPUBACK(受信確認)を受け取るまで再送するため、少なくとも1回の配信が行われます。ただし、同じメッセージが重複して届く可能性があります。

処理の流れは次のとおりです。

MQTT5 clientを作成

AWS IoT Coreへ接続

sdk/test/jsをQoS 1でSubscribe

同じsdk/test/jsへQoS 1で5回Publish

同じclientで5件Receive

Unsubscribe

clientをStopしてClose

サンプルコードでは、証明書と秘密鍵のパスを newDirectMqttBuilderWithMtlsFromPath() に渡してMQTT5 clientを作成しています。その後、sdk/test/js をQoS 1でSubscribeし、同じTopicへ5件Publishしています。

実行ログでは、各Publishの後に同じTopicの Received message が1件ずつ出力されています。接続から受信まで同じClient IDを使用しているため、別のデバイスや別のclientへの送信はテストしていません。

ログに出ているreason codeは、AWS IoT CoreのMQTT reason code一覧でも確認できます。Connectionの 0 とPubAckの 0 はSuccess、Subackの 1 はGranted QoS 1です。

Policyファイルに書かれていたこと

start.sh が実行するコマンドには、lab_iot-machine_01-Policy が含まれていません。Node.jsサンプルでも、このファイルは読み込んでいません。

ローカルのPolicyファイルには、今回のClient IDとTopicに一致する次の記述がありました。

  • iot:Connect: Resourceに client/sdk-nodejs-*
  • iot:Subscribe: Resourceに topicfilter/sdk/test/js
  • iot:Publishiot:Receive: Resourceに topic/sdk/test/js

start.sh が指定したClient IDは sdk-nodejs-v2、Topicは sdk/test/js なので、Policyファイル内の値と一致しています。Policyには iot:PublishRetain もありますが、今回のサンプルコードにはRETAINの指定がありません。

一方、ローカルのPolicyファイルだけでは、このPolicyがAWS側でどの証明書にアタッチされているかは確認できません。これはAWS IoT Coreコンソールから確認できます。

  1. Manage > All devices > Things から lab_iot-machine_01 を開く
  2. Certificates から今回の証明書を開く
  3. 証明書詳細の Things でThingとの関連付けを確認する
  4. Policies でアタッチされたPolicyを確認する
  5. Policyを開き、Policy documentがローカルの lab_iot-machine_01-Policy と一致するか確認する

try-out-iot-core_9

公式ドキュメントにも、証明書詳細の Policies からアタッチ済みPolicyを確認する手順があります。

今回確認できた範囲

今回の実行ログから直接確認できたのは、start.sh が指定した証明書、秘密鍵、Client ID、TopicでConnectionが成功し、sdk/test/js のSubscribe、5回のPublish、5件のReceive、Unsubscribeまで完了したことです。

ログにあるClient IDはThing名ではなく sdk-nodejs-v2 です。Topicの sdk/test/js も、サンプルのデフォルトではなく start.sh から指定されていました。

おわりに

ローカルのFedoraコンテナから、X.509証明書を使ってAWS IoT Coreへ接続し、MQTT5のPublish/Subscribeを確認できました。

今回一番ハマったのはAWS IoT Coreの設定ではなく、最小構成のFedoraに必要なツールがなく、途中で止まった start.sh がSDKフォルダーの存在だけを見てセットアップ済みと判断したことでした。最初に unzipgit、Node.jsを揃えてから実行するか、途中で失敗した場合はSDK側から依存関係とbuildを確認すると進めやすいです。

なお、接続キットには秘密鍵が含まれます。Gitや公開記事には載せず、検証後は証明書を無効化し、不要になったThing・証明書・Policyとその関連付けを削除しておきましょう。

この記事をシェアする

AWSのお困り事はクラスメソッドへ

関連記事