[Java] EC2を朝起動して夜間と土日祝日は自動的に停止状態にするLambdaファンクション

2017.01.17

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

はじめに

開発環境のサーバを業務時間内だけ起動状態してくれるLambdaファンクションです。Node.jsで書かれたものは以前このブログで紹介しているのですが、私はNode.jsが苦手なのでJavaで書いてみました。ついでに祝日やお盆休みなど会社の休みも環境を使わないと思いますので起動しないようになっています。IAMロールの作成やスケジュールイベントの登録方法はNode.jsと変わらないので以下の記事をご覧ください。

LambdaのScheduleイベントでEC2を自動起動&自動停止してみた#reinvent

実装する

1つのファンクションで起動・停止を行うようになっています。実行した時間が午前の場合は起動、午後の場合は停止になっています。 起動停止する時間はLambdaのスケジュールイベントを設定した時間になります。 起動したい時間(午前)と停止したい時間(午後)でスケジュールイベントを登録してください。 祝日の判定は以下のサイトのソースコードを使われていただきました。

Java祝日計算

package ec2control;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collection;
import java.util.Date;
import java.util.TimeZone;

import com.amazonaws.regions.Region;
import com.amazonaws.regions.Regions;
import com.amazonaws.services.ec2.AmazonEC2Client;
import com.amazonaws.services.ec2.model.StartInstancesRequest;
import com.amazonaws.services.ec2.model.StopInstancesRequest;
import com.amazonaws.services.lambda.runtime.Context;
import com.amazonaws.services.lambda.runtime.RequestHandler;

import yip.jholiday.Holiday;

public class Ec2StartStopFunctionHandler implements RequestHandler<Object, Object> {

	// 会社が休みの日(正月休み、お盆休みなど)
	private String[] companyHolidays2017 = {"1/3", "8/10", "8/11", "8/14" ,"8/15", "12/28" ,"12/29"};

	@Override
	public Object handleRequest(Object input, Context context) {
		// 対象のインスタンスIDを追加していきます。
		Collection<String> instanceIds = new ArrayList<String>();
		instanceIds.add("[インスタンスID]");
		
		AmazonEC2Client client = new AmazonEC2Client();
		client.setRegion(Region.getRegion(Regions.AP_NORTHEAST_1));

		Calendar cal = Calendar.getInstance();
		cal.setTimeZone(TimeZone.getTimeZone("JST"));
		cal.setTime(new Date());
		int year = cal.get(Calendar.YEAR);
		int month = cal.get(Calendar.MONTH) + 1;
		int date = cal.get(Calendar.DATE);

		boolean holidayFlag = false;
		int[] publicHolidays = Holiday.listHoliDays(year, month);

		for(int publicHoliday : publicHolidays){
			// 祝日かどうかを判定する
			if(publicHoliday == date) {
				holidayFlag = true;
				break;
			}
		}

		if(!holidayFlag) {
			// 会社が休みの日かどうかを判定する
			for(String companyHoliday :  companyHolidays2017){
				String[] array = companyHoliday.split("/");
				int companyHolidayMonth = Integer.valueOf(array[0]);
				int companyHolidayDate = Integer.valueOf(array[1]);

				if(companyHolidayMonth == month
						&& companyHolidayDate == date) {
					holidayFlag = true;
					break;
				}
			}
		}

		if(holidayFlag) {
			// 休みの場合、念のため停止する
			StopInstancesRequest request = new StopInstancesRequest().withInstanceIds(instanceIds);
			client.stopInstances(request);
			return null;
		}

		// 休みではない場合
		int hour = cal.get(Calendar.HOUR_OF_DAY);
		if(hour <= 11) {
			// 午前だったらスタート
			StartInstancesRequest request = new StartInstancesRequest().withInstanceIds(instanceIds);
			client.startInstances(request);
		} else {
			// 午後だったらストップ
			StopInstancesRequest request = new StopInstancesRequest().withInstanceIds(instanceIds);
			client.stopInstances(request);
		}

		return null;
	}
}

スケジュールを設定する

平日午前9時に起動し、午後9時に終了するCron式は以下になります。UTC時間での指定になるので日本時間から9時間引く必要があります。

↓ 起動
cron(0 0 ? * MON-FRI *) 

↓ 停止
cron(0 12 ? * MON-FRI *)

最後に

1年の3分の1位は休日で1日の半分位は業務時間外だと思いますので、使う時間だけ起動すれば費用を3分の1位に押されることができると思います。 本番環境でバッチサーバなど常に動いていないサーバがあるかもしれませんが、EC2は自動起動・停止はトラブルのもとになることもあるのでリザーブドインスタンスを使うかLambdaなど他のサービスを使うことをおすすめします。Python版をお探しの方は以下をご覧ください。

LambdaとCloudWatch EventsでEC2の自動起動&自動停止をやってみた(Python版)