HashiCorp Advent Calendar 2015の5日目の担当が空いていたので書いてみました。


クラウドを利用されているみなさんは、リージョン障害などに備えるために複数のリージョンを利用されているかと思います。 複数のリージョンに展開する際、どのような方法で行ってますでしょうか?

CloudFormation Stackを複数のリージョンで作ったり、手動でポチポチやったり、SDKやAWSCLIを使ったスクリプトを使ったり…

Terraform を使えば簡単に複数のリージョンを統一的に扱うことが出来ます。

Terraform は複数のプロバイダ(AWSやGCPなど)を一つのテンプレートの中に混在することが出来ます。 また、1種類のプロバイダを複数使用することも出来るため、AWSにマルチリージョンな環境を構築できます。

AWS謹製のCloudFormationは、S3やRoute53などのグローバルリソースを除き、そのCloudFormation Stackを作成しているリージョンのリソースしか作成することが出来ません。 もちろん、GCPやDigitalOceanなど外部のサービスを利用することも出来ません。

このあたりは Terraform の強みですね。

マルチリージョン基本編

まずは基本的な複数のプロバイダの使い方についてです。

シングルリージョンでAWSを利用する場合は以下のように記述します。 aws_ で始まるリソースは aws プロバイダが利用されます。

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

resource "aws_instance" "instance-tokyo" {
    instance_type = "t2.micro"
    ami           = "ami-383c1956"
    tag {
        Name = "Tokyo Region"
    }
}

マルチリージョンで利用したい場合、provider "aws" を複数書けば良いというわけではありません。 と言ってもそんなに難しいことをやるわけではなく、どちらのプロバイダを利用するのかを識別するためにaliasを追加するだけです。

あとは Resource の定義でどのプロバイダを使用するかをproviderで指定します。

provider "aws" {
    region = "ap-northeast-1"
    alias = "tokyo"
}
provider "aws" {
    region = "ap-southeast-1"
    alias = "singapore"
}

resource "aws_instance" "instance-tokyo" {
    provider      = "aws.tokyo"
    instance_type = "t2.micro"
    ami           = "ami-383c1956"
    tag {
        Name = "Tokyo Region"
    }
}
resource "aws_instance" "instance-singapore" {
    provider      = "aws.singapore"
    instance_type = "t2.micro"
    ami           = "ami-c9b572aa"
    tag {
        Name = "Singapore Region"
    }
}

マルチリージョン応用編

マルチリージョンにする大きな理由として、冗長性の確保が挙げられると思います。 環境を冗長構成する場合、同じ物をスタンプのように複数のリージョンに展開していくかと思いますが、前述の方法ではprovideramiだけが違うリソースを大量に定義していかなければならず、非常にめんどくさいですし、修正時に一部のリージョンを変更し忘れるといったオペミスも発生しやすくなります。

リージョン毎に同じリソースを展開する場合、共通する部分をモジュールとしてまとめてしまうのが簡単です。

今回サンプルとして作成するテンプレートのディレクトリ構成は以下のようにします。

./global.tf
./main.tf
./regional-resource/main.tf
./regional-resource/variables.tf

モジュール側のリソース定義

  • ./regional-resource/main.tf ``` provider “aws” { region = “${var.region}” }

resource “aws_instance” “instance” { instance_type = “t2.micro” ami = “${lookup(var.ami_id, var.region)}” associate_public_ip_address = true tag { Name = “${var.name}” } }

output “public_ip” { value = “${aws_instance.instance.public_ip}” }


* ./regional-resource/variables.tf

variable “region” {} variable “name” {} variable “ami_id” { default { ap-northeast-1 = “ami-383c1956” ap-southeast-1 = “ami-c9b572aa” ap-southeast-2 = “ami-48d38c2b” us-east-1 = “ami-60b6c60a” us-west-1 = “ami-d5ea86b5” us-west-2 = “ami-f0091d91” eu-west-1 = “ami-bff32ccc” eu-central-1 = “ami-bc5b48d0” sa-east-1 = “ami-6817af04” } }


### モジュールを使う側のリソース定義

* ./main.tf

module “ap-northeast-1” { source = “./regional-resource”

region   = "ap-northeast-1"
name     = "Tokyo"

} module “ap-southeast-1” { source = “./regional-resource”

region   = "ap-southeast-1"
name     = "Singapore"

}


* ./global.tf

resource “aws_route53_zone” “primary” { name = “example.com” }

resource “aws_route53_record” “www” { zone_id = “${aws_route53_zone.primary.zone_id}” name = “www.example.com” type = “A” ttl = “300” records = [ “${module.ap-northeast-1.public_ip}“, “${module.ap-southeast-1.public_ip}“, ] }


`global.tf` ではRoute53にゾーンを作成し、東京都シンガポールのインスタンスのIPアドレスを登録しています。

以上のテンプレートで出来上がる構成がこんな感じです。

![multi region diagram](/images/20151205/terraform_multi-region.png)

### Provision
モジュールを使うと、単に `terraform apply` とするだけではダメで先に `terraform get` を行う必要があります。

$ terraform get -update=true $ terraform plan -module-depth=-1 $ terraform apply ```

今回はローカルファイルシステムにあるモジュールを使いましたが、Gitなどからも取ってくることが出来ます。(Using Modules)

まとめ

このように、Terraformを使えば簡単にマルチリージョンな環境を手に入れることができます。 マルチリージョンどころかマルチクラウドな環境も、今回よりちょっと大変ではありますが実現できます。