最近個人で運営しているサービスをGCPからAWSへと少しづつ移行している
今回はElasticBeanstalk(以降はEB)の自動停止・自動起動をする
個人で使用しているアプリケーションなので、お金は出来るだけ安くしたい
そこで深夜とか使わない時間は止めて朝方とかに立ち上げることにした
EB, RDSとそれぞれの自動停止・自動起動の仕方を探る
まずEBの自動停止・起動だがEBにはCLIがあり今バージョン3で2の時はeb stop
, eb start
があったので停止と起動がコマンド一発で出来た
しかしバージョン3では廃止された
公式のdocによるとeb stop
はeb terminate
して環境を終了して
起動する時はeb start
ではなくeb create
で環境を作成してくれとのこと
RDSに関しては今までだと自動停止・自動起動というと停止する時にスナップショットをとってインスタンスを削除して、起動する時にスナップショットから復元するような方法だった
それはさすがに面倒だなと思ったら最近リリースされましたね!!
https://forums.aws.amazon.com/ann.jspa?annID=4670
RDSのコマンド一発で停止・起動できるようになりました
ただ現状だといくつかの制約事項があるようです ※ 2017/07/05時点
- Amazon RDS で MySQL、MariaDB、PostgreSQL、Oracle、および SQL Server の DB インスタンスを停止/開始できる
- つまり現状Auroraは対象ではないようです
- 停止/開始の対象となるのは、リードレプリカ設定の一部ではないシングル AZ 配置で実行中の DB インスタンス
今回はMultiAZではなくシングルでかつMySQLでたててたので停止が出来そう
実際の作業
どこかのサーバーでcronでコマンドを実行しても良いですが、ここはLambdaの出番です
LambdaでCloudWatch Eventsでスケージューリングして自動起動・停止を行います
Lambdaのコンソール上でコードを書いても良かったのですが、Pythonを使う際のawsのsdkのbotoがまだRDSのstop,startに対応しているバージョンでなかったので自前で最新版のものを用意してあげる必要がありました
それには以前も使用したServerless Frameworkを使用します
serverless用のyamlファイルは下記のようなものを定義しました
service: eb-rds-auto-service frameworkVersion: ">=1.14.0 <2.0.0" provider: name: aws runtime: python2.7 stage: prod region: ap-northeast-1 role: arn:aws:iam::111111:role/hoge plugins: - serverless-python-requirements functions: start: handler: handler.start events: - schedule: cron(0 1 ? * * *) # UTC stop: handler: handler.stop events: - schedule: cron(0 13 ? * * *) # UTC
Pythonのhandlerは下記のようなものを使用します
# -*- coding: utf-8 -*- from __future__ import print_function import json import boto3 from datetime import datetime eb = boto3.client('elasticbeanstalk') rds = boto3.client('rds') rds_instance_identifier = "test-mysql" application_name = "app_name" template_name = "template_name" environment_name = "test-env" app_version_label = 'app-eeee-111111_2222222' def stop(event, context): try: rds.stop_db_instance( DBInstanceIdentifier=rds_instance_identifier ) eb.describe_environments( ApplicationName=application_name, EnvironmentNames=[ environment_name ] ) env = [ env for env in response['Environments'] if env['EnvironmentName'] == environment_name ][0] env_id = env['EnvironmentId'] eb.delete_configuration_template( ApplicationName=application_name, TemplateName=template_name ) eb.create_configuration_template( ApplicationName=application_name, EnvironmentId=env_id, TemplateName=template_name ) eb.terminate_environment( EnvironmentId=env_id ) return { "status": "ok" } except Exception as e: print(e) message = 'STOP ERROR' raise Exception(message) def start(event, context): try: rds.start_db_instance( DBInstanceIdentifier=rds_instance_identifier ) eb.create_environment( ApplicationName=application_name, EnvironmentName=environment_name, TemplateName=template_name, CNAMEPrefix=environment_name, VersionLabel=app_version_label ) return { "status": "ok" } except Exception as e: print(e) message = 'START ERROR' raise Exception(message)
後はrequirements.txt
boto3>=1.4.4
handler内では停止の場合はRDSの停止とEBの環境の設定をS3に保存した上で環境の削除をしています
環境の設定を保存することでstartする時にその設定をロードすることで同じ環境を再現できる
ただ保存された環境に何のアプリケーションを動かすかまでは入っておらずcreate_environment
する時にはVersionLabelを指定する必要がある
でないとサンプルアプリケーションが立ち上がる
serverless deploy
でLambdaに停止、起動とそれぞれの関数が定義されて指定時間になると自動停止、自動起動を行ってくれる
これで節約しながらサービスを運用できそう!!