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"
}
}
マルチリージョン応用編
マルチリージョンにする大きな理由として、冗長性の確保が挙げられると思います。
環境を冗長構成する場合、同じ物をスタンプのように複数のリージョンに展開していくかと思いますが、前述の方法ではprovider
とami
だけが違うリソースを大量に定義していかなければならず、非常にめんどくさいですし、修正時に一部のリージョンを変更し忘れるといったオペミスも発生しやすくなります。
リージョン毎に同じリソースを展開する場合、共通する部分をモジュールとしてまとめてしまうのが簡単です。
今回サンプルとして作成するテンプレートのディレクトリ構成は以下のようにします。
./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アドレスを登録しています。
以上のテンプレートで出来上がる構成がこんな感じです。
Provision
モジュールを使うと、単に terraform apply
とするだけではダメで先に terraform get
を行う必要があります。
$ terraform get -update=true
$ terraform plan -module-depth=-1
$ terraform apply
今回はローカルファイルシステムにあるモジュールを使いましたが、Gitなどからも取ってくることが出来ます。(Using Modules)
まとめ
このように、Terraformを使えば簡単にマルチリージョンな環境を手に入れることができます。 マルチリージョンどころかマルチクラウドな環境も、今回よりちょっと大変ではありますが実現できます。