🥅

【ネットワークその2-サブネット/Elastic IPアドレス/インターネット・NATゲートウェイ編】Terraform×AWS学習記録

2024/07/12に公開

はじめに

こんにちは。awaです。

前回の続きとして、今回はサブネット、Elastic IPアドレス、インターネットゲートウェイ、NATゲートウェイをプロビジョニングしていきたいと思います。

↓前回はこちら
https://zenn.dev/awa4989/articles/5ce8d69b8535ee

(1) サブネットとは

サブネットは、VPC の IP アドレスの範囲です。特定のサブネットには、EC2 インスタンスなどの AWS リソースを作成できます。

https://docs.aws.amazon.com/ja_jp/vpc/latest/userguide/configure-subnets.html

VPCの中に作成できる分割されたネットワーク空間。それぞれ1つ以上のパブリックサブネットとプライベートサブネットを作成するのが一般的。次のような特徴をもつ。

  • 各サブネットが完全に 1 つのアベイラビリティーゾーン内に含まれている必要がある
  • 1 つのサブネットが複数のゾーンにまたがることはできない
  • 個別のアベイラビリティーゾーンで AWS リソースを起動することにより、1 つのアベイラビリティーゾーンで発生した障害からアプリケーションを保護できる

(2) Elastic IPアドレス(eip)とは

Elastic IP アドレスは、動的なクラウドコンピューティングのために設計された静的 IPv4 アドレスです。

https://docs.aws.amazon.com/ja_jp/AWSEC2/latest/UserGuide/elastic-ip-addresses-eip.html

インターネットからアクセス可能なパブリック IPv4 アドレスであるが、普通のパブリックIPアドレスとは異なり、IPアドレスを固定し、時間の経過で変わらないようにできる。(つまり静的である)また、再マッピングできるので、障害が発生したあるリソースから別のリソースへとマッピングし直すことで耐障害性の向上に役立つ。Elastic IP アドレス(以下、eipと呼称)はユーザーの AWS アカウントに割り当てられ、リリースするまでユーザーのアドレスになる。

個々のインスタンスや、NATゲートウェイなどのネットワークインターフェースに関連付けることができるが、後者の場合、そのネットワーク内に属するインスタンスに対して一括でeipを割り当てることができるため、IPアドレスを管理する手間が大幅に削減できるというメリットが生まれる。

(3) インターネットゲートウェイとは

インターネットゲートウェイは、VPC とインターネットとの間の通信を可能にする VPC コンポーネントであり、冗長性と高い可用性を備えており、水平スケーリングが可能です。IPv4 トラフィックおよび IPv6 トラフィックをサポートしています。

https://docs.aws.amazon.com/ja_jp/vpc/latest/userguide/VPC_Internet_Gateway.html

パブリックサブネット内にあるリソースと外部のインターネットとの相互通信を可能にするため、VPC内に設置する。

(4) NATゲートウェイとは

NAT ゲートウェイは、ネットワークアドレス変換 (NAT) サービスです。NAT ゲートウェイを使用すると、プライベートサブネット内のインスタンスは VPC 外のサービスに接続できますが、外部サービスはそれらのインスタンスとの接続を開始できません。

https://docs.aws.amazon.com/ja_jp/vpc/latest/userguide/vpc-nat-gateway.html

用途に応じて大まかに2種類ある。

・パブリックNATゲートウェイ
プライベートサブネットからインターネットへの一方向通信を可能にするため、パブリックサブネットに設置する。(インターネットからのアクセスは拒否されるところがポイント)
・プライベートNATゲートウェイ
プライベートサブネットから他のVPCまたはオンプレミスへの一方向通信を可能にするためにプライベートサブネットに設置する。

(5) 設定項目


■サブネット(aws_subnet)の設定項目

#サブネット
resource "aws_subnet" "private_subnet" {
  vpc_id     = aws_vpc.test.id #・・・(1)
  cidr_block = "10.0.1.0/24" #・・・(2)
  availability_zone = "ap-northeast-1a" #・・・(3)
  
#  ipv6_cidr_block = "xxxx:xxxx:xxxx:xxxx::/xx" #・・・(4)
#  ipv6_native = false  #・・・(5)
#  assign_ipv6_address_on_creation = false #・・・(6)
#  map_public_ip_on_launch = false #・・・(7)
#  map_customer_owned_ip_on_launch = true #・・・(8)
#  customer_owned_ipv4_pool = aws_vpc_ipam_pool.test.id #・・・(9)
#  outpost_arn = "arn:aws:outposts:region:accountId:outpost/op-11223344EXAMPLE" #・・・(10)
#  enable_resource_name_dns_a_record_on_launch = true #・・・(11)
#  enable_resource_name_dns_aaaa_record_on_launch = false #・・・(12)
#  private_dns_hostname_type_on_launch = "resource-name" #・・・(13)
#  enable_dns64 = false #・・・(14)
#  enable_lni_at_device_index = 1 #・・・(15)

  tags = {
    Name = "Main" #・・・(16)
  }
}

(1) VPC ID(vpc_id)
必須。サブネットを作成するVPCのIDを指定する。
(2) IPv4 VPC CIDR ブロック(cidr_block)
オプション。サブネットの IPv4 CIDR ブロック。VPCのCIDRブロックの範囲内で指定する必要がある。
(3) アベイラビリティーゾーン(availability_zone)
オプション。サブネットのAZを指定する。VPCに指定されているリージョンに属するAZを選択する必要がある。指定しなかった場合、Amazon側で自動的に選択される。また、こちらの代わりにavailability_zone_idを使用してAZのIDを指定することもできる。
(4) IPv6 VPC CIDR ブロック(ipv6_cidr_block)
オプション。サブネットの IPv6 CIDR ブロック。これか(2) IPv4 VPC CIDR ブロック(cidr_block)のいずれか1つ以上は必ず指定しなければならない。
(5) IPv6 CIDR ブロック(ipv6_native)
オプション。IPv6 のみのサブネットを作成するかどうか指定する。デフォルトはfalse。マネコンでは「手動入力」「IPv6(IPv4) CIDRがありません」のラジオボタンでIPv6を持つサブネットを作成するか否かをコントロールできる。
(6) ★IPv6 アドレスの自動割り当てを有効にする(assign_ipv6_address_on_creation)
オプション。指定されたサブネットに作成されたネットワークインターフェースにIPv6アドレスを割り当てる場合はtrueを指定する。デフォルトはfalse。
(7) ★パブリック IPv4 アドレスの自動割り当てを有効化(map_public_ip_on_launch)
オプション。サブネットに起動されたインスタンスにパブリック IP アドレスを割り当てる必要がある場合はtrue を指定。デフォルトはfalse。
(8) ★お客様が所有する IPv4 アドレスの自動割り当てを有効(map_customer_owned_ip_on_launch)
オプション。サブネット内に作成されたネットワーク インターフェイスに顧客所有の IP アドレスを割り当てる必要があることを示すために指定する。trueにする場合は、(9) IPv4 アドレス プール(customer_owned_ipv4_pool)と(10) Outpost の Amazon リソース名(outpost_arn)を指定する必要がある。デフォルトはfalse。
(9) ★IPv4 アドレス プール(customer_owned_ipv4_pool)
オプション。顧客所有の IPv4 アドレス プールのID。
(10) ★Outpost の Amazon リソース名(outpost_arn)
オプション。AWS Outposts の Amazon リソース名 (ARN)。
※AWS Outposts・・・AWSのインストラクチャ、サービス、APIなどの環境をオンプレミスに拡張して使える機能のこと。
(11) ★起動時にリソース名 DNS A レコードを有効化(enable_resource_name_dns_a_record_on_launch)
オプション。DNS A レコードを使用してインスタンス ホスト名の DNS クエリに応答するかどうかを示す。デフォルトはfalse。
※DNS Aレコード:ドメインをIPアドレスに置き換えるレコードのこと。
(12) ★起動時にリソース名 DNS AAAA レコードを有効にする(enable_resource_name_dns_aaaa_record_on_launch)
オプション。DNS AAAA レコードを使用してインスタンス ホスト名の DNS クエリに応答するかどうかを示す。デフォルトはfalse。
※DNS AAAAレコード:ドメインをIPv6IPアドレスに置き換えるレコードのこと。同じドメイン名に対して複数設定できる。
(13) ★ホスト名のタイプ(private_dns_hostname_type_on_launch)
オプション。このサブネット内の EC2 インスタンスのゲスト OS ホスト名が、リソース名 (RBN) または IP 名 (IPBN) のどちらに基づく必要があるかを決定。リソース名("resource-name")かIP名("ip-name")のいずれかから選択。IPv6 のみのサブネットの場合、リソース名しか指定できないが、デュアルスタックおよび IPv4 のみのサブネットの場合は双方指定可能。
※デュアルスタック:IPv4とIPv6が共存できるようにするための仕組みのこと。
(14) ★DNS64 を有効化(enable_dns64)
オプション。DNS64 を有効化して、Amazon VPC の IPv6 専用のサービスが IPv4 専用のサービスおよびネットワークと通信できるようにする。デフォルトはfalse。※DNS64:IPv6アドレスを持つ端末から、IPv4アドレスのみを持つサイトへアクセスを可能とする技術。IPv4アドレスしか持たないドメインに問い合わせがあった場合に、IPv4アドレスと特殊なIPv6アドレスを回答する。一般的にNAT64/DNS64と呼ばれる。
(15) ★ローカルネットワークインターフェイス(enable_lni_at_device_index)
オプション。このサブネット内のローカル ネットワーク インターフェイスのデバイス位置を示す。0は指定不可。
※LNI:Outposts サブネット内の Amazon EC2 インスタンスをオンプレミスネットワークに接続するインターフェース。
(16) サブネット名(Name)
オプション。サブネットの名称。タグブロックで設定。


■Elastic IPアドレス(aws_eip)の設定項目

マネコンの場合、eipの割当て→eipにパブリックIPアドレスを関連付け→リソースへのeipの関連付けという三段階の手順を踏む必要があるが、terraformでは一気通貫で設定できる。

#Elastic IPアドレス
resource "aws_eip" "lb" {
  domain   = "vpc" #・・・(1)

  #インスタンスに関連付ける場合
#  instance = aws_instance.web.id #・・・(2)
#  associate_with_private_ip = "xxx.xx.xx.xxx" #・・・(3)
  #ネットワークインターフェースに関連付ける場合
#  network_interface = aws_network_interface.multi-ip.id #・・・(4)
  #BYOIPプールからeipを割り当てる場合
#  address = "xx.xx.xx.xx" #・・・(5)
  #CoIPプールからeipを割り当てる場合
#  customer_owned_ipv4_pool = "xx.xx.xx.xx" #・・・(6)
#  network_border_group = "ap-northeast-1" #・・・(7)
#  public_ipv4_pool = "amazon" #・・・(8)
}

(1) ドメイン(domain)
必須。このeipがVPCで使用されるかどうかを指定。
(2) インスタンス(instance)
オプション。インスタンスに関連付ける場合のみ使用。関連付けたいEC2 インスタンス ID。
(3) プライベート IP アドレス(associate_with_private_ip)
オプション。Elastic IP アドレスを関連付けるプライベート IP アドレス。
(4) ネットワークインターフェース(network_interface)
オプション。ネットワークインターフェースに関連付ける場合のみ使用。関連付けたいネットワークインターフェースの ID。
(5) アドレス(address)
オプション。EC2 BYOIP プールからの IP アドレス。BYOIPプールからeipを割り当てるときのみ使用。※BYOIP(Bring Your Own IP Address):自社のオンプレ環境からAWSに持ち込むことのできるIPv4、またはIPv6アドレス。
(6) 顧客所有のIPアドレス(customer_owned_ipv4_pool)
オプション。顧客所有のアドレス プールの ID(CoIP)
※CoIP:オンプレミスネットワークを介して Outpost サブネット内のリソースへのローカル接続または外部接続を提供するためのIPアドレス。
(7) ネットワークボーダーグループ(network_border_group)
オプション。IP アドレスがアドバタイズされる場所。このパラメータを使用して、アドレスをこの場所に制限する。
(8) Amazon の IPv4 アドレスプール(public_ipv4_pool)
オプション。EC2 IPv4 アドレス プール識別子または"amazon"を指定。


■インターネットゲートウェイ(aws_internet_gateway)の設定項目

■インターネットゲートウェイ(aws_internet_gateway)の設定項目

resource "aws_internet_gateway" "this" {
  vpc_id = aws_vpc.test.id #・・・(1)

  tags = {
    Name = "main" #・・・(2)
  }
}

(1) 使用可能な VPC(vpc_id)
オプション。このインターネットゲートウェイをアタッチしたいVPCのIDを指定する。オプションとはいえ常にデタッチされた状態のインターネットゲートウェイを作成するということはあまりないと思うのでほぼ必須?
(2) 名前タグ(Name)
オプション。インターネットゲートウェイの名称。タグブロックで設定。


■NATゲートウェイ(aws_nat_gateway)の設定項目

今回はパブリックNATゲートウェイを取り上げる。

#NATゲートウェイ(パブリック)
resource "aws_nat_gateway" "example" {
  allocation_id = aws_eip.this.id #・・・(1)
  subnet_id     = aws_subnet.private_subnet.id #・・・(2)
  connectivity_type = "public" #・・・(3)

#  secondary_allocation_ids = [aws_eip.secondary.id]  #・・・(4)
  #プライベートNATゲートウェイの場合
#  private_ip = "xx.xx.xx.xx/xx"  #・・・(5)
#  secondary_private_ip_address_count = 3  #・・・(6)
#  secondary_private_ip_addresses = ["10.0.1.5"] #・・・(7)

  tags = {
    Name = "gw NAT"
  }

  depends_on = [aws_internet_gateway.this] #・・・(8)
}

(1) Elastic IP 割り当て ID(allocation_id)
オプション。割り当てたいNAT ゲートウェイの Elastic IP アドレスのID。
(2) サブネット(subnet_id)
必須。NAT ゲートウェイを配置するサブネットのサブネット ID。
(3) 接続タイプ(connectivity_type)
オプション。NATゲートウェイがパブリック("public")かプライベート("private")かを指定する。デフォルトは"public"。
(4) ★Elastic IP アドレス割り当て ID(secondary_allocation_ids)
オプション。この NAT ゲートウェイのセカンダリ割り当て EIP ID のリスト。
(5) プライマリプライベート IPv4 アドレス(private_ip)
オプション。NAT ゲートウェイに割り当てるプライベート IPv4 アドレス。アドレスを指定しない場合は、プライベート IPv4 アドレスが自動的に割り当てられる。
(6) 自動で割り当てられたプライベート IPv4 アドレスの数(secondary_private_ip_address_count)
オプション。NAT ゲートウェイに割り当てるセカンダリ プライベート IPv4 アドレスの数。(1~31まで指定可)
(7) セカンダリプライベート IPv4 アドレス(secondary_private_ip_addresses)
オプション。NAT ゲートウェイに割り当てるセカンダリ プライベート IPv4 アドレスのリスト。
(8) 依存関係の明示
terraform推奨。通信の順序を示すため、パブリックサブネットのインターネットゲートウェイとの依存関係を明示的にする。

(5)実際にプロビジョニングしてみた

今回は2つのAZに対し、それぞれパブリックサブネットとプライベートサブネットを1つずつ配置する。
また、各パブリックサブネットにはElastic IPアドレスが1つ割り当てられたNATゲートウェイを1つずつ配置する。
※ルートテーブルなどの設定は次回行う予定のため、通信経路などの設定は抜きにして、とりあえず個々のリソースだけ作成する。

【アーキテクチャ図】

terraform plan実行結果

Terraform will perform the following actions:

  # aws_eip.eip-1a will be created
  + resource "aws_eip" "eip-1a" {
      + allocation_id        = (known after apply)
      + arn                  = (known after apply)
      + association_id       = (known after apply)
      + carrier_ip           = (known after apply)
      + customer_owned_ip    = (known after apply)
      + domain               = "vpc"
      + id                   = (known after apply)
      + instance             = (known after apply)
      + network_border_group = (known after apply)
      + network_interface    = (known after apply)
      + private_dns          = (known after apply)
      + private_ip           = (known after apply)
      + ptr_record           = (known after apply)
      + public_dns           = (known after apply)
      + public_ip            = (known after apply)
      + public_ipv4_pool     = (known after apply)
      + tags                 = {
          + "Name" = "eip-1a"
        }
      + tags_all             = {
          + "Name" = "eip-1a"
        }
      + vpc                  = (known after apply)
    }

  # aws_eip.eip-1c will be created
  + resource "aws_eip" "eip-1c" {
      + allocation_id        = (known after apply)
      + arn                  = (known after apply)
      + association_id       = (known after apply)
      + carrier_ip           = (known after apply)
      + customer_owned_ip    = (known after apply)
      + domain               = "vpc"
      + id                   = (known after apply)
      + instance             = (known after apply)
      + network_border_group = (known after apply)
      + network_interface    = (known after apply)
      + private_dns          = (known after apply)
      + private_ip           = (known after apply)
      + ptr_record           = (known after apply)
      + public_dns           = (known after apply)
      + public_ip            = (known after apply)
      + public_ipv4_pool     = (known after apply)
      + tags                 = {
          + "Name" = "eip-1c"
        }
      + tags_all             = {
          + "Name" = "eip-1c"
        }
      + vpc                  = (known after apply)
    }

  # aws_internet_gateway.this will be created
  + resource "aws_internet_gateway" "this" {
      + arn      = (known after apply)
      + id       = (known after apply)
      + owner_id = (known after apply)
      + tags     = {
          + "Name" = "main"
        }
      + tags_all = {
          + "Name" = "main"
        }
      + vpc_id   = (known after apply)
    }

  # aws_nat_gateway.nat_gateway-1a will be created
  + resource "aws_nat_gateway" "nat_gateway-1a" {
      + allocation_id                      = (known after apply)
      + association_id                     = (known after apply)
      + connectivity_type                  = "public"
      + id                                 = (known after apply)
      + network_interface_id               = (known after apply)
      + private_ip                         = (known after apply)
      + public_ip                          = (known after apply)
      + secondary_private_ip_address_count = (known after apply)
      + secondary_private_ip_addresses     = (known after apply)
      + subnet_id                          = (known after apply)
      + tags                               = {
          + "Name" = "nat_gateway-1a"
        }
      + tags_all                           = {
          + "Name" = "nat_gateway-1a"
        }
    }

  # aws_nat_gateway.nat_gateway-1c will be created
  + resource "aws_nat_gateway" "nat_gateway-1c" {
      + allocation_id                      = (known after apply)
      + association_id                     = (known after apply)
      + connectivity_type                  = "public"
      + id                                 = (known after apply)
      + network_interface_id               = (known after apply)
      + private_ip                         = (known after apply)
      + public_ip                          = (known after apply)
      + secondary_private_ip_address_count = (known after apply)
      + secondary_private_ip_addresses     = (known after apply)
      + subnet_id                          = (known after apply)
      + tags                               = {
          + "Name" = "nat_gateway-1a"
        }
      + tags_all                           = {
          + "Name" = "nat_gateway-1a"
        }
    }

  # aws_subnet.private_subnet-1a will be created
  + resource "aws_subnet" "private_subnet-1a" {
      + arn                                            = (known after apply)
      + assign_ipv6_address_on_creation                = false
      + availability_zone                              = "ap-northeast-1a"
      + availability_zone_id                           = (known after apply)
      + cidr_block                                     = "10.0.3.0/24"
      + enable_dns64                                   = false
      + enable_resource_name_dns_a_record_on_launch    = false
      + enable_resource_name_dns_aaaa_record_on_launch = false
      + id                                             = (known after apply)
      + ipv6_cidr_block_association_id                 = (known after apply)
      + ipv6_native                                    = false
      + map_public_ip_on_launch                        = false
      + owner_id                                       = (known after apply)
      + private_dns_hostname_type_on_launch            = (known after apply)
      + tags                                           = {
          + "Name" = "private_subnet-1a"
        }
      + tags_all                                       = {
          + "Name" = "private_subnet-1a"
        }
      + vpc_id                                         = (known after apply)
    }

  # aws_subnet.private_subnet-1c will be created
  + resource "aws_subnet" "private_subnet-1c" {
      + arn                                            = (known after apply)
      + assign_ipv6_address_on_creation                = false
      + availability_zone                              = "ap-northeast-1c"
      + availability_zone_id                           = (known after apply)
      + cidr_block                                     = "10.0.4.0/24"
      + enable_dns64                                   = false
      + enable_resource_name_dns_a_record_on_launch    = false
      + enable_resource_name_dns_aaaa_record_on_launch = false
      + id                                             = (known after apply)
      + ipv6_cidr_block_association_id                 = (known after apply)
      + ipv6_native                                    = false
      + map_public_ip_on_launch                        = false
      + owner_id                                       = (known after apply)
      + private_dns_hostname_type_on_launch            = (known after apply)
      + tags                                           = {
          + "Name" = "private_subnet-1c"
        }
      + tags_all                                       = {
          + "Name" = "private_subnet-1c"
        }
      + vpc_id                                         = (known after apply)
    }

  # aws_subnet.public_subnet-1a will be created
  + resource "aws_subnet" "public_subnet-1a" {
      + arn                                            = (known after apply)
      + assign_ipv6_address_on_creation                = false
      + availability_zone                              = "ap-northeast-1a"
      + availability_zone_id                           = (known after apply)
      + cidr_block                                     = "10.0.1.0/24"
      + enable_dns64                                   = false
      + enable_resource_name_dns_a_record_on_launch    = false
      + enable_resource_name_dns_aaaa_record_on_launch = false
      + id                                             = (known after apply)
      + ipv6_cidr_block_association_id                 = (known after apply)
      + ipv6_native                                    = false
      + map_public_ip_on_launch                        = false
      + owner_id                                       = (known after apply)
      + private_dns_hostname_type_on_launch            = (known after apply)
      + tags                                           = {
          + "Name" = "public_subnet-1a"
        }
      + tags_all                                       = {
          + "Name" = "public_subnet-1a"
        }
      + vpc_id                                         = (known after apply)
    }

  # aws_subnet.public_subnet-1c will be created
  + resource "aws_subnet" "public_subnet-1c" {
      + arn                                            = (known after apply)
      + assign_ipv6_address_on_creation                = false
      + availability_zone                              = "ap-northeast-1c"
      + availability_zone_id                           = (known after apply)
      + cidr_block                                     = "10.0.2.0/24"
      + enable_dns64                                   = false
      + enable_resource_name_dns_a_record_on_launch    = false
      + enable_resource_name_dns_aaaa_record_on_launch = false
      + id                                             = (known after apply)
      + ipv6_cidr_block_association_id                 = (known after apply)
      + ipv6_native                                    = false
      + map_public_ip_on_launch                        = false
      + owner_id                                       = (known after apply)
      + private_dns_hostname_type_on_launch            = (known after apply)
      + tags                                           = {
          + "Name" = "public_subnet-1c"
        }
      + tags_all                                       = {
          + "Name" = "public_subnet-1c"
        }
      + vpc_id                                         = (known after apply)
    }

  # aws_vpc.main will be created
  + resource "aws_vpc" "main" {
      + arn                                  = (known after apply)
      + cidr_block                           = "10.0.0.0/16"
      + default_network_acl_id               = (known after apply)
      + default_route_table_id               = (known after apply)
      + default_security_group_id            = (known after apply)
      + dhcp_options_id                      = (known after apply)
      + enable_dns_hostnames                 = true
      + enable_dns_support                   = true
      + enable_network_address_usage_metrics = (known after apply)
      + id                                   = (known after apply)
      + instance_tenancy                     = "default"
      + ipv6_association_id                  = (known after apply)
      + ipv6_cidr_block                      = (known after apply)
      + ipv6_cidr_block_network_border_group = (known after apply)
      + main_route_table_id                  = (known after apply)
      + owner_id                             = (known after apply)
      + tags                                 = {
          + "Name" = "main"
        }
      + tags_all                             = {
          + "Name" = "main"
        }
    }

Plan: 10 to add, 0 to change, 0 to destroy.

Elastic IPアドレス:2つ
インターネットゲートウェイ:1つ
パブリックNATゲートウェイ:2つ
パブリックサブネット:2つ
プライベートサブネット:2つ
VPC:1つ
・・・計10のリソースが作成される予定であることが確認できる。

terraform apply実行結果

■VPC

■サブネット

■Elastic IPアドレス

■インターネットゲートウェイ

■パブリックNATゲートウェイ

おわりに

とりあえず簡単な箱だけできた!という感じ。
ここからルーティングやらなんやらしたらネットワーク編は終わりかな。。。
初めてアーキテクチャ図を書いてみたけど、普段見る機会が多いにもかかわらず、自作しようとしたら意外にも時間がかかってしまった。
でも図に起こしてみたら腹落ち度が深まった気がするから、大事な工程なんだなという気づきを得た。

Discussion