InfraAcademy

InfraAcademy Blog

その通信、どこで止まっていますか? ping・ss・tcpdumpで疎通トラブルを層ごとに切り分ける

| #Linux #ネットワーク #トラブルシューティング
Linuxをブラウザで試してみる

Linux・ネットワーク・AWSを、環境構築なしで実践学習できます

こんにちは、インフラエンジニアのryuです。

「サーバーにつながりません」という一報が飛んできたとき、最初に何を確認しているでしょうか。

とりあえず ping を打つ、という人は多いと思います。私も昔はそうでした。ただ、ping が返ってきたところで安心はできませんし、返ってこなくても障害とは限りません。

つながらないという言葉は、実はかなり大ざっぱです。名前が引けていないのか、パケットが届いていないのか、届いているけれど受け取る相手がいないのか。全部ひっくるめて、つながらないと表現されます。

だから、切り分けの一歩目は原因を探すことではありません。いま止まっているのが、どの段階なのかを特定することです。ここさえ決まれば、次に打つコマンドは自動的に決まります。

今日は、疎通トラブルを層ごとに切り分けていく順番の話をします。個々のコマンドの使い方より、どこで何を見て、どう分岐するかという流れに重心を置きます。

なぜ順番にこだわるのかというと、経験がある人ほど勘で飛ばしたくなるからです。過去に似た事象を見ていると、いきなり犯人の見当がついてしまう。当たれば速いのですが、外れたときに戻る場所を失います。

そして障害対応で時間を溶かすのは、たいてい外れたあとです。あちこち触ったせいで、何が元の状態だったのかも分からなくなる。順番を決めておくのは、当たりを速くするためではなく、外れたときに損をしないためのものだと思っています。

「つながらない」は、少なくとも4種類ある

まず、症状を分けるところから始めます。利用者からの報告をそのまま受け取らず、実際に手元で再現して、どんな止まり方をするかを見ます。

見るポイントは、返ってくるまでの時間と、返ってくる言葉です。この2つで、疑うべき場所がかなり絞れます。

症状 よくある返り方 疑う場所
名前が引けない 名前解決の失敗がすぐ返る DNS、/etc/hostsresolv.conf
数十秒待たされて失敗 タイムアウト 経路、ファイアウォールの破棄、戻りの経路
一瞬で拒否される 接続拒否 サービスが起動していない、待ち受けアドレス違い
つながるが途中で固まる 最初だけ応答して停止 MTU、経路上の機器、アプリ側の詰まり

この表で大事なのは、2行目と3行目の違いです。同じ「つながらない」でも、待たされるのか即座に断られるのかで、原因はまったく別のところにあります。

待たされるのか、断られるのか

なぜこの2つが分かれるのか、仕組みから押さえておくと忘れません。

TCPの接続は、SYNを送り、SYN/ACKが返り、ACKを返す三段階で始まります。いわゆる3ウェイハンドシェイクです。

このとき、相手のサーバーで誰もそのポートを待ち受けていなければ、カーネルがRSTを返します。呼び出した側は即座に拒否を受け取るので、待たされません。

いっぽう、途中のファイアウォールがパケットを黙って捨てている場合、返事そのものが返りません。送った側は返事を待ち続け、再送を繰り返し、やがて諦めます。これがタイムアウトです。

即座に断られたら、パケットは相手まで届いている。待たされたら、届いていないか、戻ってきていない。この一点だけで、調べる範囲が半分になります。

ちなみに iptablesREJECT は既定でICMPの到達不能を返し、--reject-with tcp-reset を付けるとTCPのRSTを返します。どちらにしても、呼び出し側にはすぐ結果が返ります。黙って捨てるのは DROP のほうです。

下から順に見ていく

症状の分類ができたら、あとは順番に確認していきます。おすすめは、通信の入口に近いところから順に上がっていく形です。飛ばすと戻ることになるので、面倒でも順番どおりに進めます。

ステップ1 そもそも名前は引けているか

最初は名前解決です。ここでつまずいていると、それより先を調べても意味がありません。

getent hosts example.internal    # OSの解決順序どおりに引く
dig example.internal +short      # DNSサーバーへ直接聞く
cat /etc/resolv.conf             # どのDNSを見にいくか
grep example /etc/hosts          # 静的な定義が残っていないか

getentdig の両方を打つ理由は、見ているものが違うからです。dig はDNSサーバーに直接問い合わせますが、getent/etc/hosts を含めたOSの解決順序をたどります。

だから、dig では正しいIPが返るのに、アプリからは古いIPに行ってしまう、という事故が起きます。犯人はたいてい /etc/hosts に残った検証時の書き置きです。

DNSの仕組みそのものが怪しいと感じたら、DNSの基本を一度おさらいしておくと、切り分けの精度が上がります。名前解決コマンドが入っていないサーバーで困ったときは、nslookupコマンドが無い場合の対処方法も参考になります。

ステップ2 パケットは相手まで行けるか

名前からIPが決まったら、そのIPまで届くかを見ます。ここで ping の出番ですが、扱いには注意が要ります。

ping -c 3 10.0.2.15
ip route get 10.0.2.15     # どの経路で出ていくか
traceroute -n 10.0.2.15    # どこまで進んでいるか

ping が返らないことを、そのまま「届いていない」と読んではいけません。ICMPだけを止めている環境はかなり多いからです。クラウドの既定のセキュリティ設定でも、ICMPは閉じていることがあります。

つまり、ping が通ればネットワークは生きていると分かりますが、通らなくても障害とは限りません。判定材料としては片側だけ有効、と覚えておくと安全です。

コマンドそのものの読み方に不安があれば、pingコマンドの使い方tracerouteコマンドの使い方を確認しておいてください。経路そのものの考え方はルーティングとは?にまとめています。

ここで見落とされがちなのが、ip route get です。実際にどのインターフェースからどのゲートウェイへ出るのかが一行で分かります。ルーティングテーブルを全部眺めるより速いので、私は先にこちらを打ちます。

ステップ3 ポートは開いていて、誰が待っているか

経路が問題なさそうなら、次はポートです。サーバー側とクライアント側の両方から見ます。

ss -ltnp                        # 待ち受けているポートとプロセス
ss -tn state syn-sent           # 接続しようとして返事待ちのもの
nc -zv 10.0.2.15 443            # 外から叩いてみる
curl -v https://example.internal/

ss -ltnp の結果でいちばん見てほしいのは、ポート番号ではなく、その左側の待ち受けアドレスです。

127.0.0.1:8080 と表示されていたら、そのサービスは自分自身からの接続しか受け付けません。外から見ると、ポートが閉じているのと同じです。設定ファイルの bind の行を確認してください。

0.0.0.0:8080*:8080 なら、すべてのインターフェースで待っています。ここが違うだけで、何時間も溶けることがあります。

そして ss -tn state syn-sent は地味に便利です。SYNを送ったまま返事が来ていない接続が並んでいれば、相手まで届いていないか、戻ってこられていない証拠になります。

止めているのは誰なのか、を分けて考える

ポートまで見て、待ち受けは正しいのに外から届かない。この段階で次に見るのは、通信を止めうる関門です。

ここでつまずく人が多いのは、関門がひとつではないからです。サーバー自身のファイアウォール、その手前のネットワーク機器、クラウドであればセキュリティグループやネットワークACL、さらにミドルウェア自身の接続許可設定。少なくとも4つの層で、それぞれ独立に拒否できます。

firewall-cmd --list-all       # firewalld を使っている場合
nft list ruleset              # nftables の全ルール
iptables -S                   # 旧来の記法で確認する場合

厄介なのは、どれか1つでも閉じていれば同じ症状になることです。サーバー側で開けたのに変わらない、という報告の裏では、たいてい別の層が閉じています。

だから、ひとつ開けて試すたびに、必ず tcpdump で「行きのパケットがサーバーまで届いたか」を見ます。届くようになったなら前進、届かないままなら、まだ手前で止められています。開けた場所と結果を1対1で確かめていくのが、遠回りに見えて最短です。

ステップ4 パケットを直接のぞく

ここまでで決まらなければ、実物を見ます。推測を重ねるより速いことが多いです。

tcpdump -nn -i any 'tcp port 443 and host 10.0.2.15'

見るのはこの3点です。SYNが相手に届いているか。SYN/ACKが返っているか。RSTが飛んでいるとしたら、どこから飛んでいるか。

出力は最初こそ呪文に見えますが、注目するのは角かっこの中だけです。

10.0.2.10.51234 > 10.0.2.15.443: Flags [S], seq 123456789
10.0.2.15.443 > 10.0.2.10.51234: Flags [S.], seq 987654321, ack 123456790
10.0.2.10.51234 > 10.0.2.15.443: Flags [.], ack 1

[S] がSYN、[S.] がSYN/ACK、その次の [.] がACKです。この3行がそろっていれば、接続は成立しています。つまり、ここから先で止まっているならネットワークの問題ではありません。

逆に、1行目だけが同じ内容で何度も並んでいたら、それは再送です。相手から何も返ってきていないので、送り側が繰り返し呼びかけている状態を見ていることになります。

[R] が見えたら拒否です。このとき、送信元のIPをよく見てください。相手サーバーのIPなら本人が断っています。まったく別のIPなら、途中の機器が代わりに断っています。原因の場所がここで確定します。

大事なのは、サーバー側とクライアント側の両方で同時に取ることです。片側だけだと、届いていないのか、戻れていないのかが区別できません。

クライアント側でSYNの再送だけが並び、サーバー側では何も見えなければ、行きの途中で捨てられています。逆に、サーバー側にSYNは届いていてSYN/ACKも出しているのに、クライアントに届いていなければ、戻りの経路の問題です。

戻りが原因のケースは、経路が非対称になっている環境で起きます。行きだけを見ていると絶対に見つからないので、両側で取る習慣をつけておくと強いです。

見落としやすい4つのパターン

最後に、実際によく出会う形をまとめておきます。切り分けの途中で、ここに当てはまらないか思い出してみてください。

パターン 見え方 確認方法
ループバックだけで待ち受け 拒否が即返る ss -ltnp の待ち受けアドレス
破棄ルールで黙殺 タイムアウト 経路上の許可設定、tcpdump で行きの到達
戻りの経路がない タイムアウト 両側同時の tcpdump
経路上のMTU不一致 接続はできるが途中で停止 大きいデータの転送で再現するか

4つ目のMTUの話は、初見だとかなり不思議に見えます。接続は成立するのに、応答が大きいときだけ固まるからです。

理由は、ハンドシェイクで飛ぶパケットが小さいからです。小さいうちは通り抜けられて、データが大きくなった瞬間に、途中の機器で通れなくなります。本来なら「大きすぎる」と教えるICMPが返るのですが、それを止めている環境では、送り側が何も知らないまま再送を続けます。

「ログインはできるのに、一覧画面だけ真っ白になる」といった症状が出たら、この線を疑ってみてください。拠点間をVPNでつないでいる環境や、トンネルを挟む構成で起きやすい話です。

確かめ方は単純で、少しずつ大きいデータを送ってみて、どのあたりから止まるかを見ます。ある大きさを境にぴたりと通らなくなるなら、まず間違いなくこの類です。

3つ目の戻りの経路の話も、初めて当たると納得しづらいところです。サーバーは受け取って返事もしているのに、その返事が別の出口から出ていって、依頼した相手に届かない。サーバー側のログには何も残らないので、両側で同時にパケットを見るまで気づけません。

こうしたパターンは、知っていれば数分、知らなければ数時間かかります。覚えておくというより、症状の形と結びつけて記憶しておくのがコツです。

「つながるけれど遅い」は、また別の切り分け

つながらないではなく、遅いという報告も同じくらい来ます。こちらは、どこで時間を使っているかを分解するところから始めます。

便利なのが curl の時間表示です。名前解決、接続、TLSの手続き、最初の1バイトが返るまで、といった内訳を出せます。

curl -o /dev/null -s -w \
  'dns:%{time_namelookup} connect:%{time_connect} tls:%{time_appconnect} first_byte:%{time_starttransfer} total:%{time_total}\n' \
  https://example.internal/

内訳を見れば、疑う相手が変わります。名前解決だけ1秒近くかかっているなら、DNSの設定か応答しないサーバーが混じっている。接続までは速いのに最初の1バイトが遅いなら、ネットワークではなくアプリケーションやデータベースの側です。

遅い、という報告をネットワークの問題として調べ続けてしまう時間は、案外ばかになりません。最初の数十秒で内訳を取っておくと、そもそも自分の担当範囲なのかがはっきりします。

数字を1回だけ取って判断しないことも大切です。時間帯や回数によって揺れるので、何回か繰り返して傾向を見ます。たまたま遅かった1回を原因だと思い込むと、そこから抜け出せなくなります。

調べた順番を、そのまま記録に残す

切り分けが終わったら、打ったコマンドと結果を時系列で残しておくことをおすすめします。

同じ障害は、形を変えてまた来ます。そのとき、前回どこで分岐したかの記録があれば、判断は一気に速くなります。Linux全般の障害対応の型については、Linuxのトラブルシューティング手順もあわせてどうぞ。サーバー側の別系統の詰まりを疑うなら、Linuxの容量トラブルを切り分ける順番が役に立つはずです。

こうした切り分けは、TCPの動きやルーティングの考え方が頭に入っているほど速くなります。InfraAcademyのネットワークのロードマップでは、こうした土台を手を動かしながら順に固められるようにしています。現場経験があっても、断片的に覚えてきた部分を一度つなぎ直すと、判断のスピードが変わります。

まとめ

つながらないという報告を受けたら、まず止まり方を見ます。待たされるのか、即座に断られるのか。この違いだけで、調べる範囲は半分になります。

そのうえで、名前解決、経路、ポート、パケットの順に下から上がっていきます。順番を守るのは、勘で飛ばした場所こそが原因だった、という事故を防ぐためです。

そして、迷ったら tcpdump を両側で取る。推測を重ねるより、実際に飛んでいるパケットを見にいくほうが速いというのは、何度やっても変わらない結論でした。

切り分けは才能ではなく、順番の習慣です。今日の流れを一度自分の手でなぞっておくと、次に一報が来たときの落ち着きがまるで違ってきます。

参考記事

Next Action

記事で読んだ内容を、講座で実装してみましょう

InfraAcademyでは、ブラウザ上でLinuxやネットワークの実践環境を使いながら学習できます。無料で始められる講座から、学習の流れを試せます。

この記事を書いた人

ryu

InfraAcademy運営 / エンジニア

エンジニア歴10年。Linux、ネットワーク、クラウドを中心に、実務で役立つインフラ技術を初心者にもわかりやすく解説しています。

X: @ryu63614894

Related

関連記事

ブログ一覧へ

Roadmap

まずはこの4講座から

ログインすれば無料で始められる講座です。気になったテーマから手を動かして学べます。

講座一覧を見る