要約

  • AWS IoTを使用したソリューションの数は日々増加している。
  • デバイスの数が増えるにつれて、AWSを利用したDevOps担当者は、デバイスとAWS IoTの間で障害が発生した際にログを調査する時間が増えている。
  • DevOps担当者は、予め最適かつ自動化されたログ出力を用意し、極力開発時間に専念できるようにしていきたい。
  • そこで今回、AWS IoTソリューション用の自動ログ出力スキームを提案する。

AsIs

  • AWS CLIを手動で使用して、S3からログ出力を実行している状態。
  • Slackでの問い合わせにより開発作業が停止してしまう。
  • PC利用しない限りログを取得ができず、ログファイルも大量にあるため調査時間がかかる。

ToBe

  • サーバーレスアーキテクチャを利用して、可能な限り低コストで自動ログ出力を実現させる。
  • Slackでのコミュニケーション時間を最小限に減らして開発に専念できるようにする。
  • PCや携帯にSlackをインストールしていれば、誰でもどこからでもログ出力を実行可能にさせる。

シーケンス

  • 大量のログファイルを一つのZipファイルにまとめるためにAWS Batchを採用。
  • Slackのスラッシュコマンドは3秒以内に応答が必要であることに注意。

AWS構成

コマンド仕様

指針

/iot-log [識別子] [カテゴリ] [日付(UTC)]

識別子例:

  • ThingName
  • 証明書ID
  • etc…

カテゴリ:
スラッシュコマンドとAWS Batchの仕様に応じてカテゴリのパラメーター指定は上限を設けること

  • telemetry
  • command
  • lifecycle
  • etc…

日付:

  • 例: 2023/01/01
  • ここに関してはAWS S3使用上UTCとなるので注意

Slackスラッシュコマンドの例

# 2023/10/01のTHING00001のテレメトリーを取得したい場合
/iot-log THING00001 telemetry 2023/10/01
# 2023/10/01のTHING00001のテレメトリーとコマンドを取得したい場合
/iot-log THING00001 telemetry,command 2023/10/01
# 2023年10月1日にAWS IoT Coreに接続/切断時の、すべてのデバイスのログを取得したい場合
/iot-log client_id lifecycle 2023/10/01

Slack上での表示イメージ図

ポストしたユーザーのスレッド上にて返信させる
Slackのchat.postMessageにthread_ts(timestamp)があるのでこちらを利用。

https://api.slack.com/methods/chat.postMessage

S3上の圧縮されたオブジェクトに対して、署名付きURLを発行し、有効期限の適切な値を指定。

ここでは、2023年10月1日のモノ名 THING00001 のテレメトリとコマンドログのzipファイルを生成する際に、Slackのスラッシュコマンドがどのように表示されるかの例を説明。

本間 12:21 PM
/iot-log THING00001 telemetry,command 2023/10/01 dev
-----------------------------------------------

IoTログ出力アプリ 12:21 PM
対象識別子: THING00001
ログカテゴリ: telemetry,command
指定ログ日付: 2023/10/01

ログファイルを生成中...
-----------------------------------------------

IoTログ出力アプリ 12:22 PM
対象識別子: THING00001
ログカテゴリ: telemetry,command
指定ログ日付: 2023/10/01

出力進捗状況: xxx%
-----------------------------------------------

IoTログ出力アプリ 12:23 PM
出力進捗状況: 100%
対象識別子: THING00001
ログカテゴリ: telemetry,command
指定ログ日付: 2023/10/01

ログファイル生成完了!!

下記からアクセスしてログファイルをダウンロードしてください ダウンロード可能な有効期限は60分までです。
https://xxxxx.s3.ap-northeast-1.amazonaws.com/slack/iot-logs/YYYYMMMDDhhmmss/20231001_THING00001.zip??X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=xxxxx&X-Amz-Date=xxxxx&X-Amz-Expires=xxxx&X-Amz-SignedHeaders=host&X-Amz-Signature=xxxxxx
-----------------------------------------------

ログ出力生成の進捗状況の仕様

進捗状況を%にするか文字列定義するかは実装時に考えて確定してください。

想定進捗(例):

  • starting: 10%
  • progress: 10~99%
    • <カテゴリ名> is starting
    • <カテゴリ名> is done
  • completed: 100%

zipファイルのディレクトリ構成

zipファイルをダウンロードした後に解凍後した時のディレクトリ構成を記載します。
スラッシュコマンドのパラメーターに応じてディレクトリ構成は変わります。

#例: ディレクトリ構造
root
├── telemetry_20231001.log
└── command_20231001.log

構築の流れ

  1. Slack Appの作成
    1. Slack APIのYour Appsページで新しいAppを作成。
    2. スラッシュコマンドを作成し、リクエストURLを指定。このURLは後でAWS Lambda関数のエンドポイントとなる。
  2. AWS Lambda関数の作成
    1.  Lambda関数を作成し、Slackからのリクエストを受け取るように実装。
    2. この関数内で、AWS Batchのジョブを起動するコードを実装。
  3. AWS Batchの構築&実装
    1. AWS Batchのジョブ定義、ジョブキューを構築。
    2.  ジョブのDockerコンテナ内で、S3から複数のログファイルをダウンロードし、一つのログファイルに集約して再度S3にアップロードする処理を行う。
    3.  処理に応じて、進捗状況をSlackに通知。
    4. S3にアップロードしたZipファイルに署名付きURLを生成。
  4. LambdaからBatchジョブの起動
    1. Lambda関数内で、Batchジョブを起動するAPIを呼び出す。
  5. ログファイルのSlackへの通知
    1. Batchジョブが完了したら、S3のログファイル署名付きURLをSlackに通知。
  6. セキュリティ
    1.  Slackの署名を検証して、リクエストが本当にSlackからのものであることを確認。
    2. S3のバケットポリシーやIAMロールを適切に設定して、不正なアクセスを防ぐ。
  7. テスト
    1. Slackでスラッシュコマンドを実行し、AWS Batchが起動し、Zipファイルが生成されてSlackに通知されることを確認。
    2. 署名付きURLへアクセスしてZipファイルがダウンロードできることを確認。

AWS Lambda仕様

  • SlackのスラッシュコマンドによってトリガーされるAWS Lambdaは一つだけ用意。
  • 最小実装を実現するために、API Gateway + Lambdaの代わりにLambda HTTPSエンドポイントを使用。
  • インフラ構築にはCDK、SAMなどの便利なツールを利用。
  • 今回はPythonで実装。
  • 一つのAWS Lambdaから複数のAWS Batchにジョブを提出する際には、IAMロールにAssumeRoleを定義すること。

サンプルソースコード

AWS LambdaからAWS Batchジョブをトリガーするためには、以下の手順でPythonのAWS SDK(boto3)を使用。

  1. IAMロールの設定
    1. AWS Lambda関数に適切なIAMロールを割り当てることが重要。これには、batch:SubmitJob権限を含む必要な他の権限も含まれる。
  2. AWS Lambda関数の作成:
    1. LambdaコンソールまたはAWS CLIを使用して新しいLambda関数を作成。
  3. 依存関係のインストール
    1. 必要に応じてboto3ライブラリなどの依存関係をインストール。
  4. コードの実装
    1. 以下のPythonコードスニペットは、AWS BatchジョブをトリガーするAWS Lambda関数の例を提示。
import boto3

def lambda_handler(event, context):
# Create AWS Batch client
batch_client = boto3.client('batch')

# Specify job queue and job definition
job_queue = 'your-job-queue-name'
job_definition = 'your-job-definition-name'

# Specify job name and priority
job_name = 'example-job-name'
job_priority = 1 # Priority can take a value between 1 and 99

# Submit the job
response = batch_client.submit_job(
jobName=job_name,
jobQueue=job_queue,
jobDefinition=job_definition,
priority=job_priority
)

# Log the Job ID
print(f'Job ID: {response["jobId"]}')

return {
'statusCode': 200,
'body': f'Job {response["jobId"]} submitted successfully.'
}

このコードスニペットでは、boto3を使用してAWS Batchクライアントを作成し、submit_jobメソッドを使用して新しいジョブを送信している。
ジョブキュー、ジョブ定義、ジョブ名、およびジョブの優先順位を指定する必要がある。
AWS Lambda関数のハンドラーとして使用される場合、このコードスニペットは関数がトリガーされるたびに新しいAWS Batchジョブを送信。
さらにこの関数はジョブIDをログに記録し、レスポンスでジョブIDを返却する。

AWS S3仕様

以下のバケット管理設定は例です。必要に応じて最適に設計してください。

デバイスとAWS IoTの間のログは、dev-iot-logsバケットに出力。
ログ出力の出力先もdev-iot-logsバケットで管理。

バケット名 インプットディレクトリ名 MQTT Topic
dev-iot-logs lifecycle $aws/events/presence/connected/+
$aws/events/presence/disconnected/+
dev-iot-logs telemetry iot/telemetry/#
dev-iot-logs commands iot/commands/#
#例: インプットディレクトリ構造
dev-iot-logs
├── lifecycle
├── telemetry
└── commands
#例: アウトプットディレクトリ構造
dev-iot-logs
└── slack
└── iot-logs
└── Request Time (e.g., YYYYMMMDDhhmmss)
└── 20231001_THING00001.zip

S3 ライフサイクルルールの設定

ライフサイクル設定の詳細です。
以下の設定は例です。必要に応じて最適に設計してください。

設定項目 設定値
ライフサイクルルール名 Slack2IoTLogs
ルールスコープ選択 1 つ以上のフィルターを使用してこのルールのスコープを制限する
フィルタータイプ プレフィックス slack
ライフサイクルルールのアクション オブジェクトの現行バージョンを有効期限切れにする, オブジェクトの非現行バージョンを完全に削除
オブジェクトの現行バージョンの有効期限が切れる: オブジェクト作成後の日数 1日
オブジェクトの非現行バージョンを完全に削除: オブジェクトが現行バージョンでなくなってからの日数 1日

AWS Batch仕様

以下の設定は例のため、必要に応じて最適に設計してください。
サーバーレスアーキテクチャが採用し、コンピューティングはFargateで用意。

実装内容

  1. 対象のS3バケットからログをすべてAWS CLIを利用して保存
  2. 指定されたパラメーターに応じて、対象ログをピックアップしフォーマット化してZIPファイル生成
  3. S3署名付きURLを発行
  4. Slackへ通知

ECRの設定

  • プライベートリポジトリで作成
  • ECR リポジトリ名: slack
  • スキャン頻度: push時

ジョブキューの設定

項目
ジョブキュー名 queue-sample
優先度 1
スケジュールポリシー ARN – オプション 指定なし
オーケストレーションタイプ Fargate
ジョブキューを有効にする 有効
コンピューティング環境を選択 FARGATE_SPOT

ジョブ定義の設定

項目
ジョブタイプ 単一ノード
名前 Job_Slack
実行タイムアウト 3600(1時間)
スケジュールの優先度 無効
プラットフォームタイプ Fargate
Fargate プラットフォームのバージョン default値
パブリック IP を割り当て 有効
イメージ .dkr.ecr.ap-northeast-1.amazonaws.com/slack:latest
コマンド (JSON) [“python3″,”main.py”,”generate-ziplog”,”–identifier”,”Ref::Identifier”,”–category”,”Ref::Category”,”–date”,”Ref::Date”]
vCPU 1.0
メモリ 2 GB (2048MB)
ジョブロール設定 arn:aws:iam:::role/JobRole
実行ロール arn:aws:iam:::role/TaskExecutionRole
ログ設定 awslogs: /batch/slack

CloudWatch Log Groupの作成

項目
ロググループ名 /batch/slack
保持期間の設定 30日

運用時

SlackチャンネルのCanvasに使用方法とドキュメンテーションを記述
よく使われるスラッシュコマンド(テレメトリやコマンドなど)をリストアップ

結論

IoTソリューションでのトラブルシューティングには、リアルタイムな調査と根本原因の分析が必要。
今回のスキームをもとに必要要件に応じてカスタマイズし、迅速なDevOpsを実現してください。