はじめに
皆さま初めましてjimbot3です。botと入っておりますがchatbotではなくboto3のほうです。
Qiitaを読む習慣を持っている皆さまであれば、1度は"サーバレス"という単語を聞いたことがあると思います。
ざっくり言うと「サーバを持たずコードを実行する仕組み」のこと。
今回以下の理由でAWSのサーバレスアーキテクチャ"Lambda"とLambdaのトリガ用に"CloudWatch"を使ってサーバレスを体験してみました。
- これから徐々に、もしくは急激にサーバレスは流行るだろうと考えている(jimbot3リサーチ社調べ)
- 自分のコーディングスキルを向上させたい
- 「この前サーバレスやってみたんだけど~」とか言ってみたい
- 研修でLambdaを使う課題が出されたから、と言う最たる理由があったりなかったり
つくってみたLambdaとその解説
1. EC2インスタンスの起動・停止をLambda経由でSNS(E-mail)通知するLambda
実行するLambdaの中身
import boto3
def lambda_handler(event, context):
ec2 = boto3.client('ec2')
sns = boto3.client('sns')
mystatus= ec2.describe_instances(
Filters=[{'Name':'instance-id', 'Values':['i-04XXXXXXXXXXXXXXX']}] # 取得したいEC2のインスタンスIDを入力
)["Reservations"][0]["Instances"][0]['State']['Name']
if mystatus == 'stopped':
print ('stop!!')
topic = 'arn:aws:sns:ap-northeast-1:08XXXXXXXXXX:Myaddress' # 送付したいARNを入力
subject = 'StatusChangeMail'
message = 'stop!!'
region = '{ap-northeast-1}'
response = sns.publish(
TopicArn=topic,
Message=message,
Subject=subject,
MessageStructure='raw'
)
elif mystatus == 'running': # 後程説明しますが、今回はCloudWatchでrunningとstoppedの値のみ拾う仕様なのでelifでrunningを指定しております
print ('running!!')
topic = 'arn:aws:sns:ap-northeast-1:08XXXXXXXXXX:Myaddress' # 送付したいARNを入力
subject = 'StatusChangeMail'
message = 'running!!'
region = '{ap-northeast-1}'
response = sns.publish(
TopicArn=topic,
Message=message,
Subject=subject,
MessageStructure='raw'
)
- mystatusに取得したいEC2インスタンスのStatusを格納
- 停止(==stop)状態であればstopメッセージ/起動(==start)状態であればstartメッセージのメール送付
↑のLambdaをトリガーさせるCloudWatchの中身
※Lambda画面からCloudWatch Eventsの設定もできますが今回はCloudWatch画面での設定
取得したいEC2のインスタンスIDを指定し、起動もしくは停止したのをトリガーにLambdaを実行
届いたメールはこんな感じ
つまりLambdaとCloudWatchを使うことで以下のような通知が行える
- EC2のステータス変更をきっかけにCloudWatchがLambdaを実行
- LambdaでEC2の状態を拾いメール
2. 24hのEC2インスタンスの起動・停止の集計をSNS(E-mail)通知するLambda
実行するLambdaの中身
import boto3
import datetime
import calendar
def lambda_handler(event, context):
group_name = '/aws/lambda/XXXXXXX' # 先ほどの1.でつくった起動停止Lambdaのロググループを入力
client = boto3.client('logs')
timeto = datetime.datetime.now() # print(timeto) → 2018-12-08 10:24:20.499645
u_to = calendar.timegm(timeto.utctimetuple()) * 1000 # print(u_to) → 1544264660000
timefrom = timeto - datetime.timedelta(days=1) # print(timefrom) → 2018-12-07 10:24:20.499645
u_from = calendar.timegm(timefrom.utctimetuple()) * 1000 # print(u_from) → 1544178260000
#u_toとu_fromを*1000したのは後のfilter_log_eventsの期間指定がミリ秒のため
response = client.filter_log_events(
logGroupName=group_name,
startTime = u_from,
endTime = u_to
)
#print(response) → {'events': [], 'searchedLogStreams': [], 'ResponseMetadata': {'RequestId': '6cdXXXXX~
file_name = '/tmp/' + 'temp.log'
with open(file_name, 'a') as f:
for stream in response['events']:
message = '[{}] {}'.format(datetime.datetime.fromtimestamp(int(str(stream['timestamp'])[:10]))
+ datetime.timedelta(hours=9), stream['message']) #[:10]はミリ秒を秒に直すため14桁のうち左から10桁
f.write(message)
topic = 'arn:aws:sns:ap-northeast-1:08XXXXXXXXXX:Myaddress' # 送付したいARNを入力
subject = '過去24時間分 起動停止記録'
file = open(file_name, 'r')
message = file.read()
sns = boto3.client('sns')
region = '{ap-northeast-1}'
if message:
response = sns.publish(
TopicArn=topic,
Message=message,
Subject=subject,
MessageStructure='raw'
)
else:
response = sns.publish(
TopicArn=topic,
Message="直近24時間に起動/停止はございませんでした。",
Subject=subject,
MessageStructure='raw'
)
- 先ほどの1. で作った起動停止Lambdaの過去24時間(1日)のログを"client.filter_log_events"でフィルターし拾う。
- そのログをtempファイルとしてに1行ずつフォーマットしながら格納。
- 格納が終わったファイルを開き、そのtempファイルの中身をメッセージとしてメールの送付。ログが無い場合は起動/停止が無かった旨のメールを送付。
↑のLambdaをトリガーさせるCloudWatchの設定
Lambdaをcronでスケジュール実行。(0 8 * * ? *)だと日次で日本時間の17:00に実行されます。
届いたメールはこんな感じ
つまりLambdaとCloudWatchを使うことで以下のような通知が行える
- 日次で日本時間の17:00にCloudWatchがLambdaを実行
- Lambdaで過去24hのEC2インスタンスの起動停止履歴を拾いメール
感想
- 取り敢えず1回手を動かすとサーバレスの仕組みは"何となく"理解できる。
- 1回触ってみると「次はこれを作ってみよう」と思えるので、成長のサイクルが生まれるサービスだと思う。
(例えばEC2やVPCを1回作っても、もう1回作ってみようとは私は思えなかった。) - LambdaはAWSのサービスであるが必要なのはプログラミングスキルで、AWSコンソールのボタンポチポチスキルではない。