こんにちは。よっしーです(^^)
今日は、terraformを利用したRDSクラスタの作成についてご紹介します。
背景
AWS環境でTerraformを利用したRDSのクラスタを構築する機会がありましたので、その検証用として作成したときのtfファイルを備忘として残しました。
terraformコマンドの設定
下記のコマンドで terraform の使用するバージョンを指定します。
asdf local terraform 1.5.4
詳しい設定方法は下記の記事をご覧ください。
インフラ構成図
今回構築するインフラ構成は下図のようになります。
構成内容
今回作成するファイル構成は下記のようになります。
├── main.tf
├── output.tf
├── variables.tf
├── rds
│ ├── output.tf
│ ├── rds.tf
│ └── variables.tf
├── sg
│ ├── output.tf
│ ├── sg.tf
│ └── variables.tf
└── vpc
├── output.tf
├── variables.tf
└── vpc.tf
main.tf
terraform {
cloud {
organization = "(自身の組織名)"
workspaces {
name = "learn-tfc-aws"
}
}
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 4.16"
}
}
required_version = ">= 1.2.0"
}
provider "aws" {
region = "ap-northeast-1"
}
# VPC
module "vpc" {
source = "./vpc"
name_prefix = var.name_prefix
region = var.region
az_a = var.az_a
az_c = var.az_c
tag_name = var.tag_name
tag_group = var.tag_group
}
# SG
module "sg" {
source = "./sg"
name_prefix = var.name_prefix
region = var.region
az_a = var.az_a
az_c = var.az_c
tag_name = var.tag_name
tag_group = var.tag_group
vpc_id = module.vpc.vpc_id
}
# RDS
module "rds" {
source = "./rds"
name_prefix = var.name_prefix
region = var.region
az_a = var.az_a
az_c = var.az_c
tag_name = var.tag_name
tag_group = var.tag_group
vpc_id = module.vpc.vpc_id
public_a_id = module.vpc.public_a_id
public_c_id = module.vpc.public_c_id
sg_id = module.sg.sg_id
}
variables.tf
variable "region" {
default = "ap-northeast-1"
}
variable "az_a" {
default = "ap-northeast-1a"
}
variable "az_c" {
default = "ap-northeast-1c"
}
variable "name_prefix" {
default = "ltar" # Learn-Terraform-Aws-Rds
}
variable "tag_name" {
default = "learn-terraform-aws-rds"
}
variable "tag_group" {
default = "learn-terraform-aws"
}
output.tf
output "rds_endpoint" {
value = module.rds.endpoint
}
vpc/vpc.tf
# VPC
resource "aws_vpc" "default" {
cidr_block = "10.0.0.0/16"
enable_dns_support = true
enable_dns_hostnames = true # DNSホスト名を有効化
tags = {
Name = "${var.tag_name}"
}
}
# Subnet
resource "aws_subnet" "public_1a" {
vpc_id = aws_vpc.default.id
cidr_block = "10.0.0.0/20"
availability_zone = var.az_a
tags = {
Name = "${var.tag_name}"
}
}
resource "aws_subnet" "public_1c" {
vpc_id = aws_vpc.default.id
cidr_block = "10.0.16.0/20"
availability_zone = var.az_c
tags = {
Name = "${var.tag_name}"
}
}
# Internet Gateway
resource "aws_internet_gateway" "default" {
vpc_id = aws_vpc.default.id
tags = {
Name = "${var.tag_name}"
}
}
# Route table
resource "aws_route_table" "default" {
vpc_id = aws_vpc.default.id
tags = {
Name = "${var.tag_name}"
}
}
resource "aws_route" "default" {
route_table_id = aws_route_table.default.id
gateway_id = aws_internet_gateway.default.id
destination_cidr_block = "0.0.0.0/0"
}
# SubnetとRoute tableの関連付け
resource "aws_route_table_association" "public_1a" {
subnet_id = aws_subnet.public_1a.id
route_table_id = aws_route_table.default.id
}
resource "aws_route_table_association" "public_1c" {
subnet_id = aws_subnet.public_1c.id
route_table_id = aws_route_table.default.id
}
vpc/variables.tf
variable "region" {}
variable "az_a" {}
variable "az_c" {}
variable "name_prefix" {}
variable "tag_name" {}
variable "tag_group" {}
vpc/output.tf
output "vpc_id" {
value = "${aws_vpc.default.id}"
}
output "public_a_id" {
value = "${aws_subnet.public_1a.id}"
}
output "public_c_id" {
value = "${aws_subnet.public_1c.id}"
}
sg/sg.tf
resource "aws_security_group" "default" {
vpc_id = "${var.vpc_id}"
tags = {
Name = "${var.tag_name}"
}
}
resource "aws_security_group_rule" "mysql" {
type = "ingress"
from_port = "3306"
to_port = "3306"
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
security_group_id = aws_security_group.default.id
}
resource "aws_security_group_rule" "out_all_allow" {
type = "egress"
from_port = 0
to_port = 0
protocol = "all"
cidr_blocks = ["0.0.0.0/0"]
security_group_id = aws_security_group.default.id
}
sg/variables.tf
variable "region" {}
variable "az_a" {}
variable "az_c" {}
variable "name_prefix" {}
variable "tag_name" {}
variable "tag_group" {}
variable "vpc_id" {}
sg/output.tf
output "sg_id" {
value = "${aws_security_group.default.id}"
}
rds/rds.tf
locals {
aurora_database_name = "learn"
aurora_master_username = "learn_user"
aurora_master_password = "Passw0rd"
}
resource "aws_rds_cluster" "learn" {
cluster_identifier = "${var.name_prefix}-rds-cluster"
engine = "aurora-mysql"
engine_version = "8.0.mysql_aurora.3.02.0"
database_name = local.aurora_database_name
master_username = local.aurora_master_username
master_password = local.aurora_master_password
port = 3306
vpc_security_group_ids = ["${var.sg_id}"]
db_subnet_group_name = aws_db_subnet_group.learn.name
db_cluster_parameter_group_name = aws_rds_cluster_parameter_group.learn.name
skip_final_snapshot = true
apply_immediately = true
}
resource "aws_rds_cluster_parameter_group" "learn" {
name = "${var.name_prefix}-database-cluster-parameter-group"
family = "aurora-mysql8.0"
parameter {
name = "character_set_client"
value = "utf8mb4"
apply_method = "immediate"
}
parameter {
name = "character_set_connection"
value = "utf8mb4"
apply_method = "immediate"
}
parameter {
name = "character_set_database"
value = "utf8mb4"
apply_method = "immediate"
}
parameter {
name = "character_set_filesystem"
value = "utf8mb4"
apply_method = "immediate"
}
parameter {
name = "character_set_results"
value = "utf8mb4"
apply_method = "immediate"
}
parameter {
name = "character_set_server"
value = "utf8mb4"
apply_method = "immediate"
}
parameter {
name = "collation_connection"
value = "utf8mb4_general_ci"
apply_method = "immediate"
}
parameter {
name = "collation_server"
value = "utf8mb4_general_ci"
apply_method = "immediate"
}
parameter {
name = "time_zone"
value = "Asia/Tokyo"
apply_method = "immediate"
}
}
resource "aws_db_subnet_group" "learn" {
name = "${var.name_prefix}-db-subnet"
subnet_ids = ["${var.public_a_id}", "${var.public_c_id}"]
}
resource "aws_rds_cluster_instance" "learn" {
count = 1
cluster_identifier = aws_rds_cluster.learn.id
identifier = "${var.name_prefix}-rds-instance-${count.index}"
engine = aws_rds_cluster.learn.engine
engine_version = aws_rds_cluster.learn.engine_version
instance_class = "db.t3.medium"
db_subnet_group_name = aws_db_subnet_group.learn.name
db_parameter_group_name = aws_db_parameter_group.learn.name
publicly_accessible = true
}
resource "aws_rds_cluster_instance" "learn-replica" {
count = 2
cluster_identifier = aws_rds_cluster.learn.id
identifier = "${var.name_prefix}-rds-instance-${count.index}-replica"
engine = aws_rds_cluster.learn.engine
engine_version = aws_rds_cluster.learn.engine_version
instance_class = "db.t3.medium"
db_subnet_group_name = aws_db_subnet_group.learn.name
db_parameter_group_name = aws_db_parameter_group.learn.name
depends_on = [aws_rds_cluster_instance.learn]
publicly_accessible = true
}
resource "aws_db_parameter_group" "learn" {
name = "${var.name_prefix}-db-parameter-group"
family = "aurora-mysql8.0"
}
rds/variables.tf
variable "region" {}
variable "az_a" {}
variable "az_c" {}
variable "name_prefix" {}
variable "tag_name" {}
variable "tag_group" {}
variable "vpc_id" {}
variable "public_a_id" {}
variable "public_c_id" {}
variable "sg_id" {}
rds/output.tf
output "endpoint" {
value = aws_rds_cluster.learn.endpoint
}
作成
下記のコマンドを実施します。
# Terraform Cloudへのログイン
terraform login
# プロジェクトの初期化
terraform init
# フォーマット
terraform fmt
# バリデーション
terraform validate
# AWSリソースの作成内容を確認
terraform plan
# AWSリソースの作成
terraform apply
# 現在の状態の確認
terraform show
# 出力値の確認
terraform output
下記のような出力になっていれば成功です。
Apply complete! Resources: 18 added, 0 changed, 0 destroyed.
Outputs:
rds_endpoint = "xxx.ap-northeast-1.rds.amazonaws.com"
動作確認
publicに配置しているため、下記のコマンドで接続可能です。
mysql -ulearn_user -p -h xxx.ap-northeast-1.rds.amazonaws.com
パスワードは、aurora_master_password の値を入力してください。
下図のようなログインができれば成功です。
/ # mysql -ulearn_user -p -h ltar-rds-cluster.cluster-cswrkwegsb5l.ap-northeast-1.rds.amazonaws.com
Enter password:
Welcome to the MariaDB monitor. Commands end with ; or \g.
Your MySQL connection id is 95
Server version: 8.0.23 Source distribution
Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others.
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
MySQL [(none)]>
show databasesを実行して、learnの存在確認ができれば、動作確認は完了になります。
MySQL [(none)]> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| learn |
| mysql |
| performance_schema |
| sys |
+--------------------+
5 rows in set (0.010 sec)
補足
mysqlコマンドの実行は下記のdocker環境で実施しました。
├── mysql
│ └── Dockerfile
└── compose.yml
mysql/Dockerfile
FROM alpine
RUN apk update && \
apk add mysql-client
compose.yml
version: '3.8'
services:
mysql-client:
build: ./mysql
container_name: mysql-client
profiles:
- tools
networks:
- net
networks:
net:
実行コマンド
# ビルド
docker compose --profile tools build
# mysqlクライアントのコンテナにログイン
docker compose --profile tools run -it --rm mysql-client sh
解説
rds/rds.tf
このTerraformコードは、Amazon Web Services (AWS) のインフラストラクチャを定義し、データベースクラスターを構築するためのものです。以下にコードのセクションごとの解説を提供します。
locals
ブロック: このブロックは、コード内で使用する変数を定義しています。これにより、後のリソース定義でこれらの変数を使用することができます。aurora_database_name
はデータベースの名前、aurora_master_username
はマスターユーザー名、aurora_master_password
はマスターユーザーのパスワードを表しています。aws_rds_cluster
リソース: このリソースは、Amazon RDS データベースクラスターを定義しています。Aurora MySQL データベースクラスターを作成するための設定が含まれています。以下はいくつかの重要な設定です。cluster_identifier
: データベースクラスターの一意の識別子。engine
,engine_version
: 使用するデータベースエンジンとそのバージョン。database_name
,master_username
,master_password
: データベースの名前と、マスターユーザーの認証情報。port
: データベースがリッスンするポート番号。vpc_security_group_ids
: データベースへのアクセスを制御するセキュリティグループの ID。db_subnet_group_name
: データベースが配置されるサブネットグループ。db_cluster_parameter_group_name
: データベースクラスターのパラメータグループの名前。skip_final_snapshot
,apply_immediately
: データベースの削除時に最終スナップショットを作成せずに、変更を即座に適用するかどうか。
aws_rds_cluster_parameter_group
リソース: これはデータベースクラスター用のパラメータグループを定義しています。ここでは、データベースの文字セットや照合順序などのパラメータを設定しています。aws_db_subnet_group
リソース: データベースクラスターやそのインスタンスが配置されるサブネットグループを定義しています。aws_rds_cluster_instance
リソース: これはデータベースクラスター内のインスタンスを定義しています。このリソースはマスターインスタンスを1つ定義しており、publicly_accessible
パラメータを通じてパブリックアクセスを有効にしています。aws_rds_cluster_instance
リソース (replica インスタンス): こちらはデータベースクラスターのリードレプリカインスタンスを2つ定義しています。マスターインスタンスへの依存関係を設定しており、同様にpublicly_accessible
パラメータを通じてパブリックアクセスを有効にしています。aws_db_parameter_group
リソース: データベースインスタンスのパラメータグループを定義しています。
このコード全体は、Aurora MySQL データベースクラスターを構築し、それに関連するインスタンスとパラメータ設定を行うためのものです。Terraform を使用することで、コードで定義されたインフラストラクチャを簡単に作成、変更、管理することができます。
resource “aws_rds_cluster_instance” “learn”
このコードは、Amazon RDS Aurora MySQLデータベースクラスターのマスターインスタンスを定義するためのものです。こちらも詳細について解説します。
resource "aws_rds_cluster_instance" "learn"
:aws_rds_cluster_instance
リソースを作成し、名前が"learn"
であることを示しています。マスターインスタンスを1つ作成するため、count
パラメータを使って1と設定しています。count
: インスタンスの数を指定しています。この例では1つのマスターインスタンスを作成します。cluster_identifier
: マスターインスタンスが所属するデータベースクラスターの識別子です。既存のデータベースクラスターのID (aws_rds_cluster.learn.id
) を使用しています。identifier
: インスタンスの一意の識別子を定義しています。${var.name_prefix}-rds-instance-${count.index}
というフォーマットで名前が設定されます。ここではcount.index
を使用して、複数のインスタンスが作成される場合にそれぞれにユニークな名前を付けるためのカウンターを利用しています。engine
,engine_version
: データベースエンジンとエンジンバージョンを既存のクラスターから取得して設定します。instance_class
: インスタンスのクラス(サイズ)を指定しています。この例では “db.t3.medium” を使用しています。db_subnet_group_name
: インスタンスが配置されるサブネットグループの名前です。aws_db_subnet_group.learn.name
から取得されます。db_parameter_group_name
: インスタンスに適用するパラメータグループの名前です。aws_db_parameter_group.learn.name
から取得されます。publicly_accessible
: インスタンスにパブリックアクセスを有効にするかどうかを指定します。
このコードセクションでは、データベースクラスターのマスターインスタンスを定義しています。このインスタンスはクラスターのコアとなり、データベースの読み書き操作を処理します。
resource “aws_rds_cluster_instance” “learn-replica”
depends_on
: リソースの依存関係を指定しています。リードレプリカインスタンスの作成前に、マスターインスタンス (aws_rds_cluster_instance.learn
) が作成されることを保証しています。
おわりに
今日は、terraformを利用したRDSクラスタの作成についてご紹介しました。public subnetに配置しているので、次回は、private subnetに配置したいと思います。
何か質問や相談があれば、コメントをお願いします。また、エンジニア案件の相談にも随時対応していますので、お気軽にお問い合わせください。
それでは、また明日お会いしましょう(^^)
コメント