AWS 環境で IoT デバイスの監視を行う際に役立ちそうなツールを調べていたところ、 amazon-cloudwatch-publisher
というツールが GitHub の AWS Labs リポジトリ にて公開されていたので、勉強がてら検証してみました。
・amazon-clourwatch-publisher
何ができるのか?
デバイス内のログファイルや、CPUやメモリ使用率などのメトリクスデータを、簡単に CloudWatch に連携できるようになります。
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
は後工程で使用するため控えておきます。
$ 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"
}
}
(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
は後工程で使用するため控えておきます。
$ 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"
}
Windows 機に、ポリシードキュメント用の Json ファイルを作成します。
検証が目的であるため、Resource
には *
を指定しています。
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"iot:Publish",
"iot:Subscribe",
"iot:Receive",
"iot:Connect"
],
"Resource": [
"*"
]
}
]
}
ポリシーを作成します。
レスポンスの policyName
は後工程で使用するため控えておきます。
$ 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"
}
作成したポリシーをデバイス証明書にアタッチします。
--target
には、前工程で作成したデバイス証明書の Arn が入り、具体的には arn:aws:iot:ap-northeast-1:123XXXXXXXXX:cert/adXXXXXXXXXXXXXXXX
のような値になります。
$ aws iot attach-policy --policy-name "MyThing01Policy" --target "certificateArn"
デバイス証明書を AWS IoT のモノのリソースにアタッチします。
--principal
には、前工程で作成したデバイス証明書の Arn が入ります。
$ aws iot attach-thing-principal --thing-name "MyThing01" --principal "certificateArn"
これでプロビジョニングは終わりとなります。
デバイスに 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 公式ドキュメントが参考になるので、この内容をなぞっていく形で進めていきます。
以下、AWS マネジメントコンソールから抜粋した、ロールエイリアスの説明となります。
AWS IoT ロールのエイリアスは、認証されたデバイスに、デバイスの証明書にアタッチされたポリシーに含まれていない AWS サービスへの一時的なアクセス権を付与します。
Windows 機に、ポリシードキュメント用の Json ファイルを作成します。
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "credentials.iot.amazonaws.com"
},
"Action": [
"sts:AssumeRole"
]
}
]
}
IAM Role を作成します。
レスポンスの RoleName
と Arn
は、後工程で使用するため控えておきます。
$ 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
には *
を指定しています。
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"cloudwatch:PutMetricData",
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:DescribeLogGroups",
"logs:DescribeLogStreams",
"logs:PutLogEvents"
],
"Resource": [
"*"
]
}
]
}
IAM Role にアタッチするポリシーを作成します。
レスポンスの Arn
は、後工程で使用するため控えておきます。
$ 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 に ポリシーをアタッチします。
$ aws iam attach-role-policy --policy-arn arn:aws:iam::123XXXXXXXXX:policy/MyIoTCloudWatchPublisherPolicy --role-name MyIoTCloudWatchPublisherRole
ロールエイリアスを作成します。
レスポンスの roleAlias
は、後工程で使用するため控えておきます。
$ 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つ目の要素を追記します。
(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
を使用します。
検証用に、以下のような書き換えを行いました。
- 認証周りの
authentication
の記述が Cognito 認証用の記述になっているため、X.509 証明書を用いた記述に書き換える - 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
を使用します。
こちらも、以下のような書き換えを行いました。
- Cognito 認証ではなく、X.509 証明書を用いた認証を行うためそれ用に書き換える
-
/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日間 で指定したつもりでしたが、なぜか「失効しない」になっていました。
ログファイルの内容(中身)が、CloudWatch で確認できました。
メトリクスの確認
カスタムメトリクス名は、設定ファイルに記載したものになっています。
ディメンションについては、Python プログラム本体「/amazon-cloudwatch-publisher
」の 242 行目あたりで設定されているようです。
git clone してきたままの Python プログラム本体「/amazon-cloudwatch-publisher
」を編集せずにそのまま使用すると、以下の13メトリクスが取得できるようです。
今回は検証していませんが、プログラム本体を修正すれば、自分で好きなメトリクスを取得して CloudWatch と連携することもできそうです。
Publisher 自体のログは、/opt/aws/amazon-cloudwatch-publisher/logs/amazon-cloudwatch-publisher.log
に出力されます。※configs ファイルで設定しています。
さいごに
今回は、IoT デバイスの監視で役立つかもしれない amazon-cloudwatch-publisher
というツールを紹介しました。
もちろん、Publisher を使うだけでは監視としては不十分で、
- メトリクスデータが異常値だった場合、それを検出してアラーム(CloudWatch Alarm)をあげる
- ログファイルに "error" や "failed" などのエラー系メッセージが含まれていた場合に、それを検知してアラームをあげる
などの「異常が起きた場合に、人間がそれを知る仕組み」も構築する必要があると思います。
とはいえ、ログファイルやメトリクスデータと CloudWatch の連携が簡単に実現できるのは便利だなと感じました。
初回は、ポリシーやロールエイリアス作成周りが面倒ではありますが、2台目以降は使い回せるので楽を出来ると思います。
機会があれば、ラズパイ以外のデバイスを使ってみたり、独自のメトリクスデータを連携してみたり、色々試してみようと思います。