本日も乙

ただの自己満足な備忘録。

【Terraform】GCPインスタンステンプレートを変更するときに削除エラーが起きたときの解決方法

なんとこの記事でブログ200記事目です!2012年5月から始めたので約6年半での到達はペースとしてはかなり遅いですが、今後はガンガン書いていきます!

今回はTerraform + GCPネタです。
Auto Scaling on GCEをTerraformで書いていて、インスタンステンプレートの更新時にハマったのでそのメモです。

インスタンステンプレートは変更の度に新規作成する

AWSのLaunch Configrationと同じで一度作成すると変更ができないため、イメージなどを変更するときは新規作成して、インスタンスグループ *1 に新しいインスタンステンプレートを紐付ける必要があります。

Terraformで実装するときは Lifecycle という設定を入れることで、変更が入る度にインスタンステンプレートが新規作成され、インスタンスグループに紐付けた後、古いインスタンステンプレートを削除してくれます。

resource "google_compute_instance_template" "instance_template" {
  name_prefix  = "instance-template-"
  machine_type = "n1-standard-1"
  region       = "us-central1"

  // boot disk
  disk {
    # ...
  }

  // networking
  network_interface {
    # ...
  }

  lifecycle {
    create_before_destroy = true
  }
}

resource "google_compute_instance_group_manager" "instance_group_manager" {
  name               = "instance-group-manager"
  instance_template  = "${google_compute_instance_template.instance_template.self_link}"
  base_instance_name = "instance-group-manager"
  zone               = "us-central1-f"
  target_size        = "1"
}

Using with Instance Group Manager のコードを拝借しました。
create_before_destroy = true とすることで、新規リソースを作成してから旧リソースを削除してくれるようになります。

resourceInUseByAnotherResource エラーが起こる

次のようなコードでインスタンステンプレートを更新するときに resourceInUseByAnotherResource エラーが出てしまい、新しいインスタンステンプレートがインスタンスグループに紐付けができなくなってしまいました。

1 error(s) occurred:

* google_compute_instance_template.instance_template: Error deleting instance template: googleapi:
 Error 400: The instance_template resource 'instance-template' is already being used by '
instance-group-manager', resourceInUseByAnotherResource
resource "google_compute_instance_template" "instance_template" {
  name_prefix  = "instance-template-"
  machine_type = "n1-standard-1"
  region       = "asia-northeast1"

  // boot disk
  disk {
    # ...
  }

  // networking
  network_interface {
    # ...
  }

  // 追加
  update_strategy = "NONE"

  lifecycle {
    create_before_destroy = true
  }
}

resource "google_compute_instance_group_manager" "instance_group_manager" {
  name               = "instance-group-manager"
  instance_template  = "${google_compute_instance_template.instance_template.self_link}"
  base_instance_name = "instance-group-manager"
  zone               = "asia-northeast1-a"
  target_size        = "10"
}

インスタンスグループの更新方法を指定する必要がある

原因は update_strategy = "NONE" としていたことでした。
update_strategy についてドキュメントを確認します。

  • update_strategy - (Optional, Default "REPLACE") If the instance_template resource is modified, a value of "NONE" will prevent any of the managed instances from being restarted by Terraform. A value of "REPLACE" will restart all of the instances at once. "ROLLING_UPDATE" is supported as a beta feature. A value of "ROLLING_UPDATE" requires rolling_update_policy block to be set

https://www.terraform.io/docs/providers/google/r/compute_instance_group_manager.html#update_strategy

つまり、デフォルトは REPLACE (GCEインスタンスの再起動) になっていたのを NONE にしたことで、旧インスタンステンプレートで起動していたGCEインスタンスから、新インスタンステンプレートで起動するGCEインスタンスへの変更ができなくなったためエラーになったというわけです。

ローリングアップデートでインスタンスを入れ替える

では、デフォルト値 REPLACE で良いかと言われるとそうでもありません。
REPLACE だと(一度切りの)再起動 なので、イメージを入れ替えるといった場合には適しません。ROLLING_UPDATE にすべきです。
ドキュメントでは ROLLING_UPDATE にする場合は rolling_update_policy ブロックの追記が必要とのことなので、このようになります。

resource "google_compute_instance_template" "instance_template" {
  name_prefix  = "instance-template-"
  machine_type = "n1-standard-1"
  region       = "asia-northeast1"

  // boot disk
  disk {
    # ...
  }

  // networking
  network_interface {
    # ...
  }

  // 追加
  update_strategy = "ROLLING_UPDATE"

  rolling_update_policy{
    type = "PROACTIVE"
    minimal_action = "REPLACE"
    max_surge_percent = 20
    max_unavailable_fixed = 1
    min_ready_sec = 60
  }

  lifecycle {
    create_before_destroy = true
  }
}

resource "google_compute_instance_group_manager" "instance_group_manager" {
  name               = "instance-group-manager"
  instance_template  = "${google_compute_instance_template.instance_template.self_link}"
  base_instance_name = "instance-group-manager"
  zone               = "asia-northeast1-a"
  target_size        = "10"
}

こうすることで、2台ずつ(10台 * 20%) 新しいインスタンステンプレートによって起動されるGCEインスタンスに置き換えられるようになります。

インスタンスグループのアップデートについては私自身に完全に理解しきれていないのでちゃんと理解できるようになったら別記事で紹介したいと思います。

まとめ

Terraformでインスタンステンプレートを更新できない場合の対処方法を紹介しました。インスタンスグループ配下にある、GCEインスタンスの入れ替え(ローリングアップデート)を行なうことでインスタンステンプレートの入れ替えもできるようになります。 このローリングアップデートはGCPのマネジメントコンソール(GUI)からでも操作が可能なので、Terraformを使わないときでも便利なので、ぜひ使ってみてください。

参考

*1:今回はマネージドインスタンスグループを前提としています