【AWS】TerraformでマルチAZ構成のインフラを構築をやってみた(マルチAZ, RDS, ALB, AutoScaling)
こんにちは、駆け出しエンジニアです。
前回に引き続きAWSとTerraformの記事になります。
そろそろRoR
とかVue
の方を勉強していかないとまずいのではと焦っています。
何をやるか
前回同様に、AWSアソシエイト
の勉強を継続しています。
自分の引き出しを増やしておきたいと思いどうせなら構築はTerraform
でやってみようと四苦八苦中です。
今回は前回作成した構成をより冗長性が高い構成にしようという記事です。
(経緯)
- 12月の記事で、Terraformを利用したインフラ構築をやってみました。
- 前回の記事で、Well-Architected フレームワーク(以下から、W-A)についてまとめました。
- 本記事では、作成したインフラ構成を少しでも
W-A
に沿った構成に改善していきたいと思います。
得られた内容
- 冗長性を高めるためのインフラ構成案
- ELB、AutoScalingについての概要
- RDSの概要
- Terraformを利用した、
マルチAZ
、ELB(Elastic Load Balancer)
、RDS(Relational Database Service)
の構築
12月に作成したインフラの構成
- 単一のAZに1つの
VPC
を作成 - 作成した
VPC
内にパブリックサブネットとプライベートサブネットを1つずつ構築 - パブリックサブネット内に、Webサーバを想定したEC2インスタンスを1つ構築
- プライベートサブネット内に、DBサーバを想定してEC2インスタンスを1つ構築
- DBサーバには、EC2インスタンスからしかトラフィックを受け取らないように、セキュリティグループを設定
W-A
にこれでもかというぐらい意に反した、我が道を行く構成になっています。
改善すべき点の洗い出し
冗長性を高めるための基本的な改善点しか思いついていません。
1点目 単一のAZインフラ構成
個人利用程度の小さなWebサービス(ブログなど)であれば問題がないのかもしれませんが、
単一のAZ
で構成されているためAZ
自体が障害に見舞われた時に、サービスが全てダウンしてしまいます。
これはW-Aでいう信頼性
を担保できていない改善点となるのではないでしょうか。
2点目 単一のEC2で稼働しているWebサーバ
トラフィック増加や予期せぬサービスダウンにより、Webサーバがダウンすることはリスクとして捉える必要があります。
先ほどの単一AZの改善点同様に、EC2も複数台による冗長な構成を作る必要があります。
実際のサービスでは複数台動いているWebサーバに対して、不可分散をしながら特定のインスタンスへアクセスが集中しないロードバランサが必要です。
3点目 自前でEC2に構築したDBサーバ
特殊な業務要件(ファイルシステムと連携する必要がある場合やミドルウェアをもっとカスタマイズしたいなど)の場合には、EC2上に自前で構築でもいいかもしれません。
今回は、このような業務要件はないことを想定して構築しますので、マネージドサービスを利用した方が、構築や運用の効率がよくなります。
改善策
上記の改善点に対する、改善案は表のようにまとめました。
VPC
の冗長化については今回は別ものとさせてください。
改善点 | 改善後の構成 |
---|---|
単一AZのインフラ構成 | マルチAZ構成をとる |
単一のEC2で稼働しているWebサーバ | 前段にロードバランサを配置。要求(リクエスト)に応じて水平方向のスケーリング(AutoScaling) |
自前でEC2に構築したDBサーバ | マネージドサービスのRDS を利用する |
インフラ構成を図に示すと下記のようなイメージです。
まだまだシンプルで簡易的な構成ですが、入門編として見逃してください。
予備知識
今回の構成で新規に追加するELB
、AutoScaling
、RDS
について簡単にまとめていきます。
ELB
マネージド型のロードバランシングサービスです。
EC2と組み合わせて複数リソースへの不可分散やヘルスチェックができるようになります。
現状主要なELB
としてはALB(アプリケーションロードバランサ:レイヤ7で負荷分散)
とNLB(レイヤ4で負荷分散)
がありますが、以降はALB
を前提とした説明です。
特徴
複数AZにまたがって、トラフィックの不可分散をすることができる
パブリックサブネット、プライベートサブネットどちらにも配置ができる
マネージドサービスで、リクエスト量に応じてELB自体も自動的にスケーリングされる
主要な機能
ヘルスチェック
負荷分散の方式
SSL Termination
- ロードバランサ側で
SSL認証
を実施する機能 - ロードバランサに
SSL証明書
を設定することで、クライアント~ロードバランサ間はHTTPS
で、ロードバランサ~リソース間はHTTP
で通信を流すことができる- いちいち、リソース1個ずつに対して証明書をダウンロードすることが不要になってくる
- ロードバランサ側で
スティッキーセッション
Connection Draining
X-forwarded
ヘッダーのサポート- ロードバランサを介したHTTPリクエストの場合、インスタンスからみたリクエスト元のIPアドレスは全てロードバランサとなってしまう
- X-forwardedをHTTPヘッダーに付与して、実際のリクエスト元(クライアント)のIPアドレスを判別することができる
X-Forwarded-Forとは、HTTPヘッダフィールドの1つであり、ロードバランサなどの機器を経由して Webサーバに接続するクライアントの送信元IPアドレスを特定する際のデファクトスタンダードです。 クライアントの送信元IPアドレスの特定は、ロードバランサなどでクライアントの送信元IPアドレスが 変換された場合でも、HTTPヘッダに元のクライアントIPアドレスの情報を付加することで実現します。
Auto-Scaling
需要(トラフィック量)やリソースの障害発生時に、自動的にインスタンスの数を増減することができる機能。
前述のELB
と併用して利用されるケースが多くなる。
利用するメリット
可用性の向上
- 繁忙期やピーク時間帯には、需要量に応じて自動的にインスタンスの数を
スケールアウト(台数を増やす)
ことができる - 逆に閑散期については、
スケールイン(台数を減らす)
こともできる
- 繁忙期やピーク時間帯には、需要量に応じて自動的にインスタンスの数を
耐障害性の向上
コスト効率の向上
- 需要量に応じて、適切な量のインスタンスを立ち上げるため、ピーク時に備えた台数を常に稼働させておくよりもコストがかからなくなる
設定する項目
Auto-Scalingグループ
Auto-Scaling Configration
Auto-Scaling Plan
- どのようにスケール(台数を増やしたり減らしたりする)かを設定する。下記の方法を複数組み合わせることもできる。
- 手動でのスケーリング
- 事前にスケーリングする必要が分かっている(キャンペーンやユーザ告知)した場合には手動でスケーリングすることができる
- Auto-Scalingグループの
最小数
、最大数
、希望する数
を調整する
- スケジュールに応じてスケーリング
- ある一定時間(お昼休みなど)だけスケーリングさせるといった設定ができる
- 予定アクションを定義することで実現する
- 手動でのスケーリング
- 需要に応じてスケーリング
- CloudWatchのCPU Utilizationを監視してしきい値を超えた場合といった設定ができる
- ポリシーを定義することで実現する
- どのようにスケール(台数を増やしたり減らしたりする)かを設定する。下記の方法を複数組み合わせることもできる。
リソース削除の設定
需要が減ったりすれば、余剰なインスタンスを終了(Terminate)する必要があります。 この時、どのインスタンスを削除するかについて設定することが可能です。
- OldestInstance/NewestInstance
- 作成されてから最も古い/新しいインスタンスから削除
- 後からAuto-Scaling作成した場合、オリジナルのEC2(スケール時のクローン元)が削除される
- 作成されてから最も古い/新しいインスタンスから削除
- OldestLaunch Configration
- 最も古い起動設定により起動しているインスタンスなどから削除
- ClosestToNextInstanceHour
- 次の課金が始まるタイミングが最も近いインスタンスから削除
- デフォルト設定
RDS
マネージドサービス型のリレーショナルDBサーバです。
パッチ適用といった運用業務が不要なりますが、VPC内に配置されるため可用性の確保は利用者側で設定が必要です。
また、OSのログインは不可であることから、DBサーバの細かい設定をした場合やNFS(Network File System)を利用するといったカスタマイズはできません。
代表的な仕組みとしてマルチAZ構成(マスタ/スレーブ構成)
とリードレプリカ
がある。
マルチAZ構成
1つのリージョン内の2つのAZにDBインスタンスを配置して、障害発生時やメンテナンス時に自動的にフェイルオーバさせる
マスタとスレーブでインスタンスを待機させていて、それぞれのインスタンスは同期レプリケーションされている
リードレプリカ
参照専用のDBインスタンスを配置されることができる(マスタのインスタンスと別AZにも配置ができる)
非同期レプリケーション
であるため、更新された内容が即時反映されていない場合がある
Terraformによるコード実装
実際に予備知識は置いたということで、Terraformで実装していきたいと思います。
ディレクトリ構成は下記の通りです。
. ├── README.md ├── alb │ ├── main.tf │ └── output.tf ├── autoscaling │ ├── main.tf │ └── output.tf ├── config.tfvars ├── ec2 │ ├── main.tf │ ├── output.tf │ └── setup.sh ├── main.tf ├── network │ ├── main.tf │ └── output.tf ├── rds │ ├── main.tf │ └── output.tf ├── sg │ ├── main.tf │ └── output.tf |── variables.tf
ソースの細かい説明は省くので、詳細はgitリポジトリにコードを挙げていますので参照ください。
以下、自分用の忘備録になるので、飛ばしてもらって大丈夫です。
VPC構築
基本的に前回作成したコードを流用します。
前回との変更点としては、マルチAZ構成に変更する
ことがメインです。
実際にコードを見てもらった方が分かりやすいかと思います。
組み込み関数のlength
とlookup
とelement
を利用して、冗長な書き方にならないようにしました。
例えばこんな感じで、変数でmapを使って複数のサブネットを作成しました。
###################################### # Varibales # ###################################### variable "vpc_parameter" { default = { vpc_subnets = "10.0.0.0/16" public_subnets = { "0" = "10.0.1.0/24", "1" = "10.0.2.0/24" }, private_subnets = { "0" = "10.0.5.0/24", "1" = "10.0.6.0/24" }, availability_zones = { "0" = "ap-northeast-1a", "1" = "ap-northeast-1c" } } } ###################################### # VPC # ###################################### resource "aws_vpc" "example_vpc" { cidr_block = var.vpc_parameter.vpc_subnets enable_dns_support = true # DNS名前解決をサポート enable_dns_hostnames = true # パブリックIPアドレスをもつインスタンスの名前解決を有効化 tags = { Name = "Example" } } ###################################### # Public Subnet # ###################################### resource "aws_subnet" "example_public" { vpc_id = aws_vpc.example_vpc.id count = length(var.vpc_parameter.public_subnets) cidr_block = lookup(var.vpc_parameter.public_subnets , count.index, "Not Found") availability_zone = lookup(var.vpc_parameter.availability_zones, count.index, "Not Found") map_public_ip_on_launch = true tags = { Name = format("Example-VPC-Public-Subnet-%d", count.index) } }
複数作成したリソースの出力を取得したい場合は、join
を使えば良さそうです。
配列要素をマージして1つの文字列として出力してくれます。
output "private_subnets_id" { value = join(",", aws_subnet.example_private.*.id) } output "public_subnets_id" { value = join(",", aws_subnet.example_public.*.id) }
Security Group作成
プライベートサブネットに作成したRDS用のセキュリティグループに、NAT Gatewayのみ(2つのAZにそれぞれ1つずつ)をルールに加える必要がありました。
サブネット/32
で指定したかったので、for
を使って実現しています。
resource "aws_security_group_rule" "egress_all_rds" { type = "egress" from_port = 0 to_port = 0 protocol = "tcp" cidr_blocks = [for ip in local.nat_gateway_ips : "${ip}/32"] security_group_id = aws_security_group.rds_sg.id }
EC2作成
EC2は各AZのパブリックサブネットに1つずつ作る必要がありましたが、aws_instance
を2ブロック書くのは冗長になってしまいます。。
count
を使って、複数のリソースを1つのブロックで書けるようにしました。
resource "aws_instance" "web_servers" { count = length(var.subnet_ips) ami = data.aws_ami.recent_amazon_linux_2.id instance_type = var.instance_type key_name = aws_key_pair.instance_ssh_key_pairs.id vpc_security_group_ids = [var.sg_id] subnet_id = element(split(",", var.subnet_ids), count.index) tags = { Name = "Test ELB Web Server ${count.index}" } user_data = file("./ec2/setup.sh") depends_on = [var.subnet_ids] }
ただ、count
を利用する際には、terraform plan
の段階で何個リソースを作成するか分かっていないければいけません。
別のモジュールで作成したリソースの個数は指定することができません。
下記のようなErrorが出てしまうことがあります。
The "count" value depends on resource attributes that cannot be determined until apply, so Terraform cannot predict how many instances will be created. To work around this, use the -target argument to first apply only the resources that the count depends on.
今回のコードではcount
の値にvar.subnet_ips
を利用しているのですが、これは事前にvariables.tf
に定義している変数なので、繰り返す回数がすでに分かっている状態です。
ALB作成
まずは、ALBのアクセスログを保管するたhttps://docs.aws.amazon.com/ja_jp/elasticloadbalancing/latest/application/load-balancer-access-logs.htmlめのBucketを作成しました。
S3のBucketポリシーを定義していく必要があります。参考
data
リソースを定義して、Bucketポリシーを外部リソースとして定義することができます。
################################################ # S3 bucket to writing access logs Settings # ################################################ resource "aws_s3_bucket" "alb-logs-bucket" { bucket = "turedure-alb-logs-bucket-test-one" acl = "private" # For test force_destroy = true lifecycle_rule { enabled = true id = "alb-log" prefix = "alb-log/" transition { days = 30 storage_class = "STANDARD_IA" } transition { days = 60 storage_class = "GLACIER" } expiration { days = 90 } } } resource "aws_s3_bucket_public_access_block" "alb-logs-bucket" { bucket = aws_s3_bucket.alb-logs-bucket.id block_public_acls = true block_public_policy = true ignore_public_acls = true restrict_public_buckets = true } resource "aws_s3_bucket_policy" "alb-logs-bucket-policy" { bucket = aws_s3_bucket.alb-logs-bucket.id policy = data.aws_iam_policy_document.alb_log.json depends_on = [aws_s3_bucket_public_access_block.alb-logs-bucket] } # Bucket Policy data "aws_iam_policy_document" "alb_log"{ statement { effect = "Allow" actions = ["s3:PutObject"] resources = ["arn:aws:s3:::${aws_s3_bucket.alb-logs-bucket.id}/*"] principals { type = "AWS" identifiers = ["582318560864"] } } }
次にALBを作成していきます。
作成時に定義する必要があるリソースは下記です。
リソース名 | 説明 |
---|---|
aws_lb | ELBの本体。LBのタイプやセキュリティグループをアタッチする。 |
aws_lb_target_group | LBのターゲットグループ。負荷分散するグループを定義する。 |
aws_lb_listener | LBのリスナーを作成。ターゲットグループをアタッチしてルーティングをどのようにするかを設定する |
aws_lb_target_group_attachment | ターゲットグループとインスタンス(ECSの場合はコンテナ)を紐付ける |
aws_lb
パラメータ名 | 説明 | 必須か? |
---|---|---|
name | ALBの名前 | No |
internal | trueであればInternal LB(プライベートサブネット用のLB) | No |
security_groups | アタッチするセキュリティグループ | No |
subnets | ALBを紐付けるサブネットID | No |
access_logs | ELBのアクセスログの保存先 | No |
resource "aws_lb" "example_alb" { name = "turedure-example-alb" internal = false load_balancer_type = "application" security_groups = [var.alb_sg_id] subnets = local.public_subnets_list access_logs { bucket = aws_s3_bucket.alb-logs-bucket.bucket enabled = true } tags = { Name = "Test-Alb" Environment = "development" } # S3のアクセス許可が出るまでペンディング depends_on = [aws_s3_bucket_policy.alb-logs-bucket-policy] }
aws_lb_target_group
パラメータ名 | 説明 | 必須か? |
---|---|---|
name | ターゲットグループの名前 | Yes |
port | ターゲットグループがトラフィックを待ち受けるポート | Yes |
protocol | ターゲットグループがトラフィックを待ち受けるプロトコル | Yes |
vpc_id | ターゲットグループが所属するVPCのID | Yes |
deregistration_delay | ELBが異常と判断してから実際にターゲットを登録解除するまでに待つ時間 | No |
health_check | ヘルスチェックの設定 | No |
resource "aws_lb_target_group" "example_alb" { name = "turedure-example-target-group" port = 80 protocol = "HTTP" deregistration_delay = 300 vpc_id = var.vpc_id health_check { interval = 30 path = "/index.html" port = 80 timeout = 5 unhealthy_threshold = 2 matcher = 200 } depends_on = [aws_lb.example_alb] }
aws_lb_listener
今回は証明書などは発行していないく、HTTP
でクライアントからリクエストが来る想定です。
パラメータ名 | 説明 | 必須か? |
---|---|---|
load_balancer_arn | LBのARN | Yes |
port | LBが待ち受けるポート | Yes |
protocol | クライアント~LB間のプロトコル | No |
default_action | LBがトラフィックを受けた際にどのように処理するか | Yes |
resource "aws_lb_listener" "example_alb" { load_balancer_arn = aws_lb.example_alb.arn port = "80" protocol = "HTTP" default_action { target_group_arn = aws_lb_target_group.example_alb.arn type = "forward" } depends_on = [aws_lb_listener.example_alb] }
aws_lb_target_group_attachment
パラメータ名 | 説明 | 必須か? |
---|---|---|
target_group_arn | 紐づけるターゲットグループのARN | Yes |
target_id | ターゲットグループに紐づけるインスタンスのID | Yes |
port | ターゲットが待ち受けるポート番号 | Yes |
複数のインスタンスを紐付ける必要があったためcount
で回しています。
resource "aws_lb_target_group_attachment" "example_alb" { count = length(var.public_subnet_ips) target_group_arn = aws_lb_target_group.example_alb.arn target_id = element(local.instance_ids_list, count.index) port = 80 depends_on = [aws_lb.example_alb] }
Auto Scaling作成
|リソース名|説明| |aws_launch_configuration| Auto Scalingから作成されるインスタンスの起動設定| |aws_autoscaling_group| Auto Scalingグループ| |aws_autoscaling_policy| スケール(イン or アウト)の設定| |aws_cloudwatch_metric_alarm| CloudWatchとAutoScalingを連携させるためのアラート処理|
aws_launch_configuration
事前にAMIを作成済みであればここら辺の細かい設定が不要になると思います。
パラメータ名 | 説明 | 必須か? |
---|---|---|
name_prefix | AMIの設定名(nameにすると、terraformで再作成できなくなる。) | No |
image_id | AMIのID | Yes |
instance_type | インスタンスタイプ | Yes |
key_name | 作成したインスタンスに接続するためのキー名 | No |
security_groups | アタッチするセキュリティグループ | No |
associate_public_ip_address | 作成したインスタンスにパブリックIPアドレスをアタッチするか | No |
user_data | インスタンスを作成する際に実行するスクリプト | No |
resource "aws_launch_configuration" "example_as_conf" { name_prefix = "webserver" image_id = var.ami_id instance_type = var.instance_type key_name = var.key_name security_groups = [var.sg_id] associate_public_ip_address = true user_data = file("./ec2/setup.sh") lifecycle { create_before_destroy = true } }
aws_autoscaling_group
パラメータ名 | 説明 | 必須か? |
---|---|---|
name_prefix | グループ名 | Yes |
max_size | インスタンスの最大数 | Yes |
min_size | インスタンスの最小数 | Yes |
launch_configuration | 起動するAMIの設定 | No |
vpc_zone_identifier | インスタンスを配置するサブネットのID | No |
desired_capacity | デフォルト(Auto Scaling起動時)のインスタンスの台数 | No |
health_check_grace_period | インスタンスが起動してからヘルスチェックするまでの時間 | No |
health_check_type | ヘルスチェックの方式(EC2単体かELBと連携するか) | No |
target_group_arns | ターゲットグループのARN | No |
resource "aws_autoscaling_group" "example_asg" { name_prefix = "turedure-websv-v1" max_size = 4 min_size = 1 launch_configuration = aws_launch_configuration.example_as_conf.name vpc_zone_identifier = split(",", var.public_subnets_id) # Number of creating instance size when initialize auto scaling group desired_capacity = 1 health_check_grace_period = 300 health_check_type = "ELB" target_group_arns = [var.alb_target_group_arn] force_delete = true lifecycle { create_before_destroy = true } tags = [ { key = "Name" value = "test-autoscaling-group" propagate_at_launch = false }, { key = "Environment" value = "development" propagate_at_launch = false } ] }
auto_scaling_policy, aws_cloudwatch_metric_alarm
auto_scaling_policy
で増減する方法を定義して、aws_cloudwatch_metric_alarm
と連携させるイメージです。
- auto_scaling_policy
パラメータ名 | 説明 | 必須か? |
---|---|---|
name | ポリシー名 | Yes |
scaling_adjustment | 何台増減させるか | No |
adjustment_type | 増減のさせかた(ChangeInCapacityは指定した値だけ既存の値から増減) | No |
cooldown | 一度増減させてから、次に増減できるまでの間隔(一時的なリクエスト増で何台もインスタンスが作成されないようにする) | No |
autoscaling_group_name | AutoScalingグループの名前 | Yes |
- aws_cloudwatch_metric_alarm
パラメータ名 | 説明 | 必須か? |
---|---|---|
alarm_name | アラートの名前 | Yes |
comparison_operator | しきい値からどうなった場合にアラートが発生するか | Yes |
evaluation_periods | しきい値を超えるイベントが繰り返し発生した回数 | Yes |
metric_name | しきい値に使うメトリック名 | No |
namespace | メトリックの名前空間。metric_nameとセットで使う | No |
period | チェックする間隔 | No |
statistic | どの統計値を判断材料に利用するか | No |
dimentions | AutoScalingグループ名を指定して、通知されるように設定(よくわからん) | No |
alarm_actions | アラートが生成された際に、自動的に行う動作 |
resource "aws_autoscaling_policy" "scale_out" { name = "Instance-Scaleout-Policy" scaling_adjustment = 1 adjustment_type = "ChangeInCapacity" cooldown = 300 autoscaling_group_name = aws_autoscaling_group.example_asg.name } resource "aws_autoscaling_policy" "scale_in" { name = "Instance-Scalein-policy" scaling_adjustment = -1 adjustment_type = "ChangeInCapacity" cooldown = 300 autoscaling_group_name = aws_autoscaling_group.example_asg.name } resource "aws_cloudwatch_metric_alarm" "cpu_usage_high" { alarm_name = "test-cpu-usage-high" comparison_operator = "GreaterThanOrEqualToThreshold" evaluation_periods = "1" metric_name = "CPUUtilization" namespace = "AWS/EC2" period = "300" statistic = "Average" dimensions = { AutoScalingGroupName = aws_autoscaling_group.example_asg.name } alarm_actions = [aws_autoscaling_policy.scale_out.arn] } resource "aws_cloudwatch_metric_alarm" "cpu_usage_low" { alarm_name = "test-cpu-usage-low" comparison_operator = "LessThanThreshold" evaluation_periods = "1" metric_name = "CPUUtilization" namespace = "AWS/EC2" period = "300" statistic = "Average" dimensions = { AutoScalingGroupName = aws_autoscaling_group.example_asg.name } alarm_actions = [aws_autoscaling_policy.scale_in.arn] }
RDS
RDS
のリソース名は、シンプルなので説明を省きます。(疲れました)
aws_db_parameter_group
で動作するDBのパラメータをいじることができます。
あとは、skip_final_snapshot
をtrue
にしておかないと、terraform destory
時にエラーが吐かれて削除できません。
(GUIでも、スナップショットをどうするか問われるダイアログがあったので、コードベースだと明示的に定義する必要があるのだろうと思っています)
resource "aws_db_parameter_group" "db_parameter" { name = "${var.db_parameter_group.name}-parameter" family = var.db_parameter_group.family parameter { name = "character_set_server" value = "utf8" } parameter { name = "character_set_client" value = "utf8" } } resource "aws_db_subnet_group" "db_subnets" { name = "private-subnet" subnet_ids = split(",", var.private_subnets_ids) tags = { Name = "Private Subnet" } } resource "aws_db_instance" "db_server" { identifier = "test" allocated_storage = 20 engine = "mysql" engine_version = "5.7.22" instance_class = "db.t2.micro" name = var.rds_name db_subnet_group_name = aws_db_subnet_group.db_subnets.name vpc_security_group_ids = [var.db_security_group_id] parameter_group_name = aws_db_parameter_group.db_parameter.name multi_az = true backup_retention_period = "7" backup_window = "23:00-23:30" apply_immediately = true auto_minor_version_upgrade = false username = var.db_identifilter.user_name password = var.db_identifilter.password skip_final_snapshot = true }
まとめ
今回の記事では、
ELB
、AutoScaling
を使って冗長性が高い構成についてterraform
で実装しました。- ついでに
RDS
を使ってマネージドサービスを利用しました。
まだまだシンプルなサービス(サービスを利用したとしても代表的な設定)しか利用していませんが、
来週以降も記事を投稿していきます。