LoginSignup
2
1

More than 1 year has passed since last update.

AWS環境で IoT デバイスの監視を行う際に役立ちそうなツールの紹介

Posted at

AWS 環境で IoT デバイスの監視を行う際に役立ちそうなツールを調べていたところ、 amazon-cloudwatch-publisher というツールが GitHub の AWS Labs リポジトリ にて公開されていたので、勉強がてら検証してみました。

・amazon-clourwatch-publisher

GitHub - awslabs/amazon-cloudwatch-publisher: Push metrics and logs to CloudWatch from any system that can run Python

何ができるのか?

デバイス内のログファイルや、CPUやメモリ使用率などのメトリクスデータを、簡単に CloudWatch に連携できるようになります。

・ログファイル
image.png

・メトリクスデータ
image.png

README.md に結構詳しく記載されていますので、使用前に一読されることをお勧めします。

検証環境

IoT デバイス

  • Raspberry Pi 3B+(以下、ラズパイ
    • Python 3.9.2
    • pip 20.3.4 from /usr/lib/python3/dist-packages/pip (python 3.9)

AWS CLI 実行用端末

  • Windows 10 Pro 64bit 機(以下、Windows 機)
  • ラズパイに amazon-cloudwatch-publisher(以下、Publisher) 以外を入れたくなかったため、AWS CLI をインストールした Windows 機を使用してコマンドを実行しています
  • もちろん、ラズパイに AWS CLI をインストールして頂いてもOKです

ラズパイの OS は以下の通りです。

$ cat /etc/os-release 
PRETTY_NAME="Raspbian GNU/Linux 11 (bullseye)"
NAME="Raspbian GNU/Linux"
VERSION_ID="11"
VERSION="11 (bullseye)"
VERSION_CODENAME=bullseye
ID=raspbian
ID_LIKE=debian
HOME_URL="http://www.raspbian.org/"
SUPPORT_URL="http://www.raspbian.org/RaspbianForums"
BUG_REPORT_URL="http://www.raspbian.org/RaspbianBugs"

今回の検証内容

ラズパイに Publisher をインストールしてデバイス内のログファイルとメトリクスデータをクラウドに連携し、AWS マネジメントコンソールから、それらを確認することろまでやってみたいと思います。

デバイスのセットアップ

まず最初に、ラズパイのセットアップを行い、ラズパイが AWS IoT に認識されるようにします。
手順としては、AWS 公式ドキュメントの内容をなぞっていく形になります。
ステップ 2: AWS IoT で Raspberry Pi をプロビジョニングする - AWS IoT Core (amazon.com)

(1) デバイス証明書を格納するディレクトリを作成する

ラズパイに pi ユーザでログインし、適当に以下のようなディレクトリを作成しました。

ラズパイ
$ mkdir -p ~/certs/aws

(2) デバイス証明書の作成及びアクティブ化

Windows機で AWS CLI を使い、デバイス証明書(X.509証明書)の作成及びアクティブ化を行います。
コマンドを実行すると、証明書と鍵ファイルが出力されるので、それらをラズパイの (1) で作成したディレクトリ「/home/pi/certs/aws」に転送します。
レスポンスの certificateArn は後工程で使用するため控えておきます。

Windows機
$ aws iot create-keys-and-certificate --set-as-active --certificate-pem-outfile ".\device.pem.crt" --public-key-outfile ".\public.pem.key" --private-key-outfile ".\private.pem.key"
{
    "certificateArn": "arn:aws:iot:ap-northeast-1:123XXXXXXXXX:cert/adXXXXXXXXXXXXXXXX",
    "certificateId": "adXXXXXXXXXXXXXXXX",
    "certificatePem": "-----BEGIN CERTIFICATE-----\nXXXXXXXX_SAMPLE_XXXXXXXX\n-----END CERTIFICATE-----\n",
    "keyPair": {
        "PublicKey": "-----BEGIN PUBLIC KEY-----\nXXXXXXXX_SAMPLE_XXXXXXXX\n-----END PUBLIC KEY-----\n",
        "PrivateKey": "-----BEGIN RSA PRIVATE KEY-----\nXXXXXXXX_SAMPLE_XXXXXXXX\n-----END RSA PRIVATE KEY-----\n"
    }
}

image.png

(3) 証明書ディレクトリ及びファイルのパーミッションを設定

(1) で作成した「/home/pi/certs/aws」ディレクトリ及び証明書と鍵ファイルのパーミッションを設定しておきます。
本来であればきちんとした設定を行うべきですが、今回は検証が目的であるため緩めの設定をしています。
※本番環境で利用される場合はご注意ください。

ラズパイ
$ ls -l certs/
drwxr-xr-x 2 pi pi 4096 Aug 22 22:26 aws

$ls -l certs/aws
-rw-r--r-- 1 pi pi 1240 Aug 22 22:26 device.pem.crt
-rw-r--r-- 1 pi pi 1702 Aug 22 22:26 private.pem.key
-rw-r--r-- 1 pi pi  460 Aug 22 22:26 public.pem.key

(4) AWS IoT でデバイスをプロビジョニングする

まず最初に、ラズパイ用の AWS IoT のモノのリソース(Thing)を作成します。
レスポンスの thingName は後工程で使用するため控えておきます。

Windows機
$ aws iot create-thing --thing-name "MyThing01"
{
    "thingName": "MyThing01",
    "thingArn": "arn:aws:iot:ap-northeast-1:123XXXXXXXXX:thing/MyThing01",
    "thingId": "efd786af-XXXX-XXXX-XXXX-XXXXXXXXXXXX"
}

image.png

Windows 機に、ポリシードキュメント用の Json ファイルを作成します。
検証が目的であるため、Resource には * を指定しています。

Windows機
{
    "Version": "2012-10-17",
    "Statement": [
        {
			"Effect": "Allow",
			"Action": [
				"iot:Publish",
				"iot:Subscribe",
				"iot:Receive",
				"iot:Connect"
			],
			"Resource": [
				"*"
			]
	 	}
	]
}

ポリシーを作成します。
レスポンスの policyName は後工程で使用するため控えておきます。

Windows機
$ aws iot create-policy --policy-name "MyThing01Policy" --policy-document "file://ポリシードキュメント.json"
{
    "policyName": "MyThing01Policy",
    "policyArn": "arn:aws:iot:ap-northeast-1:123XXXXXXXXX:policy/MyThing01Policy",
    "policyDocument": "{\n    \"Version\": \"2012-10-17\",\n    \"Statement\": [\n        {\n\t\t\t\"Effect\": \"Allow\",\n\t\t\t\"Action\": [\n\t\t\t\t\"iot:Publish\",\n\t\t\t\t\"iot:Subscribe\",\n\t\t\t\t\"iot:Receive\",\n\t\t\t\t\"iot:Connect\"\n\t\t\t],\n\t\t\t\"Resource\": [\n\t\t\t\t\"*\"\n\t\t\t]\n\t \t}\n\t]\n}\n",
    "policyVersionId": "1"
}

image.png

作成したポリシーをデバイス証明書にアタッチします。
--target には、前工程で作成したデバイス証明書の Arn が入り、具体的には arn:aws:iot:ap-northeast-1:123XXXXXXXXX:cert/adXXXXXXXXXXXXXXXX のような値になります。

Windows機
$ aws iot attach-policy --policy-name "MyThing01Policy" --target "certificateArn"

image.png

デバイス証明書を AWS IoT のモノのリソースにアタッチします。
--principal には、前工程で作成したデバイス証明書の Arn が入ります。

Windows機
$ aws iot attach-thing-principal --thing-name "MyThing01" --principal "certificateArn"

image.png

これでプロビジョニングは終わりとなります。

デバイスに Publisher をインストールする

ラズパイで git clone して、clone してきたリポジトリ内の README.md を参考にしながら進めていきます。

(1) git clone を実行する

ラズパイで amazon-cloudwatch-publisher を git clone してきます。

ラズパイ
$ git clone https://github.com/awslabs/amazon-cloudwatch-publisher.git

(2) ロールエイリアスを作成する

今回、ラズパイと AWS IoT の認証は、前工程で作成したX.509 証明書 を用いて行います。
そのために必要なロールエイリアスの作成を、まず最初に行います。
こちらの AWS 公式ドキュメントが参考になるので、この内容をなぞっていく形で進めていきます。

How to Eliminate the Need for Hardcoded AWS Credentials in Devices by Using the AWS IoT Credentials Provider | AWS Security Blog (amazon.com)

以下、AWS マネジメントコンソールから抜粋した、ロールエイリアスの説明となります。

AWS IoT ロールのエイリアスは、認証されたデバイスに、デバイスの証明書にアタッチされたポリシーに含まれていない AWS サービスへの一時的なアクセス権を付与します。

Windows 機に、ポリシードキュメント用の Json ファイルを作成します。

Windows機
{
    "Version": "2012-10-17",
    "Statement": [
        {
			"Effect": "Allow",
			"Principal": {
				"Service": "credentials.iot.amazonaws.com"
			},
			"Action": [
				"sts:AssumeRole"
			]
		}
	]
}

IAM Role を作成します。
レスポンスの RoleNameArn は、後工程で使用するため控えておきます。

Windows機
$ aws iam create-role --role-name MyIoTCloudWatchPublisherRole --assume-role-policy-document file://ポリシードキュメント.json
{
    "Role": {
        "Path": "/",
        "RoleName": "MyIoTCloudWatchPublisherRole",
        "RoleId": "ARXXXXXXXXXXXXXXXXXXX",
        "Arn": "arn:aws:iam::123XXXXXXXXX:role/MyIoTCloudWatchPublisherRole",
        "CreateDate": "2022-08-22T23:31:06+00:00",
        "AssumeRolePolicyDocument": {
            "Version": "2012-10-17",
            "Statement": [
                {
                    "Effect": "Allow",
                    "Principal": {
                        "Service": "credentials.iot.amazonaws.com"
                    },
                    "Action": [
                        "sts:AssumeRole"
                    ]
                }
            ]
        }
    }
}

ラズパイが AWS にメトリクスデータやログファイルを送信できるように、それらのアクションを定義したポリシードキュメント用の Json ファイルを、Windows 機に作成します。
こちらも、検証が目的であるため、Resource には * を指定しています。

Windows機
{
    "Version": "2012-10-17",
    "Statement": [
        {
			"Effect": "Allow",
			"Action": [
				"cloudwatch:PutMetricData",
				"logs:CreateLogGroup",
				"logs:CreateLogStream",
				"logs:DescribeLogGroups",
				"logs:DescribeLogStreams",
				"logs:PutLogEvents"
			],
			"Resource": [
				"*"
			]
	 	}
	]
}

IAM Role にアタッチするポリシーを作成します。
レスポンスの Arn は、後工程で使用するため控えておきます。

Windows機
$ aws iam create-policy --policy-name MyIoTCloudWatchPublisherPolicy --policy-document file://ポリシードキュメント.json
{
    "Policy": {
        "PolicyName": "MyIoTCloudWatchPublisherPolicy",
        "PolicyId": "ANXXXXXXXXXXXXXXXXXXX",
        "Arn": "arn:aws:iam::123XXXXXXXXX:policy/MyIoTCloudWatchPublisherPolicy",
        "Path": "/",
        "DefaultVersionId": "v1",
        "AttachmentCount": 0,
        "PermissionsBoundaryUsageCount": 0,
        "IsAttachable": true,
        "CreateDate": "2022-08-22T23:35:47+00:00",
        "UpdateDate": "2022-08-22T23:35:47+00:00"
    }
}

IAM Role に ポリシーをアタッチします。

Windows機
$ aws iam attach-role-policy --policy-arn arn:aws:iam::123XXXXXXXXX:policy/MyIoTCloudWatchPublisherPolicy --role-name MyIoTCloudWatchPublisherRole

ロールエイリアスを作成します。
レスポンスの roleAlias は、後工程で使用するため控えておきます。

Windows機
$ aws iot create-role-alias --role-alias MyIoTCloudWatchPublisherRoleAlias --role-arn arn:aws:iam::123XXXXXXXXX:role/MyIoTCloudWatchPublisherRole
{
    "roleAlias": "MyIoTCloudWatchPublisherRoleAlias",
    "roleAliasArn": "arn:aws:iot:ap-northeast-1:123XXXXXXXXX:rolealias/MyIoTCloudWatchPublisherRoleAlias"
}

デバイス証明書にアタッチしているポリシー MyThing01Policy を編集します。
具体的にはStatement の2つ目の要素を追記します。
image.png

(3) configs ファイルの編集を行う

git clone してきたリポジトリ内の configs ディレクトリ内に、デバイス種類に応じた設定ファイルが格納されています。

ラズパイ
$ ls -l configs/
-rw-r--r-- 1 pi pi 1030 Aug 23 22:17 amazon-cloudwatch-publisher-iot.json
-rw-r--r-- 1 pi pi 1036 Aug 23 22:17 amazon-cloudwatch-publisher-osx.json
-rw-r--r-- 1 pi pi 1006 Aug 23 22:17 amazon-cloudwatch-publisher-rpi.json
-rw-r--r-- 1 pi pi  663 Aug 23 22:17 amazon-cloudwatch-publisher-ssm.json

今回は、ラズパイを使っているため、amazon-cloudwatch-publisher-rpi.json を使用します。
検証用に、以下のような書き換えを行いました。

  1. 認証周りの authentication の記述が Cognito 認証用の記述になっているため、X.509 証明書を用いた記述に書き換える
  2. CloudWatch に連携するログファイルの指定を(好きなものに)書き換える

1つ目については、amazon-cloudwatch-publisher-iot.json が X.509 証明書を用いた認証になっているため、これを参考にします。
2つ目については、今回は /var/log/syslog を CloudWatch に連携するため、そのように書き換えます。

最終的に、amazon-cloudwatch-publisher-rpi.jsonは以下のように編集しました。

ラズパイ
{
  "agent": {
    "instance": {
      "prefix": "rpi",
      "command": "cat /proc/cpuinfo | grep Serial | awk '{print $NF}'"
    },
    "authentication": {
      "iotUrl": "IOT_URL",
      "roleAlias": "IOT_ROLE_ALIAS",
      "thingName": "IOT_THING_NAME",
      "certificate": [
        "IOT_CERT_PATH_AND_FILENAME",
        "IOT_KEY_PATH_AND_FILENAME"
      ]
    },
    "region": "REGION",
    "metrics_collection_interval": 60,
    "logs_collection_interval": 10,
    "logfile": "/opt/aws/amazon-cloudwatch-publisher/logs/amazon-cloudwatch-publisher.log",
    "debug": false
  },
  "metrics": {
    "namespace": "System/IoTDevice"
  },
  "logs": {
    "logs_collected": {
      "files": {
        "collect_list": [
          {
            "file_path": "/var/log/syslog"
          }
        ]
      }
    },
    "log_group_name": "/system/iot/{instance_id}",
    "retention_in_days": 7
  }
}

(4) installers ファイルの編集を行う

git clone してきたリポジトリ内の installers ディレクトリ内に、デバイス種類に応じたインストール用スクリプトが格納されています。

ラズパイ
$ ls -l installers/
-rwxr-xr-x 1 pi pi 2546 Aug 23 22:17 install-osx.bash
-rwxr-xr-x 1 pi pi 2286 Aug 23 22:17 install-rpi.bash

今回は、ラズパイを使っているため、install-rpi.bash を使用します。
こちらも、以下のような書き換えを行いました。

  1. Cognito 認証ではなく、X.509 証明書を用いた認証を行うためそれ用に書き換える
  2. /var/log/syslog を参照できるように、daemon の設定ファイル作成の箇所を書き換える

最終的に、install-rpi.bashは以下のように編集しました。
※上記2点以外も書き換えたため、要所にコメントを入れています。

ラズパイ
#!/usr/bin/env bash
set -e

#
# Create a user for the script and make sure it's in a group with sudo powers
sudo_groups="sudo wheel"
for sudo_group in $sudo_groups; do
  getent group $sudo_group && break
done
useradd -g $sudo_group -s /sbin/nologin cwpublisher

# Install dependencies
pip3 install -r requirements.txt

# Create folders and copy source and config
mkdir -p /opt/aws/amazon-cloudwatch-publisher/bin/
mkdir -p /opt/aws/amazon-cloudwatch-publisher/etc/
mkdir -p /opt/aws/amazon-cloudwatch-publisher/logs/
cp amazon-cloudwatch-publisher /opt/aws/amazon-cloudwatch-publisher/bin/
cp configs/amazon-cloudwatch-publisher-rpi.json /opt/aws/amazon-cloudwatch-publisher/etc/amazon-cloudwatch-publisher.json
chown -R cwpublisher: /opt/aws/amazon-cloudwatch-publisher
chmod -R u+rwX,g-rwx,o-rwx /opt/aws/amazon-cloudwatch-publisher

# Write configuration file details
# X.509 証明書を用いた認証用に書き換えています
# sed コマンドの IOT_CERT_PATH_AND_FILENAME と IOT_KEY_PATH_AND_FILENAME の箇所は、
# /(slash)を含む文字列で置換する場合に挙動がおかしくなるため書き換えています
read -p "Region: " region
read -p "IOT_URL: " iot_url
read -p "IOT_ROLE_ALIAS: " iot_role_alias
read -p "IOT_THING_NAME: " iot_thing_name
read -p "IOT_CERT_PATH_AND_FILENAME: " iot_cert_path_and_filename
read -p "IOT_KEY_PATH_AND_FILENAME: " iot_key_path_and_filename
sed -i "s/REGION/$region/" /opt/aws/amazon-cloudwatch-publisher/etc/amazon-cloudwatch-publisher.json
sed -i "s/IOT_URL/$iot_url/" /opt/aws/amazon-cloudwatch-publisher/etc/amazon-cloudwatch-publisher.json
sed -i "s/IOT_ROLE_ALIAS/$iot_role_alias/" /opt/aws/amazon-cloudwatch-publisher/etc/amazon-cloudwatch-publisher.json
sed -i "s/IOT_THING_NAME/$iot_thing_name/" /opt/aws/amazon-cloudwatch-publisher/etc/amazon-cloudwatch-publisher.json
sed -i "s|IOT_CERT_PATH_AND_FILENAME=*|$iot_cert_path_and_filename|" /opt/aws/amazon-cloudwatch-publisher/etc/amazon-cloudwatch-publisher.json
sed -i "s|IOT_KEY_PATH_AND_FILENAME=*|$iot_key_path_and_filename|" /opt/aws/amazon-cloudwatch-publisher/etc/amazon-cloudwatch-publisher.json

# Write the daemon configuration file
# syslog の所有グループが adm のため、[Service] の Group は adm を設定しています
# ついでに、[Service] の Restart に always を設定しています
cat << EOF > /etc/systemd/system/amazon-cloudwatch-publisher.service
[Unit]
Description=amazon-cloudwatch-publisher
Requires=network.target
After=network.target
[Service]
Type=simple
User=cwpublisher
Group=adm
ExecStart=/opt/aws/amazon-cloudwatch-publisher/bin/amazon-cloudwatch-publisher
Restart=always
[Install]
WantedBy=multi-user.target
EOF

# Install the publisher as a daemon that runs at boot, and then start it
systemctl enable amazon-cloudwatch-publisher
systemctl start amazon-cloudwatch-publisher

(5) installers を実行する

ラズパイに pi ユーザでログインし、installers を実行します。

ラズパイ
$ sudo bash installers/install-rpi.bash

実行すると、途中で、Region, IOT_URL, IOT_ROLE_ALIAS などの変数値を入力するように促されるため、適宜入力をしていきます。
参考までに、今回の検証で私が入力した値は以下のようになります。

変数 入力値 備考
Region ap-northeast-1 リージョン
IOT_URL XXXXXXXXXXXXXX.credentials.iot.ap-northeast-1.amazonaws.com $ aws iot describe-endpoint --endpoint-type iot:CredentialProvider の取得値
IOT_ROLE_ALIAS MyIoTCloudWatchPublisherRoleAlias 前項で作成したロールエイリアスの名称
IOT_THING_NAME MyThing01 前項で作成した Thing の名称
IOT_CERT_PATH_AND_FILENAME /home/pi/certs/aws/device.pem.crt デバイス証明書のパス
IOT_KEY_PATH_AND_FILENAME /home/pi/certs/aws/private.pem.key 秘密鍵のパス

インストールが完了すると、amazon-cloudwatch-publisher サービスが作られ(起動もかかる)、同時に、サービスを起動するcwpublisher というユーザも作られます。

ラズパイ
$ sudo bash installers/install-rpi.bash

Publisher インストール後の動作確認

AWS マネジメントコンソールにログインし、動作確認を行います。

ログファイルの確認

今回は、ラズパイ内の /var/log/syslog が CloudWatch に連携されるように設定を行ったので、そこの確認を行います。

ロググループ名は、設定ファイルに記載したものになっています。
保持期間が、設定ファイルでは 7日間 で指定したつもりでしたが、なぜか「失効しない」になっていました。
image.png

ログファイル名が、ログストリーム名になるようです。
image.png

ログファイルの内容(中身)が、CloudWatch で確認できました。
image.png

メトリクスの確認

カスタムメトリクス名は、設定ファイルに記載したものになっています。
image.png

ディメンションについては、Python プログラム本体「/amazon-cloudwatch-publisher」の 242 行目あたりで設定されているようです。
image.png
image.png

git clone してきたままの Python プログラム本体「/amazon-cloudwatch-publisher」を編集せずにそのまま使用すると、以下の13メトリクスが取得できるようです。

今回は検証していませんが、プログラム本体を修正すれば、自分で好きなメトリクスを取得して CloudWatch と連携することもできそうです。
image.png
image.png

Publisher 自体のログは、/opt/aws/amazon-cloudwatch-publisher/logs/amazon-cloudwatch-publisher.log に出力されます。※configs ファイルで設定しています。

さいごに

今回は、IoT デバイスの監視で役立つかもしれない amazon-cloudwatch-publisher というツールを紹介しました。

もちろん、Publisher を使うだけでは監視としては不十分で、

  • メトリクスデータが異常値だった場合、それを検出してアラーム(CloudWatch Alarm)をあげる
  • ログファイルに "error" や "failed" などのエラー系メッセージが含まれていた場合に、それを検知してアラームをあげる

などの「異常が起きた場合に、人間がそれを知る仕組み」も構築する必要があると思います。

とはいえ、ログファイルやメトリクスデータと CloudWatch の連携が簡単に実現できるのは便利だなと感じました。
初回は、ポリシーやロールエイリアス作成周りが面倒ではありますが、2台目以降は使い回せるので楽を出来ると思います。
機会があれば、ラズパイ以外のデバイスを使ってみたり、独自のメトリクスデータを連携してみたり、色々試してみようと思います。

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1