RaspberryPiを遠隔操作してみた!
はじめに
本記事では、Azure IoTとOSConfigを活用し、遠隔地にあるRaspberryPiの操作を行う方法について、具体的な手順を説明していこうと思います。特にデバイスの再起動やシャットダウン、AzureDevOps上の任意のスクリプトを実行させるといったことを試したので、そちらに焦点を当てて解説します。
OSConfigについて
OSConfigとは、デバイスのOS設定をプロビジョニングおよび管理するためのサービスを指します。OSConfigを使用することで、LinuxデバイスなどのデバイスOS設定を効率的に管理し、デバイスのプロビジョニングや設定変更を自動化することが可能になります。
OSConfigで出来ること
OSConfigを用いることで、主に以下のようなことが出来るみたいです!
- ネットワークアダプターの状態やIPアドレスの取得
- パッケージマネージャーの設定
- ホスト名の設定
- ホストファイルの内容確認および内容変更
- ファイアーウォールの設定
- OS、バージョン、メモリなどのデバイス情報取得
- デバイスの再起動、シャットダウン
- オンラインリポジトリから任意のスクリプトをデプロイする
前提条件
ここからいよいよ本題に移ります。前提条件をよく確認したうえで次に進んでください!
- IoT Edgeのないデバイス、またはIoT Edge1.2以降のデバイスであること
- アーキテクチャおよびOSが以下のものであること
- アーキテクチャ
- arm64 (別名 aarch64)
- amd64 (別名x86_64)
- OS
- Debian 10
- Debian 11
- Ubuntu Server 18.04
- Ubuntu Server 20.04
- アーキテクチャ
- Windows環境の場合、WSLをインストールしてあること(Windows環境からも実行できることを確認してはいますが、MicroSoftのドキュメントはbashコマンドですべて記載されており、Linux環境で実行させた方がスムーズに進められるためです)
※WSLのインストールがまだの方はこちら - Azureのアカウントを持っていること
※Azureのアカウント作成がまだの方はこちら - 使用するAzureのサブスクリプションにサインインしていること
az login --tenant <tenantID> --use-device-code
※tenantIDは、AzurePortalの「Microsoft Entra IDを選択」⇒ 「概要を選択」⇒ 「テナントID」から確認できます。
- Azure CLIのAzure IoT拡張機能を追加してあること
az extension add --name azure-iot
1. パッケージインストール
1.1 デバイスを packages.microsoft.com に接続する
## For Ubuntu and on Debian 10+
## Register packages.microsoft.com key and prod channel
sudo wget https://packages.microsoft.com/keys/microsoft.asc -O /etc/apt/trusted.gpg.d/packages-microsoft-com_key.asc
os_name=$(grep ^ID= /etc/os-release | tr -d "ID=")
os_version=$(grep ^VERSION_ID /etc/os-release | tr -d "VERSION_ID=")
sudo wget https://packages.microsoft.com/config/"$os_name"/"$os_version"/prod.list -O /etc/apt/sources.list.d/packages-microsoft-com_prod.list
sudo apt update
1.2 AIS(Azure Identity Service)パッケージをインストールする
sudo apt-get install -y aziot-identity-service
1.3 OSConfigパッケージをインストールする
sudo apt-get install -y osconfig
2. Azureに接続する
2.1 Azure IoTリソースを作成する
※AzurePortal上に既に作成済みの場合は不要ですので2.2から進めてください。
2.1.1 IoT ハブの一意の名前を定義する
# In the following command replace <Your_Made_Up_Name_Here>
# with your own preferred hub name, for example:
# MY_IOT_HUB_NAME="MyQuickStart54321"
MY_IOT_HUB_NAME="<任意の一意な名前>"
2.1.2 リソースグループとIoT Hubを作成する
az group create --resource-group <リソースグループ名> --location <地域名>
az iot hub create --resource-group <リソースグループ名> --name "$MY_IOT_HUB_NAME" --query id
2.1.3 デバイスIDを作成し、デバイスの接続文字列を取得する
az iot hub device-identity create --hub-name "$MY_IOT_HUB_NAME" -d <デバイス名> --query deviceId
az iot hub device-identity connection-string show --hub-name "$MY_IOT_HUB_NAME" --device-id device01
2.2 接続文字列を用い、Azure IoTに対して認証させる
sudo aziotctl config mp --connection-string "<接続文字列>"
sudo aziotctl config apply
※connection-string(接続文字列)は2.1.3のコマンドの出力で得られる「HostName=...」と表示されている値です。
2.3 OSConfigパッケージの実行状態を確認する
sudo systemctl status osconfig | grep --color=never Active
3. OSConfigを使用し、リモートで操作する
3.1 OSConfigの接続状態を確認する
az iot hub query --hub-name "$MY_IOT_HUB_NAME" -q "select deviceId,moduleId,connectionState from devices.modules where deviceID='device01' and moduleId='osconfig'"
3.2 任意のコマンドをOSConfigツインで実行する
後述する3.2.1(再起動), 3.2.2(シャットダウン), 3.2.3(AzureDevOps内の任意のスクリプト実行)の実行結果は下記の手順で確認できます。
- 1.AzurePortalにサインインする
- 2.該当のIoTHubリソースを選択する
- 3.作成したデバイスを選択する
- 4.「osconfig」を選択する
- 5.「モジュールIDツイン」を選択する
- 6.commandStatusプロパティのresultCode, textResultを確認する(成功している場合はresultCodeが0となり、textResultに結果が出力される)
3.2.1 再起動させたい場合
az iot hub module-twin update --hub-name "<IoTリソース名>" -d <デバイス名> -m osconfig --desired '{"CommandRunner": {"__t": "c","commandArguments": {"commandId": "<任意の一意なコマンドID>","arguments": "","timeout": 30, "singleLineTextResult": false,"action": 1}}}' --query "properties.desired.CommandRunner"
・出力結果(再起動・シャットダウンの場合、出力するコマンドは無いため、testResultは空文字で出力されます)
"CommandRunner": {
"__t": "c",
"commandStatus": {
"commandId": "reboot_device",
"resultCode": 0,
"textResult": "",
"currentState": 2
}
}
上記の出力後、実際にデバイスが再起動されているか確認してみてください。
3.2.2 シャットダウンさせたい場合
az iot hub module-twin update --hub-name "<IoTリソース名>" -d <デバイス名> -m osconfig --desired '{"CommandRunner": {"__t": "c","commandArguments": {"commandId": "<任意の一意なコマンドID>","arguments": "","timeout": 30, "singleLineTextResult": false,"action": 2}}}' --query "properties.desired.CommandRunner"
・出力結果(再起動・シャットダウンの場合、出力するコマンドは無いため、testResultは空文字で出力されます)
"CommandRunner": {
"__t": "c",
"commandStatus": {
"commandId": "shutdown_device",
"resultCode": 0,
"textResult": "",
"currentState": 2
}
}
上記の出力後、実際にデバイスがシャットダウンされているか確認してみてください。
3.2.3 AzureDevOps内の任意のスクリプトを実行させたい場合
az iot hub module-twin update --hub-name "<IoTリソース名>" -d <デバイス名> -m osconfig --desired '{"CommandRunner": {"__t": "c","commandArguments": {"commandId": "<任意の一意なコマンドID>","arguments": "curl 'https://<トークン名>:<アクセストークン>@dev.azure.com/<組織名>/_apis/git/repositories/<リポジトリID>/items?path=<実行させたいスクリプトのパス>&version=<ブランチ名>&api-version=4.1' | tr -d \r| bash","timeout": 30,"singleLineTextResult": false,"action": 3}}}' --query "properties.desired.CommandRunner"
※アクセストークンの作成方法およびリポジトリIDの確認方法について
- アクセストークンの作成方法はこちら
- リポジトリIDの確認方法
- 1.AzureDevOpsにアクセスする
- 2.該当の組織を選択する
- 3.該当のProjectを選択する
- 4.左メニューの「Repos」を選択する
- 5.該当のリポジトリに切り替え、「Manege repositories」を選択する
- 6.再び該当のリポジトリを選択する
- 7.リンクで表示されている「repo=...」の値を確認する
・実行させたスクリプト内容(例1)
#!/bin/bash
# 年-月-日 時:分:秒 の形式で現在の日付と時間を取得
current_date_time=$(date +"%Y-%m-%d %H:%M:%S")
echo "現在の日付と時間: $current_date_time"
# 年齢に基づいた条件判定
age=18
if [ $age -lt 18 ]; then
echo "未成年です。"
else
echo "成人済みです。"
fi
・期待する出力
現在の日付と時間: 2024-05-21 19:03:50
成人済みです。
・出力結果
"CommandRunner": {
"__t": "c",
"commandStatus": {
"commandId": "test012",
"resultCode": 0,
"textResult": " % Total % Received % Xferd Average Speed Time Time Time Current\n Dload Upload Total Spent Left Speed\n 0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0 100 334 100 334 0 0 1445 0 --:--:-- --:--:-- --:--:-- 1445\n現在の日付と時間: 2024-05-21 19:03:50\n成人済みです。\n",
"currentState": 2
}
curlコマンドを使用しているため、データ転送した際の進行状況も出力に含まれていますが、期待する出力がちゃんと出力結果に表示されていますね!
・実行させたスクリプト内容(例2)
#!/bin/bash
i=1
while [ $i -le 5 ]
do
# 現在の日付と時間を取得
current_date_time=$(date +"%Y-%m-%d %H:%M:%S")
echo "$i 回目: 現在の日付と時間: $current_date_time"
# 1秒待機
sleep 1
i=$((i+1))
done
・期待する出力
1 回目: 現在の日付と時間: 2024-05-28 06:55:43
2 回目: 現在の日付と時間: 2024-05-28 06:55:44
3 回目: 現在の日付と時間: 2024-05-28 06:55:45
4 回目: 現在の日付と時間: 2024-05-28 06:55:46
5 回目: 現在の日付と時間: 2024-05-28 06:55:47
・出力結果
"CommandRunner": {
"__t": "c",
"commandStatus": {
"commandId": "test14",
"resultCode": 0,
"textResult": " % Total % Received % Xferd Average Speed Time Time Time Current\n Dload Upload Total Spent Left Speed\n 0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0 0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0 100 245 100 245 0 0 361 0 --:--:-- --:--:-- --:--:-- 361\n1 回目: 現在の日付と時間: 2024-05-28 07:02:57\n2 回目: 現在の日付と時間: 2024-05-28 07:02:58\n3 回目: 現在の日付と時間: 2024-05-28 07:02:59\n4 回目: 現在の日付と時間: 2024-05-28 07:03:00\n5 回目: 現在の日付と時間: 2024-05-28 07:03:01\n",
"currentState": 2
}
ループ処理も実行させてみましたが、大丈夫そうです!
・実行させたスクリプト内容(例3)
#!/bin/bash
i=1
while true
do
# 現在の日付と時間を取得
current_date_time=$(date +"%Y-%m-%d %H:%M:%S")
echo "$i 回目: 現在の日付と時間: $current_date_time"
# 1秒待機
sleep 1
i=$((i+1))
done
・期待する出力
1 回目: 現在の日付と時間: 2024-05-28 06:55:43
2 回目: 現在の日付と時間: 2024-05-28 06:55:44
3 回目: 現在の日付と時間: 2024-05-28 06:55:45
4 回目: 現在の日付と時間: 2024-05-28 06:55:46
5 回目: 現在の日付と時間: 2024-05-28 06:55:47
・
・
・
・出力結果
"CommandRunner": {
"__t": "c",
"commandStatus": {
"commandId": "test14",
"resultCode": 0,
"textResult": "",
"currentState": 1
}
こちらはresultCodeが0で成功はしているのですが、textResultに出力が表示されていませんでした。スクリプト自体が無限ループの処理をしているため、そういったスクリプトはうまく出力できないのかもしれないですね。
おわりに
本記事ではOSConfigを活用し、遠隔地にあるRaspberryPiの操作を行う方法について説明しました。まだまだ試せてないことがたくさんあり、トラブルシューティングやシステム・ソフトウェアの更新などの用途での使用もできそうですし、他にも色々用途はあると思いますので参考にしていただければと思います!
参考資料
Discussion
めちゃわかりやすい!!