NAT Gatewayは、AWS環境で重要な役割を果たすリソースの一つです。この記事では、NAT Gatewayの基本的な概念と特徴、Terraformでの実装方法を解説します。
AWSを扱う実際の現場では、NAT Gatewayを使うことでより安全なインターネット通信を実現することができます。ぜひ一緒に理解を深めながら、Terraformでの実装方法を学んでいきましょう。
NAT Gatewayとは
NAT Gatewayは、AWSが提供するNAT(Network Address Translation)サービスです。
このNAT Gatewayを使用することで、プライベートサブネット内のリソースがNAT GatewayのIPアドレスを使用してインターネットへ通信することが可能になります。
プライベートサブネットのルートテーブルにはインターネットゲートウェイ(IGW)へのルートが設定されていないため、外部からの通信は受け付けません。つまり、インターネットから直接アクセスされることはなく、内部リソースのセキュリティを保つことができます。
VPC内部からの通信はNAT Gatewayを使用して行われるため、より安全な通信を実現することができます。
NATとは
NAT Gatewayが提供する機能であるNATとは、グローバルIPアドレスとプライベートIPアドレスを変換する技術のことです。
NATはSNAT(Source NAT)とDNAT(Destination NAT)の2種類に分類されます。SNATは送信元のIPアドレスを変換するのに対し、DNATは送信先のIPアドレスを変換します。NAT GatewayはこのうちSNATを担当し、プライベートネットワーク内のリソースがインターネットにアクセスする際に使用されます。
NATに関しては、以下の記事を参照ください。
https://envader.plus/article/15
NAT Gatewayの仕組み
ここでは、NAT Gatewayがどのように機能するかについて詳しく説明します。
プライベートサブネットからのアウトバウンド通信
プライベートサブネット内のリソースは、インターネットと直接通信することはできません。そこで、NAT Gatewayが仲介役として機能します。
NAT Gatewayは受け取ったリクエストの送信元IPアドレス(プライベートIPアドレス)を、自身のElastic IPアドレスに変換し、インターネットに送信します。こうすることで、プライベートサブネット内のリソースは、NAT GatewayのパブリックIPアドレスを介してインターネットにアクセスすることができます。
インターネットからのインバウンド通信
インターネット上のリソースからのレスポンスは、NAT GatewayのパブリックIPアドレスを介して受信します。NAT Gatewayはこのレスポンスの宛先IPアドレスを、元のリクエストを送信したプライベートサブネット内にあるリソースのプライベートIPアドレスに変換し、プライベートサブネット内のリソースに通信を送ります。
理解しておくポイントとして、インバウンド通信はプライベートサブネット内からのアウトバウンド通信が発生した場合にのみ許可されるという点です。この仕組みのおかげで、外部からの不正なアクセスが直接プライベートサブネット内のリソースに届くことを防ぎます。
https://docs.aws.amazon.com/ja_jp/vpc/latest/userguide/vpc-nat-gateway.html
冗長性を確保する必要がある
NAT Gatewayで冗長性を確保するためには、各アベイラビリティゾーン(AZ)に少なくとも1つのNAT Gatewayを配置する必要があります。こうすることで、特定のAZが障害を起こした場合でも他のAZで動作するNAT Gatewayを通じて通信が継続され、高可用性を実現できます。
耐障害性を高めるための方法として、公式ドキュメントに記載されています。
https://docs.aws.amazon.com/ja_jp/vpc/latest/userguide/nat-gateway-basics.html
NAT Gatewayのトラブルシューティングに関しては、以下記事で詳しく解説しています。
https://envader.plus/article/260
NAT Gatewayの料金
NAT Gatewayの料金は、主に以下の2つの要素に基づいて計算することができます。
利用時間
NAT Gatewayの利用料金は、作成されたNAT Gatewayの稼働時間に対して課金されます。
作成後は1時間単位で料金が加算されるため、検証で使用する場合は無駄なコストが発生しないよう、検証後の削除は必ず行いましょう。2024年8月時点で、1時間/0.062USDの料金が発生します。(東京リージョン)
また、NAT Gatewayを複数のアベイラビリティゾーンごとにデプロイした場合、それぞれのNAT Gatewayに対して料金が発生することになるため注意が必要です。
データ処理量
NAT Gatewayを通じて転送されるデータの量に応じても料金が発生します。データの種類としては、NAT Gatewayを介してインターネットに送信されるデータと、インターネットから受信されるデータが含まれます。
こちらは2024年8月時点で、処理したデータ量1GB/0.062USDの料金が発生します。(東京リージョン)
https://aws.amazon.com/jp/vpc/pricing/#NAT_Gateway
TerraformでNAT Gatewayを構築してみよう
ここからは、Terraformを使って実際にNAT Gatewayを構築してみましょう。
main.tf
にリソースを集約した場合、以下のようになります。
# main.tf
locals {
vpc = {
cidr_block = "10.0.0.0/16"
subnet_cidr = "10.0.10.0/24"
subnet_cidr_private = "10.0.20.0/24"
}
dev_ec2 = {
prefix = "dev-ec2"
ami_id = data.aws_ami.amazonlinux2_latest.id # amzn2-ami-hvm-2.0.20231116.0-x86_64-gp2
instance_type = "t3.nano"
root_volume_type = "gp3"
root_volume_size = 30
}
}
# AMIs
data "aws_ami" "amazonlinux2_latest" {
owners = ["amazon"]
most_recent = true
filter {
name = "name"
values = ["amzn2-ami-hvm-2.0.*.0-x86_64-gp2"]
}
}
# EC2へAmazonSSMManagedInstanceCoreをアタッチするためのassumerole
data "aws_iam_policy_document" "assumerole_ec2" {
statement {
actions = ["sts:AssumeRole"]
principals {
type = "Service"
identifiers = ["ec2.amazonaws.com"]
}
}
}
# VPC
resource "aws_vpc" "main" {
cidr_block = local.vpc.cidr_block
enable_dns_support = true
enable_dns_hostnames = true
tags = {
Name = "main"
}
}
# Subnet
resource "aws_subnet" "public" {
vpc_id = aws_vpc.main.id
cidr_block = local.vpc.subnet_cidr
availability_zone = "ap-northeast-1a"
map_public_ip_on_launch = true
tags = {
Name = "public"
}
}
resource "aws_subnet" "private" {
vpc_id = aws_vpc.main.id
cidr_block = local.vpc.subnet_cidr_private
availability_zone = "ap-northeast-1a"
tags = {
Name = "private"
}
}
# RouteTable
resource "aws_route_table" "public" {
vpc_id = aws_vpc.main.id
tags = {
Name = "public"
}
}
# RouteTableの関連付け
resource "aws_route_table_association" "public" {
subnet_id = aws_subnet.public.id
route_table_id = aws_route_table.public.id
}
# InternetGateway
resource "aws_internet_gateway" "igw" {
vpc_id = aws_vpc.main.id
tags = {
Name = "igw"
}
}
# Route
resource "aws_route" "public" {
route_table_id = aws_route_table.public.id
destination_cidr_block = "0.0.0.0/0"
gateway_id = aws_internet_gateway.igw.id
}
# Private subnetのRouteTable
resource "aws_route_table" "private" {
vpc_id = aws_vpc.main.id
tags = {
Name = "private"
}
}
# Private subnetのRouteTable関連付け
resource "aws_route_table_association" "private" {
subnet_id = aws_subnet.private.id
route_table_id = aws_route_table.private.id
}
# Private subnetのRoute
resource "aws_route" "private" {
route_table_id = aws_route_table.private.id
destination_cidr_block = "0.0.0.0/0"
nat_gateway_id = aws_nat_gateway.nat_gateway.id
}
# Elastic IP
resource "aws_eip" "nat" {
vpc = true
tags = {
Name = "nat-eip"
}
}
# NAT Gateway
resource "aws_nat_gateway" "nat_gateway" {
allocation_id = aws_eip.nat.id
subnet_id = aws_subnet.public.id
tags = {
Name = "nat-gateway"
}
depends_on = [aws_eip.nat]
}
# EC2インスタンス
resource "aws_instance" "dev_ec2" {
ami = local.dev_ec2.ami_id
instance_type = local.dev_ec2.instance_type
iam_instance_profile = aws_iam_instance_profile.dev_ec2.name
subnet_id = aws_subnet.private.id
vpc_security_group_ids = [
aws_security_group.dev_ec2.id
]
root_block_device {
volume_type = local.dev_ec2.root_volume_type
volume_size = local.dev_ec2.root_volume_size
}
tags = {
Name = local.dev_ec2.prefix
}
}
# SecurityGroup
resource "aws_security_group" "dev_ec2" {
name = "${local.dev_ec2.prefix}-sg"
description = "${local.dev_ec2.prefix}-sg"
vpc_id = aws_vpc.main.id
tags = {
Name = "${local.dev_ec2.prefix}-sg"
}
}
# アウトバウンドのみ定義
resource "aws_security_group_rule" "dev_ec2_rule_egress" {
type = "egress"
from_port = 0
to_port = 0
protocol = -1
cidr_blocks = ["0.0.0.0/0"]
security_group_id = aws_security_group.dev_ec2.id
}
# InstanceProfile
resource "aws_iam_instance_profile" "dev_ec2" {
name = local.dev_ec2.prefix
role = aws_iam_role.dev_ec2.name
}
# IAM Role
resource "aws_iam_role" "dev_ec2" {
name = local.dev_ec2.prefix
path = "/"
assume_role_policy = data.aws_iam_policy_document.assumerole_ec2.json
}
# IAM Role Policy Attachment
resource "aws_iam_role_policy_attachment" "dev_ec2" {
role = aws_iam_role.dev_ec2.name
policy_arn = "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore"
}
# インスタンスIDを取得する
output "ec2_instance_id" {
value = aws_instance.dev_ec2.id
}
Elastic IPの取得
Elastic IPをTerraformを使って取得します。
# Elastic IP
resource "aws_eip" "nat" {
vpc = true
tags = {
Name = "nat-eip"
}
}
ここで取得したElastic IPを、NAT Gatewayに関連付けます。
# NAT Gateway
resource "aws_nat_gateway" "nat_gateway" {
allocation_id = aws_eip.nat.id
subnet_id = aws_subnet.public.id
tags = {
Name = "nat-gateway"
}
depends_on = [aws_eip.nat]
}
allocation_id
で、NAT Gatewayに関連付けるElastic IPのIDを指定します。
subnet_id
では、NAT Gatewayを配置するサブネットを指定します。
depends_on
で、Elastic IPを作成された後にNAT Gatewayを作成するよう指定しています。
plan
で作成するリソースを確認し、apply
でリソースを作成します。
terraform plan
terraform apply
NAT Gatewayからの通信を確認する
リソース作成後、NAT GatewayのElastic IPを確認します。
terraform state show
でリソースの詳細を確認し、表示された内容のpublic_ip
がElastic IPになります。
# 確認コマンド
terraform state show aws_nat_gateway.nat_gateway
# aws_nat_gateway.nat_gateway:
resource "aws_nat_gateway" "nat_gateway" {
allocation_id = "eipalloc-0993aeeb1a58a0cae"
association_id = "eipassoc-084725d2d12dd2f63"
connectivity_type = "public"
id = "nat-0fb4f96f52c2aafb6"
network_interface_id = "eni-0145433da74f94d0d"
private_ip = "10.0.10.12"
public_ip = "52.195.125.184" # NAT GatewayのElastic IP
secondary_private_ip_address_count = 0
secondary_private_ip_addresses = []
subnet_id = "subnet-0a5014a7b4b37ff49"
tags = {
"Name" = "nat-gateway"
}
tags_all = {
"Name" = "nat-gateway"
}
}
apply時にoutputで取得したインスタンスIDを使用して、SSMでEC2へ接続します。
aws ssm start-session --target <instance-id>
インスタンスから、外部に接続できるかpingコマンドを実行します。
ping 8.8.8.8
PING 8.8.8.8 (8.8.8.8) 56(84) bytes of data.
64 bytes from 8.8.8.8: icmp_seq=1 ttl=116 time=3.31 ms
64 bytes from 8.8.8.8: icmp_seq=2 ttl=116 time=2.91 ms
64 bytes from 8.8.8.8: icmp_seq=3 ttl=116 time=2.88 ms
64 bytes from 8.8.8.8: icmp_seq=4 ttl=116 time=2.92 ms
EC2インスタンスから、外部へ接続できることを確認できれば完了です。
まとめ
この記事では、NAT Gatewayの基本の解説と、Terraformを使用してNAT Gatewayを作成する方法を解説しました。NAT Gatewayは、AWSにおけるセキュリティ対策の一環として、プライベートIPアドレスを持つリソースがインターネットにアクセスする際の重要な役割を果たします。
意識しておきたい点として、NAT Gatewayの料金は配置しているだけで料金が発生することです。無駄なコストが発生しないよう、リソース作成後不要になった場合は削除するように注意しましょう。
https://docs.aws.amazon.com/ja_jp/vpc/latest/userguide/nat-gateway-pricing.html
【番外編】USBも知らなかった私が独学でプログラミングを勉強してGAFAに入社するまでの話
プログラミング塾に半年通えば、一人前になれると思っているあなた。それ、勘違いですよ。「なぜ間違いなの?」「正しい勉強法とは何なの?」ITを学び始める全ての人に知って欲しい。そう思って書きました。是非読んでみてください。
「フリーランスエンジニア」
近年やっと世間に浸透した言葉だ。ひと昔まえ、終身雇用は当たり前で、大企業に就職することは一種のステータスだった。しかし、そんな時代も終わり「優秀な人材は転職する」ことが当たり前の時代となる。フリーランスエンジニアに高価値が付く現在、ネットを見ると「未経験でも年収400万以上」などと書いてある。これに釣られて、多くの人がフリーランスになろうとITの世界に入ってきている。私もその中の1人だ。数年前、USBも知らない状態からITの世界に没入し、そこから約2年間、毎日勉学を行なった。他人の何十倍も努力した。そして、企業研修やIT塾で数多くの受講生の指導経験も得た。そこで私は、伸びるエンジニアとそうでないエンジニアをたくさん見てきた。そして、稼げるエンジニア、稼げないエンジニアを見てきた。
「成功する人とそうでない人の違いは何か?」
私が出した答えは、「量産型エンジニアか否か」である。今のエンジニア市場には、量産型エンジニアが溢れている!!ここでの量産型エンジニアの定義は以下の通りである。
比較的簡単に学習可能なWebフレームワーク(WordPress, Rails)やPython等の知識はあるが、ITの基本概念を理解していないため、単調な作業しかこなすことができないエンジニアのこと。
多くの人がフリーランスエンジニアを目指す時代に中途半端な知識や技術力でこの世界に飛び込むと返って過酷な労働条件で働くことになる。そこで、エンジニアを目指すあなたがどう学習していくべきかを私の経験を交えて書こうと思った。続きはこちらから、、、、
エンベーダー編集部
エンベーダーは、ITスクールRareTECHのインフラ学習教材として誕生しました。 「遊びながらインフラエンジニアへ」をコンセプトに、インフラへの学習ハードルを下げるツールとして運営されています。
関連記事
2020.02.25
完全未経験からエンジニアを目指す爆速勉強法
USBも知らなかった私が独学でプログラミングを勉強してGAFAに入社するまでの話
- キャリア・学習法
- エンジニア
2023.11.21
【Part 1/4】AWSの環境構築で学ぶトラブルシューティング【VPC Peering】
今回は、異なるVPCにいるインスタンス間同士の通信を行える環境を構築します。VPCピアリングを用いて、インターネットを介さない安全な通信を実現する方法を学習します。
- AWS
2024.09.30
【Terraformハンズオン】LambdaとGatewayエンドポイントを使ってS3にファイルを配置しよう
この記事では、Gatewayエンドポイントについて解説し、Terraformを使用してプライベートサブネットにあるLambda関数から、S3バケットにファイルをアップロードするハンズオンを行います。
- Terraform
- AWS
2024.10.19
【Terraformハンズオン】Terraformでモジュールを作成してみよう
この記事では、Terraformのモジュールに焦点を当て、記事前半で基本を解説し、後半でEC2、VPCモジュールを作成するハンズオンを行います。
- AWS
- Terraform
- ハンズオン