TerraformでAmazon EMR クラスターを構築
今月初めにリリースされたv0.7.5でTerraformがAmazon EMRに対応しました。2015年の5月には機能追加の要望が挙がっていたので、1年以上越しで機能追加が実現された形です。
Amazon EMR関連のリソース
新たに利用可能となったのは以下の2つのリソースです。
aws_emr_cluster
EMRクラスターを作成するためのリソースです。
設定項目
設定項目 | 説明 | 必須/オプション | デフォルト値 | 備考 |
---|---|---|---|---|
name | クラスター名 | 必須 | - | - |
release_label | EMRのリリースラベル | 必須 | - | emr-5.0.0など |
master_instance_type | マスターノードのインスタンスタイプ | 必須 | - | - |
core_instance_type | コアノードのインスタンスタイプ | オプション | - | - |
core_instance_count | ノード数 | オプション | 0 | マスターノードとコアノードの合計数を指定 |
log_uri | EMRのログファイル保存先のS3バケット | オプション | - | 例:s3://emr-log/elasticmapreduce/ (指定がない場合はログファイルが生成されない) |
applications | EMRクラスターにインストールするアプリケーション | オプション | - | Hadoop、Hive、Sparkなど |
ec2_attributes | EC2インスタンスの詳細設定 | オプション | - | 詳細な設定項目については下の「ec2_attributes」の表を参照 |
bootstrap_action | ブートストラップアクション | オプション | - | 詳細な設定項目については下の「bootstrap_action」の表を参照 |
configurations | アプリケーションの設定ファイル | オプション | - | json形式の設定ファイルを指定 |
service_role | EMRのサービスロール(EMRサービス用のIAMロール) | 必須 | - | - |
visible_to_all_users | クラスターをすべてのIAMユーザーに表示 | オプション | true | falseに設定するとクラスターを作成したIAMユーザーのみがそのクラスターを参照可能となる |
tags | タグ設定 | オプション | - | - |
- ec2_attributes
設定項目 | 説明 | 必須/オプション | デフォルト値 | 備考 |
---|---|---|---|---|
key_name | EC2キーペア名 | オプション | - | "hadoop"ユーザーでEC2にログインする際に使用する |
subnet_id | EMRクラスターを起動するサブネットID | オプション | デフォルトVPCのパブリックサブネット | - |
additional_master_security_groups | マネージドセキュリティグループの他にマスターノードに追加で割り当てるセキュリティグループIDのリスト | オプション | - | - |
additional_slave_security_groups | マネージドセキュリティグループの他にコアノードに追加で割り当てるセキュリティグループIDのリスト | オプション | - | - |
emr_managed_master_security_group | マスターノードのマネージドセキュリティグループID | オプション | - | 指定なしの場合はマネージドセキュリティグループが新規に作成される |
emr_managed_slave_security_group | コアノードのマネージドセキュリティグループID | オプション | - | 指定なしの場合はマネージドセキュリティグループが新規に作成される |
instance_profile | EC2に割り当てるインスタンスプロファイル名 | 必須 | - | - |
- bootstrap_action
設定項目 | 説明 | 必須/オプション | デフォルト値 | 備考 |
---|---|---|---|---|
name | ブートストラップアクション名 | オプション | - | - |
path | ブートストラップアクションで実行するスクリプトファイルのパス | オプション | - | S3上のファイルまたはローカルファイルを指定 |
args | ブートストラップアクションで実行するスクリプトの引数 | オプション | - | - |
aws_emr_cluster
リソースで作成できるのはマスターノードとコアノードです。タスクノードを追加するには後述のaws_emr_instance_group
を使います。
core_instance_count
にはマスターノードとコアノードの合計数を指定します(マスターノードは常に1台なので、「1 + コアノード数」を指定します)。core_instance_count
のデフォルト値は「0」のようですが、1以上を指定しないとエラーとなりますのでご注意ください。
applications
は、Terraformのオンラインドキュメントをみると「Hadoop, Hive, Mahout, Pig, Spark」しか選択できないようにも読めますが、実際に試したところPrestoなど、Terraformのオンラインドキュメントに記載のアプリケーションも問題なくインストールできました。
EMRで利用可能なアプリケーションについてはAWSの公式ドキュメントを確認するのがよいかと思います。
Amazon EMR リリースについて - Amazon EMR
エクスポートされる属性
属性名 | 説明 | 備考 |
---|---|---|
id | クラスターID | - |
name | クラスター名 | - |
release_label | リリースラベル | - |
master_instance_type | マスターノードのインスタンスタイプ | - |
core_instance_type | コアノードのインスタンスタイプ | - |
core_instance_count | ノード数 | - |
log_uri | EMRのログファイル保存先のS3バケット | - |
applications | EMRクラスターにインストールされたアプリケーション | - |
ec2_attributes | EC2インスタンスの詳細設定 | - |
bootstrap_action | ブートストラップアクション | - |
configurations | アプリケーションの設定 | - |
service_role | EMRのサービスロール(EMRサービス用のIAMロール) | - |
visible_to_all_users | クラスターをすべてのIAMユーザーに表示する(true or false) | - |
tags | タグ | - |
aws_emr_instance_group
EMRクラスターにタスクノード(タスクインスタンスグループ)を追加するためのリソースです。
設定項目
設定項目 | 説明 | 必須/オプション | デフォルト値 | 備考 |
---|---|---|---|---|
name | タスクインスタンスグループ名 | オプション | - | - |
cluster_id | タスクインスタンスグループを追加するEMRクラスターのID | 必須 | - | - |
instance_type | タスクノードのインスタンスタイプ | 必須 | - | - |
instance_count | タスクノード数 | オプション | - | - |
- ec2_attributes
設定項目 | 説明 | 備考 |
---|---|---|
name | タスクインスタンスグループ名 | - |
cluster_id | タスクインスタンスグループを追加したEMRクラスターのID | - |
instance_type | タスクノードのインスタンスタイプ | - |
instance_count | タスクノード数 | - |
running_instance_count | 実行中のタスクノード数 | - |
status | タスクインスタンスグループのステータス | PROVISIONING BOOTSTRAPPING RUNNING RESIZING SUSPENDED TERMINATING TERMINATED ARRESTED SHUTTING_DOWN ENDED |
Terraformのテンプレートファイルの例
以下のテンプレートファイルを作成して、実際にEMRクラスターを構築してみました。
variable "names" { type = "map" default = { account_id = "" profile = "" prefix = "" } } variable "region" { default = "ap-northeast-1" } variable "vpc_cidr" {} variable "source_ips" { type = "list" } variable "ssh_key_name" {} data "aws_availability_zones" "available" {}
provider "aws" { region = "${var.region}" profile = "${var.names["profile"]}" allowed_account_ids = ["${var.names["account_id"]}"] } ## S3 Bucket for ERM Logs resource "aws_s3_bucket" "emr-log" { bucket = "emr-logs-${var.names["account_id"]}" acl = "private" force_destroy = true } ## VPC resource "aws_vpc" "emr-test" { cidr_block = "${var.vpc_cidr}" instance_tenancy = "default" enable_dns_support = true enable_dns_hostnames = true tags { Name = "${var.names["prefix"]}-vpc" } } ## Internet GW resource "aws_internet_gateway" "emr-test" { vpc_id = "${aws_vpc.emr-test.id}" tags { Name = "${var.names["prefix"]}-igw" } } ## Subnet resource "aws_subnet" "public" { count = 2 vpc_id = "${aws_vpc.emr-test.id}" cidr_block = "${cidrsubnet(var.vpc_cidr, 8, count.index)}" availability_zone = "${data.aws_availability_zones.available.names[count.index]}" map_public_ip_on_launch = true tags { Name = "${format("${var.names["prefix"]}-public-subnet%02d", count.index + 1)}" } } ## Route Table resource "aws_route_table" "public" { vpc_id = "${aws_vpc.emr-test.id}" route { cidr_block = "0.0.0.0/0" gateway_id = "${aws_internet_gateway.emr-test.id}" } tags { Name = "${var.names["prefix"]}-public" } } resource "aws_route_table_association" "public" { count = 2 subnet_id = "${element(aws_subnet.public.*.id, count.index)}" route_table_id = "${aws_route_table.public.id}" } ## IAM role for EMR data "aws_iam_policy_document" "emr-assume-role-policy" { statement { effect = "Allow" actions = ["sts:AssumeRole"] principals = { type = "Service" identifiers = ["elasticmapreduce.amazonaws.com"] } } } resource "aws_iam_role" "emr-service-role" { name = "EMR_DefaultRole" assume_role_policy = "${data.aws_iam_policy_document.emr-assume-role-policy.json}" } resource "aws_iam_role_policy_attachment" "emr-service-role" { role = "${aws_iam_role.emr-service-role.name}" policy_arn = "arn:aws:iam::aws:policy/service-role/AmazonElasticMapReduceRole" } ## IAM Role for EC2 data "aws_iam_policy_document" "ec2-assume-role-policy" { statement { effect = "Allow" actions = ["sts:AssumeRole"] principals = { type = "Service" identifiers = ["ec2.amazonaws.com"] } } } resource "aws_iam_role" "emr-ec2-role" { name = "EMR_EC2_DefaultRole" assume_role_policy = "${data.aws_iam_policy_document.ec2-assume-role-policy.json}" } resource "aws_iam_role_policy_attachment" "emr-ec2-role" { role = "${aws_iam_role.emr-ec2-role.name}" policy_arn = "arn:aws:iam::aws:policy/service-role/AmazonElasticMapReduceforEC2Role" } resource "aws_iam_instance_profile" "emr-ec2-profile" { name = "${var.names["prefix"]}-emr-ec2-profile" roles = ["${aws_iam_role.emr-ec2-role.name}"] } ## EMR Cluster resource "aws_emr_cluster" "emr-test-cluster" { name = "emr-test-cluster" release_label = "emr-5.0.0" applications = ["Hadoop", "Hive"] log_uri = "s3://${aws_s3_bucket.emr-log.id}/elasticmapreduce/" service_role = "${aws_iam_role.emr-service-role.arn}" master_instance_type = "m1.medium" core_instance_type = "m1.medium" core_instance_count = 2 ec2_attributes { key_name = "${var.ssh_key_name}" subnet_id = "${aws_subnet.public.0.id}" instance_profile = "${aws_iam_instance_profile.emr-ec2-profile.name}" } }
まとめ
EMRはデータ分析のプリプロセス(非構造化データ → 構造化データへの変換、整形、フィルタリングなど)実行のためオンデマンドでクラスターを起動&シャットダウンするケースが多いかと思います。
この場合はTerraformで構成管理、、は必要ないかもしれませんが、TerraformでVPCやセキュリティグループ含めてサクッとEMRの検証環境を起動できるようになったのは嬉しいポイントです。これまでEMRはなかなか触れる機会がなかったのですが、これを機に色々と検証していいきたいと思います。