使わない時間帯はRDSを自動停止しよう!(AWS, Terraform)
はじめに
RDSの料金高いですよね、、
個人開発のサービス(特にポートフォリオ)では常時アプリケーションが起動している必要はないケースが多いかなと思います。となると使っていない時間帯のリソースは停止しておきたいですね。
今回は自動でRDSを起動停止する実装をTerraformで実装したのでご紹介できればと思います。
注意書き
- 細かい料金計算は行いません
- 今回は自動起動停止のやり方の紹介でありそれに至るまでのネットワーク構築等は割愛させていただきます🙇♂️(記事のボリュームが大きくなるため)モジュールの定義等はイメージとして捉えていただけると嬉しいです。
最終的なディレクトリ構成
├── enviroments
│ └── stg
│ └── api
│ └── rds.tf
└── modules
├── sg
│ ├── main.tf
│ ├── output.tf
│ └── variable.tf
└── rds
├── assume_policy.json
├── main.tf
├── output.tf
├── rds_scheduler_policy.json
└── variable.tf
実際は他のリソースもありますが今回説明のために省略しております。
モジュールをmodules
フォルダ内にて定義し、リソース名.tf
で利用するという一般的流れで実装しております。
/modules/rds/main.tf
共通化するリソースの定義を行います。またvariable.tf
ではrds.tf
にて変数とし扱うものの定義、output.tf
では他リソースで参照したいRDSリソース等の値を定義しています。
(一旦DBインスタンスを作成するまでを記述しております)
# RDSパラメーターグループ、オプショングループ、サブネットの記述(省略)
~
# RDS Instance
resource "aws_db_instance" "main" {
identifier = "${var.common_name}-${var.enviroment}"
db_name = replace(var.db_name, "-", "_")
instance_class = var.db_instance_class
engine = var.engine
engine_version = var.engine_version
allocated_storage = 20
max_allocated_storage = 20
storage_type = "gp2"
storage_encrypted = false
username = replace(var.db_user_name, "-", "_")
password = random_password.rds.result
multi_az = var.multi_az
publicly_accessible = false
backup_window = "14:10-14:40"
backup_retention_period = 7
maintenance_window = "mon:13:10-mon:13:40"
auto_minor_version_upgrade = true
deletion_protection = false
skip_final_snapshot = true # 削除時のスナップショットは作成しない
port = var.port
apply_immediately = false
vpc_security_group_ids = [module.rds_security_group.security_group_id]
parameter_group_name = aws_db_parameter_group.main.name
option_group_name = aws_db_option_group.main.name
db_subnet_group_name = aws_db_subnet_group.main.name
enabled_cloudwatch_logs_exports = var.enabled_cloudwatch_logs_exports
lifecycle {
ignore_changes = [password]
}
}
# SSM等の設定
~
enviroments/stg/api/rds.tf
今回はMYSQLで構築しております。エンジンの種類による自動起動停止の差異はないかと思われます。
module "rds_stg" {
enviroment = var.environment
source = "../../../modules/rds"
common_name = var.common_name
parameter_group_family = "mysql8.0"
engine = "mysql"
major_engine_version = "8.0"
engine_version = "8.0"
db_instance_class = "db.t3.micro"
db_name = "${var.common_name}-rds-${var.environment}"
db_user_name = var.service
multi_az = true
port = 3306
vpc_id = # VPCのidを設定
cidr_blocks = # subnetのCIDRブロックを設定
private_subnet_ids = # subnetのIDを設定
enabled_cloudwatch_logs_exports = ["error", "general", "slowquery"]
}
上記設定した上でterraform apply
を行いRDSを構築します。
<本題>自動起動停止の設定
IAMポリシー群の用意
/modules/rds/assume_policy.json
今回はEventBridgeSchedulerを用いるのでassume用のポリシーのprincipalにはscheduler.amazonaws.com
を設定します。
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": ["scheduler.amazonaws.com"]
},
"Action": "sts:AssumeRole"
}
]
}
/modules/rds/rds_scheduler_policy.json
scheduler用の実行ロールにはRDS起動・停止のためrds:StartDBInstance
,rds:StopDBInstance
をAction
セクションに設定します。
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "rdsstop",
"Effect": "Allow",
"Action": [
"rds:StartDBInstance",
"rds:StopDBInstance"
],
"Resource": "*"
}
]
}
/modules/rds/main.tf
に対しschedulerを記述
先程記述した/modules/rds/main.tf
にscheduler実行に必要なリソースを書いていきます。
今回実装のポイントとしてはaws_scheduler_schedule
のtarget
内の設定だと思います。
arn
arnは色々な取得の仕方がありますが自分はAWSコンソールのEventBridgeSchedulerより取得しました。
arnはarn:aws:scheduler:::aws-sdk:{リソース名}:{アクション名}
という形式だと思います。
input
startDBInstance
,stopDBInstance
の場合以下の形になると思います(こちらもAWSコンソールより決められた形を確認)
アクションごとに求められている形が違うため確認が必要です。
{
"DbInstanceIdentifier": "{RDSのID}"
}
/modules/rds/main.tf
# EventBridgeScheduler Resources
# Iamリソース群前項で作成したポリシー等を用いる
resource "aws_iam_role" "rds_scheduler_stg" {
name = "${var.common_name}-rds-scheduler-${var.enviroment}-role"
assume_role_policy = file("${path.module}/assume_policy.json")
}
resource "aws_iam_policy" "rds_scheduler" {
name = "${var.common_name}-rds-scheduler-${var.enviroment}-policy"
policy = file("${path.module}/rds_scheduler_policy.json")
}
resource "aws_iam_role_policy_attachment" "rds_scheduler" {
role = aws_iam_role.rds_scheduler_stg.id
policy_arn = aws_iam_policy.rds_scheduler.arn
}
# いかに起動・停止時間を設定
locals {
stop_rds_schedule = "cron(0 15 * * ? *)" // 00:00 JST
start_rds_schedule = "cron(30 10 * * ? *)" // 19:30 JST
}
# Stop RDS
resource "aws_scheduler_schedule" "rds_stop_stg" {
name = "${var.common_name}-stop-scheduler-${var.enviroment}"
schedule_expression = local.stop_rds_schedule
flexible_time_window {
mode = "OFF"
}
target {
arn = "arn:aws:scheduler:::aws-sdk:rds:stopDBInstance"
role_arn = aws_iam_role.rds_scheduler_stg.arn
input = jsonencode({
DbInstanceIdentifier = aws_db_instance.main.identifier
})
}
}
#Start RDS
resource "aws_scheduler_schedule" "rds_start_stg" {
name = "${var.common_name}-start-scheduler-${var.enviroment}"
schedule_expression = local.start_rds_schedule
flexible_time_window {
mode = "OFF"
}
target {
arn = "arn:aws:scheduler:::aws-sdk:rds:startDBInstance"
role_arn = aws_iam_role.rds_scheduler_stg.arn
input = jsonencode({
DbInstanceIdentifier = aws_db_instance.main.identifier
})
}
}
上記をterraform apply
すれば自動起動停止を実装できるはずです、、!
まとめ
読んでいただきありがとうございました。つまったポイントとして今回DBインスタンスの起動停止にもかかわらずstartDBCluster
を選んでおりました、、理解が浅い証拠ですね、、
実装自体は比較的簡単にできたと思います。もう一つコストのかかるリソースとしてNATGatewayもあげられますがこちらに関しても実行を行なってみたので記事にできればと思います✊
Discussion