0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Terraform、AWS DMSによるデータベース移行

Posted at

はじめに

この技術記事では、Terraform、AWS DMS (Database Migration Service)を使用して、データベース移行を安全かつ効率的に実行する方法を説明します。

Terraformを利用したAWS DMSは、安全かつ効率的なデータベース移行を実現し、コードによるバージョン管理と踏み台サーバーによるRDSインスタンスへの安全な接続、DMSレプリケーション機能による高可用性とスケーラビリティにより、時間と労力を大幅に削減します。

aws-1.png

この技術記事で解説するコードは、以下の 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ロールを作成します。

infra/modules/iam/aws_iam_role_policy_attachment.tf
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"
}
infra/modules/iam/aws_iam_role.tf
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はエラーを出力し、移行作業が中断してしまう可能性があります。

infra/modules/network/security_group.tf
# 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"
  }
}
infra/modules/network/security_group_rule.tf
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 からホスト名のみを取得するには、以下のコードを使用できます。

infra/modules/rds/outputs.tf
output "sorce_db_address" {
  value = split(":", "${aws_db_instance.source-db.address}")[0]
}

DMS

DMS インスタンスは、Database Migration Service のインスタンスのことを指します。こちらは、データベース間のデータ移行を管理するサービスです。

インスタンス

DMS レプリカは、ソースデータベースのデータを複製し、ターゲットデータベースに同期するための論理的な構成です。
vpc_security_group_ids を設定しないと、RDS と通信できず、データ移行が実行できません。

infra/modules/dms/aws_dms_replication_instance.tf
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"
  }
}
infra/modules/dms/aws_dms_replication_subnet_group.tf
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 エンドポイントは、ソースデータベースとターゲットデータベースへの接続情報を設定するための構成要素です。

infra/modules/dms/aws_dms_endpoint.tf
# 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 タスクは、レプリカとエンドポイントを使用して、実際にデータ移行を実行するための設定です。

infra/modules/dms/aws_dms_replication_task.tf
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はデータベース名です。

infra/modules/dms/dms_table_mapping.json
{
    "rules": [
        {
            "rule-type": "selection",
            "rule-id": "1",
            "rule-name": "1",
            "object-locator": {
                "schema-name": "todoproject",
                "table-name": "%"
            },
            "rule-action": "include"
        }
    ]
}

結果

sourceDBのデータ

Screenshot 2024-04-09 at 16.55.25.png

AWSコンソール

Screenshot 2024-04-10 at 9.10.10.png

targetDBのデータ
バッチリ移行できてます。

Screenshot 2024-04-10 at 9.12.22.png

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?