Lambdaで祝日判定

こんにちは。katoです。

Lambdaで祝日判定を行いインスタンス自動起動する関数を作成していきたいと思います。

 

概要

「毎朝インスタンスを起動するのが面倒」
「営業日のみインスタンス自動起動させたい」
インスタンスランニングコストを削減したい」

今回作成するLambda関数は上記のような悩みの解決につながります。

Lambdaで週末・祝日の判定を行い、営業日のみインスタンス自動起動するといった内容の関数を作成していきます。

 

祝日リストの取得

祝日判定を行うにあたって、祝日のリストが必要になります。
今回はGoogleカレンダーで取得できる祝日リストを利用したいと思います。
下記URLから自由にダウンロードでき、現在から前後1年間を含む3年間分の祝日データが取得可能となっております。

https://www.google.com/calendar/ical/ja.japanese%23holiday%40group.v.calendar.google.com/public/basic.ics

ダウンロードしてきたファイルを開いてみるとわかりますが、いろいろ書いてあってわかりづらいです。

祝日リストをダウンロードし、日付のみを抽出したものをS3バケットにアップロードするという関数をまず作成していきます。
バケット名やTopicArnは適宜変更してください。

 

import boto3
import urllib2
import re
import os

s3 = boto3.resource('s3')
bucket = s3.Bucket('xp-holiday-data')
sns = boto3.client('sns')
TOPIC_ARN = u'arn:aws:sns:ap-northeast-1:xxxxxxxxxxx:xp-sns-send'

def lambda_handler(event, context):

    url = 'https://www.google.com/calendar/ical/ja.japanese%23holiday%40group.v.calendar.google.com/public/basic.ics'
    try:
        response = urllib2.urlopen(url)
        list = response.readlines()
    except Exception as e:
        request = {
            'TopicArn': TOPIC_ARN,
            'Message': "URL is not found.\n---error message---\n"+e.message,
            'Subject': "holiday-check is failed."
        }
        sns.publish(**request)
        return 1
        
    f = open("/tmp/holiday.txt","w")
    for num in range(len(list)):
        pattern = r"DTSTART;VALUE=DATE:"
        repattern = re.compile(pattern)
        match = repattern.search(list[num-1])
        if match is None:
                pass
        else:
                f.write(list[num-1][-10:-2] + "\n")
    f.close()

    data = open('/tmp/holiday.txt', 'rb')
    result = bucket.put_object(Key='holiday.txt', Body=data)
    data.close()
    os.remove('/tmp/holiday.txt')

 

インスタンス自動起動の関数と処理をまとめてしまってもいいですが、処理の時間が長くなりますし、祝日が急に増えたり減ったりするわけでもないので、自動起動の関数とは分けて、別関数で数日おきに実行するという方法をとっております。
また、祝日リストが取得できくなった場合に備えて、リストの取得に失敗した場合はSNSで通知するように設定しています。

上記の関数を実行するとS3バケット内に「holiday.txt」というファイルが作成されます。
ファイルには下記の様に祝日の日付データのみが記載されています。

 

20170923
20180321
20180811
20170811
20160811
 
~省略~

20160503
20181224
20170102
20160101
20180505

 

祝日判定(インスタンス起動 & SNS通知)

リストの取得が完了したので祝日判定の関数を作成していきましょう。

今回は平日のみインスタンスを起動し、週末・祝日はインスタンスを起動しないという関数を作成していきます。
バケット名やTopicArnは適宜変更してください。

 

import boto3
import os
import datetime

def lambda_handler(event, context):
    instance_start()

def instance_start():
        ec2 = boto3.client('ec2', region_name='ap-northeast-1')
        desc = ec2.describe_instances(Filters=[{'Name':'instance-id','Values':[os.environ['instanceid']]}])
        status = desc['Reservations'][0]['Instances'][0]['State']['Name']
        print status
        day = datetime.date.today()
        today = day.strftime("%Y%m%d")
        week = day.weekday()

        s3 = boto3.resource('s3')
        bucket = s3.Bucket('xp-holiday-data')
        obj = bucket.Object('holiday.txt')
        with open('/tmp/holiday.txt', 'wb') as data:
                obj.download_fileobj(data)
        f = open('/tmp/holiday.txt')
        holidays = f.readlines()
        f.close()
        os.remove('/tmp/holiday.txt')

        check = today + "\n" in holidays
        print check

        if check == True:
                message = "today is holiday"
        elif week == 5 or week == 6:
                message = "today is weekend"
        elif check == False:
                print "bad day"
                if status == "running":
                        message = "instance is already running. nothing to do"
                elif status == "stopped":
                        ec2.start_instances(InstanceIds=[os.environ['instanceid']])
                        message = "instance start"
                else:
                        message = "instance is not stopped. can not start instance"
        else:
                message = "holiday check is failed"
        sns_publish(message)

def sns_publish(message):
    client = boto3.client('sns')
    response = client.publish(
        TopicArn='arn:aws:sns:ap-northeast-1:xxxxxxxxxxx:xp-sns-send',
        Message=message,
        Subject='['+os.environ['instanceid']+']:startup_instance'
    )

 

今回の関数では自動起動の対象となるインスタンスをLambdaの環境変数で指定しているので、キーに「instanceid」、値に対象のインスタンスIDを入れるようにLambdaの設定を行ってください。

あとはCloudWatch Eventsのスケジュールで毎朝Lambda関数が実行されるように設定するだけです。
平日のみインスタンス自動起動するようになります。

 

まとめ

これまでの説明でLambdaでの祝日判定が可能となり、平日のみインスタンス自動起動するといった運用が可能となります。

インスタンスの自動シャットダウンスクリプトなどと組み合わせれば、利用時間外はインスタンスを停止、利用時間になったら自動起動といった運用が可能になり、不要なランニングコストの削減につながります。
インスタンスをいちいち立ち上げる必要がないので、管理者のタスク削減やインスタンスの立ち上げ忘れ防止などにもつながります。

利用の少ない時間帯にインスタンスを停止することを検討している場合などは、本記事のようなLambdaでの自動起動を導入してみてはいかがでしょうか。

 

 

 

アプリケーション開発バナー