Terraformで構築したRDS(Amazon Aurora MySQL)の、メジャーバージョンアップデートが意外と面倒だった

    Terraformで構築したRDS(Amazon Aurora MySQL)の、メジャーバージョンアップデートが意外と面倒だった

     

    本記事はAsiaQuest Advent Calendarの21日目です。

    はじめに

    クラウドインテグレーション部の渡邊です。
    今回は、Terraformを利用して、RDS(Amazon Aurora MySQL)のメジャーバージョンをアップグレードする際にハマったことと、その対処について説明します。

    環境情報

    • Terraform v1.2.6
    • macOS 13.4

    発生事象詳細

    今回使用した初期コードはこちらです。
    ネットワークリソース等は、既存環境の値を用いる想定でコードを作成しました。
    variableは省略しているため、ご自身の環境に合ったものをご用意ください。

    provider "aws" {
    region = "ap-northeast-1"
    }

    terraform {
    required_version = "~> 1.2.0"
    backend "s3" {
    bucket = "test-bucket"
    key = "actions/rds/terraform.tfstate"
    region = "ap-northeast-1"
    }
    required_providers {
    aws = {
    source = "hashicorp/aws"
    version = "~> 4.64.0"
    }
    }
    }

    resource "aws_db_subnet_group" "db-subgp" {
    name = "test-dbsg"
    subnet_ids = [
    var.subnet-1a,
    var.subnet-1c
    ]
    }

    resource "aws_rds_cluster_parameter_group" "dbcpg" {
    name = "test-dbcpg"
    family = "aurora-mysql5.7"
    description = "cluster parameter group"
    }

    resource "aws_rds_cluster" "db" {
    cluster_identifier = "test-db-cluster"
    db_cluster_parameter_group_name = aws_rds_cluster_parameter_group.dbcpg.name
    db_subnet_group_name = aws_db_subnet_group.db-subgp.name
    vpc_security_group_ids = [var.sg]
    engine_mode = "provisioned"
    master_username = "admin"
    master_password = "Passw0rd!"
    engine = "aurora-mysql"
    engine_version = "5.7.mysql_aurora.2.12.0"
    port = 3306
    final_snapshot_identifier = "temp"
    }

    resource "aws_rds_cluster_instance" "db-instance" {
    identifier = "test-db-instance-01"
    cluster_identifier = aws_rds_cluster.db.id
    instance_class = "db.t3.small"
    engine = aws_rds_cluster.db.engine
    engine_version = aws_rds_cluster.db.engine_version
    ca_cert_identifier = "rds-ca-rsa2048-g1"
    }

    事象

    メジャーバージョンをアップグレードするために、少なくとも、本構成では以下のパラメータを変更する必要があります。

    • aws_rds_clusterのパラメータ:engine_version
    • aws_rds_cluster_instanceのパラメータ:engine_version
    • aws_rds_cluster_parameter_groupのパラメータ:family

    これらを変更してterraform applyコマンドを実行して、設定変更を適用したところ、エラーが発生しました。

    $ terraform apply
    aws_rds_cluster_parameter_group.dbcpg: Refreshing state... [id=test-dbcpg]
    aws_db_subnet_group.db-subgp: Refreshing state... [id=test-dbsg]
    aws_rds_cluster.db: Refreshing state... [id=test-db-cluster]
    aws_rds_cluster_instance.db-instance: Refreshing state... [id=test-db-instance-01]

    Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
    ~ update in-place
    -/+ destroy and then create replacement

    Terraform will perform the following actions:

    # aws_rds_cluster.db will be updated in-place
    ~ resource "aws_rds_cluster" "db" {
    ~ engine_version = "5.7.mysql_aurora.2.12.0" -> "8.0.mysql_aurora.3.05.0"
    id = "test-db-cluster"
    tags = {}
    # (36 unchanged attributes hidden)
    }

    # aws_rds_cluster_instance.db-instance will be updated in-place
    ~ resource "aws_rds_cluster_instance" "db-instance" {
    ~ engine_version = "5.7.mysql_aurora.2.12.0" -> "8.0.mysql_aurora.3.05.0"
    id = "test-db-instance-01"
    tags = {}
    # (26 unchanged attributes hidden)
    }

    # aws_rds_cluster_parameter_group.dbcpg must be replaced
    -/+ resource "aws_rds_cluster_parameter_group" "dbcpg" {
    ~ arn = "arn:aws:rds:ap-northeast-1:315408206347:cluster-pg:test-dbcpg" -> (known after apply)
    ~ family = "aurora-mysql5.7" -> "aurora-mysql8.0" # forces replacement
    ~ id = "test-dbcpg" -> (known after apply)
    name = "test-dbcpg"
    + name_prefix = (known after apply)
    - tags = {} -> null
    ~ tags_all = {} -> (known after apply)
    # (1 unchanged attribute hidden)
    }

    Plan: 1 to add, 2 to change, 1 to destroy.
    aws_rds_cluster_parameter_group.dbcpg: Destroying... [id=test-dbcpg]
    aws_rds_cluster_parameter_group.dbcpg: Still destroying... [id=test-dbcpg, 10s elapsed]
    aws_rds_cluster_parameter_group.dbcpg: Still destroying... [id=test-dbcpg, 20s elapsed]

    aws_rds_cluster_parameter_group.dbcpg: Still destroying... [id=test-dbcpg, 2m50s elapsed]

    │ Error: deleting RDS Cluster Parameter Group (test-dbcpg): operation error RDS: DeleteDBClusterParameterGroup, https response error StatusCode: 400, RequestID: d851f851-9ed1-4da7-a322-a1637a468a80, InvalidDBParameterGroupState: One or more database instances are still members of this parameter group test-dbcpg, so the group cannot be deleted


    aws_rds_cluster_parameter_groupのreplaceによってdestroyの実行を試みますが、DBがデフォルトのクラスタパラメータグループ(以降、CPGと呼ぶ)に関連付けられているため、エラーが発生しました。

    対処

    状況や考慮したこと

    • Terraformはreplaceの場合、通常はcreateの前にdestroyから実行されます。(下記参照)

    By default, when Terraform must change a resource argument that cannot be updated in-place due to remote API limitations, Terraform will instead destroy the existing object and then create a new replacement object with the new configured arguments.

    引用元:The lifecycle Meta-Argument

    • depend_on
      • リソースの依存関係を定義するメタ引数
      • DBの変更はdestroyが発生しないため、以下のようにdepend_onを使用しても、まずdestroyが実行されてしまいます。
      • よって、実行順序が変更されず、エラーが解消されないです。
      depends_on = [ aws_rds_cluster.db , aws_rds_cluster_instance.db-instance ]
    • lifecycle
      • リソースの動作をカスタマイズするメタ引数
      • lifecycleでcreate_before_destroyを使用することで、destroyの前に、createが実行されます。
      • しかし、以下の通り、同名のCPGがすでにあるので失敗します。
      lifecycle {
    create_before_destroy = true
    }

    │ Error: creating DB Cluster Parameter Group (test-dbcpg): DBParameterGroupAlreadyExists: Parameter group test-dbcpg already exists
    │ status code: 400, request id: 3f62eb3b-a4b8-4186-8fbb-da2a02ccb29a

    │ with aws_rds_cluster_parameter_group.dbcpg,
    │ on main.tf line 37, in resource "aws_rds_cluster_parameter_group" "dbcpg":
    │ 37: resource "aws_rds_cluster_parameter_group" "dbcpg" {

    以上の対応でも難しいことから、手動で正しくメジャーバージョンアップグレードができるように、適切な順番で設定変更して対処する方針となりました。
    なお、今回はCPGのみカスタムし、インスタンスパラメータグループはデフォルトを使用しております。
    よって、パラメータグループの設定変更はCPGのみを対象としております。

    実際に対処してみた

    設定適用の流れは以下の通りです。

    ① 1回目の適用

    • メジャーバージョンアップグレードを許可するパラメータallow_major_version_upgradeをtrueにする
    • デフォルトのCPG(default.aurora-mysql X.X)へ関連付ける
    • 設定変更を即時反映するパラメータapply_immediatelyをtrueにする

    ② 2回目の適用

    • カスタムしたCPGaws_rds_cluster_parameter_groupのファミリーfamilyを変更する
    • (オプション)変更後のengine_versionのサポートするインスタンスクラスinstance_classへ変更

    ③ 3回目の適用

    • クラスタaws_rds_clusterおよびインスタンスaws_rds_cluster_instanceのエンジンバージョンengine_versionを変更してバージョンアップ
    • カスタムCPGaws_rds_cluster_parameter_groupへ再度関連付ける

     

    ① 1回目の適用

    • メジャーバージョンアップグレードを許可するパラメータallow_major_version_upgradeをtrueにする
      allow_major_version_upgradeをtrueにしないと、メジャーバージョンアップグレードに失敗するので、事前に設定しておきます。
      デフォルトでfalseであるため注意しましょう。

    • デフォルトのCPG(default.aurora-mysql X.X)へ関連付ける
      カスタムCPGおよびエンジンバージョンをアップグレードするために、デフォルトのCPGに関連付けた後、もともとアタッチしていたCPGのバージョンを変更する方針とします。

    • 設定変更を即時反映するパラメータapply_immediatelyをtrueにする
      設定変更を即時反映するために、インスタンスおよびクラスタにパラメータapply_immediatelyを追加します。
      インスタンス、およびクラスタのapply_immediatelyのデフォルト値はfalseなので注意しましょう。

    1回目の適用による変更後のコードは以下です。

    resource "aws_rds_cluster" "db" {
    cluster_identifier = "test-db-cluster"
    - db_cluster_parameter_group_name = aws_rds_cluster_parameter_group.dbcpg.name
    + db_cluster_parameter_group_name = "default.aurora-mysql5.7"
    db_subnet_group_name = aws_db_subnet_group.db-subgp.name
    vpc_security_group_ids = [var.sg]
    engine_mode = "provisioned"
    master_username = "admin"
    master_password = "Passw0rd!"
    engine = "aurora-mysql"
    engine_version = "5.7.mysql_aurora.2.12.0"
    + allow_major_version_upgrade = true
    port = 3306
    final_snapshot_identifier = "temp"
    + apply_immediately = true
    }
    resource "aws_rds_cluster_instance" "db-instance" {
    identifier = "test-db-instance-01"
    cluster_identifier = aws_rds_cluster.db.id
    instance_class = "db.t3.small"
    engine = aws_rds_cluster.db.engine
    engine_version = aws_rds_cluster.db.engine_version
    ca_cert_identifier = "rds-ca-rsa2048-g1"
    + apply_immediately = true
    }

    terraform applyコマンドで設定変更の適用に成功しました。

    $ terraform apply

    Terraform will perform the following actions:

    # aws_rds_cluster.db will be updated in-place
    ~ resource "aws_rds_cluster" "db" {
    ~ allow_major_version_upgrade = false -> true
    ~ db_cluster_parameter_group_name = "test-dbcpg" -> "default.aurora-mysql5.7"
    + apply_immediately = true
    id = "test-db-cluster"
    tags = {}
    # (36 unchanged attributes hidden)
    }

    # aws_rds_cluster_instance.db-instance will be updated in-place
    ~ resource "aws_rds_cluster_instance" "db-instance" {
    + apply_immediately = true
    id = "test-db-instance-01"
    tags = {}
    # (26 unchanged attributes hidden)
    }
    Plan: 0 to add, 2 to change, 0 to destroy.

    Apply complete! Resources: 0 added, 2 changed, 0 destroyed.


    ② 2回目の適用

    • カスタムしたCPGaws_rds_cluster_parameter_groupのファミリーfamilyを変更する
      この時点で、カスタムCPGに関連付けられているDBはない想定のため、以下のように、familyを変更します。
    resource "aws_rds_cluster_parameter_group" "dbcpg" {
    name = "test-dbcpg"
    - family = "aurora-mysql5.7"
    + family = "aurora-mysql8.0"
    description = "cluster parameter group"
    }
    • (オプション)変更後のエンジンバーションengine_versionをサポートするインスタンスクラスinstance_classへ変更
      インスタンスクラスによっては、アップグレード前にインスタンスクラスinstance_classを変更する必要があります。
      なぜなら、アップグレード後にそのインスタンスサイズをサポートしていない場合があるからです。
      また、「① 1回目の適用」で実施した、設定変更を即時反映するパラメータapply_immediatelyをtrueに設定しておかないと、インスタンスクラスの変更が反映されず、以下のように後続のアップグレード作業でエラーが発生します。

    │ Error: updating RDS Cluster (test-db-cluster): InvalidDBInstanceState: Cannot modify engine version because there is a pending orderable config change for DB instance: test-db-instance-01
    │ status code: 400, request id: 9618c4fc-b42e-405e-8d91-0645d04b23bc

    │ with aws_rds_cluster.db,
    │ on main.tf line 44, in resource "aws_rds_cluster" "db":
    │ 44: resource "aws_rds_cluster" "db" {

    サポートしているインスタンスクラスは以下のように確認できます。
    こちらは8.0.mysql_aurora.3.05.0のサポートするインスタンスクラスを表示します。

    aws rds describe-orderable-db-instance-options \
    --engine aurora-mysql \
    --query 'OrderableDBInstanceOptions[].[DBInstanceClass,StorageType,Engine,EngineVersion]' \
    --output table \
    --region ap-northeast-1 \
    | grep 8.0.mysql_aurora.3.05.0

    以下のように、インスタンスクラスinstance_classを変更します。

    resource "aws_rds_cluster_instance" "db-instance" {
    identifier = "test-db-instance-01"
    cluster_identifier = aws_rds_cluster.db.id
    - instance_class = "db.t3.small"
    + instance_class = "db.t4g.medium"
    engine = aws_rds_cluster.db.engine
    engine_version = aws_rds_cluster.db.engine_version
    ca_cert_identifier = "rds-ca-rsa2048-g1"
    apply_immediately = true
    }

    即時反映も含めた設定を適用することで、後続の作業もエラーにならないはずです。

    $ terraform apply     

    Terraform will perform the following actions:

    # aws_rds_cluster.db will be updated in-place
    ~ resource "aws_rds_cluster" "db" {
    id = "test-db-cluster"
    tags = {}
    # (38 unchanged attributes hidden)
    }

    # aws_rds_cluster_instance.db-instance will be updated in-place
    ~ resource "aws_rds_cluster_instance" "db-instance" {
    id = "test-db-instance-01"
    ~ instance_class = "db.t3.small" -> "db.t4g.medium"
    tags = {}
    # (26 unchanged attributes hidden)
    }

    Plan: 0 to add, 2 to change, 0 to destroy.

    Apply complete! Resources: 0 added, 2 changed, 0 destroyed.

    また、インスタンスクラスとメジャーバージョンアップグレードを同時に行おうとすると、メジャーバージョンアップグレード後のエンジンバージョンと、現在のインスタンスクラスの組み合わせが正しくない旨のエラーがでます。
    そのため、まずインスタンスクラスの変更完了後、メジャーバージョンアップグレードを試みる必要があります。


    │ Error: updating RDS Cluster (test-db-cluster): InvalidParameterCombination: RDS does not support creating a DB instance with the following combination: DBInstanceClass=db.t3.small, Engine=aurora-mysql, EngineVersion=8.0.mysql_aurora.3.05.0, LicenseModel=general-public-license. For supported combinations of instance class and database engine version, see the documentation.
    │ status code: 400, request id: da1645b8-8bfd-4b23-a31f-5530fbe9bacd

    │ with aws_rds_cluster.db,
    │ on main.tf line 44, in resource "aws_rds_cluster" "db":
    │ 44: resource "aws_rds_cluster" "db" {


    ③ 3回目の適用

    • クラスタaws_rds_clusterおよびインスタンスaws_rds_cluster_instanceのエンジンバージョンengine_versionを変更してバージョンアップ
    • カスタムCPGaws_rds_cluster_parameter_groupへ再度関連付ける

    以上を踏まえて、変更したコードは以下のようになります。

    resource "aws_rds_cluster_parameter_group" "dbcpg" {
    name = "test-dbcpg"
    family = "aurora-mysql8.0"
    description = "cluster parameter group"
    }

    resource "aws_rds_cluster" "db" {
    cluster_identifier = "test-db-cluster"
    db_cluster_parameter_group_name = aws_rds_cluster_parameter_group.dbcpg.name
    db_subnet_group_name = aws_db_subnet_group.db-subgp.name
    vpc_security_group_ids = [var.sg]
    engine_mode = "provisioned"
    master_username = "admin"
    master_password = "Passw0rd!"
    engine = "aurora-mysql"
    engine_version = "8.0.mysql_aurora.3.05.0"
    allow_major_version_upgrade = true
    port = 3306
    final_snapshot_identifier = "temp"
    apply_immediately = true
    }

    resource "aws_rds_cluster_instance" "db-instance" {
    identifier = "test-db-instance-01"
    cluster_identifier = aws_rds_cluster.db.id
    # instance_class = "db.t3.small"
    instance_class = "db.t4g.medium"
    engine = aws_rds_cluster.db.engine
    engine_version = aws_rds_cluster.db.engine_version
    ca_cert_identifier = "rds-ca-rsa2048-g1"
    apply_immediately = true
    }

    適用すると、成功しました。

    $ terraform apply

    # aws_rds_cluster.db will be updated in-place
    ~ resource "aws_rds_cluster" "db" {
    ~ db_cluster_parameter_group_name = "default.aurora-mysql5.7" -> "test-dbcpg"
    ~ engine_version = "5.7.mysql_aurora.2.12.0" -> "8.0.mysql_aurora.3.05.0"
    id = "test-db-cluster"
    tags = {}
    # (37 unchanged attributes hidden)
    }

    # aws_rds_cluster_instance.db-instance will be updated in-place
    ~ resource "aws_rds_cluster_instance" "db-instance" {
    ~ engine_version = "5.7.mysql_aurora.2.12.0" -> "8.0.mysql_aurora.3.05.0"
    id = "test-db-instance-01"
    tags = {}
    # (27 unchanged attributes hidden)
    }

    Plan: 0 to add, 2 to change, 0 to destroy.

    Apply complete! Resources: 0 added, 2 changed, 0 destroyed.

     

    メジャーバージョンアップグレードが成功しているか確認

    コンソール画面およびTerraform側から確認すると、設定変更が問題なく行われていることがわかります。

    202312_Terraform_RDS_02

    $ terraform apply
    aws_rds_cluster_parameter_group.dbcpg: Refreshing state... [id=test-dbcpg]
    aws_db_subnet_group.db-subgp: Refreshing state... [id=test-dbsg]
    aws_rds_cluster.db: Refreshing state... [id=test-db-cluster]
    aws_rds_cluster_instance.db-instance: Refreshing state... [id=test-db-instance-01]

    No changes. Your infrastructure matches the configuration.

    Terraform has compared your real infrastructure against your configuration and found no differences, so no changes are needed.

    Apply complete! Resources: 0 added, 0 changed, 0 destroyed.

    まとめ

    • メジャーバージョンのアップグレードは以下に注意する
      • カスタムCPGの関連付けの依存関係によるエラー
      • Terraformの実行順序(createの前にdestroyが実行される)の影響でdepend_onでは順番を制御できないことがある点
      • 既存のリソース名と同じにしたい場合、lifecycleで実行順序を制御しても発生する、同名のリソース作成のエラー
      • メジャーバージョンのアップグレード後にサポートするインスタンスサイズ
      • apply_immediatelyやallow_major_version_upgradeの有効化
      • 設定変更の順番(同時に設定変更すると失敗するパラメータがいくつかある)

    【参考】

    Resource: aws_rds_cluster

    Resource: aws_rds_cluster_instance

    The depends_on Meta-Argument