Lambda、Amazon SNS、CloudWatchを使ってサーバの死活監視を行う方法（備忘録）

作業の備忘録としてまとめました。
# やりたいこと
- 15分間隔でWebサーバーを死活監視
- 正常動作はリクエストした時に帰ってくるHTTPステータスコード（200）で判断
- エラーが発生した場合はメール通知

# 参考にした記事
- [サーバ死活監視→メール送付をLambda、AmazonSNSで実現する](https://qiita.com/kojiisd/items/bbbb1994fe23d0bf3a2c)

# 事前準備
- ローカル環境にPython3をインストールしておくこと

# 手順
本設定は以下の手順で行いました。
なお、手順1~手順3はローカル環境、手順4～手順8のはAWS上での作業となります。

### **1. PythonのHTTPライブラリのインストール**
カレントディレクトにlibサブディレクトリを作成して、libサブディレクトリにHTTPライブラリをインストールします。

```
# pip install -U requests -t ./lib
```

### **2. 死活監視用のプログラム作成**
[上記記事](https://qiita.com/kojiisd/items/bbbb1994fe23d0bf3a2c)を参考にプログラムを作成。作成したプログラムは、libサブディレクトリと同じ階層に配置します。

```Python:checkservers.py
import json
import boto3
import sys
import os

sys.path.append(os.path.join(os.path.abspath(os.path.dirname(__file__)), 'lib'))

import requests

BUCKET_NAME = os.environ['BUCKET_NAME']
OBJECT_NAME = os.environ['OBJECT_NAME']
SNS_TOPICS_NAME = os.environ['SNS_TOPICS_NAME']

def lambda_handler(event, context):
	if len(BUCKET_NAME) == 0 or len(OBJECT_NAME) == 0 or len(SNS_TOPICS_NAME) == 0:
		print("Please input BUCKT NAME, OBJECT NAME and SNS_TOPICS_NAME")
		sys.exit()
	target_json = get_target_servers()
	check_target_servers(target_json)

def get_target_servers():
	s3 = boto3.resource('s3')
	obj = s3.Object(BUCKET_NAME, OBJECT_NAME)
	response = obj.get()
	body = response['Body'].read()
	return body.decode('utf-8')

def check_target_servers(target_json):
	data = json.loads(target_json)
	servers = data['servers']

	error_servers = []

	for server in servers:
		name = server['name']
		url = server['url']
		try:
			res = requests.get(url)
			if res.status_code != 200:
				error_servers.append(server)
		except Exception:
			error_servers.append(server)
	
	if len(error_servers) == 0:
		print("Successful finished servers checking")
	else:
		response = send_error(name, url, error_servers)
		print("Error occured:")
		print(response)
		print(error_servers)

def send_error(name, url, error_servers):
	sns = boto3.client('sns')
	sns_message = "Error happens:\n\n" + json.dumps(error_servers, indent=4, separators=(',', ': '))
	subject = '[ServerMonitor] Error happens'
	response = sns.publish(
		TopicArn=SNS_TOPICS_NAME,
		Message=sns_message,
		Subject=subject
	)
	return response

```

### **3. Lambdaにアップロードするzipファイルの作成**
手順1と手順2で作成したlibサブディレクトリとプログラム（checkserver.py）をzip化します。この時、libサブディレクトリとプログラムを配置したディレクトリをzip化するのではなく、libサブディレクトリとプログラムを直接zip化します。
（Lambda関数のハンドラー設定に関わってくるので注意が必要）

### **4. Amazon SNSの通知設定**
死活監視でエラーとなった際に通知するメール送信先を設定します。
Amazon SNSコンソールを開き、「新しいトピックの作成」からトピックを作成します。「サブスクリプションの作成」でプロトコルに「Email」、エンドポイントにエラー通知先のメールアドレスを入力してサブスクリプションを作成します。

### **5. IAM ロールの作成**
Lambda関数作成時に設定するロールを作成します。アタッチするポリシーには、以下の３つを選択します。
（ポリシーの内容をきちんと確認できていませんが、動作確認は取れています）

- AWSLambdaBasicExecutionRole
- AWSLambdaS3ExecutionRole-XXXXXXXXXXXXX
- AmazonSNSFullAccess

### **6. S3に監視対象を記載したjsonファイルをアップロード**
手順2で作成したプログラムから参照する監視対象を記載したjsonファイルをS3のバケットにアップロードします。jsonファイルは、以下のように記載します。

```
{
	"servers": [
		{ "name": "server_name_1", "url": "https://www.google.co.jp" },
		{ "name": "server_name_2", "url": "https://www.yahoo.co.jp" }
	]
}
```

### **7. Lambda関数の作成**
関数の作成は「設計図」からではなく「一から作成」を選択します。続いて、任意の名前を入力してからランタイムに「Python 3.6」を選択、ロールには手順5で作成したロールを選択します。

関数を作成した後、関数コードのコードエントリタイプから「.zipファイルをアップロード」を選択し、手順3で作成したzipファイルをアップロードします。ハンドラは、***「プログラムファイル名.プログラム内のハンドラー定義」***の形で設定します。例えば、アップロードしたプログラムが「checkservers.py」の場合、「checkservers.lambda_handler」と設定します。この時、手順3の内容で正しくzip化しておかないとハンドラが認識されないため注意が必要です。
![2.png](https://qiita-image-store.s3.amazonaws.com/0/137478/40e26f05-6fd0-f872-a7af-ade446264fac.png)

次に、プログラム内で参照している環境変数を定義します。

- BUCKET_NAME　　：jsonファイルをアップロードしたバケット名
- OBJECT_NAME　　：アップロードしたjsonファイル名
- SNS_TOPICS_NAME：手順4で作成したAmazon SNSのトピックのARN
![3.png](https://qiita-image-store.s3.amazonaws.com/0/137478/ab00f352-e995-2d27-d287-708749a54731.png)

ここで、テストを行います。テストイベントの設定でイベントテンプレートに「Amazon CloudWatch」を選択し、任意のイベント名を設定してテストイベントを作成します。このとき、テンプレートの内容を変更する必要はありません。
![4.png](https://qiita-image-store.s3.amazonaws.com/0/137478/cacb8ade-f583-171f-4e12-26b148334b4c.png)
テストを実行する際、jsonファイルに実在しないURLを設定しておけば、死活監視のエラーメールが送信されることを確認することができます。テストに失敗する場合は、「所要時間」や「使用中の最大メモリ」の結果を確認し、必要に応じて基本設定の「メモリ（MB）」や「タイムアウト」を調整してください。

### **8. CloudWatch イベントのルール作成**
CloudWatch イベントのルールを作成します。今回は15分間隔で死活監視を行うため、イベントソースのスケジュールから「一定の速度」を選択して15分を設定します。ターゲットの機能には、作成したLambda関数を設定します。
![5.png](https://qiita-image-store.s3.amazonaws.com/0/137478/ce668e99-1f8a-dc26-1865-e065880f60f9.png)

最後に、Lambda関数のDesignerから「CloudWatch Events」を選択して、作成したルールが正しく設定されていることを確認できたら終了です。設定が「有効」になっていることも忘れずに確認してください。
![6.png](https://qiita-image-store.s3.amazonaws.com/0/137478/93cecb68-76cc-fb03-304c-16621130bf4c.png)