Amazon Linux 2 版 NAT インスタンスの構築方法

記事タイトルとURLをコピーする

みなさん、こんにちは。AWS CLI が好きなテクニカルサポート課の市野です。

さて、再現性などの検証目的で、新たな VPC 自体の準備をして検証に臨むことが多々あるのですが、リソースの消し忘れや検証の長期化で高額になりがちな NAT ゲートウェイの課金問題を回避したく、NAT インスタンスの構築が簡単にできたらいいのに、とつねづね思っていました。

ただ、NAT インスタンスの構築のための AMI は Amazon Linux の頃のままで止まっており、できれば古い AMI は使いたくないなということで、なるべく最新の Amazon Linux 2 を利用しつつ、いつかの自分が楽になるように、手順をまとめてみました。

手順

以下の手順は、Cloudshell で実行する想定をしています。

事前準備:最新の Amazon Linux 2 の AMI ID の取得

以下のコマンドで、実行時点での最新版の Amazon Linux 2 の AMI ID の取得をしています。

AMI_ID=$(aws ec2 describe-images \
  --owners amazon \
  --filters "Name=architecture,Values=x86_64" \
            "Name=name,Values=amzn2-ami-kernel-*" \
  --query 'Images[].[Name, ImageId]' --output text | sort | tail -n 1 | awk -F ' ' '{print $2}') \
  && echo ${AMI_ID}

NAT インスタンス用 EC2インスタンスの構築

userdata の作成と内容確認

NAT 処理の実現は iptables で行うため、iptables のインストールと NAT のための設定、そして iptables の永続化の処理をするための userdata を作成する。

USERDATA=${HOME}/userdata.txt

cat << EOF > ${USERDATA}
#!/bin/sh
yum -y update
yum -y upgrade
yum -y install iptables-services

iptables -L
iptables -F

echo 1 > /proc/sys/net/ipv4/ip_forward
echo net.ipv4.ip_forward=1 >> /etc/sysctl.conf

iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
iptables -t nat -L
service iptables save

systemctl enable iptables --now
EOF

cat ${USERDATA}

NAT インスタンス用 EC2インスタンスの作成

変数の設定

インスタンスタイプやキーペアなどの情報は、環境に応じて適宜設定します。

INSTANCE_TYPE="t2.micro"
KEY_NAME="test-key"
SUBNET_ID="subnet-xxxxxxxxxxxxxxxxx"
TAG_VALUE="NAT-Instance"
構築処理の実施

なお、本手順では該当の NAT インスタンスに対するセキュリティグループの設定を割愛していますが、正確には、以下の通信が行える必要があります。

  • インバウンド:該当の NAT インスタンスを経由するインスタンスからのすべてのトラフィック
  • アウトバウンド:0.0.0.0/0 へのすべてのトラフィック
aws ec2 run-instances \
  --instance-type ${INSTANCE_TYPE} \
  --key-name ${KEY_NAME} \
  --subnet-id ${SUBNET_ID} \
  --user-data file://${USERDATA} \
  --tag-specifications "ResourceType=instance,Tags=[{Key=Name,Value=${TAG_VALUE}}]" \
  --associate-public-ip-address \
  --image-id ${AMI_ID}

NAT インスタンスの ENI の送信先チェックの無効化

NAT インスタンスのインスタンス ID の取得

INSTANCE_ID=$(aws ec2 describe-instances \
  --filters "Name=tag-value,Values=NAT-Instance" \
  --query "Reservations[].Instances[].[LaunchTime, InstanceId]" \
  --output text | sort | tail -n 1 | awk -F ' ' '{print $2}') \
  && echo ${INSTANCE_ID}

【注意!】
一応、tag の値に重複があった場合に備え、tag の値で取得してソートした結果を取得していますが、状況に応じて適宜調整が必要です。

NAT インスタンスの ENI の送信先チェックの無効化

aws ec2 modify-instance-attribute \
  --no-source-dest-check \
  --instance-id ${INSTANCE_ID}

プライベートサブネット側 ルートテーブルへの反映

変数の設定

ROUTE_TABLE_ID="rtb-xxxxxxxxxxxxxxxxx"
DESTINATION_CIDR_BLOCK="0.0.0.0/0"

ルートテーブルへのルートの作成

プライベートサブネット用のルートテーブルには、インターネット向けのルート設定が存在しない前提で、create-route サブコマンドの実行を行なっています。

aws ec2 create-route \
  --route-table-id ${ROUTE_TABLE_ID} \
  --destination-cidr-block ${DESTINATION_CIDR_BLOCK} \
  --instance-id ${INSTANCE_ID}

【注意!】
なお、前述の通り、既存のインターネット向けのルートが存在しない前提のコマンドとなっています。
既存のルートがある場合は、replace-route サブコマンドの利用への切り替えの検討が必要です。

終わりに

上記の通り、やっていることは複雑ではありませんが、手順の内容は、あくまで検証用途として必要最低限 NAT インスタンスとして動作すれば良い、という観点で作成しています。

本番環境ではもちろん NAT ゲートウェイのご利用が望ましいことは言うまでもありませんが、検証用途や一時利用でない目的で NAT インスタンスのご利用を検討される場合は、ご留意ください。

市野 和明 (記事一覧)

マネージドサービス部・テクニカルサポート課

お客様から寄せられたご質問や技術検証を通じて得られた気づきを投稿していきます。

情シスだった前職までの経験で、UI がコロコロ変わる AWS においては GUI で手順を残していると画面構成が変わってしまって後々まごつくことが多かった経験から、極力変わりにくい AWS CLI での記事が多めです。

X(Twitter):@kazzpapa3(AWS Community Builder)