🐡

Amazon VPC設計時の要点まとめ&実践

2024/10/19に公開

はじめに

普段の構築作業において、VPCやサブネット、ルートテーブルは別チームが既に構築済で、私はそこにEC2インスタンスやLamdba関数、SGを作成しています。インターネットと通信可能になっているため、インスタンスのセットアップ(パッチ適用、ライブラリインストール)も難なく実行できます。便利な環境が容易されている一方で、ネットワークに関する知識に疎く、SGの作成にさえ苦労していました。
今回はVPC設計時に考慮すべき点、必要な設定項目、それに付随するネットワーク知識を簡潔にまとめ、自身が担える構築作業の幅を広げられることを目標とします。最後に、大好きなCloudFormationを用いてネットワークリソースのIaC化を行い、理解を深めたいと思います。
((っ´・ω・c))ウズウズ

学習リソース

下記を参考に執筆しています。

VPC設計のポイント

  • VPCのCIDRブロックは/16~/28のネットマスクを指定する。
    • IPアドレスの数としては2^{16}=65536個、2^{4}=16個となる。
  • IPアドレスはRFC1918で定義されているプライベートIPアドレスの範囲で指定する(推奨)。
    • RFC1918とはプライベートIPアドレスの運用方法を標準化するためのドキュメント。
    • プライベートIPアドレスの範囲はIANA(Internet Assigned Numbers Authority)という組織によって予約されている。
      class range prefix
      A 10.0.0.0 – 10.255.255.255 10.0.0.0/8
      B 172.16.0.0 – 172.31.255.255 172.16.0.0/12
      C 192.168.0.0 – 192.168.255.255 192.168.0.0/16

サブネット設計のポイント

  • サブネットのCIDRブロックは/16~/28のネットマスクを指定する。
    • IPアドレスの数としては2^{16}=65536個、2^{4}=16個となる。
  • VPCに複数のサブネットを作成する際はCIDRブロックの重複が起こらないよう設計する。
  • 2つ以上のAZにサブネットを作成する(AZに跨ってサブネットを作成することは不可)。
  • 各サブネットの先頭4つのIPアドレスと最後尾のIPアドレス(計5つ)はAWSによって予約されているため、ユーザが利用することはできない。
  • 利用目的別にサブネットを作成する。
    • e.g. インターネットとの通信可否によってパブリックサブネットとプライベートサブネットを作成する。

VPC内で起動するAWSリソース特有の制約

  • ALB
    • ALBがマネージドにスケールアウトするために8個のIPアドレスを空ける。
  • VPC Lamdba
    • Lamdba関数のVPC設定を有効化すると、Hyperplane ENI(マネージドリソース)が自動的に作成される。
    • 1つのHyperplane ENIは最大65000接続をサポートし、接続数がこれを越えた場合はHyperplane ENIがスケールアウトされる。
    • 既存のHyperplane ENIは関数間、実行間で再利用されるため、サブネットのIPアドレスが枯渇するリスクが大幅に低減されている。
    • Hyperplane ENIはサブネットとSGのペアで作成される。
      • あるLambda関数に単一のSG、2つのサブネットを設定した場合、Hyperplane ENIが各サブネットに1つずつ作成される(合計2つ)。

ルートテーブル

  • ルートテーブルとは?
    • VPC内の通信を振り分けるルールの一覧。
  • ルートテーブルはサブネットに関連付けて利用する。
    • 1つのサブネットには1つのルートテーブルしか関連付けられない。
    • 異なるサブネットに同じルートテーブルを関連付けることは可能。
  • ルートテーブルの種類
    • メインルートテーブル(デフォルトルートテーブル)
      • VPC作成時に自動的に作成される。
      • ルートテーブルが関連付けられていないサブネットに自動的に適用される。
    • カスタムルートテーブル
      • ユーザがカスタマイズするためのルートテーブル
    • サブネットルートテーブル
      • サブネットに関連付けられたルートテーブル
    • ゲートウェイルートテーブル
      • IGWまたはVGWに関連付けられたルートテーブル
    • Transit Gatewayルートテーブル
      • TGWに関連付けられたルートテーブル
    • ローカルゲートウェイルートテーブル
      • Outpostsローカルゲートウェイに関連付けられたルートテーブル

実践

CloudFormationを用いて、VPC、サブネット、ルートテーブル等のネットワークリソースをIaC化する。

設計

  • VPC

    • VPCの設計は下表の通り。
    VPC Name CIDR Block Number of IPs AZ Explanation
    MyVpc 10.0.0.0/16 65536 - VPC Peeringやオンプレとの接続は想定しないためブロックサイズを最大の/16とする(IPの重複を考慮しない)。
  • Subnet

    • サブネットの設計は下表の通り。
    Subnet Name CIDR Block Number of IPs IP ranges AZ Explanation
    PublicSubnetLB001 10.0.0.0/20 4096 10.0.0.0 - 10.0.15.255 ap-northeast-1a LBを配置する
    PublicSubnetLB002 10.0.16.0/20 4096 10.0.16.0 - 10.0.31.255 ap-northeast-1c id.
    PublicSubnetLB003 10.0.32.0/20 4096 10.0.32.0 - 10.0.47.255 ap-northeast-1d id.
    PrivateSubnetApp001 10.0.48.0/20 4096 10.0.48.0 - 10.0.63.255 ap-northeast-1a アプリケーションリソースを配置する(EC2,ECS,Lamnda等)
    PrivateSubnetApp002 10.0.64.0/20 4096 10.0.64.0 - 10.0.79.255 ap-northeast-1c id.
    PrivateSubnetApp003 10.0.80.0/20 4096 10.0.80.0 - 10.0.95.255 ap-northeast-1d id.
    PrivateSubnetDB001 10.0.96.0/20 4096 10.0.96.0 - 10.0.111.255 ap-northeast-1a DB,ストレージを配置する
    PrivateSubnetDB002 10.0.112.0/20 4096 10.0.112.0 - 10.0.127.255 ap-northeast-1c id.
    PrivateSubnetDB003 10.0.128.0/20 4096 10.0.128.0 - 10.0.143.255 ap-northeast-1d id.

    ※IP数(Number of IPs)はAWS予約IPを含む。

    • IP数を整理
      • VPCに確保したIP数は65536個。このうちサブネットに割り当てたIPの合計数は4096×9=36864個、割り当たっていないIP数は65536-36864=28672個となる。
      • 異なるアプローチで検算すると、PrivateSubnetDB003の次のIPは10.0.144.0から始まり、最後はVPC(10.0.0.0/16)末尾のIP(10.0.255.255)で終わることから、(256-144)×256=28672個となる。
  • アーキテクチャ

テンプレート

AWSTemplateFormatVersion: 2010-09-09
Description: ""

Parameters:
  PjName:
    Type: String
    Default: zenn
  Env:
    Type: String
    Default: dev
  VpcCidrBlock:
    Type: String
    Default: 10.0.0.0/16

Mappings:
  RegionMap:
    ap-northeast-1:
      az1: ap-northeast-1a
      az2: ap-northeast-1c
      az3: ap-northeast-1d

Resources:
##############################
### Allow resources in the public subnet to communicate over the Internet
##############################
  IGW:
    Type: AWS::EC2::InternetGateway
    Properties:
      Tags:
        - Key: Name
          Value: !Sub ${PjName}-igw-${Env}

  # attach an IGW to a VPC
  Attachment:
    Type: AWS::EC2::VPCGatewayAttachment
    Properties:
      InternetGatewayId: !Ref IGW
      VpcId: !Ref MyVpc

  PublicRouteTable:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref MyVpc
      Tags:
        - Value: Name
          Key: !Sub ${PjName}-public-rtb-${Env}

  RouteIgw:
    Type: AWS::EC2::Route
    Properties:
      RouteTableId: !Ref PublicRouteTable
      DestinationCidrBlock: 0.0.0.0/0
      GatewayId: !Ref IGW

  # Associates a public subnet with a route table
  AssociatePublicRouteTable001:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      RouteTableId: !Ref PublicRouteTable
      SubnetId: !Ref PublicSubnetLB001

  AssociatePublicRouteTable002:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      RouteTableId: !Ref PublicRouteTable
      SubnetId: !Ref PublicSubnetLB002

  AssociatePublicRouteTable003:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      RouteTableId: !Ref PublicRouteTable
      SubnetId: !Ref PublicSubnetLB003

##############################
### Allow resources in the private subnet to communicate over the Internet
##############################
  # Allocate an IP address for NATGW
  EIP001:
    Type: AWS::EC2::EIP
    Properties:
      Domain: vpc
      Tags:
        - Key: Name
          Value: !Sub ${PjName}-eip-natgw-${Env}

  Natgw001:
    Type: AWS::EC2::NatGateway
    Properties:
        AllocationId: !GetAtt EIP001.AllocationId
        SubnetId: !Ref PublicSubnetLB001
        ConnectivityType: public
        MaxDrainDurationSeconds: 350
        Tags:
          - Key: Name
            Value: !Sub ${PjName}-natgw-001

  PrivateRouteTable:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref MyVpc
      Tags:
        - Key: Name
          Value: !Sub ${PjName}-private-rtb-${Env}

  RouteNatgw:
    Type: AWS::EC2::Route
    Properties:
      RouteTableId: !Ref PrivateRouteTable
      DestinationCidrBlock: 0.0.0.0/0
      NatGatewayId: !Ref Natgw001

  AssociatePrivateRouteTable001:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      RouteTableId: !Ref PrivateRouteTable
      SubnetId: !Ref PrivateSubnetApp001

  AssociatePrivateRouteTable002:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      RouteTableId: !Ref PrivateRouteTable
      SubnetId: !Ref PrivateSubnetApp002

  AssociatePrivateRouteTable003:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      RouteTableId: !Ref PrivateRouteTable
      SubnetId: !Ref PrivateSubnetApp003

##############################
### VPC and Subnet
##############################
  MyVpc: 
    Type: AWS::EC2::VPC
    Properties: 
      CidrBlock: !Ref VpcCidrBlock 
      EnableDnsHostnames: true
      EnableDnsSupport: true
      InstanceTenancy: default
      Tags: 
        - Key: Name
          Value: !Sub ${PjName}-vpc-${Env}

  PublicSubnetLB001:
    Type: AWS::EC2::Subnet
    Properties:
      AvailabilityZone: !FindInMap [ RegionMap, !Ref AWS::Region, az1 ]
      CidrBlock: !Select [0, !Cidr [ !GetAtt MyVpc.CidrBlock, 9, 12 ] ]
      VpcId: !Ref MyVpc
      Tags:
        - Key: Name
          Value: !Sub ${PjName}-public-subnet-lb-001-${Env}

  PublicSubnetLB002:
    Type: AWS::EC2::Subnet
    Properties:
      AvailabilityZone: !FindInMap [ RegionMap, !Ref AWS::Region, az2 ]
      CidrBlock: !Select [1, !Cidr [ !GetAtt MyVpc.CidrBlock, 9, 12 ] ]
      VpcId: !Ref MyVpc
      Tags:
        - Key: Name
          Value: !Sub ${PjName}-public-subnet-lb-002-${Env}

  PublicSubnetLB003:
    Type: AWS::EC2::Subnet
    Properties:
      AvailabilityZone: !FindInMap [ RegionMap, !Ref AWS::Region, az3 ]
      CidrBlock: !Select [2, !Cidr [ !GetAtt MyVpc.CidrBlock, 9, 12 ] ]
      VpcId: !Ref MyVpc
      Tags:
        - Key: Name
          Value: !Sub ${PjName}-public-subnet-lb-003-${Env}

  PrivateSubnetApp001:
    Type: AWS::EC2::Subnet
    Properties:
      AvailabilityZone: !FindInMap [ RegionMap, !Ref AWS::Region, az1 ]
      CidrBlock: !Select [3, !Cidr [ !GetAtt MyVpc.CidrBlock, 9, 12 ] ]
      VpcId: !Ref MyVpc
      Tags:
        - Key: Name
          Value: !Sub ${PjName}-private-subnet-app-001-${Env}

  PrivateSubnetApp002:
    Type: AWS::EC2::Subnet
    Properties:
      AvailabilityZone: !FindInMap [ RegionMap, !Ref AWS::Region, az2 ]
      CidrBlock: !Select [4, !Cidr [ !GetAtt MyVpc.CidrBlock, 9, 12 ] ]
      VpcId: !Ref MyVpc
      Tags:
        - Key: Name
          Value: !Sub ${PjName}-private-subnet-app-002-${Env}

  PrivateSubnetApp003:
    Type: AWS::EC2::Subnet
    Properties:
      AvailabilityZone: !FindInMap [ RegionMap, !Ref AWS::Region, az3 ]
      CidrBlock: !Select [5, !Cidr [ !GetAtt MyVpc.CidrBlock, 9, 12 ] ]
      VpcId: !Ref MyVpc
      Tags:
        - Key: Name
          Value: !Sub ${PjName}-private-subnet-app-003-${Env}

  PrivateSubnetDB001:
    Type: AWS::EC2::Subnet
    Properties:
      AvailabilityZone: !FindInMap [ RegionMap, !Ref AWS::Region, az1 ]
      CidrBlock: !Select [6, !Cidr [ !GetAtt MyVpc.CidrBlock, 9, 12 ] ]
      VpcId: !Ref MyVpc
      Tags:
        - Key: Name
          Value: !Sub ${PjName}-private-subnet-db-001-${Env}

  PrivateSubnetDB002:
    Type: AWS::EC2::Subnet
    Properties:
      AvailabilityZone: !FindInMap [ RegionMap, !Ref AWS::Region, az2 ]
      CidrBlock: !Select [7, !Cidr [ !GetAtt MyVpc.CidrBlock, 9, 12 ] ]
      VpcId: !Ref MyVpc
      Tags:
        - Key: Name
          Value: !Sub ${PjName}-private-subnet-db-002-${Env}

  PrivateSubnetDB003:
    Type: AWS::EC2::Subnet
    Properties:
      AvailabilityZone: !FindInMap [ RegionMap, !Ref AWS::Region, az3 ]
      CidrBlock: !Select [8, !Cidr [ !GetAtt MyVpc.CidrBlock, 9, 12 ] ]
      VpcId: !Ref MyVpc
      Tags:
        - Key: Name
          Value: !Sub ${PjName}-private-subnet-db-003-${Env}

個人的スッキリポイント

  • VPCを作成するとメインルートテーブルが自動的に作成される。
  • サブネットを作成する際、ルートテーブルを明示的に関連付けなかった場合はメインルートテーブルがそのサブネットに自動的に関連付けられる。
    • LB向けサブネットは、IGWへのルートをもつルートテーブルと明示的に関連付けている(インターネット通信可能な状態)。
    • アプリケーションリソース向けサブネットは、NATGWへのルートをもつルートテーブルと明示的に関連付けている(NATGW経由でインターネット通信可能な状態)。
    • DB向けサブネットは明示的にルートテーブルとの関連付けを行っていないため、メインルートテーブルが関連付けられ、インターネットとの通信は不可能になっている。
  • メインルートテーブル(下画像)には、ローカルルートと呼ばれるルート情報が含まれる。
    • 送信先(通信の宛先IPアドレス)にVPC CIDR、ターゲット(通信の行先)にlocalが指定される。
    • つまり、送信先IPがVPC CIDRの範囲に含まれる場合、VPC内部(local)の当該IPアドレスをもつリソースへルーティングされる。
    • i.e. ローカルルートはサブネット間の通信を許可している。
  • パブリックサブネットのリソースがインターネット通信するには、パブリックIPの付与、IGW、IGWへのルートをもつルートテーブルが必要。
  • プライベートサブネットのリソースがインターネット通信するには、NATGW、NATGWへのルートをもつルートテーブルが必要。
  • NATGWは異なるサブネットやAZのリソースからも利用できる。
    • PublicSubnetLB001にのみNATGWを作成し、すべてのアプリケーションリソース向けサブネット(PrivateSubnetApp001~003)から利用できるよう設定。
    • 耐障害性を高めるには、各AZごとにNATGWを作成し、各AZのリソースが同AZのNATGWを経由するようルーティング設定する。
    • NATGWはデータ転送量に加え時間単位の課金が発生するため、コストと耐障害性のバランスを考慮する。

さいごに

今回はVPC設計時に必要なAWS/ネットワークの知識を整理し、実際に設計/構築を行いました。その中で「そもそもこの設定って何で必要なんだっけ??」という疑問が出てきました。これら疑問に対する回答を「個人的スッキリポイント」に書き、無事に腑に落ちることができました。これまでモヤモヤしていた部分を解消できたので、清々しい気持ちで執筆を終えられました。(๑˃̵ᴗ˂̵)و ヨシ!
次回もお楽しみに。

Discussion