LoginSignup
0
0

AWS初学者がCloudFormation使ってインフラを自動構築に挑戦してみたの話

Last updated at Posted at 2023-07-04

インフラをコード化して自動構築に挑戦してみました!

なんとインフラの構築をコード化できてて、再利用性が高く面倒臭い設定が
ぽちぽちっと一気にできてしまう方法を共有します!
Terraformではありません。 ←ものすごく気になりできるようになりたいがまずは転職成功から!
転職成功したら勉強したいですね!
本当にインフラコード化楽なので、一度しっかりとした構築をコードで残せば再利用できるので

気楽に構築を破壊できて気が楽です

今回はCloudFormationを使用しています

基本的にコストの低いものと必要最低限なもので
PF作成など未経験転職を目指している方向けです

余談↓

NATゲートウェイやVPCエンドポイント使ったら、、恐ろしくて放置できませんw

どこまでコード化したのか

  • VPC
  • Internet Gateway
  • Public Route Table
  • Public Subnet
  • Private Route Table
  • Private Subnet
  • Security Group

あとはVPCをIGWにアタッチやPublic Route TableとIGWをアタッチ
あとは各Route Tableをそれぞれのsubnetとassociationなど
必須で細かいところもやってます

できるインフラ構成図

インフラ.png

雛形みたいなものですね!
自動化してみよう!

コード説明

cloudformation-vpc.ymlでymlファイルに書いていきます

まず最初に

# CloudFormationテンプレートのバージョンを示します。
AWSTemplateFormatVersion: "2010-09-09" 
# テンプレートの概要を記述します,名前みたいなものです
Description: vpc construction template

VPCとそれぞれのサブネットのサイダー(CIDR)を設定する

# キーと値のマッピングを作成,この例ではVPCやサブネットのCIDRブロックが定義してます。
Mappings:
  StackConfig:
    VPC:
      CIDR: 10.1.0.0/16
    PublicSubnet1:
      CIDR: 10.1.0.0/24
    PublicSubnet2:
      CIDR: 10.1.1.0/24
    PrivateSubnet1:
      CIDR: 10.1.2.0/24
    PrivateSubnet2:
      CIDR: 10.1.3.0/24

後々の設定で直接CIDRを設定するとタイポミスなどが起きてしまう可能性があるからです
このように設定してあげるとミスが減ります。コードを変数に代入するイメージと一緒ですね!

ここから構築スタートまずはVPCから!

Resources: AWSリソースを定義します!
ここからネストして各それぞれのサービスの設定をしていきます
なので次からのブロックコードはResourcesを書きませんがネストしていると思ってください!

Resources:
  VPC:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: !FindInMap [StackConfig, VPC, CIDR]
      EnableDnsSupport: true # DNS サポートを有効化
      EnableDnsHostnames: true # DNS ホスト名を有効化
      Tags:
        - Key: Name
          Value: sample-vpc # VPC に名前タグを付ける

Type: AWS::EC2::VPCAWSのリソースタイプはVPC
Propertiesは設定を定義する感じです
CidrBlock: !FindInMap [StackConfig, VPC, CIDR]この部分は
Mappingsで設定した、StackConfigのVPCのCIDRなので、、、
CidrBlockには10.1.0.0/16が入ります。
次にタグでキーが名前値が自分が名付けたいvpcの名前を入れてください。

次インターネットゲートウェイを作成します

InternetGateway:
    Type: AWS::EC2::InternetGateway
    Properties:
      Tags:
        - Key: Name
          Value: vpc-igw

ここはそのままですね、IGWを作成しsample-vpc-igwと名付けています

IGWを作成したVPCとアタッチします

AttachGateway:
# VPC ゲートウェイアタッチメント。上記のVPCとインターネットゲートウェイを接続します
    Type: AWS::EC2::VPCGatewayAttachment
    Properties:
      VpcId: !Ref VPC # 作成した VPC への参照
      InternetGatewayId: !Ref InternetGateway # 作成した IGW への参照

VpcId: !Ref VPCこれは上記で作成したVPCIDを返します。
InternetGatewayId: !Ref InternetGateway作成した IGWのIDを返す
この二行でインターネットゲートウェイはVPCとアタッチできます

!Refを使用して今回はVPCリソースIDを参照しました

ルートテーブルやサブネット、セキュリティグループを特定のVPCに関連付ける際にも使用します

公開のルートテーブルを作成

PublicRouteTable:
    Type: AWS::EC2::RouteTable
    DependsOn: AttachGateway
    Properties:
      VpcId: !Ref VPC
      Tags:
        - Key: Name
          Value: public-route-table

DependsOn: AttachGateway
これはIGWがVPCにアタッチされた後に作成してくださいという意味です。

ここでもきました!VpcId: !Ref VPCです
これはこの公開ルートテーブルが関連づけられるVPCのIDを指定しています
あとは名前でpublic-route-tableとしています

IGWと公開するルートテーブルのルートを追加する

PublicRoute:
    Type: AWS::EC2::Route # 公開ルートのリソースを定義します。
    DependsOn: AttachGateway # インターネットゲートウェイがVPCにアタッチされた後に作成されます。
    Properties:
      RouteTableId: !Ref PublicRouteTable # このルートが追加されるルートテーブルのIDを指定します。
      DestinationCidrBlock: 0.0.0.0/0
      GatewayId: !Ref InternetGateway # このルートのターゲットとなるインターネットゲートウェイを指定します。

先ほどと似たようなところはコメントアウトでまとめました

DestinationCidrBlock: 0.0.0.0/0これは
このルートの宛先CIDRを指定します。ここでは全てのIPアドレスを意味します

次にパブリックサブネット達を作成

PublicSubnet1:
    Type: AWS::EC2::Subnet # 公開サブネットのリソースを定義します。
    DependsOn: AttachGateway # インターネットゲートウェイがVPCにアタッチされた後に作成されます。
    Properties:
      AvailabilityZone: "ap-northeast-1a"
      VpcId: !Ref VPC # このサブネットが関連付けられるVPCのIDを指定します。
      CidrBlock: !FindInMap [StackConfig, PublicSubnet1, CIDR]
      Tags:
        - Key: Name
          Value: Public Subnet a

AvailabilityZone: "ap-northeast-1a"これはサブネットが配置されるAZを指定してます

久々に来ましたCidrBlock: !FindInMap [StackConfig, PublicSubnet1, CIDR]
これはVPCのサイダーを指定する時に出ましたね
10.1.0.0/24を意味します。
名前はPublic Subnet aと名付けました

先程のサブネットを公開ルートテーブルに紐づけています

PublicSubnet1RouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation # サブネットとルートテーブルの関連付けを定義します。
    Properties:
      SubnetId: !Ref PublicSubnet1 # 関連付けられるサブネットのIDを指定します。
      RouteTableId: !Ref PublicRouteTable # 関連付けられるルートテーブルのIDを指定します。

公開ルートテーブルに先ほど作ったPublic Subnet aを紐付け(関連づけ)ています

同じ感じでもう一つのパブリックサブネットを作成

PublicSubnet2:
    Type: AWS::EC2::Subnet
    DependsOn: AttachGateway
    Properties:
      AvailabilityZone: "ap-northeast-1c"
      VpcId: !Ref VPC
      CidrBlock: !FindInMap [StackConfig, PublicSubnet2, CIDR]
      Tags:
        - Key: Name
          Value: Public Subnet c
  PublicSubnet2RouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref PublicSubnet2
      RouteTableId: !Ref PublicRouteTable

まとめてどん!

変更点はAZをaからcへサイダーもPublicSubnet2になっています
名前はPublic Subnet cです
関連づけはPublicSubnet2も同じ公開ルートテーブルに関連づけています

次にプライベートサブネットです大体同じ感じなのでまとめます

  PrivateRouteTable:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref VPC
      Tags:
        - Key: Name
          Value: private-route-table
  PrivateSubnet1:
    Type: AWS::EC2::Subnet
    Properties:
      AvailabilityZone: "ap-northeast-1a"
      VpcId: !Ref VPC
      CidrBlock: !FindInMap [StackConfig, PrivateSubnet1, CIDR]
      Tags:
        - Key: Name
          Value: Private Subnet b
  PrivateSubnet1RouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref PrivateSubnet1
      RouteTableId: !Ref PrivateRouteTable
  PrivateSubnet2:
    Type: AWS::EC2::Subnet
    Properties:
      AvailabilityZone: "ap-northeast-1c"
      VpcId: !Ref VPC
      CidrBlock: !FindInMap [StackConfig, PrivateSubnet2, CIDR]
      Tags:
        - Key: Name
          Value: Private Subnet d
  PrivateSubnet2RouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref PrivateSubnet2
          RouteTableId: !Ref PrivateRouteTable

ルートテーブル

作成したVPCにprivate-route-tableと名付け
非公開ルートテーブルを作成

サブネット

これらも作成したVPCの中に作ってます

PrivateSubnet1: Private Subnet bと名付け
AZは東京リージョンのaでサイダーもそれぞれ違うものを指定

PrivateSubnet2: Private Subnet dと名付け
AZは東京リージョンのcでサイダーもそれぞれ違うものを指定

作ったサブネットを非公開ルートテーブルと関連づけ

PrivateSubnet1RouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref PrivateSubnet1
      RouteTableId: !Ref PrivateRouteTable

PrivateSubnet1はPrivateRouteTableと関連づけできたなと
みた感じそのまんまのイメージでいいと思います。

ちなみに非公開なのでIGWとはルートはつなげません。

最後にセキュリティグループ作成

まずはパブリックな方から

PublicSecurityGroup:
    Type: AWS::EC2::SecurityGroup # 公開セキュリティグループのリソースを定義します。
    Properties:
      GroupDescription: Allow SSH snd HTTP and HTTPS # グループの説明を設定します。
      VpcId: !Ref VPC # このセキュリティグループが関連付けられるVPCのIDを指定します。
      SecurityGroupIngress: # セキュリティグループのインバウンドルールを定義します。
        - IpProtocol: tcp # プロトコルを指定します。ここではTCPを指定しています。
          FromPort: 22 # 開始ポート番号を指定します。ここでは22(SSH)を指定しています。
          ToPort: 22 # 終了ポート番号を指定します。ここでは22(SSH)を指定しています。
          CidrIp: 0.0.0.0/0 # 許可する送信元CIDR IPアドレス範囲を指定します。ここでは全てのIPアドレスを意味します。
        - IpProtocol: tcp
          FromPort: 80 # HTTP(80)を許可します。
          ToPort: 80
          CidrIp: 0.0.0.0/0
        - IpProtocol: tcp
          FromPort: 443 # HTTPS(443)を許可します。
          ToPort: 443
          CidrIp: 0.0.0.0/0
        - IpProtocol: tcp
          FromPort: 3000 # ポート3000を許可します。
          ToPort: 3000
          CidrIp: 0.0.0.0/0
      Tags:
        - Key: Name
          Value: public-sg # 名前

ブロックコードでコメントアウトで書いた方が自分も理解が深まる気がする

RDS用のセキュリティグループ

RDSSecurityGroup:
    Type: AWS::EC2::SecurityGroup # RDS用のセキュリティグループを定義します。
    Properties:
      GroupDescription: Allow Postgres SQL from ECS only # グループの説明を設定します。
      VpcId: !Ref VPC # このセキュリティグループが関連付けられるVPCのIDを指定します。
      SecurityGroupIngress: # セキュリティグループのインバウンドルールを定義します。
        - IpProtocol: tcp # プロトコルを指定します。ここではTCPを指定しています。
          FromPort: 5432 # 開始ポート番号を指定します。ここでは5432(PostgreSQL)を指定しています。
          ToPort: 5432 # 終了ポート番号を指定します。ここでは5432(PostgreSQL)を指定しています。
          SourceSecurityGroupId: !GetAtt PublicSecurityGroup.GroupId # 許可する送信元セキュリティグループを指定します。ここでは公開セキュリティグループを指定しています。
      Tags:
        - Key: Name
          Value:rds-sg # 名前

DB置き場なのでここでは接続許可できるのは自分のアプリのみとなります。
外部からの通信はできないようにしています

これにて必要最低限の設定をコード化しました!

次はどうやってこのファイルを使うのかを紹介します。

AWSのコンソールへ行ってCloudFormationと検索

スクリーンショット 2023-07-04 11.58.41.png
このマークです

スタック作成でymlファイルを選択してください。
スクリーンショット 2023-07-04 12.00.45.png

デザイナーで表示するとこんな感じの図が

スクリーンショット 2023-07-04 12.02.59.png

なんもいじってない状態です。この図でインフラ構成図を理解するには僕にはまだ早かったようです

次にスタック名はお好きにどうぞ
他は何もいじってません
送信ボタンを押したらできます

スクリーンショット 2023-07-04 12.06.26.png

こんな感じでステータスが緑色でcreate_completeになればおk!!

VPC周りが自動で構築してくれています

同じような設定を何度もするのはめんどくさいですし
このように一回作ったりテンプレができれば名前とかサイダーの変更などひょいひょいっと変更するだけで
あとは自動でやってくれます。

終わります!
お疲れ様でした!!!

余談

実はRDSとECS(クラスター、タスク、サービス)も自動化したのですが細かいところの設定がわからなく諦めました
コストの高い部分ですし慎重にいきたい部分なので手動がいいかなと。

一様挑戦したコード達
ECS編

Resources:
  MyLogGroup:
    Type: "AWS::Logs::LogGroup"
    Properties:
      LogGroupName: "/ecs/real_world"
  MyCluster:
    Type: AWS::ECS::Cluster
    Properties:
      ClusterName: real_world_ecs 

  MyTaskDefinition:
    Type: AWS::ECS::TaskDefinition 
    Properties:
      Family: real-world-api
      Cpu: '256' 
      Memory: '512' 
      NetworkMode: awsvpc 
      RequiresCompatibilities:
        - FARGATE
      ExecutionRoleArn: !Sub "arn:aws:iam::${AWS::AccountId}:role/ecsTaskExecutionRole"
      ContainerDefinitions: 
        - Name: rails-api
          Image: ここにECRでpushしたイメージのURIが入る
          Memory: 128
          PortMappings:
            - ContainerPort: 3000
          LogConfiguration:
            LogDriver: awslogs
            Options:
              awslogs-group: /ecs/real_world 
              awslogs-region: ap-northeast-1 
              awslogs-stream-prefix: ecs 
    MyECSService: 
        Type: "AWS::ECS::Service"
        Properties: 
          Cluster: !Ref MyCluster 
          TaskDefinition: !Ref MyTaskDefinition 
          DesiredCount: 1 
          LaunchType: FARGATE
          NetworkConfiguration:
            AwsvpcConfiguration: 
              AssignPublicIp: ENABLED 
              SecurityGroups: 
                - !Ref PublicSecurityGroup
              Subnets:
                - !Ref PublicSubnet1
                - !Ref PublicSubnet2

RDS編

MyDBInstance:
  Type: "AWS::RDS::DBInstance"
  Properties:
    Engine: postgres
    EngineVersion: "14.6"
    DBInstanceIdentifier: "real-world-db"
    MasterUsername: "admin"
    MasterUserPassword: "password" 
    AllocatedStorage: "20"  
    StorageType: "gp2" 
    DBInstanceClass: "db.t3.micro"  
    VPCSecurityGroups: 
      - !Ref RDSSecurityGroup
    DBSubnetGroupName: !Ref DBSubnetGroup
    PubliclyAccessible: false
    MultiAZ: false 
    AutoMinorVersionUpgrade: false 
    BackupRetentionPeriod: 0
    DBName: "real_world"  
DBSubnetGroup: 
  Type: AWS::RDS::DBSubnetGroup
  Properties: 
    DBSubnetGroupDescription: "My DB Subnet Group"
    SubnetIds: 
      - !Ref PrivateSubnet1
      - !Ref PrivateSubnet2

本当はマルチAZのがいいのはわかっていますがコストが高くて。。。。。orz
細かい設定が大事だから今回は手動にしました。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0