はじめに
この技術記事では、Terraform、AWS DMS (Database Migration Service)を使用して、データベース移行を安全かつ効率的に実行する方法を説明します。
Terraformを利用したAWS DMSは、安全かつ効率的なデータベース移行を実現し、コードによるバージョン管理と踏み台サーバーによるRDSインスタンスへの安全な接続、DMSレプリケーション機能による高可用性とスケーラビリティにより、時間と労力を大幅に削減します。
この技術記事で解説するコードは、以下の Github リポジトリで公開されています。
また、以下の記事を参考にしました。
使用する AWS サービス
DMS (Database Migration Service)
EC2 (Elastic Compute Cloud)
IAM (Identity and Access Management)
RDS (Relational Database Service)
構成概要
- 踏み台サーバー: EC2 インスタンスを踏み台サーバーとして構築し、RDS インスタンスへの SSH 接続に使用します
- DMS レプリケーションインスタンス: DMS レプリケーションインスタンスを作成し、ソースデータベースとターゲットデータベース間のデータ移行を管理します
- ネットワーク: サブネットグループとセキュリティグループを作成し、各コンポーネント間の安全な通信を確保します
- データ移行タスク: レプリケーショントーナメントとテーブルマッピングを設定し、移行するデータと方法を定義します
コード
iam
AWS DMSの通信に必要なIAMロールを作成します。
resource "aws_iam_role_policy_attachment" "dms_policy" {
role = aws_iam_role.main_role.name
policy_arn = "arn:aws:iam::aws:policy/service-role/AmazonDMSVPCManagementRole"
}
resource "aws_iam_role" "main_role" {
name = "dms-vpc-role"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Sid = ""
Action = "sts:AssumeRole"
Effect = "Allow"
Principal = {
Service = "dms.amazonaws.com"
}
Action = "sts:AssumeRole"
},
]
})
tags = {
Name = "${var.app_name}-app-iam-role"
}
}
network
TerraformでAWS DMSのsecurity_group設定を正確に行うことは、安全かつスムーズなデータベース移行を実現するために不可欠です。設定に誤りがあると、DMSはエラーを出力し、移行作業が中断してしまう可能性があります。
# SecurityGroup for DMS
resource "aws_security_group" "dms_sg" {
name = "dms-sg"
vpc_id = aws_vpc.main.id
tags = {
Name = "${var.app_name}-dms-sg"
}
}
# SecurityGroup for opmng
resource "aws_security_group" "opmng_sg" {
name = "opmng-sg"
vpc_id = aws_vpc.main.id
tags = {
Name = "${var.app_name}-opmng-sg"
}
}
# SecurityGroup for RDS
resource "aws_security_group" "rds_source_sg" {
name = "rds-source-sg"
vpc_id = aws_vpc.main.id
tags = {
Name = "${var.app_name}-rds-source-sg"
}
}
resource "aws_security_group" "rds_target_sg" {
name = "rds-target-sg"
vpc_id = aws_vpc.main.id
tags = {
Name = "${var.app_name}-rds-target-sg"
}
}
locals {
opmng_sg_id_list = [
{ type = "ingress", port = "22" },
{ type = "egress", port = "80" },
{ type = "egress", port = "443" },
]
}
# SecurityGroupRules for dms
resource "aws_security_group_rule" "dms_out_tcp3306" {
type = "egress"
from_port = var.db_ports[0].internal
to_port = var.db_ports[0].external
protocol = var.db_ports[0].protocol
cidr_blocks = ["0.0.0.0/0"]
security_group_id = aws_security_group.dms_sg.id
}
# SecurityGroupRules for opmng
resource "aws_security_group_rule" "opmng_web" {
for_each = { for i in local.opmng_sg_id_list : i.port => i }
type = each.value.type
from_port = tonumber(each.value.port)
to_port = tonumber(each.value.port)
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
security_group_id = aws_security_group.opmng_sg.id
}
resource "aws_security_group_rule" "opmng_out_db" {
type = "egress"
from_port = var.db_ports[0].internal
to_port = var.db_ports[0].external
protocol = var.db_ports[0].protocol
cidr_blocks = ["0.0.0.0/0"]
security_group_id = aws_security_group.opmng_sg.id
}
# SecurityGroupRules for source db
resource "aws_security_group_rule" "dbsource_in_tcp3306_from_opmng" {
type = "ingress"
from_port = var.db_ports[0].internal
to_port = var.db_ports[0].external
protocol = var.db_ports[0].protocol
source_security_group_id = aws_security_group.opmng_sg.id
security_group_id = aws_security_group.rds_source_sg.id
}
resource "aws_security_group_rule" "dbsource_in_tcp3306_from_dms" {
type = "ingress"
from_port = var.db_ports[0].internal
to_port = var.db_ports[0].external
protocol = var.db_ports[0].protocol
source_security_group_id = aws_security_group.dms_sg.id
security_group_id = aws_security_group.rds_source_sg.id
}
# SecurityGroupRules for target db
resource "aws_security_group_rule" "dbtarget_in_tcp3306_from_opmng" {
type = "ingress"
from_port = var.db_ports[0].internal
to_port = var.db_ports[0].external
protocol = var.db_ports[0].protocol
source_security_group_id = aws_security_group.opmng_sg.id
security_group_id = aws_security_group.rds_target_sg.id
}
resource "aws_security_group_rule" "dbtarget_in_tcp3306_from_dms" {
type = "ingress"
from_port = var.db_ports[0].internal
to_port = var.db_ports[0].external
protocol = var.db_ports[0].protocol
source_security_group_id = aws_security_group.dms_sg.id
security_group_id = aws_security_group.rds_target_sg.id
}
RDS
Terraformモジュールの出力で、aws_db_instance.source-db.address からホスト名のみを取得するには、以下のコードを使用できます。
output "sorce_db_address" {
value = split(":", "${aws_db_instance.source-db.address}")[0]
}
DMS
DMS インスタンスは、Database Migration Service のインスタンスのことを指します。こちらは、データベース間のデータ移行を管理するサービスです。
インスタンス
DMS レプリカは、ソースデータベースのデータを複製し、ターゲットデータベースに同期するための論理的な構成です。
vpc_security_group_ids を設定しないと、RDS と通信できず、データ移行が実行できません。
resource "aws_dms_replication_instance" "main" {
replication_instance_id = "main-dms-replication"
engine_version = "3.5.1"
replication_instance_class = "dms.t3.micro"
allocated_storage = 50
publicly_accessible = true
multi_az = false
availability_zone = "ap-northeast-1a"
apply_immediately = true
auto_minor_version_upgrade = false
preferred_maintenance_window = "sun:10:30-sun:14:30"
replication_subnet_group_id = aws_dms_replication_subnet_group.main.id
vpc_security_group_ids = [var.sg_dms_id]
tags = {
Name = "${var.app_name}-main"
}
}
resource "aws_dms_replication_subnet_group" "main" {
replication_subnet_group_description = "replication subnet group"
replication_subnet_group_id = "dms-replication-subnet-group"
subnet_ids = [
"${var.subnet_private_subnet_1a_id}",
"${var.subnet_private_subnet_1c_id}",
]
tags = {
Name = "${var.app_name}-main"
}
depends_on = [var.iam_role_policy_attachment_dms_policy]
}
タスク
DMS エンドポイントは、ソースデータベースとターゲットデータベースへの接続情報を設定するための構成要素です。
# source endpoint
resource "aws_dms_endpoint" "source_endpoint" {
endpoint_id = "source-endpoint"
endpoint_type = "source"
engine_name = "mysql"
username = var.db_username
password = var.db_password
port = 3306
server_name = var.sorce_db_address
database_name = var.db_name
tags = {
Name = "${var.app_name}-source-endpoint"
}
}
# target endpoint
resource "aws_dms_endpoint" "target_endpoint" {
endpoint_id = "target-endpoint"
endpoint_type = "target"
engine_name = "mysql"
username = var.db_username
password = var.db_password
port = 3306
server_name = var.target_db_address
database_name = var.db_name
tags = {
Name = "${var.app_name}-target-endpoint"
}
}
DMS タスクは、レプリカとエンドポイントを使用して、実際にデータ移行を実行するための設定です。
resource "aws_dms_replication_task" "main" {
replication_task_id = "replication-main-task"
target_endpoint_arn = aws_dms_endpoint.target_endpoint.endpoint_arn
source_endpoint_arn = aws_dms_endpoint.source_endpoint.endpoint_arn
replication_instance_arn = aws_dms_replication_instance.main.replication_instance_arn
migration_type = "full-load"
table_mappings = file("./modules/dms/dms_table_mapping.json")
tags = {
Name = "${var.app_name}-main"
}
}
schema-nameはデータベース名です。
{
"rules": [
{
"rule-type": "selection",
"rule-id": "1",
"rule-name": "1",
"object-locator": {
"schema-name": "todoproject",
"table-name": "%"
},
"rule-action": "include"
}
]
}
結果
sourceDBのデータ
AWSコンソール
targetDBのデータ
バッチリ移行できてます。