RDS for MySQLからAurora MySQLへ移行したよ
こんにちは、すずきです。
3ヶ月くらい前に、RDS for MySQLからAurora MySQLへDBを移行したので、そのときに検討したことや実際の作業手順などをまとめました。
同様の作業をされる方の参考になれば幸いです。
背景
サービスのリリース後もRDS for MySQLを利用していましたが、クライアントやトラフィックが日々増えていくなかで、データの耐久性と将来的なパフォーマンスに不安がでてきていました。そこで、複数AZにデータを分散し、MySQLのパフォーマンスが最大5倍向上するAuroraへ移行することにしました。
移行
新規で作成するリソース(Auroraクラスター、インスタンス、サブネットグループ、パラメータグループ)についてはTerraformでコード管理することにしました(移行元のRDSはコード管理していなかった)
アプリケーション(Lambda)との接続はRDS Proxyのターゲット先を変更することで完了しました
移行手段としてスナップショットかDMSを検討しましたが、ダウンタイムを前提としてスナップショットを選択しました。これによってRDSインスタンスで使用されていたユーザー名とパスワードがそのまま引き継がれます
Auroraのストレージレイヤーは選択したDBサブネットグループ内のAZに関係なく、自動的にデータを複数のAZに分散してレプリケーションします
Aurora vs Aurora Serverless
将来的なトランザクションレートとデータ量の増加を考慮し、Aurora ServerlessよりもAuroraが適していると判断しました。データの3AZに渡るレプリケーションは無料で提供され、追加のコストは使用するストレージ量に依存します。
コスト見積
初期設定
一旦、レプリカは使用しない
料金体系
RDS: インスタンスの起動時間 + ストレージの容量に応じた料金
Aurora: インスタンスの起動時間 + ストレージの容量に応じた料金 + I/Oリクエスト数
費用試算
インスタンス: db.r6g.xlargeにすると約0.1 USD × 24 × 30 = 約72 USD/月増
ストレージ: 変更なし
I/Oリクエスト: 秒間200回のリクエストで、約124 USD/月の増加(0.24 USD/100万リクエスト)
AWSサポートによると、RDSのI/OからAuroraのI/Oは見積もれないとのこと。そのため、試算はあくまで参考
検討事項
インスタンスの選択: 移行元のdb.t3.xlargeと近いスペックのdb.r6g.xlargeを初期選定として採用しました
インスタンスタイプの選択: 高I/Oが見込まれるのでI/O最適化インスタンスを使用し、日毎のI/Oコストが全体の25 %以下だった場合にはスタンダードインスタンスに切り替える方針です
Aurora Serverlessの検討: まずはAuroraを採用し、Serverlessの方が適していそうであれば途中で切り替えを検討します
バージョン互換性の問題: 移行元RDSのMySQLバージョンが8.0.36で、2024/5時点では互換のAuroraエンジンがありませんでした。mysqldumpから論理移行する方法も検討しましたが、互換エンジンのリリースを待つことにしました
⇒ 2024/6/4にAurora MySQLデータベースエンジン3.0.7(MySQL 8.0.36互換)がリリースされたので、こちらを使用することにしました
移行手順
以下が移行の大まかな流れです。
サービスの一時停止
移行元RDSのスナップショットdatabase-prod-snapshot作成
Terraformの適用
RDS Proxyの接続先をAuroraに変更
サービス再開
動作確認
動作確認で問題なければ、移行元RDSを停止(または削除)
移行元RDSのスナップショットdatabase-prod-snapshot作成
サービスが停止している間に、移行元のRDSインスタンスから安全にスナップショットを取得します。これにより、データの一貫性が保たれ、移行中のデータ損失を防ぎます。
Terraformの適用
以下のTerraformコードは、Auroraクラスター、Auroraインスタンス、クラスターパラメータグループ、インスタンスパラメータグループ、およびサブネットグループの作成を自動化します。こちらは本番環境用の設定となります。
resource "aws_rds_cluster" "aurora_cluster_prod" {
cluster_identifier = "aurora-cluster-prod" # クラスタの識別子
engine = "aurora-mysql" # 使用するデータベースエンジン
engine_version = "8.0.mysql_aurora.3.07.0" # データベースエンジンのバージョン
db_subnet_group_name = aws_db_subnet_group.aurora_db_subnet_group_prod.name # DBサブネットグループの指定
skip_final_snapshot = false # 削除時の最終スナップショットのスキップを無効化
snapshot_identifier = "arn:aws:rds:ap-northeast-1:************:snapshot:database-prod-snapshot" # スナップショットのARN
vpc_security_group_ids = [var.security_group_rds_sg_id] # セキュリティグループIDのリスト
db_cluster_parameter_group_name = aws_rds_cluster_parameter_group.aurora_cluster_parameter_group_prod.name # DBクラスタパラメータグループ
deletion_protection = true # 削除保護の有効化
backup_retention_period = 7 # バックアップの保持期間(日)
preferred_maintenance_window = "sat:08:00-sat:12:00" # 優先メンテナンスウィンドウ(UTC)
preferred_backup_window = "13:00-19:00" # バックアップ実行の優先時間帯(UTC)
copy_tags_to_snapshot = true # スナップショットへのタグのコピーを有効化
enabled_cloudwatch_logs_exports = ["audit", "error", "general", "slowquery"] # CloudWatchにエクスポートするログタイプ
storage_type = "aurora-iopt1" # ストレージタイプ
tags = {
Environment = "prod"
}
}
resource "aws_rds_cluster_instance" "aurora_cluster_instance_prod" {
identifier = "aurora-instance-prod-1" # インスタンス識別子
cluster_identifier = aws_rds_cluster.aurora_cluster_prod.id # クラスタ識別子
instance_class = "db.r6g.xlarge" # インスタンスクラス
engine = "aurora-mysql" # エンジンタイプ
engine_version = "8.0.mysql_aurora.3.07.0" # エンジンバージョン
availability_zone = "ap-northeast-1a" # 利用するAZ
db_parameter_group_name = aws_db_parameter_group.aurora_instance_parameter_group_prod.name # DBパラメータグループ
ca_cert_identifier = "rds-ca-rsa4096-g1" # SSL証明書の識別子
auto_minor_version_upgrade = true # マイナーバージョンの自動アップグレードを有効化
performance_insights_enabled = true # パフォーマンスインサイトを有効化
monitoring_interval = 60 # モニタリング間隔(秒)
monitoring_role_arn = "arn:aws:iam::************:role/rds-monitoring-role" # モニタリング用IAMロールのARN
tags = {
Environment = "prod"
}
}
resource "aws_rds_cluster_parameter_group" "aurora_cluster_parameter_group_prod" {
name = "aurora-cluster-parameter-group-prod" # パラメータグループ名
family = "aurora-mysql8.0" # パラメータグループのファミリ
description = "Aurora MySQL Cluster Parameter Group for prod" # パラメータグループの説明
# パラメータ設定は省略してデフォルト設定を使用
}
resource "aws_db_parameter_group" "aurora_instance_parameter_group_prod" {
name = "aurora-instance-parameter-group-prod" # パラメータグループ名
family = "aurora-mysql8.0" # パラメータグループのファミリ
description = "Aurora MySQL Instance Parameter Group for prod" # パラメータグループの説明
# パラメータ設定は省略してデフォルト設定を使用
}
resource "aws_db_subnet_group" "aurora_db_subnet_group_prod" {
name = "aurora-db-subnet-group-prod" # サブネットグループ名
description = "Subnet group for Aurora MySQL cluster in production environment" # サブネットグループの説明
subnet_ids = [var.subnet_db_private_1a_id, var.subnet_db_private_1c_id] # サブネットID
tags = {
Environment = "prod"
}
}
補足(開発用のクラスターとインスタンス)
開発用のAuroraクラスターとインスタンスの設定は本番環境のものと類似していますが、コスト削減と柔軟性の観点から以下のような変更を加えています。
ログ出力: CloudWatchへのログ出力設定(enabled_cloudwatch_logs_exports)を省略しています。これにより、開発環境でのログ記録コストを抑えることができます
ストレージタイプ: storage_typeの指定を省略しており、デフォルトのスタンダードインスタンスが使用されます。開発環境ではI/Oが少ないため、これでコストを最適化できます
削除保護: deletion_protectionを無効化しています。開発中にクラスタを迅速に再デプロイまたは削除することができます
インスタンスクラス: 低コストのインスタンスクラス(instance_class)を選択しています。開発環境での運用コストを削減しつつ、必要十分なパフォーマンスを確保しています
パフォーマンスインサイト: performance_insights_enabledを無効化しています。開発環境ではパフォーマンス監視の詳細な分析が不要な場合が多いため、コスト削減のために無効化します
拡張モニタリング: monitoring_role_arnとmonitoring_intervalの設定を省略し、拡張モニタリングを無効化しています。開発環境の運用コストをさらに削減します
resource "aws_rds_cluster" "aurora_cluster_dev" {
cluster_identifier = "aurora-cluster-dev"
engine = "aurora-mysql"
engine_version = "8.0.mysql_aurora.3.07.0"
db_subnet_group_name = aws_db_subnet_group.aurora_db_subnet_group_dev.name
skip_final_snapshot = false
snapshot_identifier = "arn:aws:rds:ap-northeast-1:************:snapshot:database-dev-snapshot"
vpc_security_group_ids = [var.security_group_rds_sg_id]
db_cluster_parameter_group_name = aws_rds_cluster_parameter_group.aurora_cluster_parameter_group_dev.name
deletion_protection = false
backup_retention_period = 7
preferred_maintenance_window = "sat:08:00-sat:12:00"
preferred_backup_window = "13:00-19:00"
copy_tags_to_snapshot = true
tags = {
Environment = "dev"
}
}
resource "aws_rds_cluster_instance" "aurora_cluster_instance_dev" {
identifier = "aurora-instance-dev-1"
cluster_identifier = aws_rds_cluster.aurora_cluster_dev.id
instance_class = "db.t3.medium"
engine = "aurora-mysql"
engine_version = "8.0.mysql_aurora.3.07.0"
availability_zone = "ap-northeast-1a"
db_parameter_group_name = aws_db_parameter_group.aurora_instance_parameter_group_dev.name
ca_cert_identifier = "rds-ca-rsa4096-g1"
auto_minor_version_upgrade = true
performance_insights_enabled = false
tags = {
Environment = "dev"
}
}
RDS Proxyの接続先をAuroraに変更
RDS Proxyの詳細画面からターゲットグループセクションをみつけて、編集ボタンを押すと以下のページに飛びます。データベースを今回作成したAuroraクラスターaurora-cluster-prodに設定して保存することで、接続先が変更されます。
反省点
移行時のコスト
具体的な料金は書きませんが、DB移行日のAuroraコストが想定以上だったので焦りました。
スナップショットからインスタンスを作成する過程で大量の書き込みが発生したので、その分のI/Oコストが発生したのだと思います。この点についても移行前に考慮すべきでした。
セッションタイムアウト
GitHub Actionsからterraform applyを実行したところ、AssumeRoleするIAMロールの最大セッション時間が短くてタイムアウトしてしまいました。結局、日をあらためて再リリースすることになってしまいました。
RDS停止後の自動再起動
移行元のRDSを一旦停止したのですが、7日後に自動再起動してしまい、余計なコストが発生してしまいました。移行後のAuroraに問題がないことを確認できた時点で、すぐにRDSを削除すべきでした。