AuroraのエンジンバージョンアップグレードをTerraformで実行した際にエラーが発生して解消に少し手間取ってしまう事象が発生した為、次回のアップグレード時にまたハマることがないようにエラー内容の詳細と回避方法をまとめておきます。
何も考えずに普通にやろうとすると高確率で発生しそうな内容だったので、同じ内容でハマるケースはかなり多そうで、この記事が少しでも役に立てば嬉しいです。
何が起こったか
- やろうとしていたこと
- 対象リソース
- 変更内容
- 発生したエラー
Auroraのエンジンバージョンアップグレードを実施しました。
マネージドのRDSを使用している都合上、AWSが管理する最低バージョン以下を使用している場合は指定された期日より前に手動でアップグレードを行う必要があり、この対応は数年に一度のスパンで行う必要があります。
対応しなかった場合は、AWS側が不定期で実施するメンテナンス中にアップグレードが実施される
また、AuroraのリソースはTerraformで管理していた為、アップグレードもTerraformのコマンドから実施しました。
アップグレード実施前のAuroraのリソースは、以下のような内容でした。
最小限の項目と、発生した事象に関係がありそうな項目だけを設定した再現リソースです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
locals { engine = "aurora-mysql" engine_version = "5.7.mysql_aurora.2.06.0" } resource "aws_rds_cluster" "cluster" { cluster_identifier = "sample-cluster" engine = local.engine engine_version = local.engine_version db_subnet_group_name = "sample-db-subnet-group" master_username = "root" master_password = "dummymasterpwd" final_snapshot_identifier = "sample-rds-cluster-final-snapshot" skip_final_snapshot = true vpc_security_group_ids = [module.security_group.this_security_group_id] lifecycle { ignore_changes = [master_password] } } resource "aws_rds_cluster_instance" "instance" { count = 1 identifier = "sample-instance-${count.index}" cluster_identifier = aws_rds_cluster.cluster.id engine = local.engine engine_version = local.engine_version instance_class = "db.t3.small" depends_on = [ aws_rds_cluster.cluster ] } |
インスタンスのエンジンとエンジンバージョンをクラスターの設定と必ず合わせる必要がある為、変数で共通の値を設定しています。
見るのはそこぐらいで、他は再現リソースを作る為のサンプルの値を入れているだけで無視してください。
mysqlのバージョンが5.7、auroraのエンジンバージョンを2.06.0で作成していますが、バージョンに依存して発生する問題ではないので、変更前はこの値を使用していた程度にご参照ください。
また、今回のエラーを再現するのにインスタンスも1台あれば十分だった為、再現リソースは最小の1台で作成しています。
今回は、エンジンバージョンを以下のように変更します。
Before | 5.7.mysql_aurora.2.06.0 |
---|---|
After | 5.7.mysql_aurora.2.09.0 |
上記対象リソースの項目にも記載した通り、変更前/変更後のバージョンに依存する問題ではないので、今回はこの前後値で再現させていた程度にご参照ください。
Terraformのコードのエンジンバージョンの値を5.7.mysql_aurora.2.09.0に変更し、まずはplan結果を確認します。
~ resource “aws_rds_cluster” “cluster” {
~ engine_version = “5.7.mysql_aurora.2.06.0” -> “5.7.mysql_aurora.2.09.0”
}
# aws_rds_cluster_instance.instance[0] must be replaced
-/+ resource “aws_rds_cluster_instance” “instance” {
~ engine_version = “5.7.mysql_aurora.2.06.0” -> “5.7.mysql_aurora.2.09.0” # forces replacement
}
Plan: 1 to add, 1 to change, 1 to destroy.
クラスターはエンジンバージョンを更新する変更のみ、インスタンスはリソースを再作成する挙動になるようです。
インスタンスのエンジンバージョン以外の変更結果は、今回の内容に関係ないので割合します。
続いてapplyを実行すると、クラスターの更新は成功したものの、インスタンスの再作成が以下のエラーで失敗してしまいました。
aws_rds_cluster_instance.instance[0]: Destruction complete after 4m53s
aws_rds_cluster_instance.instance[0]: Creating…
Error: error creating RDS Cluster (sample-cluster) Instance: InvalidParameterCombination: The engine version that you requested for your DB instance (5.7.mysql_aurora.2.09.0) does not match the engine version of your DB cluster (5.7.mysql_aurora.2.06.0).
status code: 400, request id: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
結果、インスタンスの再作成時に削除までは成功している為、クラスターだけが残された状態で紐づくインスタンスが全て消えてしまいました。
1 2 |
aws rds describe-db-instances \ --filters "Name=db-cluster-id, Values=sample-cluster" |
“DBInstances”: []
}
クラスターが残っている為、インスタンスが消えても中のデータが消えてしまうなどはありません。
ただ、有効なインスタンスが存在しない状態になる為、インスタンスを再作成しないとAuroraに対して接続ができない状態になります。
とりあえずインスタンスを再作成したい場合は、エンジンバージョンを変更前の状態(今回の例だと2.06.0)に戻してから、もう一度terraform applyを実行すれば元の状態に戻すことができます。
解決方法
アップグレードできなかったので、失敗原因を追っていきます。
1. 実行ログから分かる情報で現状整理
Terraform apply時の実行ログから確認できるのは、以下3点です。
- クラスターに対する変更は、成功している
- インスタンス再作成時の削除は、成功している
- インスタンス再作成時の削除後の作成は、失敗している
Error: error creating RDS Cluster (sample-cluster) Instance: InvalidParameterCombination: The engine version that you requested for your DB instance (5.7.mysql_aurora.2.09.0) does not match the engine version of your DB cluster (5.7.mysql_aurora.2.06.0).
インスタンス作成時に失敗してしまったエラーログをもう少し詳細に見ると、クラスターとインスタンスのエンジンバージョンが異なる状態でインスタンスを作ろうとしているのが失敗原因で、クラスターのバージョンが変更前の2.06.0で比較されてしまっているのが確認できます。
クラスターに対する変更は成功していて、変更後の値もTerraformのコード上で共通の変数の値を使用しているのに何故??など不明点は残りますが、クラスターのエンジンバージョンが変更できていない可能性が高そうな為、apply実行後のクラスターのエンジンバージョンを確認してみます。
1 2 3 |
aws rds describe-db-clusters \ --db-cluster-identifier sample-cluster \ --query "DBClusters[*].{Cluster:DBClusterIdentifier, EngineVersion:EngineVersion}" |
[
“Cluster”: “sample-cluster”,
“EngineVersion”: “5.7.mysql_aurora.2.06.0”
]
]
やはり、クラスターのエンジンバージョンが更新できていないようです。
2. AWS APIの実行履歴の確認
クラスターのエンジンバージョン更新ができていないことが原因と分かったので、次はTerraform apply時のAPI実行履歴からクラスターの変更詳細を確認します。
AWSのAPI実行履歴はCloudTrailで確認できるので、イベント履歴からTerraformのapply実行時の履歴を探します。
CloudTrailのイベント履歴は、CLIコマンドの aws cloudtrail lookup-events
でも参照可能ですが、深いネスト構造のjsonでとても読み辛いので、ブラウザ画面から確認すると見易くて便利です。
apply実行時の履歴を見付けたら、その中からイベント名がModifyDBClusterの詳細を確認しにいきます。
“dBClusterIdentifier”: “sample-cluster”,
“applyImmediately”: false,
“engineVersion”: “5.7.mysql_aurora.2.09.0”
},
“responseElements”: {
“engineVersion”: “5.7.mysql_aurora.2.06.0”,
“pendingModifiedValues”: {
“engineVersion”: “5.7.mysql_aurora.2.09.0”
}
}
クラスター変更時の変更履歴の詳細全てを載せると長くなる為、関係のある部分だけを抽出しています。
すると、pendingModifiedValuesの中にアップグレード後のエンジンバージョンの値が入っていて、変更が保留中になってしまっているのが確認できます。
これで、エンジンバージョンのアップグレードがクラスターに反映されていない原因がクラスターへの反映タイミングであることが分かりました。
3. クラスターのエンジンバージョン変更を即時反映させるように修正
原因が判明したので、クラスターのエンジンバージョンのアップグレードを即時で反映させるように修正を入れていきます。
変更した値の適用タイミングは、クラスターのapply_immediatelyの設定で即時反映に指定できる為、この値をtrueに指定する設定を一行追加します。
Optional項目でデフォルトはfalseになっているので、明示的にtrueの設定を追加する必要があります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
resource "aws_rds_cluster" "cluster" { cluster_identifier = "sample-cluster" engine = "aurora-mysql" engine_version = "5.7.mysql_aurora.2.06.0" db_subnet_group_name = "sample-db-subnet-group" master_username = "root" master_password = "dummymasterpwd" final_snapshot_identifier = "sample-rds-cluster-final-snapshot" skip_final_snapshot = true vpc_security_group_ids = [module.security_group.this_security_group_id] apply_immediately = true lifecycle { ignore_changes = [master_password] } } resource "aws_rds_cluster_instance" "instance" { count = 1 identifier = "sample-instance-${count.index}" cluster_identifier = aws_rds_cluster.cluster.id engine = aws_rds_cluster.cluster.engine engine_version = aws_rds_cluster.cluster.engine_version instance_class = "db.t3.small" depends_on = [ aws_rds_cluster.cluster ] } |
また、22,23行目のインスタンスのエンジンとエンジンバージョンの設定を、共通の変数の値を使用する形から、クラスターリソースの値をそのまま設定する形への変更も併せて行います。
クラスターへの設定変更の反映タイミングを即時反映に変更した後で、再度Terraformを実行してみます。
まずは、plan結果の確認から。
~ resource “aws_rds_cluster” “cluster” {
+ apply_immediately = true
~ engine_version = “5.7.mysql_aurora.2.06.0” -> “5.7.mysql_aurora.2.09.0”
}
# aws_rds_cluster_instance.instance[0] must be replaced
-/+ resource “aws_rds_cluster_instance” “instance” {
~ engine_version = “5.7.mysql_aurora.2.06.0” -> “5.7.mysql_aurora.2.09.0” # forces replacement
}
Plan: 1 to add, 1 to change, 1 to destroy.
クラスターの変更内容に、apply_immediatelyがtrueになる差分が追加されました。
リソースを再作成するインスタンスの差分は、前回と全く同じです。
続いて、applyを実行してみます。
When expanding the plan for aws_rds_cluster_instance.instance[0] to include
new values learned so far during apply, provider
“registry.terraform.io/hashicorp/aws” produced an invalid new value for
.engine_version: was cty.StringVal(“5.7.mysql_aurora.2.09.0”), but now
cty.StringVal(“5.7.mysql_aurora.2.06.0”).
This is a bug in the provider, which should be reported in the provider’s own
issue tracker.
エラーの内容が少し変わりましたが、クラスターの変更を即時反映にしても、インスタンスの再作成時にクラスターのエンジンバージョンが更新できておらず更新が失敗してしまいます。
「クラスターの設定変更を即時反映にしても駄目かー」と思ってエラー内容で調べていると、Terraformで同じ内容のissueが切られており、まだ対応がされていないことと、自分の環境でだけ発生する問題ではなさそうなことが分かりました。
AuroranのDB エンジンがMySQLとPostgresで若干環境が異なりますが、発生事象的に同じ内容のようです。
これは困ったと対応を考えていたところ、とても根本的な疑問が湧きました。
そもそもエンジンバージョンってクラスターの設定であって、インスタンスに設定するものではないのでは?
マネージドコンソール上からRDSのエンジンバージョンなどを変更する場合に、インスタンスからだと変更の画面にすら遷移できないので全て親のクラスターの設定から変更していたのを思いだし、インスタンスに対してエンジンバージョンの変更をやろうとしていること自体が間違いで、クラスターのエンジンバージョンを変更すればインスタンスの設定も自動的に変わるのでは?と考えた感じです。
それなら何故インスタンスのAPIでエンジンバージョンの指定ができるようになっているかは謎ですが、、、w
4. Terraformコマンド実行時の適用対象リソースをクラスターのみに絞る
Terraformはコマンド実行時の同階層に存在するtfファイルに書かれた全てのリソースをコマンド実行時の適用対象として認識します。
その中から対象リソースを限定したい場合は、実行時にtargetオプションを付けて対象リソース名を指定して実行することで、その他のリソースを適用対象外にすることができます。
今回はクラスターリソースのみを対象にしたい為、以下の感じです。
1 |
terraform <CMD> --target aws_rds_cluster.cluster |
対象リソースを絞り、まずはplan結果を確認します。
~ resource “aws_rds_cluster” “cluster” {
+ apply_immediately = true
~ engine_version = “5.7.mysql_aurora.2.06.0” -> “5.7.mysql_aurora.2.09.0”
}
Plan: 0 to add, 1 to change, 0 to destroy.
plan結果からインスタンスの再作成が消え、クラスターの変更のみになりました。
続いて、applyを実行します。
Apply complete! Resources: 0 added, 1 changed, 0 destroyed.
これまでクラスターの変更は数分ですぐ終わっていたのが、変更の実行完了までの時間が倍以上かかるようになり、apply結果も成功するようになりました。
実行時間はデータ量やクラスターに紐づくインスタンス数に依存するので、ここで時間を出しているのは前回実行時のクラスター変更までの時間(1m52s)と比較して3倍以上かかるようになった点を記載したい為です。
実行時間が大幅に延びているのはエンジンバージョンの完了まで待ったからであろうと期待を込めて再度クラスターのエンジンバージョンを確認してみると…
1 2 3 |
aws rds describe-db-clusters \ --db-cluster-identifier sample-cluster \ --query "DBClusters[*].{Cluster:DBClusterIdentifier, EngineVersion:EngineVersion}" |
[
“Cluster”: “sample-cluster”,
“EngineVersion”: “5.7.mysql_aurora.2.09.0”
]
]
無事に変更できていました!
インスタンスの方のエンジンバージョンも同じように確認してみると…
1 2 3 |
aws rds describe-db-instances \ --filters "Name=db-cluster-id, Values=sample-cluster" \ --query "DBInstances[*].{Cluster:DBClusterIdentifier, Instance:DBInstanceIdentifier, EngineVersion:EngineVersion}" |
[
“Cluster”: “sample-cluster”,
“Instance”: “sample-instance-0”,
“EngineVersion”: “5.7.mysql_aurora.2.09.0”
]
]
インスタンスの方のエンジンバージョンも自動的に更新したようで、アップグレード後のバージョンに変更されていました!
ということで、TerraformからAuroraのエンジンバージョンアップグレードが無事成功するようになりました。
【重要】諸注意
とりあえず何とかアップグレードできるようにはなりましたが、最後に2点ほど注意事項を書いて終わりにします。
1. ターゲットオプションを付けた実行は非推奨
apply実行後の出力内容にも以下のWarningが出力されるように、ターゲットオプションを付けた実行は非推奨な実行です。
今回のエラーのような他の回避策が難しい特殊ケースだけの使用に留め、普段は使用しないように注意する必要があります。
only for exceptional situations such as recovering from errors or mistakes, or
when Terraform specifically suggests to use it as part of an error message.
また、今回のケースで考えるとターゲットオプションを付けて対象リソースを絞る方法を取らずとも、クラスターとインスタンスのリソースを作成するtfファイルの階層を分ける事で、ターゲットオプションを付けなくてもクラスターのみへの変更適用が可能になる為、正しく対応するのであれば、その対応が望ましいです。
ただ、多くの環境ではクラスターとクラスターに紐づくインスタンスは同ファイルに定義されているのと、構成変更するコストを払ってまで正しい対応は選択せず、非推奨ではあるがターゲットオプション付与の選択をする場合が多いと思います。
その場合でも、問題が根本解決されるまでの暫定対応である意識は持ちたいです。
2. 次回plan時に差分は出なくなっているが、stateファイル内の記述は変更前の値から更新されていない
対象リソースを絞ったTerraformの実行からクラスター(と自動で更新されるインスタンスも)のエンジンバージョンをアップグレード後に、インスタンスのリソースも含めたTerraformのplanを再度流した結果として、差分が出ないようになっている点は、今回の対応後に自分の方でも確認を行いました。
ただし、今回の対応はクラスターのみに対して変更を適用させている為、インスタンスに対してはTerraformの変更を適用させていません。
よって、stateファイルに記録されたインスタンスの設定は、アップグレード前のエンジンバージョンのままになっている点に注意する必要があります。
stateファイルに記録されたインスタンスのエンジンバージョンが差分として無視される挙動を見ても、インスタンスが持つエンジンバージョンの設定値はあまり意味のない値なのかもしれません。
この状態が正しいのかは正直分かりませんが、stateファイルの中身を人間が参照するのは意図しない挙動があった際に現状の確認をするぐらいでしか機会がないと思うので、stateファイルのインスタンスのエンジンバージョンには、インスタンスの初回作成時のエンジンバージョンが記録されるぐらいに思っておく方が良いのかもしれませんね。