CloudFormationd で EMR を Private サブネットで起動する方法をまとめました。
構築する AWS リソース
以下の AWS リソースを構築します。
VPC エンドポイントは連携するサービスに応じて追加してください。
- VPC
- サブネット
- Public サブネット
- Private サブネット
- IGW
- NAT GW
- ルートテーブル
- Public サブネット用
- Private サブネット用
- VPC エンドポイント
- S3
- DynamoDB
- セキュリティグループ
- マスターインスタンス用
- コアタスクインスタンス用
- サービスアクセス用
- IAM Role
- インスタンスプロファイル
Private サブネットに EMR インスタンスを構築するポイント
Private サブネットに EMR インスタンスを構築する場合は以下に注意します。
- ブートストラップアクションでインターネット接続が必要な場合は デフォルトゲートウェイに IGW を指定した Public サブネットに NAT GW を配置し Private サブネットのデフォルトゲートウェイに NAT GW を指定します。
- S3,DynamoDB にアクセスできるようにするために VPC エンドポイントを構築します。
- マネージドで作成されるものと同じルールを設定した Private サブネット用のセキュリティグループを構築します。
- EC2 インスタンスプロファイルを指定するためには EMR EC2 用の IAM Role と同じ名前のインスタンスプロファイルを構築します。
CloudFormation テンプレート
VPC周り
まとめると以下のようにになります。
AWSTemplateFormatVersion: 2010-09-09
Parameters:
SystemName:
Type: String
Default: sample
Description: System name.
EnvironmentName:
Type: String
Default: dev
AllowedValues:
- dev
- qa
- prod
Description: Environment name. Choose from [dev, qa, prod].
Region:
Type: String
Default: ore
Description: Region name. Default ore.
SegmentName:
Type: String
Default: aggregation
Description: Segment name.
VPCCidrBlock:
Type: String
# 任意のアドレス空間を指定する
Default: 10.143.160.0/20
Description: VPC CIDR Block.
Resources:
# VPC
VPC:
Type: AWS::EC2::VPC
Properties:
CidrBlock: !Ref VPCCidrBlock
Tags:
- Key: Name
Value: !Sub "${Environment}-${SystemName}-${Region}-vpc"
# インターネットゲートウェイ
InternetGateway:
Type: AWS::EC2::InternetGateway
Properties:
Tags:
- Key: !Ref CostTagKey
Value: !Ref CostTagValue
- Key: Name
Value: !Sub "${Environment}-${SystemName}-${Region}-igw"
# インターネットゲートウェイを VPC にアタッチ
AttachInternetGateway:
Type: AWS::EC2::VPCGatewayAttachment
Properties:
InternetGatewayId : !Ref InternetGateway
VpcId: !Ref VPC
# サブネット(Public)
PublicSubnet:
Type: AWS::EC2::Subnet
Properties:
# VPC のアドレス空間からサブネットを切り出す
CidrBlock: !Select [ 1, !Cidr [ !GetAtt VPC.CidrBlock, 3, 8 ]]
MapPublicIpOnLaunch: true
VpcId: !Ref VPC
AvailabilityZone: us-west-2a
Tags:
- Key: Name
Value: !Sub "${Environment}-${SystemName}-${Region}-public-sub"
# サブネット(Private)
PrivateSubnet:
Type: AWS::EC2::Subnet
Properties:
# VPC のアドレス空間からサブネットを切り出す
CidrBlock: !Select [ 0, !Cidr [ !GetAtt VPC.CidrBlock, 3, 8 ]]
MapPublicIpOnLaunch: false
VpcId: !Ref VPC
AvailabilityZone: us-west-2a
Tags:
- Key: Name
Value: !Sub "${Environment}-${SystemName}-${Region}-private-sub"
# ルートテーブル(Public)
RouteTableForPublicSubnet:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref VPC
Tags:
- Key: Name
Value: !Sub "${Environment}-${SystemName}-${Region}-public-rtb"
# ルート定義(Public)
# パブリックサブネットの通信はすべてインターネットゲートウェイを通す
RouteForPublicSubnet:
Type: AWS::EC2::Route
Properties:
RouteTableId: !Ref RouteTableForPublicSubnet
DestinationCidrBlock: 0.0.0.0/0
GatewayId: !Ref InternetGateway
# サブネット関連付け(Public)
AssocciateRouteTableForPublicSubnet:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
RouteTableId: !Ref RouteTableForPublicSubnet
SubnetId: !Ref PublicSubnet
# NAT ゲートウェイ用 Elastic IP
NatGatewayEIP:
Type: AWS::EC2::EIP
Properties:
Domain: vpc
# NAT ゲートウェイ
NatGateway:
Type: AWS::EC2::NatGateway
Properties:
AllocationId:
Fn::GetAtt:
- NatGatewayEIP
- AllocationId
SubnetId: !Ref PublicSubnet
Tags:
- Key: !Ref CostTagKey
Value: !Ref CostTagValue
- Key: Name
Value: !Sub "${Environment}-${SystemName}-${Region}-ngw"
# ルートテーブル(Private)
RouteTableForPrivateSubnet:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref VPC
Tags:
- Key: Name
Value: !Sub "${Environment}-${SystemName}-${Region}-private-rtb"
# ルート定義(Private)
# プライベートサブネットの通信はすべて NAT ゲートウェイを通す
RouteForPrivateSubnet:
Type: AWS::EC2::Route
Properties:
RouteTableId: !Ref RouteTableForPrivateSubnet
DestinationCidrBlock: 0.0.0.0/0
NatGatewayId: !Ref NatGateway
# サブネット関連付け(Private)
AssocciateRouteTableForPrivateSubnet:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
RouteTableId: !Ref RouteTableForPrivateSubnet
SubnetId: !Ref PrivateSubnet
# VPC エンドポイント
# S3
S3VPCEndpoint:
Type: 'AWS::EC2::VPCEndpoint'
Properties:
PolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Principal: '*'
Action:
- '*'
Resource:
- '*'
RouteTableIds:
- !Ref RouteTableForPrivateSubnet
ServiceName: !Sub 'com.amazonaws.$${AWS::Region}.s3'
VpcId: !Ref VPC
# DynamoDB
DynamoDBVPCEndpoint:
Type: 'AWS::EC2::VPCEndpoint'
Properties:
RouteTableIds:
- !Ref RouteTableForPrivateSubnet
ServiceName: !Sub 'com.amazonaws.$${AWS::Region}.dynamodb'
VpcId: !Ref VPC
# セキュリティグループ
# マスターインスタンス
MasterSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: Master group for Elastic MapReduce created by CloudFormation
GroupName: ElasticMapReduce-Master-Private
VpcId: !Ref VPC
# インバウンド
# マスターインスタンスからの全ての ICMP-IPv4 を許可
MasterSecurityGroupIngressAllowAllICMPFromMaster:
Type: AWS::EC2::SecurityGroupIngress
Properties:
GroupId: !Ref MasterSecurityGroup
IpProtocol: icmp
FromPort: -1
ToPort: -1
SourceSecurityGroupId: !Ref MasterSecurityGroup
# マスターインスタンスからの全ての TCP を許可
MasterSecurityGroupIngressAllowAllTCPFromMaster:
Type: AWS::EC2::SecurityGroupIngress
Properties:
GroupId: !Ref MasterSecurityGroup
IpProtocol: tcp
FromPort: 0
ToPort: 65535
SourceSecurityGroupId: !Ref MasterSecurityGroup
# マスターインスタンスからの全ての UDP を許可
MasterSecurityGroupIngressAllowAllUDPFromMaster:
Type: AWS::EC2::SecurityGroupIngress
Properties:
GroupId: !Ref MasterSecurityGroup
IpProtocol: udp
FromPort: 0
ToPort: 65535
SourceSecurityGroupId: !Ref MasterSecurityGroup
# コアインスタンス・タスクインスタンスからの全ての ICMP-IPv4 を許可
MasterSecurityGroupIngressAllowAllICMPFromCoreAndTask:
Type: AWS::EC2::SecurityGroupIngress
Properties:
GroupId: !Ref MasterSecurityGroup
IpProtocol: icmp
FromPort: -1
ToPort: -1
SourceSecurityGroupId: !Ref CoreAndTaskSecurityGroup
# コアインスタンス・タスクインスタンスからの全ての TCP を許可
MasterSecurityGroupIngressAllowAllTCPFromCoreAndTask:
Type: AWS::EC2::SecurityGroupIngress
Properties:
GroupId: !Ref MasterSecurityGroup
IpProtocol: tcp
FromPort: 0
ToPort: 65535
SourceSecurityGroupId: !Ref CoreAndTaskSecurityGroup
# コアインスタンス・タスクインスタンスからの全ての UDP を許可
MasterSecurityGroupIngressAllowAllUDPFromCoreAndTask:
Type: AWS::EC2::SecurityGroupIngress
Properties:
GroupId: !Ref MasterSecurityGroup
IpProtocol: udp
FromPort: 0
ToPort: 65535
SourceSecurityGroupId: !Ref CoreAndTaskSecurityGroup
# サービスアクセスからの TCP 8443 を許可(クラスターマネージャがマスターインスタンスと通信出来るように設定)
MasterSecurityGroupIngressAllowTCP8443FromServiceAccess:
Type: AWS::EC2::SecurityGroupIngress
Properties:
GroupId: !Ref MasterSecurityGroup
IpProtocol: tcp
FromPort: 8443
ToPort: 8443
SourceSecurityGroupId: !Ref ServiceAccessSecurityGroup
# アウトバウンド
# 全てのアウトバウンドへのアクセスを許可する
MasterSecurityGroupEgressAllowAllTCPToAny:
Type: AWS::EC2::SecurityGroupEgress
Properties:
GroupId: !Ref MasterSecurityGroup
IpProtocol: -1
FromPort: 0
ToPort: 65535
CidrIp: 0.0.0.0/0
# サービスアクセスへの TCP 9443 を明示的に許可
MasterSecurityGroupEgressAllow9443ToServiceAccess:
Type: AWS::EC2::SecurityGroupEgress
Properties:
GroupId: !Ref MasterSecurityGroup
IpProtocol: tcp
FromPort: 9443
ToPort: 9443
DestinationSecurityGroupId: !Ref ServiceAccessSecurityGroup
# セキュリティグループ
# コアインスタンス・タスクインスタンス
CoreAndTaskSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: Slave group for Elastic MapReduce created by CloudFormation
GroupName: ElasticMapReduce-Slave-Private
VpcId: !Ref VPC
# インバウンド
# コアインスタンス・タスクインスタンスからの全ての ICMP-IPv4 を許可
CoreAndTaskSecurityGroupIngressAllowAllICMPFromCoreAndTask:
Type: AWS::EC2::SecurityGroupIngress
Properties:
GroupId: !Ref CoreAndTaskSecurityGroup
IpProtocol: icmp
FromPort: -1
ToPort: -1
SourceSecurityGroupId: !Ref CoreAndTaskSecurityGroup
# コアインスタンス・タスクインスタンスからの全ての TCP を許可
CoreAndTaskSecurityGroupIngressAllowAllTCPFromCoreAndTask:
Type: AWS::EC2::SecurityGroupIngress
Properties:
GroupId: !Ref CoreAndTaskSecurityGroup
IpProtocol: tcp
FromPort: 0
ToPort: 65535
SourceSecurityGroupId: !Ref CoreAndTaskSecurityGroup
# コアインスタンス・タスクインスタンスからの全ての UDP を許可
CoreAndTaskSecurityGroupIngressAllowAllUDPFromCoreAndTask:
Type: AWS::EC2::SecurityGroupIngress
Properties:
GroupId: !Ref CoreAndTaskSecurityGroup
IpProtocol: udp
FromPort: 0
ToPort: 65535
SourceSecurityGroupId: !Ref CoreAndTaskSecurityGroup
# マスターインスタンスからの全ての ICMP-IPv4 を許可
CoreAndTaskSecurityGroupIngressAllowAllICMPFromMaster:
Type: AWS::EC2::SecurityGroupIngress
Properties:
GroupId: !Ref CoreAndTaskSecurityGroup
IpProtocol: icmp
FromPort: -1
ToPort: -1
SourceSecurityGroupId: !Ref MasterSecurityGroup
# マスターインスタンスからの全ての TCP を許可
CoreAndTaskSecurityGroupIngressAllowAllTCPFromMaster:
Type: AWS::EC2::SecurityGroupIngress
Properties:
GroupId: !Ref CoreAndTaskSecurityGroup
IpProtocol: tcp
FromPort: 0
ToPort: 65535
SourceSecurityGroupId: !Ref MasterSecurityGroup
# マスターインスタンスからの全ての UDP を許可
CoreAndTaskSecurityGroupIngressAllowAllUDPFromMaster:
Type: AWS::EC2::SecurityGroupIngress
Properties:
GroupId: !Ref CoreAndTaskSecurityGroup
IpProtocol: udp
FromPort: 0
ToPort: 65535
SourceSecurityGroupId: !Ref MasterSecurityGroup
# サービスアクセスからの TCP 8443 を許可(クラスターマネージャがコアインスタンス・タスクインスタンスと通信出来るように設定)
CoreAndTaskSecurityGroupIngressAllowTCP8443FromServiceAccess:
Type: AWS::EC2::SecurityGroupIngress
Properties:
GroupId: !Ref CoreAndTaskSecurityGroup
IpProtocol: tcp
FromPort: 8443
ToPort: 8443
SourceSecurityGroupId: !Ref ServiceAccessSecurityGroup
# アウトバウンド
# 全てのアウトバウンドへのアクセスを許可する
CoreAndTaskSecurityGroupEgressAllowAllTCPToAny:
Type: AWS::EC2::SecurityGroupEgress
Properties:
GroupId: !Ref CoreAndTaskSecurityGroup
IpProtocol: -1
FromPort: 0
ToPort: 65535
CidrIp: 0.0.0.0/0
# セキュリティグループ
# サービスアクセス
ServiceAccessSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: Service access group for Elastic MapReduce created by CloudFormation
GroupName: ElasticMapReduce-ServiceAccess
VpcId: !Ref VPC
# インバウンド
# マスターインスタンスからの TCP9443 を許可
ServiceAccessSecurityGroupIngressAllowAllTCPFromMaster:
Type: AWS::EC2::SecurityGroupIngress
Properties:
GroupId: !Ref ServiceAccessSecurityGroup
IpProtocol: tcp
FromPort: 9443
ToPort: 9443
SourceSecurityGroupId: !Ref MasterSecurityGroup
# アウトバウンド
# マスターインスタンスへの TCP 8443 を許可
ServiceAccessSecurityGroupEgressAllowTCP8443ToMaster:
Type: AWS::EC2::SecurityGroupEgress
Properties:
GroupId: !Ref ServiceAccessSecurityGroup
IpProtocol: tcp
FromPort: 8443
ToPort: 8443
DestinationSecurityGroupId: !Ref MasterSecurityGroup
# コアインスタンス・タスクインスタンスへの TCP 8443 を許可
ServiceAccessSecurityGroupEgressAllowTCP8443ToCoreAndTask:
Type: AWS::EC2::SecurityGroupEgress
Properties:
GroupId: !Ref ServiceAccessSecurityGroup
IpProtocol: tcp
FromPort: 8443
ToPort: 8443
DestinationSecurityGroupId: !Ref CoreAndTaskSecurityGroup
Outputs:
VPC:
Value: !Ref VPC
Export:
Name: !Sub "${Environment}-${SystemName}-${Region}-vpc"
PublicSubnet:
Value: !Ref PublicSubnet
Export:
Name: !Sub "${Environment}-${SystemName}-${Region}-public-sub"
PrivateSubnet:
Value: !Ref PrivateSubnet
Export:
Name: !Sub "${Environment}-${SystemName}-${Region}-private-sub"
MasterSecurityGroup:
Value: !Ref MasterSecurityGroup
Export:
Name: !Sub "${Environment}-${SystemName}-MasterSecurityGroup"
CoreAndTaskSecurityGroup:
Value: !Ref CoreAndTaskSecurityGroup
Export:
Name: !Sub "${Environment}-${SystemName}-CoreAndTaskSecurityGroup"
ServiceAccessSecurityGroup:
Value: !Ref ServiceAccessSecurityGroup
Export:
Name: !Sub "${Environment}-${SystemName}-ServiceAccessSecurityGroup"
IAM Role 周り
注意ポイントは以下です。
- インスタンスプロファイル名は EC2 のロール名と同じにしてください。
AWSTemplateFormatVersion: 2010-09-09
Parameters:
SystemName:
Type: String
Default: sample
Description: Sub system name.
EnvironmentName:
Type: String
Default: dev
AllowedValues:
- dev
- qa
- prod
Description: Environment name. Choose from [dev, qa, prod].
Region:
Type: String
Default: ore
Description: Region name. Default ore.
Resources:
# EMR EC2 用ロール
EMREC2Role:
Type: AWS::IAM::Role
Properties:
RoleName: !Sub "${Environment}-${SystemName}-iam-role-EMREC2Role"
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal:
Service:
- ec2.amazonaws.com
Action:
- sts:AssumeRole
Description: "IAM Role for EMR EC2."
Policies:
# SSM 用インラインポリシー (AmazonEC2RoleforSSM)
- PolicyName: role-policy-ec2-for-ssm
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Action:
- ssm:DescribeAssociation
- ssm:GetDeployablePatchSnapshotForInstance
- ssm:GetDocument
- ssm:DescribeDocument
- ssm:GetManifest
- ssm:GetParameters
- ssm:ListAssociations
- ssm:ListInstanceAssociations
- ssm:PutInventory
- ssm:PutComplianceItems
- ssm:PutConfigurePackageResult
- ssm:UpdateAssociationStatus
- ssm:UpdateInstanceAssociationStatus
- ssm:UpdateInstanceInformation
Resource: "*"
- Effect: Allow
Action:
- ssmmessages:CreateControlChannel
- ssmmessages:CreateDataChannel
- ssmmessages:OpenControlChannel
- ssmmessages:OpenDataChannel
Resource: "*"
- Effect: Allow
Action:
- ec2messages:AcknowledgeMessage
- ec2messages:DeleteMessage
- ec2messages:FailMessage
- ec2messages:GetEndpoint
- ec2messages:GetMessages
- ec2messages:SendReply
Resource: "*"
- Effect: Allow
Action:
- cloudwatch:PutMetricData
Resource: "*"
- Effect: Allow
Action:
- ec2:DescribeInstanceStatus
Resource: "*"
- Effect: Allow
Action:
- ds:CreateComputer
- ds:DescribeDirectories
Resource: "*"
- Effect: Allow
Action:
- logs:CreateLogGroup
- logs:CreateLogStream
- logs:DescribeLogGroups
- logs:DescribeLogStreams
- logs:PutLogEvents
Resource: "*"
- Effect: Allow
Action:
- s3:GetBucketLocation
- s3:PutObject
- s3:GetObject
- s3:GetEncryptionConfiguration
- s3:AbortMultipartUpload
- s3:ListMultipartUploadParts
- s3:ListBucket
- s3:ListBucketMultipartUploads
Resource: "*"
# EMR 用インラインポリシー
- PolicyName: role-policy-ec2-for-emr
PolicyDocument:
Version: "2012-10-17"
Statement:
- Sid: "EmrStandardPolicy"
Action:
- "ec2:Describe*"
- "cloudwatch:*"
- "elasticmapreduce:AddJobFlowSteps"
- "elasticmapreduce:Describe*"
- "elasticmapreduce:ListBootstrapActions"
- "elasticmapreduce:ListInstance*"
- "elasticmapreduce:ModifyInstanceGroups"
- "sns:*"
- "sqs:*"
- "dynamodb:*"
- "s3:*"
Resource: "*"
Effect: "Allow"
# EMR EC2 用インスタンスプロファイル
EMREC2InstanceProfile:
Type: AWS::IAM::InstanceProfile
Properties:
# EMREC2Role と同じ名前にしないと EMR クラスターを作成できない
InstanceProfileName: !Sub "${Environment}-${SystemName}-iam-role-EMREC2Role"
Path: "/"
Roles:
- !Ref EMREC2Role
Outputs:
EMREC2RoleArn:
Value: !GetAtt EMREC2Role.Arn
Export:
Name: !Sub "${Environment}-${SystemName}-iam-role-EMREC2Role-arn"
EMREC2InstanceProfileArn:
Value: !GetAtt EMREC2InstanceProfile.Arn
Export:
Name: !Sub "${Environment}-${SystemName}-instance-profile-EMREC2Role-arn"