プログラマのための IT 教科書
第3部 コンピュータとネットワークの基礎

ネットワークの仕組み

読了目安 45

この章を読むとできるようになること
  • ドメイン名が IP アドレスに解決される流れを説明できる
  • DNS を切り替えても反映されない理由を説明できる
  • HTTP/1.1 と HTTP/2 の違いを説明できる

前の章では HTTP の中身を見ました。この章では、その HTTP がどうやって相手に届くのかを見ます。

正直に言うと、Web アプリを書くだけならネットワークの詳細を知らなくても動きます。 しかし動かなくなった時、この知識がないと手も足も出ません。

  • 「DNS を切り替えたのに反映されない」
  • 「証明書エラーが出た」
  • 「ローカルでは動くのに本番だけ遅い」

これらは全部この章の話です。

層に分ける

ネットワークは、役割ごとに層に分かれています。手紙にたとえると分かりやすいです。

[ HTTP        ]  手紙の中身(何を伝えたいか)
[ TLS         ]  封筒に鍵をかける
[ TCP         ]  順番に届いたか確認する仕組み
[ IP          ]  宛先の住所
[ イーサネット ]  実際の配達手段(電線・電波)

なぜ層に分けるかというと、それぞれを独立して差し替えられるからです。 Wi-Fi でも有線でも、上の層は何も変えずに動きます。

新人が知っておくべき層は上の4つ、特に DNS・TCP・TLS・HTTP です。

DNS — 名前を住所に変える

api.example.com という名前では通信できません。 実際の通信には IP アドレス93.184.216.34 のような数字)が必要です。 その変換をするのが DNS です。

解決の流れ

1. ブラウザのキャッシュ    「さっき調べた?」
2. OS のキャッシュ         「このマシンで調べた?」
3. DNS サーバーに問い合わせ
     ↓
   ルートサーバー      「.com のことは TLD サーバーに聞いて」
     ↓
   TLD サーバー(.com)  「example.com のことは権威サーバーに聞いて」
     ↓
   権威サーバー        「api.example.com は 93.184.216.34 です」

段階的にたらい回しにされて、最終的な答えにたどり着きます。

TTL とキャッシュ

毎回この問い合わせをすると遅いので、答えはキャッシュされます。 どれくらい保持するかを決めるのが TTL(Time To Live)です。

「DNS を切り替えたのに反映されない」

実務で必ず一度は遭遇します。原因はキャッシュです。

TTL が 3600 秒(1時間)なら、切り替えても最大1時間は古い IP に繋がり続けます。 しかも、ブラウザ・OS・社内 DNS・ISP の DNS と、キャッシュは何段もあります。

対策: サーバー移行の予定があるなら、事前に TTL を短くしておきます(60秒など)。 切り替えが終わったら元に戻します。当日に慌てて短くしても、 その変更自体が古い TTL のぶん遅れて伝わるので手遅れです。

確認方法: dig api.example.comnslookup api.example.com で、 今どの IP が返ってくるかを見られます。

IP — 住所を頼りに運ぶ

IP は「この住所に届けてほしい」というだけの仕組みです。 届いたかどうかの保証はありません(それは TCP の仕事)。

知っておくと役立つこと

用語意味
プライベート IP192.168.x.x 10.x.x.x など。組織内でだけ有効
グローバル IPインターネット上で一意
NAT家庭やオフィスの複数端末が、1つのグローバル IP を共有する仕組み
localhost / 127.0.0.1自分自身。外には出ない
0.0.0.0「すべてのアドレスで待ち受ける」の意味
Docker の中の localhost は自分自身

コンテナの中で localhost:5432 に繋ごうとして失敗するのは典型です。

コンテナにとっての localhostそのコンテナ自身であって、 ホストマシンでも他のコンテナでもありません。 Docker Compose ならサービス名db:5432 など)で参照します。

TCP — 確実に届ける

IP は「届くかもしれない」だけなので、その上で「確実に、順番どおりに」届ける 仕組みが TCP です。

3ウェイハンドシェイク

通信を始める前に、3回のやり取りで「準備できてる?」を確認します。

クライアント          サーバー
    │  ─── SYN ───→   │   「話しかけていい?」
    │  ←─ SYN/ACK ──  │   「いいよ。そっちも準備できてる?」
    │  ─── ACK ───→   │   「できてる」
    │                 │
    └── ここから通信 ──┘

この往復に時間がかかります。 地球の裏側なら1往復で200ms以上かかることもあり、 接続を張り直すたびにこのコストが発生します。

TCP と UDP

TCPUDP
確実性届いたか確認し、失われたら再送投げっぱなし
順序順番を保証保証しない
速度遅い速い
用途Web、API、DB動画・音声通話、DNS
なぜ動画通話は UDP なのか

音声が0.1秒欠けても会話は成立しますが、 「聞き取れなかった部分を再送してもらって、遅れて再生する」方が困ります。

確実性より即時性が大事な場面では UDP、というのが選択の基準です。

TLS — 中身を守る

httpss が TLS です。3つのことを保証します。

  1. 暗号化 — 途中の誰かに中身を読まれない
  2. 改ざん検知 — 途中で書き換えられていない
  3. 相手の確認 — 名乗っているサーバーが本物である(証明書)

証明書は何を証明しているのか

証明書は「このドメインの持ち主は確かにこの公開鍵を持っている」ことを、 認証局(CA)が署名して保証するものです。

ブラウザは「信頼できる CA のリスト」を最初から持っていて、 そのリストにある CA の署名なら信用します。

証明書の有効期限切れ

証明書には有効期限があります(Let's Encrypt なら90日)。 切れると、すべてのユーザーがエラー画面を見ることになります

自動更新の仕組み(ACME)が入っていれば起きませんが、 更新に失敗したまま気づかないケースがあります。 証明書の期限を監視対象に入れるのが定石です。

ハンドシェイクの往復

TLS も接続の最初に往復が必要です。TCP と合わせるとこうなります。

TLS 1.2:  TCP(1往復) + TLS(2往復) = 3往復してから、やっと HTTP
TLS 1.3:  TCP(1往復) + TLS(1往復) = 2往復

TLS 1.3 で往復が1回減りました。遠い相手ほどこの差が効きます

HTTP/1.1 と HTTP/2

ここが gRPC に繋がる重要な話です。

HTTP/1.1 の問題

1つの TCP 接続では、リクエストを1つずつしか処理できません

接続1: [───req A───][───req B───][───req C───]
                    ↑ A が終わるまで B は始まらない

前のリクエストが詰まると後ろが全部待たされます。これを ヘッドオブラインブロッキングと言います。

ブラウザはこれを回避するため、同じホストに6本程度の接続を張ります。 それでも足りず、画像を1枚にまとめる(スプライト)などの 苦しい工夫が行われていました。

HTTP/2 の多重化

HTTP/2 では、1本の接続の中で複数のやり取りを同時に流せます

接続1: [A][B][C][A][C][B][A]...   ← 混ぜて流し、受け取り側で組み立てる
  • 1本の接続で済むので、ハンドシェイクのコストが1回だけ
  • ヘッダも圧縮される
  • サーバーから先に送る機能もある
gRPC が HTTP/2 の上にある理由

gRPC は「双方向にデータを流し続ける」通信(ストリーミング)ができます。 これは HTTP/2 の多重化があって初めて実用的になりました。

第13章で gRPC を扱いますが、その土台がここです。

URL を打ってから画面が出るまで

この章と前の章の総まとめです。面接でも定番の質問なので、 自分の言葉で説明できるようにしておいてください。

 1. URL を解釈する
 2. DNS でドメイン名を IP アドレスに変換する(キャッシュがあれば省略)
 3. TCP の3ウェイハンドシェイクで接続する
 4. TLS ハンドシェイクで暗号化を確立する(https の場合)
 5. HTTP リクエストを送る
 6. サーバーが処理して HTTP レスポンスを返す
 7. ブラウザが HTML をパースして DOM を作る
 8. CSS・JS・画像などのサブリソースを取得する(ここでまた 2〜6)
 9. CSSOM を作り、レイアウトを計算する
10. ペイントして合成し、画面に出る

どの段階で失敗するかで、症状が変わります。

症状疑う段階
「サーバーが見つかりません」2(DNS)
接続がタイムアウトする3(TCP。ファイアウォールやポート)
証明書の警告が出る4(TLS)
404 や 500 が返る6(サーバー側)
まっしろな画面が続く7〜9(描画のブロック)
切り分けの道具
dig example.com          # DNS の答えを見る
ping example.com         # 届くか(ICMP。塞がれていることも多い)
curl -v https://example.com   # ハンドシェイクから応答まで全部見える
traceroute example.com   # どの経路を通っているか

特に curl -v は、DNS 解決・TLS・リクエスト・レスポンスの全段階が見えるので、 「どこで失敗しているか」の切り分けに一番使えます。

実務の落とし穴まとめ

  1. DNS の TTL — 切り替え前に短くしておく。当日では手遅れ
  2. 証明書の期限切れ — 監視対象に入れる
  3. コンテナ内の localhost — 自分自身を指す。サービス名を使う
  4. ポートが塞がれている — 「繋がらない」の多くはファイアウォール
  5. 遠いリージョンとの通信 — 往復の回数がそのまま遅延になる

まとめ

  • ネットワークは層で分かれている。DNS → TCP → TLS → HTTP の順に組み上がる
  • DNS はキャッシュされる。TTL を理解していないと切り替えで事故る
  • TCP は3ウェイハンドシェイクで確実性を担保する。往復コストがある
  • TLS は暗号化・改ざん検知・相手の確認。証明書の期限切れは全断
  • HTTP/1.1 は1接続1リクエスト、HTTP/2 は多重化。gRPC の土台
  • curl -v で全段階が見える。切り分けの第一手

章末問題

DNS のレコードを新しいサーバーの IP に変更しましたが、一部のユーザーはまだ旧サーバーに繋がっています。原因は?

Docker コンテナ内のアプリから localhost:5432 の PostgreSQL に繋ごうとして失敗します。なぜ?

海外リージョンにある API を呼ぶと、処理は軽いのに毎回1秒近くかかります。改善の方向として妥当なのは?

次の章では、この上を流れる HTTP そのものを詳しく見ていきます。

読み終わったら記録しておくと、目次で進み具合が分かります。