[AWS IoT Core] DNS変更によるリージョン切り替えを試してみました

[AWS IoT Core] DNS変更によるリージョン切り替えを試してみました

2025.08.16

1. はじめに

製造ビジネステクノロジー部の平内(SIN)です。

DRや定期保守を想定した、AWS IoT Coreのリージョン切り替えをDNS変更によって実現する方法を試してみました。

本記事では、AWS IoT Coreのカスタムドメインを使用して、2つのリージョン(us-east-1とap-northeast-1)にMQTTクライアントを振り分ける構成を構築し、DNS切り替えとDelete Connection APIによる強制切断を組み合わせたシンプルな切り替えメカニズムを実装しました。

004

MQTTメッセージの到着確認には、AWSコンソールのテストクライアントを使用しました。

006

最初に動作確認している様子をご確認ください。
左の2つの黒バックのコンソールは、2つのMQTTクライアントです。右側に各リージョンに到着しているメッセージを表示しています。左下の青バックのコンソールで、リージョン切変えプログラムを実行することで、クライアントが再起動し、接続先が変わっていることを確認できます。

https://www.youtube.com/watch?v=ztU099FxUog

※ 検証用のコードは以下のGitHubリポジトリで公開しています。
https://github.com/furuya02/tryout-dns-based-mqtt-switcher

2. 証明書

証明書の準備

AWS IoT Coreのカスタムドメインで使用するための証明書を準備する必要があります。

CA証明書およびサーバ証明書は、リポジトリに含まれるgenerate_ca_and_cert.shスクリプトで生成できます。

./generate_ca_and_cert.sh

https://github.com/furuya02/tryout-dns-based-mqtt-switcher/blob/main/generate_ca_and_cert.sh

クライアント証明書については、どちらか一方のリージョンのAWS IoT Coreで発行し、もう一方のリージョンにも登録します。これにより、同一の認証情報で両リージョンにアクセスできるようになります。

certs/
├── client.crt                   # クライアント証明書(AWS IoT Core発行が必要)
├── client.key                   # クライアント秘密鍵(AWS IoT Core発行が必要)
├── ca.crt                       # CA証明書(generate_ca_and_cert.sh生成)
├── mqtt1.example.com.crt        # サーバ証明書(mqtt1用、generate_ca_and_cert.sh生成)
├── mqtt1.example.com.key        # サーバ秘密鍵(mqtt1用、generate_ca_and_cert.sh生成)
├── mqtt2.example.com.crt        # サーバ証明書(mqtt2用、generate_ca_and_cert.sh生成)
└── mqtt2.example.com.key        # サーバ秘密鍵(mqtt2用、generate_ca_and_cert.sh生成)

重要な点として、AWS IoT Coreで使用するサーバ証明書には、extendedKeyUsage=serverAuth拡張キーが必要です。

002

3. AWS IoT Coreとカスタムドメインの設定

カスタムドメインの作成

us-east-1とap-northeast-1の2つのリージョンで、それぞれカスタムドメインを作成します。両リージョンとも、同じ認証情報でアクセスできるように設定します。

005

カスタムドメインは独自証明書を使用して作成し、前述の手順で生成したサーバ証明書をインポートします。

001

DNSの設定

Route53で、2つのエンドポイント(mqtt1.example.comとmqtt2.example.com)に対して、各リージョンのエンドポイントをCNAMEレコードで設定します。TTLは0秒に設定して、できるだけ速やかに変更が反映されるようにします。

003

4. MQTTクライアントの実装

2種類のライブラリによる実装

DNS切り替え時の挙動を確認するため、AWS IoT SDKとpaho-mqttの2種類のライブラリを使用してクライアントを実装しました。

AWS IoT SDK版

def on_connection_interrupted(connection, error, **kwargs):
    print(f"Connection interrupted. error: {error}")

    # DNS伝播待機とプロセス再起動
    time.sleep(5)
    for i in range(3):
        resolved_ip = resolve_dns(ENDPOINT)
        print(f"[{i+1}/3] DNS resolved: {resolved_ip}")
        time.sleep(1)

    # AWS IoT SDKの内部キャッシュをクリアするためプロセス全体を再起動
    os.execl(sys.executable, sys.executable, *sys.argv)

https://github.com/furuya02/tryout-dns-based-mqtt-switcher/blob/main/client_awsiot_01.py

paho-mqtt版

def on_disconnect(client, userdata, rc):
    print(f"Disconnected with result code {rc}")

    # DNS伝播待機
    time.sleep(5)
    for i in range(3):
        resolved_ip = resolve_dns(ENDPOINT)
        print(f"[{i+1}/3] DNS resolved: {resolved_ip}")
        time.sleep(1)

    # paho-mqttの自動再接続機能を利用

https://github.com/furuya02/tryout-dns-based-mqtt-switcher/blob/main/client_paho_01.py

DNS切り替えスクリプト(dr.py)

Route53のDNSレコードを更新し、Delete Connection APIを使用してクライアントを強制切断するスクリプトです。

# DNS切り替えパターンの定義
dr_pattern = [
    {"mqtt1": "us-east-1", "mqtt2": "us-east-1"},
    {"mqtt1": "us-east-1", "mqtt2": "ap-northeast-1"},
    {"mqtt1": "ap-northeast-1", "mqtt2": "ap-northeast-1"},
    {"mqtt1": "ap-northeast-1", "mqtt2": "us-east-1"}
]

# Route53レコード更新後、全クライアントを強制切断
for region in ["us-east-1", "ap-northeast-1"]:
    iot_client = boto3.client("iot", region_name=region)
    # Delete Connection APIで強制切断

https://github.com/furuya02/tryout-dns-based-mqtt-switcher/blob/main/dr.py

実行方法:

export HOSTED_ZONE_ID="/hostedzone/ZXXXXXXXXXXXXX"
python dr.py mode=1  # 両方とも us-east-1
python dr.py mode=2  # client_01: us-east-1, client_02: ap-northeast-1

5. 検証結果と考察

DNSキャッシュへの対応

DNSレコードのTTLを0秒に設定しても、様々なレベルでのキャッシュ(OS、ライブラリ、ネットワーク機器など)の影響により、安定した伝播を得るために以下の考慮が必要でした。

  1. DNS切り替え後の待機時間:5秒の待機時間を設けることで、DNS伝播を待つ
  2. 複数回のDNS解決:3回連続でDNS解決を実行し、確実な切り替えを保証
  3. ライブラリ固有の対応
    • AWS IoT SDK:内部キャッシュをクリアするため、プロセス全体の再起動が必要
    • paho-mqtt:ライブラリの自動再接続機能により、プロセス再起動なしでDNS切り替えに対応

Delete Connection APIの活用

AWS IoT CoreのDelete Connection APIを使用することで、以下のメリットがありました。

  • クライアント側のコード変更を最小限に抑えられる
  • 自動再接続機能を活用して、シンプルな切り替えメカニズムを実現
  • 管理側から一元的に切断制御が可能

6. 最後に

今回の検証を通じて、DNS変更によるAWS IoT Coreのリージョン切り替えは、一定の条件下では実用的な手法であると感じました。特にDelete Connection APIと組み合わせることで、非常にシンプルな切り替えメカニズムを構築できると思います。

なお、DNSキャッシュの扱いには注意が必要で、使用するMQTTライブラリによって異なる考慮が必要になります。また、今回は検証していませんが、クライアントが動作する環境(OSなど)によっては、そこでのDNSキャッシュも意識する必要がありそうです。

DRや定期保守のシナリオで、このような仕組みがうまく活用できれば良いなと思います。

7. 参考リンク

この記事をシェアする

facebookのロゴhatenaのロゴtwitterのロゴ

© Classmethod, Inc. All rights reserved.