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

    アジアクエスト株式会社では一緒に働いていただける方を募集しています。
    興味のある方は以下のURLを御覧ください。