VPCのサブネットはルートテーブルで考えよう

# 学習前
VPCのサブネットには、パブリックサブネットとプライベートサブネットの2種類あるのは知っていました。

その2つは、サブネットを作成する際に「パブリックサブネットにする？プライベートサブネットにする？」のどちらかをコンソール上で選択できて、それによってパブリック/プライベートの違いが決定する…と考えていました。

でも実際は全然違った…ということで学びを得たのでアウトプットします。

# 結論
パブリックサブネットとプライベートサブネットの違いは、<font color="red">**ルートテーブルの設定によって左右**</font>される。
サブネットそのものを「パブリック」や「プライベート」と**定義づけることはできない！！！(というか、そんなことをしても意味がない！)**

# ハンズオンをやってみた
AWS公式が提供している初心者向けハンズオンである「Hands-on for Beginners」のネットワーク編#1 で、ネットワークについて1から学び直している時に、自分の認識が誤っていたことに気がつきました。

https://pages.awscloud.com/JAPAN-event-OE-Hands-on-for-Beginners-Network1-2022-reg-event.html?trk=aws_introduction_page

このハンズオンでは、VPC作成画面で「VPCのみ」を作成し、サブネットはその後で作成します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3616210/4b9f09d6-89c5-b0d8-1855-21feabf6df42.png)

そしてサブネットを作成しているとき、事件は起こりました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3616210/25289e81-31cd-f9c4-81b1-514f0dfe45a8.png)


**あれ？どこでパブリックとかプライベートとか指定するんだ…？？？**

<br>


最初は「まさか名前やタグだけで決まるのか…？」と考えたのですが、それだったら予め名前入力欄に固定で「Public」とか「Private」とか書かれているはず。

でも思いっきり *my-subnet-01* が例として書かれている。なので、名前やタグで決まると言う予想は否定。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3616210/31cb69f5-be37-353c-5932-5ea78aa0425f.png)

ひとまずサブネットを作成し、コンソールを眺めてみてもそれらしい設定箇所は無し。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3616210/4c52e483-92d0-229e-3357-e409f03cb6c8.png)


わからないのでとりあえずハンズオンを進めていくと、このような説明がありました。
- パブリックサブネット
    - **ルートテーブルに**インターネットゲートウェイへのエントリがあり、インターネットとインバウンド/アウトバウンドのアクセスが可能
- プライベートサブネット
    - **ルートテーブルに**インターネットゲートウェイへのエントリはなく、インターネットから直接アクセスできない

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3616210/98519170-62e8-31d2-84e5-8118173fc8a0.png)

**お前か！！！！！！！！**

<br>

また、このように説明されていました。

>「必ず、1つのサブネットには1つのルートテーブルが関連づけられている。
この<font color="red">**ルートテーブルの情報がインターネットとの接続性を持っているのか、持っていないのか、によってパブリックやプライベートが決定**</font>される。」

パブリック・プライベートと命名されているのは分かりやすくするためであり、設定上こういった表現が用いられているわけではなかったのですね…

ということで、パブリック用・プライベート用にそれぞれルートテーブルを作成し、サブネットへ明示的に関連付けます。

こちらはパブリック。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3616210/1fe104ab-9da3-60e7-715d-575997d80950.png)
↓
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3616210/b67fedc9-4259-0599-97d5-5d06c95971ae.png)
<br>
こちらはプライベート。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3616210/51443ba3-2114-81e2-3c5d-04e5bfa7b419.png)
↓
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3616210/143f2b74-8fcb-55fe-3c2f-0abb2839e20e.png)

これで、パブリックなサブネットと、プライベートなサブネットが作成できました！

***
個人的には、VPCのリソースマップを確認すると分かりやすいなと思いました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3616210/546c925b-4fbf-41bf-5f73-bc7985de2887.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3616210/a1d21e01-293f-28bc-fcfc-06b03af55b74.png)

パブリックはインターネットゲートウェイまで繋がっており、プライベートはVPC内だけに閉じていることが視覚的に分かりやすくなっています。

そもそも論なんですが、サブネットとは、大規模ネットワークを管理しやすいよう細かく分割して管理しやすくしたネットワーク空間です。

AWSで考えると、VPCという大きなネットワークを分割したものがサブネットです。

本質的に考えれば、**サブネットはただのネットワーク空間**。それ以上でも、それ以下でもないですよね。そのことが頭から抜け落ちていました。

言葉面に惑わされて、認識が歪むことのないようにしたいです！

# まとめ

言葉ではパブリックサブネットとプライベートサブネットの違いを理解しているつもりではいました。

ただ、やはり実際に環境を見てみると、**自分の認識とは違う部分もある**ということが身に染みてわかりました。

手を動かすこと、そして本質を理解することのなんと重要なことか…！

引き続き、ネットワーク周りのことは継続的に学んでいきます！

# おまけ：CDKでVPCを定義する
CDKでVPCを定義するとき、少し注意が必要です。

## 1.API Referenceの検索方法に注意
VPCの記述方法を検索しようとした際、**API Referenceを「VPC」で検索しても出てきません**。
引っかかるのは「vpclattice」という謎サービスのみ(こちらは別途まとめます)。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3616210/b343f660-46c2-3167-99ee-a707288cee6c.png)

なので、「EC2」で検索し、その配下にConstructとして存在する「VPC」を参照する必要があります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3616210/dced4ec9-6308-5e36-3bdf-d081b6dedae3.png)

## 2.VPC Constructのデフォルト設定に注意
パラメータのデフォルト設定にも、若干注意の必要な箇所があります。

例えば、`natGatewaySubnets` は、デフォルトだと**全てのパブリックサブネットにデプロイ**されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3616210/56f023f9-21e1-4b21-733e-220d1fde09d5.png)

可用性という面で考えたときには確かにベストプラクティスなのですが、知らずに設定してしまうと、要件によっては「なんでこんなにAWS利用料が高いんだ！！！」となってしまうかもしれません。

また、`createInternetGateway` もデフォルトでは作成されることになっており、「要件的には不要なのに作成されてしまっていた！」のような事故が発生するかもしれません。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3616210/03bebc12-7200-6a7f-87c4-97981735be8b.png)

例えば、以下のようにVPC Constructの設定を最小限にした場合、NATゲートウェイもインターネットゲートウェイも自動で作成されてしまいます。
```vpc-stack.ts
import * as cdk from 'aws-cdk-lib';
import * as ec2 from 'aws-cdk-lib/aws-ec2';
import { Construct } from 'constructs';

export class VPCStack extends cdk.Stack {
  constructor(scope: Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    const vpc = new ec2.Vpc(this, 'VPC', {
      ipAddresses: ec2.IpAddresses.cidr('10.0.0.0/16'),
      maxAzs: 2,
    })
```
↓
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3616210/294246be-4b80-0994-cf07-b669e3148755.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3616210/31401aa3-b531-028b-ea24-509af3e46e76.png)

この辺りは、CDK API Referenceを参考にしながら1つ1つパラメータを設定していく必要があります。
***
サブネットも、`subnetConfiguration` を設定しなければデフォルトで作成されます。
具体的には、パブリックサブネット・プライベートサブネットがAZごとに1つずつ作成されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3616210/72cefb10-ee1b-5399-53b2-b070dbdc7037.png)

このサブネットの設定で注意すべきは、`subnetConfiguration` の設定を省略しつつ、自分で独自にサブネットを作成しようとした場合、サブネットがConflictを起こします。
```vpc-stack.ts
import * as cdk from 'aws-cdk-lib';
import * as ec2 from 'aws-cdk-lib/aws-ec2';
import { Construct } from 'constructs';

export class VPCStack extends cdk.Stack {
  constructor(scope: Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    const vpc = new ec2.Vpc(this, 'VPC', {
      ipAddresses: ec2.IpAddresses.cidr('10.0.0.0/16'),
      maxAzs: 2,

      /* サブネットの設定を省略する
      subnetConfiguration: [
        {
          name: 'PublicSubnet-1a',
          subnetType: ec2.SubnetType.PUBLIC,
          cidrMask: 24,
        },
      ]*/
    })

    // 個別にサブネットを作成する
    const subnetPublic1a = new ec2.PublicSubnet(this, 'PublicSubnet1a', {
      availabilityZone: 'ap-northeast-1a',
      cidrBlock: '10.0.51.0/24',
      vpcId: vpc.vpcId
    })

    const subnetPublic1c = new ec2.PublicSubnet(this, 'PublicSubmit1c', {
      availabilityZone: 'ap-northeast-1c',
      cidrBlock: '10.0.52.0/24',
      vpcId: vpc.vpcId
    })

    const privatePublic1a = new ec2.PrivateSubnet(this, 'PrivateSubnet1a', {
      availabilityZone: 'ap-northeast-1a',
      cidrBlock: '10.0.101.0/24',
      vpcId: vpc.vpcId
    })

    const privatePublic1c = new ec2.PrivateSubnet(this, 'PrivateSubnet1c', {
      availabilityZone: 'ap-northeast-1c',
      cidrBlock: '10.0.102.0/24',
      vpcId: vpc.vpcId
    })
  }
}
```
↓
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3616210/4304290e-779a-1b2f-df80-259efbce36cf.png)

ですので、サブネットを作成する場合、`subnetConfiguration` のパラメータ内で定義するか、`subnetConfiguration: []` とした上でサブネットを定義する必要があります。

ただ、`subnetConfiguration` のパラメータ内では、`cidrMask` の設定しかできないため、独自のIPv4 CIDRを設定したい場合は、Subnet Constructを定義してその中で設定する必要があります。

また、`subnetConfiguration: []` とした場合は、VPC Constructでインターネットゲートウェイが作成されないようになるため、別途作成する必要があります。

***
以上を踏まえ、先ほど手動で作成したものと同じ環境を作成しようとすると、以下のようになります。
```vpc-stack.ts
import * as cdk from 'aws-cdk-lib';
import * as ec2 from 'aws-cdk-lib/aws-ec2';
import { Construct } from 'constructs';

export class VPCStack extends cdk.Stack {
  constructor(scope: Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    const vpc = new ec2.Vpc(this, 'VPC', {
      ipAddresses: ec2.IpAddresses.cidr('10.0.0.0/16'),
      availabilityZones: [
        'ap-northeast-1a',
        'ap-northeast-1c'
      ],
      // NAT Gatewayは作成しない
      natGateways: 0,
      // サブネットの自動作成を無効化すると、インターネットゲートウェイも作成されなくなる
      subnetConfiguration: [],
    });

    // インターネットゲートウェイを作成し、VPCにアタッチする
    const igw = new ec2.CfnInternetGateway(this, 'InternetGateway');
    const vpcGatewayAttachment = new ec2.CfnVPCGatewayAttachment(this, 'AttachGWtoVPC', {
      vpcId: vpc.vpcId,
      internetGatewayId: igw.attrInternetGatewayId
    })

    // パブリックサブネットを作成する
    const subnetPublic1a = new ec2.PublicSubnet(this, 'PublicSubnet1a', {
      availabilityZone: 'ap-northeast-1a',
      cidrBlock: '10.0.1.0/24',
      vpcId: vpc.vpcId,
      mapPublicIpOnLaunch: true
    });
    // デフォルトでルートテーブルが作成されるので、それにインターネットゲートウェイ宛の設定を追加する
    subnetPublic1a.addDefaultInternetRoute(igw.attrInternetGatewayId, vpcGatewayAttachment);

    const subnetPublic1c = new ec2.PublicSubnet(this, 'PublicSubnet1c', {
      availabilityZone: 'ap-northeast-1c',
      cidrBlock: '10.0.2.0/24',
      vpcId: vpc.vpcId,
      mapPublicIpOnLaunch: true
    });
    subnetPublic1c.addDefaultInternetRoute(igw.attrInternetGatewayId, vpcGatewayAttachment);
    
    // プライベートサブネットを作成する
    const privateSubnet1a = new ec2.PrivateSubnet(this, 'PrivateSubnet1a', {
      availabilityZone: 'ap-northeast-1a',
      cidrBlock: '10.0.11.0/24',
      vpcId: vpc.vpcId,
    });

    const privateSubnet1c = new ec2.PrivateSubnet(this, 'PrivateSubnet1c', {
      availabilityZone: 'ap-northeast-1c',
      cidrBlock: '10.0.12.0/24',
      vpcId: vpc.vpcId,
    });
  }
}
```

VPC、サブネット、インターネットゲートウェイ、ルートテーブルを作るだけなのに、意外と手こずりました…

こちらもやはり手を動かすことで、サービス間の依存関係などが見えてきました。

ただでさえネットワーク周りは苦手ですが、CDKでの定義方法も一緒に学び、発信していきます！