📝

TerraformでEC2とRoute53だけのFloating IPパターンを試してみる

2021/10/24に公開

概要

ALBを使わない、Route53とEIPを紐付けたEC2だけで動いているアプリケーションだと、Terraformの更新時にEC2のリプレースが発生すると数分のダウンタイムが発生してしまいます。
この構成のままダウンタイムを小さくする方法がないか検討している中でFloating IPパターンというものを知り、これをTerraformでやる方法を考えてみました。

http://en.clouddesignpattern.org/index.php/CDP:Floating_IPパターン

普通に構成を変更した方が楽だと思うので、こうやればできるんじゃないかというネタとしてみていただければと思います。

検証に使用したTerraformのコード

今回の検証に使用したTerraformのコードです。vpc_idだけ実行時に渡す必要があります。
公式のサンプルコードをコピーしたのでEC2のNameタグがHelloWorldになっていたりしますが、特に意味はありません。

main.tf
provider "aws" {
  region = "ap-northeast-1"
}

# https://aws.amazon.com/jp/blogs/news/query-for-the-latest-amazon-linux-ami-ids-using-aws-systems-manager-parameter-store/
data "aws_ssm_parameter" "amzn2_ami" {
  name = "/aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2"
}

resource "aws_instance" "web" {
  ami           = data.aws_ssm_parameter.amzn2_ami.value
  instance_type = "t3.micro"

  tags = {
    Name = "HelloWorld"
  }

  user_data     = file("userdata.sh")
  vpc_security_group_ids = [
    aws_security_group.web.id
  ]
}

resource "aws_eip" "web" {
  instance = aws_instance.web.id
  vpc      = true
}

resource "aws_security_group" "web" {
  name   = "web-server-sg"
  vpc_id = var.vpc_id

  ingress {
    from_port   = 80
    to_port     = 80
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }

  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }
}
variables.tf
variable "vpc_id" {}
userdata.sh
#!/bin/bash
sudo yum update -y
sudo yum install -y httpd
sudo systemctl start httpd
sudo systemctl enable httpd

userdata.shは下記の記事を参考にしました。

https://docs.aws.amazon.com/ja_jp/AWSEC2/latest/UserGuide/ec2-lamp-amazon-linux-2.html

ダウンタイムの短縮だけならTerraformの設定で実現できる

Terraformにcreate_before_destroyというオプションがあり、apacheなどをインストールしたゴールデンイメージを作成していれば、これを設定することでダウンタイムを数秒まで短縮することができました。

https://www.terraform.io/docs/language/meta-arguments/lifecycle.html

ただ、この方法だと置き換えたEC2でエラーが起きた時に巻き戻すには、再度EC2を作りなおすことになるので少し時間がかかってしまいます。

既存のEC2を削除しないで置き換える

上記の方法の問題点は、既存のEC2が削除されてしまうことにありました。
ただ、調べた限りではTerraformにはリプレース対象のリソースを残したまま作り直す機能はなさそうでした。

そこで、少し強引な方法ですが、terraform state rmというコマンドで特定のリソースをTerraformの管理外にしてから作り直す方法を試してみます。

terraform state rm aws_instance.web

上記のコマンドを実行しても、Terraformの管理外になるだけでリソースは残ったままになります。この状態で、再度terraform applyを実行してリソースを作成します。

terraform apply -var 'vpc_id={VPCのID}'

リソースが作成されてEIPの紐付けも変更されたと思います。ここでエラーが出ていなければ完了ですが、エラーが発生したと仮定して、巻き戻す場合の手順を見ていきます。

まずは、先ほどと同じように新しく作成したEC2をTerraformの管理外にして

terraform state rm aws_instance.web

terraform importコマンドで最初に作成したEC2をインポートして、terraform applyでEIPを最初のリソースに紐付けます。

terraform import aws_instance.web {EC2インスタンスのID}
terraform apply -var 'vpc_id={VPCのID}'

新しく作成したEC2がそのまま残っているので、コンパネかCLIから終了させれば完了です。

Discussion