皆さん、こんにちは!AWS/GCPといったメガクラウドの深い森を日々冒険しているSRE兼クラウドアーキテクトの〇〇です。
今日は皆さんと一緒に、クラウドのネットワークの世界を少し覗いてみましょう。特に、普段は影の立役者として頑張ってくれている「NATゲートウェイ」にスポットライトを当てます。そして、もし彼が「もう無理!」と悲鳴を上げたとき、私たちがどうやってその原因を突き止めるのか、そのための強力なツール「VPCフローログ」の活用術をお話ししたいと思います。
「パケット?フローログ?なんだか難しそう…」と感じた方もご安心ください!小難しい専門用語は極力避け、まるで郵便配達の流れや身近な仕組みに例えながら、一歩ずつ丁寧に紐解いていきますよ。さあ、一緒にクラウドネットワークの謎を解き明かしましょう!
—
郵便局の頼れる窓口!NATゲートウェイって、いったい何者?
まずは、今回の主役であるNATゲートウェイ(以下、NAT GW)が、私たちのクラウド環境でどんな役割を果たしているのかを見ていきましょう。
皆さんの会社のオフィスを想像してみてください。オフィスの中には、従業員さんたちがそれぞれ「内線番号」を持っていますよね?この内線番号は、オフィスの中でしか通じない、いわば「プライベートな住所」です。
そして、従業員さんがインターネット上のサービス(例えば、外部のウェブサイトやSaaSサービス)を利用したいとき、直接自分の内線番号を使って通信するわけではありませんよね?
多くの場合、会社の代表電話番号や、特定のプロキシサーバーを通じてインターネットに接続します。
AWSのVPC(Virtual Private Cloud)の世界もこれと似ています。
プライベートサブネットとインターネットの橋渡し役
VPCの中には、インターネットから直接アクセスできないプライベートサブネットという安全な区画があります。ここに、データベースサーバーやアプリケーションサーバーなど、外から直接触られたくない大事なリソースを配置します。
しかし、これらのプライベートなリソースも、時にはソフトウェアのアップデートのためにインターネット上のリポジトリにアクセスしたり、外部のAPIを呼び出したりする必要がありますよね?
そこで登場するのが、私たちのNAT GWです!
NAT GWは、プライベートサブネットにいるサーバーがインターネットへ出ていくときの「会社の代表電話番号」や「郵便局の窓口」のような役割を果たします。
- プライベートな住所(内線番号)しか持たないサーバーが、
- インターネット(外部)へ通信したいとき、
- NAT GWが、自分の持つパブリックな住所(会社の代表電話番号)に送り主の情報を書き換えて、通信をインターネットに送り出してくれます。
この、「送り主の住所を書き換える」仕組みこそが、SNAT(Source Network Address Translation、送信元NAT)と呼ばれるものです。まるで、個人名で書かれた手紙を、郵便局が「差出人:〇〇郵便局」と書き換えて送り出すようなイメージですね。こうすることで、インターネット側からは「〇〇郵便局から手紙が来た」と認識され、プライベートなサーバーの存在を直接知られることなく、安全に通信ができるようになるんです。
NAT GWの安心設計
NAT GWはAWSがマネージドサービスとして提供しているため、可用性(サービスが止まらないこと)やスケーラビリティ(負荷に応じて性能が上がること)は非常に高いレベルで確保されています。通常はほとんど意識することなく、私たちの代わりにインターネットとの橋渡し役をしっかり務めてくれている、本当に頼りになる存在なんです。
困った!NATゲートウェイが「枯渇」しちゃうって、どういうこと?
さて、NAT GWは頼りになりますが、どんなサービスにも得意・不得意や限界があります。そして、稀に、彼が「もう無理!」と悲鳴を上げる状況が発生することがあります。それが、今回のテーマであるSNAT枯渇です。
郵便局の窓口がパンクする?!SNAT枯渇の正体
先ほどの郵便局の例えを思い出してみましょう。NAT GWは、プライベートなサーバーからの通信を、自分の持つパブリックなIPアドレスを使ってインターネットに送り出す「窓口」のようなものでした。
この窓口、実は同時に処理できる通信の数に限りがあります。正確には、ポート番号という「通信の種類を識別する番号」と、宛先のIPアドレスやポート番号を組み合わせた「通信セッション」の数が有限なんです。
例えば、郵便局の窓口が5万個あったとして、同時に5万通の手紙を送り出すことはできます。でも、もし一斉に10万通、20万通の手紙が押し寄せたらどうなるでしょうか?窓口はパンクしてしまい、多くは「処理できませんでした」「受付拒否」となってしまいますよね。
これがSNAT枯渇のイメージです。
- プライベートサブネット内のサーバー群が、
- 同時多発的に、
- 大量の新規インターネット通信を、
- NAT GW経由で行おうとしたとき、
- NAT GWが処理しきれるセッション数を超えてしまい、
- 新たな通信が
REJECT(拒否)される状態になります。
枯渇すると何が起きる?
もしNAT GWが枯渇してしまうと、以下のような困った事態が発生します。
- アプリケーションの障害: インターネット上のAPIに接続できなくなる、外部の認証サービスにアクセスできなくなる、ソフトウェアのアップデートが失敗するなど。
- レスポンス遅延: 枯渇が一時的に発生し、再試行で通信が通ったとしても、処理が遅れてユーザー体験が悪化します。
- システムの不安定化: 予期せぬ通信エラーがアプリケーション全体に波及し、システムが不安定になる原因にもなりかねません。
このような状況は、私たちのサービス提供に大きな影響を与えてしまいますよね。だからこそ、枯渇の原因を特定し、適切に対処することが非常に重要になるんです。
枯渇の原因を探る!VPCフローログが名探偵になってくれる?!
「枯渇してるっぽいけど、何が原因なんだろう?」
こんな時、私たちはどうやって原因を突き止めるのでしょうか?そこで登場するのが、今回のキーアイテム、VPCフローログです!
VPCフローログは通信の「宅配便の伝票」
VPCフローログとは、VPC内のネットワークインターフェースを通過するIPトラフィックに関する情報をキャプチャし、記録してくれるサービスです。
これを、「宅配便の伝票」や「電話の通話記録」のようなものだと考えてみてください。
- 誰が(送信元IPアドレス)
- どこへ(宛先IPアドレス)
- 何を(送信元/宛先ポート番号)
- どれくらいの量(バイト数)
- いつ(タイムスタンプ)
- どうしたか(
ACCEPTされたのか、REJECTされたのか)
といった情報が、一つ一つの通信(フロー)ごとに記録されていくんです。
このフローログは、Amazon S3バケットに保存されるのが一般的です。そして、保存された大量のログデータの中から、私たちが知りたい情報を効率的に検索・分析するために、Amazon Athenaのようなクエリサービスが非常に役立ちます。
REJECTされたトラフィックが重要!
NAT GWのSNAT枯渇という観点では、特に注目すべきはREJECTされたトラフィックです。
なぜなら、NAT GWが処理しきれずに拒否した通信こそが、枯渇の直接的な証拠だからです。
フローログからREJECTされた通信を抽出し、その通信が「どのサーバーから」「どの宛先へ」「どれくらいの頻度で」行われていたのかを分析することで、枯渇の真犯人を見つけ出すことができる、というわけですね!
さあ、いよいよ実践編です。VPCフローログをAmazon Athenaで分析してみましょう!
実践!VPCフローログをAmazon Athenaで分析してみよう!
VPCフローログは、CloudWatch LogsやS3に出力できますが、今回はAmazon Athenaで分析することを前提に、S3に出力されているものとします。
(もしまだ設定していなければ、VPCフローログの作成時に「S3バケットへ送信」を選択して設定してくださいね。)
Amazon Athenaの準備:データベースとテーブルを作成しよう
Amazon Athenaを使うには、まずS3に保存されたフローログのデータをSQLで扱えるように、「データベース」と「テーブル」を作成する必要があります。
AWSマネジメントコンソールでAthenaのサービスを開き、クエリエディタの画面に進んでください。
1. データベースの作成
まずはデータベースを作成します。これは、テーブルをまとめておく箱のようなものです。
-- データベースがまだ存在しない場合のみ作成します
CREATE DATABASE IF NOT EXISTS vpc_flow_logs_db;
このクエリを実行すると、vpc_flow_logs_dbという名前のデータベースが作成されます。
2. フローログ用のテーブルを作成
次に、S3バケットにあるフローログのデータを参照するためのテーブルを作成します。
LOCATIONには、あなたのVPCフローログが保存されているS3バケットのパスを指定してください。
-- VPCフローログ用のテーブルを作成します
-- 'vpc_flow_logs_db'データベース内に'flow_logs_table'という名前で作成
CREATE EXTERNAL TABLE IF NOT EXISTS vpc_flow_logs_db.flow_logs_table (
version INT, -- フローログのバージョン
account_id STRING, -- AWSアカウントID
interface_id STRING, -- ネットワークインターフェースID
srcaddr STRING, -- 送信元IPアドレス
dstaddr STRING, -- 宛先IPアドレス
srcport INT, -- 送信元ポート番号
dstport INT, -- 宛先ポート番号
protocol INT, -- IPプロトコル番号
packets BIGINT, -- 転送されたパケット数
bytes BIGINT, -- 転送されたバイト数
start_time BIGINT, -- フローの開始時間 (Unixエポック形式)
end_time BIGINT, -- フローの終了時間 (Unixエポック形式)
action STRING, -- フローの処理 ('ACCEPT'または'REJECT')
log_status STRING, -- ログの書き込みステータス ('OK'または'NODATA'または'SKIPDATA')
vpc_id STRING, -- VPC ID
subnet_id STRING, -- サブネット ID
instance_id STRING, -- インスタンス ID
tcp_flags INT, -- TCPフラグ
type STRING, -- フローログのタイプ ('IPv4'または'IPv6')
pkt_srcaddr STRING, -- パケットの送信元IPアドレス
pkt_dstaddr STRING -- パケットの宛先IPアドレス
)
ROW FORMAT DELIMITED
FIELDS TERMINATED BY ' ' -- 各フィールドがスペースで区切られていることを指定
LOCATION 's3://your-flow-log-bucket/AWSLogs/your-account-id/vpcflowlogs/your-region/'; -- あなたのS3バケットのパスに置き換えてください!
ポイント:
LOCATIONのパスは、フローログが保存されているS3バケットの具体的なパスに合わせてください。通常、AWSLogs/your-account-id/vpcflowlogs/your-region/のような階層になっています。- フローログのバージョンによってはカラムが異なる場合があります。上記の例は一般的な形式ですが、もしエラーが出る場合は公式ドキュメントを参照し、ご自身のログ形式に合わせて調整してくださいね。
これで、Amazon AthenaからS3上のフローログデータをSQLクエリで分析する準備が整いました!
枯渇原因特定のためのクエリ例:REJECTされたトラフィックを分析する!
いよいよ本丸です。REJECTされた通信の中から、SNAT枯渇の原因を探るためのクエリを実行してみましょう。
私たちは、「どの送信元IPアドレス(サーバー)が、どれくらいの頻度で、どこへの通信をNAT GWに拒否されたのか」を知りたいですよね。
-- NATゲートウェイ経由でREJECTされた通信を特定し、送信元と宛先、ポートで集計します
SELECT
FROM_UNIXTIME(start_time) AS flow_start_datetime, -- フローの開始時刻を読みやすい形式に変換
srcaddr, -- 送信元IPアドレス
srcport, -- 送信元ポート
dstaddr, -- 宛先IPアドレス
dstport, -- 宛先ポート
COUNT(*) AS reject_count, -- 拒否されたフローの数
SUM(bytes) AS total_bytes -- 拒否されたフローの合計バイト数
FROM
vpc_flow_logs_db.flow_logs_table
WHERE
action = 'REJECT' -- アクションが'REJECT'のフローのみを抽出
AND interface_id IN (
SELECT DISTINCT interface_id -- NAT GWに関連するネットワークインターフェースIDを絞り込む
FROM vpc_flow_logs_db.flow_logs_table
WHERE dstaddr LIKE '10.%' OR dstaddr LIKE '172.16.%' OR dstaddr LIKE '192.168.%' -- プライベートIP宛ての通信は除外(NAT GWはインターネット向け)
AND NOT (srcaddr LIKE '10.%' OR srcaddr LIKE '172.16.%' OR srcaddr LIKE '192.168.%') -- フローログの送信元がNAT GWのパブリックIPの場合
AND interface_id LIKE 'eni-%' -- 通常のEC2 ENI形式
-- ここに、もし特定のNAT GWのENI IDが分かっていれば、直接指定することもできます
-- 例: AND interface_id = 'eni-xxxxxxxxxxxxxxxxx'
)
-- 必要に応じて期間を絞り込む(例: 過去1日分のデータ)
AND FROM_UNIXTIME(start_time) >= DATE_ADD('day', -1, CURRENT_TIMESTAMP)
GROUP BY
FROM_UNIXTIME(start_time), -- 拒否された時刻ごと
srcaddr, -- 送信元IPアドレスごと
srcport, -- 送信元ポートごと
dstaddr, -- 宛先IPアドレスごと
dstport -- 宛先ポートごと
ORDER BY
reject_count DESC -- 拒否された数が多い順にソート
LIMIT 100; -- 上位100件を表示
クエリの解説とポイント:
1. action = 'REJECT': これが最も重要です。NAT GWが通信を拒否した記録だけを抽出します。
2. interface_id IN (...): NAT GWは内部的にネットワークインターフェース(ENI)を持っています。このサブクエリで、インターネットに出ようとする通信が記録されているENIを絞り込みます。
dstaddr LIKE '10.%' ...の部分は、VPC内部の通信(Private IP宛て)を除外し、インターネット向けの通信を対象にするためのヒントです。NAT GWは主にインターネットへの出口なので、この絞り込みが有効です。- より正確には、NAT GWが持つENIのIDを直接指定できるとベストです。AWSのマネジメントコンソールでNAT GWの詳細を確認すると、関連するENIのIDが分かります。
3. FROM_UNIXTIME(start_time): start_timeはUnixエポック形式なので、人間が読みやすい日時形式に変換しています。
4. GROUP BY srcaddr, srcport, dstaddr, dstport: 同じ送信元IP、送信元ポート、宛先IP、宛先ポートの組み合わせで拒否された通信をまとめています。
5. COUNT(*) AS reject_count: この組み合わせで何回拒否されたのかをカウントします。これが多ければ多いほど、その通信が枯渇の主な原因である可能性が高い、ということになりますね!
6. SUM(bytes) AS total_bytes: 拒否された通信の合計バイト数も見ておくと、データ量の観点からも分析できます。
このクエリを実行すると、どのサーバー(srcaddr)が、どの宛先(dstaddr:dstport)に対して、どれくらいの数の通信をNAT GWに拒否されたのか、一目瞭然で分かります。
分析結果から見えてくるもの、そして対策へ
Athenaのクエリ結果を見て、reject_countが特に多い行に注目してください。そこから、以下のような原因が見えてくることがあります。
SNAT枯渇のよくある原因
1. 想定外の大量通信:
- デバッグ用のログ出力が多すぎる、あるいは無限ループに陥ったアプリケーションが外部に大量のリクエストを送り続けている。
- 開発中のアプリケーションがテスト環境から本番環境の外部サービスに誤ってアクセスを試みている。
- マルウェア感染やセキュリティスキャンのような不正な通信。
- EC2インスタンスが起動スクリプトなどで、外部の大きなファイルを何度もダウンロードしようとしている。
2. 不適切なKeep-alive設定:
- HTTPなどのプロトコルで、セッションを使い捨てにせず再利用する
Keep-aliveが適切に設定されていないと、短時間に大量の新規セッションを張ってしまい枯渇を招くことがあります。
3. DNSリゾルバの過負荷:
- 名前解決(DNSクエリ)もNAT GWを経由することがあるため、多数のインスタンスが一斉に名前解決を行うと、SNATポートを消費し枯渇の一因となることがあります。
4. アプリケーションの設計ミス:
- 外部サービスへの接続時にタイムアウト設定が短すぎる、あるいは再試行ロジックが多すぎるために、不必要な通信が頻発している。
具体的な対策例
原因が特定できたら、それに応じた対策を講じましょう。
1. 問題のインスタンスを特定し、アプリケーションを修正:
srcaddrから問題のEC2インスタンスを特定し、ログを確認したり、アプリケーションコードを見直したりします。不要な通信を停止したり、適切なKeep-alive設定を導入したりします。
2. NAT GWのスケールアウト(IPアドレスの追加):
- 複数のNAT GWをデプロイし、それぞれを異なるアベイラビリティゾーン(AZ)に配置します。サブネットのルーティングテーブルを適切に設定し、通信を分散させます。NAT GWはAZごとに持つパブリックIPアドレスが異なるため、これがポート枯渇対策になります。
- ただし、この対応はNAT GWの利用料金も増加するため、慎重な検討が必要です。
3. プロキシサーバーの導入:
- EC2インスタンス群からのHTTP/HTTPS通信を、
Squidなどのプロキシサーバー経由にする方法です。プロキシサーバーが単一のエンドポイントとしてNAT GWと通信するため、NAT GW側のポート消費を抑えられます。 - ただし、プロキシサーバー自体の運用管理コストが発生します。
4. プライベート接続の活用:
- もしアクセス先がAWSのサービスであれば、
VPCエンドポイント(Gateway EndpointやInterface Endpoint)を利用することで、NAT GWを経由せずにAWSの内部ネットワークで通信を完結させることができます。これにより、NAT GWの負荷を大幅に軽減できます。
5. 不要な通信の抑制:
- セキュリティグループやネットワークACL(NACL)で、不要な宛先への通信をブロックします。例えば、特定の開発環境からのみアクセスを許可するなど。
—
まとめ:今日の学びを活かして、安定したネットワークを!
今日は、普段はあまり意識しないけれど、私たちのクラウドサービスを支える重要なインフラ「NATゲートウェイ」について、そして彼が「SNAT枯渇」というピンチに陥ったときに、私たちがどうやって名探偵「VPCフローログ」と一緒に原因を突き止め、解決に導くのかを学びました。
- NATゲートウェイは、プライベートサブネットからインターネットへの安全な出口を提供してくれる頼れる存在でしたね。
- しかし、SNAT枯渇という、同時に処理できる通信数の限界を超えてしまうと、サービスに大きな影響が出ることが分かりました。
- そして、その原因究明には、通信の記録簿であるVPCフローログをAmazon Athenaで分析し、
REJECTされた通信に注目することが非常に有効でした。
クラウドのネットワークは、一見すると複雑に見えるかもしれませんが、一つ一つの要素がどんな役割を果たしているのか、そしてどのように連携しているのかを理解すれば、決して怖いものではありません。
今回ご紹介した手法は、NAT GWのトラブルシューティングだけでなく、ネットワーク全体の挙動を理解し、セキュリティを強化するためにも非常に強力なツールとなります。
ぜひ皆さんも、ご自身のAWS環境でVPCフローログを設定し、Athenaでクエリを実行してみてください。きっと、今まで見えなかったネットワークの裏側が見えてくるはずです。
これからも、皆さんがクラウドの旅路で出会う「困った!」を「なるほど!」に変えられるような、現場で役立つ実践的な情報をお届けしていきますので、どうぞお楽しみに!
それでは、また次の記事でお会いしましょう!
コメント