日々是クラウド

AWSで色々やってみるブログ

CloudFormationで簡単に機械学習の開発環境を整える

はじめに

ちょっと検討をしてみようと思うたびに、VPC作ってSubnet作って、InternetGatewayとElasticIP取得して、NatGateway設置して・・
と単純なprivate, publicサブネットを置くだけでも何度もやると面倒くさい。

特に機械学習開発環境代わりに使うような場合は、複数AZにまたがるような可用性云々じゃなくて、検討用土台をさくっと作りたい。
でもSageMakerのNotebookインスタンスじゃ物足りないという人がいると思う。

構成図

前回も紹介したこれを自動で作ることにする。
●ポイント

  • 踏み台サーバーでセキュリティ対策
  • NATゲートウェイでPrivate環境でもソフトを更新可能
  • さくっと検討用に使いたいのでOneZone構成
  • 自分の練習用 f:id:yohei_ok:20210205194019p:plain

はまりどころ

いろいろハマったけど、下記はその一例。参考になれば。

NATGatewayでのElasticIPの指定

IPアドレスではなくIDで指定するため、下記のように"Fn::GetAtt::"を使って指定している。

  NATGateway:
    Type: AWS::EC2::NatGateway
    Properties:
      AllocationId:
        Fn::GetAtt:
          - ElasticIP
          - AllocationId

NATGatewayへのRoute

RouteでGatewayIdではなく、NATGatewayIdを使う必要あり。

  PrivateRoute: 
    Type: AWS::EC2::Route
    Properties: 
      NatGatewayId: !Ref NATGateway

EC2のSecurityGroupの設定はリストで

SecurityGroupIdsのように、複数形になっているのでリストで渡すことをお忘れなく。

  PrivateInstance:
    Type: AWS::EC2::Instance
    Properties: 
      SecurityGroupIds: 
        - !Ref PrivateSecurityGroup

yamlファイル全体はこちら

これをcloud formationに入れることで一発で踏み台サーバーと検討用のprivateサーバーを起動することが可能。 インスタンスのファミリーとかサイズは適宜変えるとよい。

AWSTemplateFormatVersion: "2010-09-09"
Description: 
  OneZone VPC/Subnet/EC2 Create

Parameters:
  # 踏み台サーバーはAmazonLinux & t2.microで簡素に
  BationHostImageId:
    Type: AWS::SSM::Parameter::Value<String>
    Default: /aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2
  
  BationHostInstanceType:
    Type: String
    Default: t2.micro

  # 機械学習用インスタンスはDeepLearning AMIを使って、実験用と高機能用などを用意
  PrivateImageId:
    Type: String
    Default: ami-088585cfb750459af

  PrivateInstanceType:
    Type: String
    Default: m5.2xlarge
    AllowedValues : ["t2.micro", "m5.2xlarge"]

  # SSHでつなぐための接続元IP(自分のIP)
  MyIP:
    Description: IP address allowed to access EC2
    Type: String
    Default: 0.0.0.0/32

  # SSH用のKeyはすでにある前提
  KeyName:
    Description: The EC2 Key Pair to allow SSH access to the instance
    Type: "AWS::EC2::KeyPair::KeyName"
    Default: .pem

Resources:
# #####################################
# VPC & IG
# #####################################
  VPC:
    Type: AWS::EC2::VPC
    Properties: 
      CidrBlock: 10.5.0.0/16
      EnableDnsHostnames: false
      EnableDnsSupport: true
      InstanceTenancy: default
      Tags: 
        - Key: Name
          Value: my-vpc-stack

  InternetGateway: 
    Type: AWS::EC2::InternetGateway
    Properties: 
      Tags: 
        - Key: Name
          Value: my-ig-stack

  InternetGatewayAttachment: 
    Type: AWS::EC2::VPCGatewayAttachment
    Properties: 
      InternetGatewayId: !Ref InternetGateway
      VpcId: !Ref VPC


# #####################################
# Subnet & NAT
# #####################################
  PublicSubnet:
    Type: AWS::EC2::Subnet
    Properties: 
      AvailabilityZone: "ap-northeast-1a"
      CidrBlock: 10.5.0.0/24
      MapPublicIpOnLaunch: true
      Tags: 
        - Key: Name
          Value: public-subnet-stack
      VpcId: !Ref VPC

  PrivateSubnet:
    Type: AWS::EC2::Subnet
    Properties:
      AvailabilityZone: "ap-northeast-1a"
      CidrBlock: 10.5.1.0/24
      MapPublicIpOnLaunch: false
      Tags:
        - Key: Name
          Value: private-subnet-stack
      VpcId: !Ref VPC

  NatIP:
    Type: AWS::EC2::EIP
    Properties: 
      Domain: vpc
      Tags: 
        - Key: Name
          Value: nat-eip-stack

  NATGateway:
    Type: AWS::EC2::NatGateway
    Properties:
      AllocationId:
        Fn::GetAtt:
          - NatIP
          - AllocationId

      SubnetId: !Ref PublicSubnet
      Tags:
        - Key: Name
          Value: my-nat-stack

# #####################################
# RouteTable & Routing
# #####################################
  PublicRouteTable: 
    Type: AWS::EC2::RouteTable
    Properties: 
      VpcId: !Ref VPC 
      Tags: 
        - Key: Name
          Value: rt-public-stack

  PublicRoute: 
    Type: AWS::EC2::Route
    Properties: 
      RouteTableId: !Ref PublicRouteTable 
      DestinationCidrBlock: "0.0.0.0/0"
      GatewayId: !Ref InternetGateway 

  PublicRouteAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      RouteTableId: !Ref PublicRouteTable
      SubnetId: !Ref PublicSubnet

  PrivateRouteTable:
    Type: AWS::EC2::RouteTable
    Properties: 
      VpcId: !Ref VPC 
      Tags: 
        - Key: Name
          Value: rt-private-stack

  PrivateRoute: 
    Type: AWS::EC2::Route
    Properties: 
      RouteTableId: !Ref PrivateRouteTable 
      DestinationCidrBlock: "0.0.0.0/0"
      NatGatewayId: !Ref NATGateway

  PrivateRouteAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      RouteTableId: !Ref PrivateRouteTable
      SubnetId: !Ref PrivateSubnet

# #####################################
# SecurityGroup
# #####################################
  PublicSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
        GroupDescription: Allow SSH to BationHost
        GroupName: allow-ssh-to-bation-stack
        VpcId: !Ref VPC
        SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 22
          ToPort: 22
          CidrIp: !Ref MyIP

  PrivateSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
        GroupDescription: Allow SSH from bation
        GroupName: allow-ssh-from-bation-stack
        VpcId: !Ref VPC
        SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 22
          ToPort: 22
          SourceSecurityGroupId: !Ref PublicSecurityGroup

# #####################################
# EC2
# #####################################
  BationHost: 
    Type: AWS::EC2::Instance
    Properties: 
      ImageId: !Ref BationHostImageId
      InstanceType: !Ref BationHostInstanceType
      KeyName: !Ref KeyName
      NetworkInterfaces: 
        - AssociatePublicIpAddress: "true"
          DeviceIndex: "0"
          SubnetId: !Ref PublicSubnet
          GroupSet:
            - !Ref PublicSecurityGroup
      Tags:
        - Key: Name
          Value: bation-host-stack

  EC2IP:
    Type: AWS::EC2::EIP
    Properties:
      InstanceId: !Ref BationHost
      Tags: 
        - Key: Name
          Value: ec2-eip-stack

  PrivateInstance:
    Type: AWS::EC2::Instance
    Properties: 
      ImageId: !Ref PrivateImageId
      InstanceType: !Ref PrivateInstanceType
      KeyName: !Ref KeyName
      SubnetId: !Ref PrivateSubnet
      SecurityGroupIds: 
        - !Ref PrivateSecurityGroup
      Tags:
        - Key: Name
          Value: private-instance-stack

# SSH接続用にPublicとPrivateのIPを出力しておくと便利
Outputs:
  PublicIP:
    Value: !GetAtt BationHost.PublicIp
    Description: Public IP of BationHost

  PrivateIP:
    Value: !GetAtt PrivateInstance.PrivateIp
    Description: Private IP of EC2 instance