CloudFormationでEMRをPrivateサブネットで起動する方法まとめ

EMR

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"
タイトルとURLをコピーしました