Skip to content
Published on

ネットワークエンジニアリング&セキュリティ完全ガイド: TCP/IPからサービスメッシュ、AIサービングネットワークまで

Authors

目次

  1. ネットワーク基礎: OSI & TCP/IP
  2. ソケットプログラミング: asyncio & aiohttp
  3. サービスメッシュ: Istio & Envoy
  4. ネットワークセキュリティ: TLS & Zero Trust
  5. CDN & エッジコンピューティング
  6. AIサービングネットワーク: gRPC & SSE
  7. パケット解析の実践
  8. クイズ

1. ネットワーク基礎

OSI 7レイヤーモデル

OSI(Open Systems Interconnection)モデルは、ネットワーク通信を7つの層に抽象化した参照モデルです。

名称プロトコル例PDU
7アプリケーションHTTP, DNS, SMTPMessage
6プレゼンテーションTLS, JPEG, ASCIIMessage
5セッションNetBIOS, RPCMessage
4トランスポートTCP, UDPSegment
3ネットワークIP, ICMPPacket
2データリンクEthernet, 802.11Frame
1物理ケーブル、光ファイバBits

TCP/IPスタック

実際のインターネットは、OSIより単純化された4層のTCP/IPモデルを使用します。

  • アプリケーション層: HTTP/2, HTTP/3, DNS, TLS
  • トランスポート層: TCP(信頼性), UDP(速度)
  • インターネット層: IPv4, IPv6, ICMP
  • リンク層: Ethernet, Wi-Fi

HTTP/2 と HTTP/3 の違い

HTTP/1.1では、1つの接続で同時に1つのリクエストしか処理できず、Head-of-Line (HOL) ブロッキングが発生します。

HTTP/2 の改善点:

  • マルチプレキシング: 1つのTCP接続で複数ストリームを同時処理
  • ヘッダー圧縮: HPACKアルゴリズムで重複ヘッダーを削除
  • サーバープッシュ: クライアントのリクエストなしにリソースを先制送信
  • バイナリフレーミング: テキストの代わりにバイナリフレームを使用

HTTP/3 & QUIC: HTTP/3はTCPの代わりにQUIC(UDPベース)上で動作し、TCPレベルのHOLブロッキングも解消します。

HTTP/1.1:  [Req1][Res1][Req2][Res2]  (逐次処理)
HTTP/2:    [Req1, Req2, Req3][Res1, Res2, Res3]  (多重化、単一TCP)
HTTP/3:    [Req1, Req2, Req3][Res1, Res2, Res3]  (多重化、独立QUICストリーム)

DNS の動作原理

api.example.com を解決する際のフロー:

  1. ブラウザキャッシュの確認
  2. OSキャッシュの確認 (/etc/hosts)
  3. 再帰リゾルバ(ISP DNS)へのクエリ
  4. ルートNS → .com TLD NS → example.com 権威NSへの階層的解決
  5. Aレコード(IPv4)またはAAAAレコード(IPv6)を返す

TLS 1.3 ハンドシェイク

TLS 1.3 は往復回数を削減し、新規接続では1-RTT、セッション再開では0-RTTで確立します。

Client                          Server
  |--- ClientHello (鍵共有) ------>|
  |<-- ServerHello + Certificate --|
  |<-- + EncryptedExtensions ------|
  |--- Finished ------------------->|
  |<-> 暗号化されたデータ交換 <------>|

2. ソケットプログラミング

Python asyncio TCPサーバー

import asyncio

async def handle_client(reader: asyncio.StreamReader, writer: asyncio.StreamWriter):
    addr = writer.get_extra_info('peername')
    print(f"接続: {addr}")

    try:
        while True:
            data = await reader.read(1024)
            if not data:
                break
            message = data.decode('utf-8').strip()
            print(f"受信: {message} from {addr}")

            # エコー応答
            response = f"Echo: {message}\n"
            writer.write(response.encode('utf-8'))
            await writer.drain()
    except asyncio.IncompleteReadError:
        pass
    finally:
        print(f"切断: {addr}")
        writer.close()
        await writer.wait_closed()

async def main():
    server = await asyncio.start_server(
        handle_client, '0.0.0.0', 8888
    )
    addr = server.sockets[0].getsockname()
    print(f"サーバー開始: {addr}")

    async with server:
        await server.serve_forever()

if __name__ == '__main__':
    asyncio.run(main())

aiohttp を使った非同期HTTPクライアント

import asyncio
import aiohttp
from typing import List

async def fetch_url(session: aiohttp.ClientSession, url: str) -> dict:
    async with session.get(url, timeout=aiohttp.ClientTimeout(total=10)) as response:
        return {
            'url': url,
            'status': response.status,
            'body': await response.text()
        }

async def fetch_all(urls: List[str]) -> List[dict]:
    connector = aiohttp.TCPConnector(
        limit=100,          # 最大同時接続数
        limit_per_host=10,  # ホストあたりの最大接続数
        keepalive_timeout=30
    )
    async with aiohttp.ClientSession(connector=connector) as session:
        tasks = [fetch_url(session, url) for url in urls]
        return await asyncio.gather(*tasks, return_exceptions=True)

# 実行
urls = [f"https://httpbin.org/get?id={i}" for i in range(20)]
results = asyncio.run(fetch_all(urls))

gRPC vs REST の比較

項目gRPCREST
プロトコルHTTP/2HTTP/1.1 または HTTP/2
シリアライゼーションProtocol Buffers(バイナリ)JSON(テキスト)
ストリーミング双方向ストリーミング対応限定的(SSE・WebSocket別途)
型安全性強い型付け(.protoスキーマ)弱い型付け
レイテンシ低い相対的に高い
ブラウザサポートgrpc-web が必要ネイティブ対応

3. サービスメッシュ

Istio アーキテクチャ

IstioはサイドカーパターンでEnvoyプロキシを各Podに注入し、サービス間通信を制御します。

  • コントロールプレーン (Istiod): 設定管理、証明書発行、トラフィックポリシー配布
  • データプレーン (Envoy Sidecar): 実際のトラフィック処理、メトリクス収集、mTLS適用

VirtualService 設定例

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: ml-inference-vs
  namespace: production
spec:
  hosts:
    - ml-inference-svc
  http:
    - match:
        - headers:
            x-model-version:
              exact: 'v2'
      route:
        - destination:
            host: ml-inference-svc
            subset: v2
          weight: 100
    - route:
        - destination:
            host: ml-inference-svc
            subset: v1
          weight: 90
        - destination:
            host: ml-inference-svc
            subset: v2
          weight: 10

ロードバランシングアルゴリズム

  • ラウンドロビン: 順番にリクエストを分散(デフォルト)
  • 最小接続数: アクティブ接続が最も少ないサーバーを選択
  • 重み付きラウンドロビン: 重みに基づいてリクエストを分散
  • IPハッシュ: クライアントIPで特定サーバーに固定(セッション維持)
  • 一貫性ハッシュ: サーバー追加・削除時の再分散を最小化(分散キャッシュ向け)

サービスディスカバリ

Kubernetes環境ではCoreDNSがサービスディスカバリを担当します。 service-name.namespace.svc.cluster.local の形式でサービスにアクセスします。


4. ネットワークセキュリティ

TLS 証明書チェーン

ルートCA(ブラウザ/OSに組み込み済み)
  └── 中間CA
        └── リーフ証明書(実際のサーバー証明書)

各証明書は上位CAの秘密鍵で署名され、信頼チェーンを形成します。

mTLS(Mutual TLS)

通常のTLSはサーバーのみが証明書を提供します。mTLSはクライアントも証明書を提示し、双方向認証を行います。

通常TLS:  Client[サーバー証明書を検証]Server
mTLS:     Client[双方向証明書を検証]Server

サービスメッシュでは、mTLSはサイドカープロキシが自動的に処理し、アプリケーションコードの変更なしにサービス間通信を保護します。

Nginx TLS 設定

server {
    listen 443 ssl http2;
    server_name api.example.com;

    ssl_certificate     /etc/ssl/certs/server.crt;
    ssl_certificate_key /etc/ssl/private/server.key;

    # TLS 1.2以上のみ許可
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256;
    ssl_prefer_server_ciphers off;

    # HSTS(1年間)
    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;

    # OCSPステープリング
    ssl_stapling on;
    ssl_stapling_verify on;

    location /api/ {
        proxy_pass http://backend_pool;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

Zero Trust アーキテクチャ

Zero Trust の核心原則: 「決して信頼せず、常に検証せよ(Never Trust, Always Verify)」

  • IDベースのアクセス制御: IPアドレスではなくユーザー/サービスIDで認証
  • 最小権限の原則: 必要なリソースへのアクセスのみ許可
  • 継続的な検証: セッション中も継続的に信頼レベルを評価
  • マイクロセグメンテーション: ネットワークを小さなゾーンに分割し横断移動を阻止

JWT & OAuth 2.0

JWT(JSON Web Token)は3つの部分で構成されます: Header.Payload.Signature

OAuth 2.0 認可コードフロー:

  1. クライアントがユーザーを認可サーバーにリダイレクト
  2. ユーザー認証後、認可コードを発行
  3. バックエンドでコードをアクセストークンに交換
  4. アクセストークンでリソースサーバーのAPIを呼び出す

5. CDN & エッジコンピューティング

CDN の仕組み

CDN(Content Delivery Network)は世界中のPoP(Points of Presence)にコンテンツをキャッシュし、レイテンシを削減します。

キャッシュ戦略:

  • Cache-Control: max-age=86400: ブラウザとCDNで1日キャッシュ
  • Cache-Control: no-cache: 常にオリジンサーバーに再検証
  • ETag / Last-Modified: 条件付きリクエストで変更有無を確認

Cloudflare Workers の例

export default {
  async fetch(request, env) {
    const url = new URL(request.url)

    // エッジでAI推論をルーティング
    if (url.pathname.startsWith('/inference/')) {
      const modelId = url.searchParams.get('model') || 'default'

      // 最も近いGPUクラスターにルーティング
      const region = request.cf.region
      const backendUrl = selectBackend(region, modelId)

      return fetch(backendUrl, {
        method: request.method,
        headers: request.headers,
        body: request.body,
      })
    }

    // 静的アセットのキャッシュ
    const cache = caches.default
    const cachedResponse = await cache.match(request)
    if (cachedResponse) return cachedResponse

    const response = await fetch(request)
    if (response.status === 200) {
      const responseToCache = response.clone()
      await cache.put(request, responseToCache)
    }
    return response
  },
}

6. AIサービングネットワーク

gRPC を使ったMLモデルサービング

Protocol Buffers 定義:

syntax = "proto3";
package inference;

service InferenceService {
  rpc Predict(PredictRequest) returns (PredictResponse);
  rpc StreamPredict(PredictRequest) returns (stream PredictResponse);
}

message PredictRequest {
  string model_name = 1;
  repeated float input_data = 2;
  map<string, string> metadata = 3;
}

message PredictResponse {
  repeated float output_data = 1;
  float confidence = 2;
  int64 latency_ms = 3;
}

Python gRPC サーバー:

import grpc
from concurrent import futures
import inference_pb2
import inference_pb2_grpc
import numpy as np
import time

class InferenceServicer(inference_pb2_grpc.InferenceServiceServicer):
    def Predict(self, request, context):
        start = time.time()
        input_array = np.array(request.input_data)

        # 実際のモデル推論(例示)
        output = input_array * 2.0

        latency = int((time.time() - start) * 1000)
        return inference_pb2.PredictResponse(
            output_data=output.tolist(),
            confidence=0.95,
            latency_ms=latency
        )

    def StreamPredict(self, request, context):
        # ストリーミング応答(LLMトークン生成などに活用)
        tokens = ["こんにちは", "!", " gRPC", "ストリーミング", "です。"]
        for token in tokens:
            yield inference_pb2.PredictResponse(
                output_data=[float(ord(c)) for c in token],
                confidence=0.9,
                latency_ms=10
            )

def serve():
    server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))
    inference_pb2_grpc.add_InferenceServiceServicer_to_server(
        InferenceServicer(), server
    )
    server.add_insecure_port('[::]:50051')
    server.start()
    print("gRPCサーバー起動: ポート50051")
    server.wait_for_termination()

SSE を使った LLM リアルタイムストリーミング

from fastapi import FastAPI
from fastapi.responses import StreamingResponse
import asyncio
import json

app = FastAPI()

async def llm_token_generator(prompt: str):
    """LLMトークンをSSE形式でストリーミング"""
    # 本番環境ではLLMライブラリを使用
    tokens = prompt.split() + ["[完了]"]
    for i, token in enumerate(tokens):
        data = json.dumps({"token": token, "index": i})
        yield f"data: {data}\n\n"
        await asyncio.sleep(0.05)  # トークン生成のシミュレーション
    yield "data: [DONE]\n\n"

@app.get("/stream")
async def stream_llm(prompt: str = "こんにちは"):
    return StreamingResponse(
        llm_token_generator(prompt),
        media_type="text/event-stream",
        headers={
            "Cache-Control": "no-cache",
            "X-Accel-Buffering": "no",  # Nginxバッファリングを無効化
        }
    )

7. パケット解析の実践

tcpdump の基本コマンド

# 特定インターフェースでHTTPトラフィックをキャプチャ
sudo tcpdump -i eth0 -w capture.pcap port 80 or port 443

# TCPハンドシェイクのみフィルタリング(SYNパケット)
sudo tcpdump -i any 'tcp[tcpflags] & tcp-syn != 0'

# 特定ホストとの通信を監視
sudo tcpdump -i eth0 host 10.0.0.1 -n

# DNSクエリの監視
sudo tcpdump -i any udp port 53 -v

# gRPC(HTTP/2)トラフィックのキャプチャ
sudo tcpdump -i eth0 port 50051 -w grpc_trace.pcap

curl を使ったネットワークデバッグ

# TLS証明書情報の確認
curl -vI https://api.example.com 2>&1 | grep -A 20 "SSL connection"

# HTTP/2の使用確認
curl --http2 -I https://api.example.com

# レスポンス時間の詳細測定
curl -o /dev/null -s -w \
  "DNS: %{time_namelookup}s\nTCP: %{time_connect}s\nTLS: %{time_appconnect}s\nTotal: %{time_total}s\n" \
  https://api.example.com

# gRPCエンドポイントのテスト(grpcurl)
grpcurl -plaintext localhost:50051 list
grpcurl -plaintext -d '{"model_name": "bert", "input_data": [1.0, 2.0]}' \
  localhost:50051 inference.InferenceService/Predict

ネットワーク性能指標

  • RTT(ラウンドトリップタイム): パケットの往復時間、ping で計測
  • スループット: 単位時間あたりの転送データ量、iperf3 で計測
  • パケットロス: 損失率、不安定なUDP環境で重要
  • ジッター: RTTの変動、リアルタイムストリーミングで重要
# ネットワーク帯域幅の測定
iperf3 -s                           # サーバー
iperf3 -c server_ip -t 30 -P 4     # クライアント(4並列ストリーム)

クイズ

Q1. TCP 3ウェイハンドシェイクと4ウェイ終了の過程を説明してください。

答え:

3ウェイハンドシェイク(接続確立):

  1. Client → Server: SYN (seq=x)
  2. Server → Client: SYN-ACK (seq=y, ack=x+1)
  3. Client → Server: ACK (ack=y+1)

4ウェイ終了(接続切断):

  1. Client → Server: FIN
  2. Server → Client: ACK
  3. Server → Client: FIN
  4. Client → Server: ACK(TIME_WAIT状態後に完全終了)

解説: 終了が4段階になる理由は、サーバーがFINを受け取った後も送信中のデータがある場合があるためです。クライアントはACK送信後にTIME_WAIT状態(2MSL期間)に入り、遅延パケットに対処します。

Q2. HTTP/2のマルチプレキシングがHTTP/1.1のHOLブロッキングを解決する仕組みを説明してください。

答え: HTTP/2は1つのTCP接続上で複数のストリームを同時に送受信します。各ストリームは独立したIDを持ち、あるストリームの処理が遅延しても他のストリームには影響しません。

解説: HTTP/1.1はパイプライニングをサポートしますが、レスポンスはリクエスト順に受け取る必要があるため、先頭のレスポンスが遅いと後続もすべてブロックされます(HOLブロッキング)。HTTP/2はフレーム単位でインターリービングすることでこの問題をアプリケーション層で解決します。ただし、TCPレベルのHOLブロッキングはHTTP/3(QUIC)によって完全に解消されます。

Q3. mTLSが通常のTLSと異なる点と、サービスメッシュにおける役割を説明してください。

答え: 通常のTLSはサーバーの証明書のみを検証しますが、mTLS(Mutual TLS)はクライアントも証明書を提示し、双方向認証を行います。

解説: Istioなどのサービスメッシュでは、mTLSはサイドカープロキシ(Envoy)が透過的に処理します。各サービスはSPIFFE IDを持つX.509証明書を発行され、サービス間通信はすべてアプリケーションコードの変更なしに相互認証・暗号化されます。これにより、内部ネットワーク上でも盗聴、なりすまし、中間者攻撃を防ぐことができます。

Q4. gRPCがRESTよりMLモデルサービングに有利な理由を説明してください。

答え: gRPCはProtocol Buffersによるバイナリシリアライゼーション、HTTP/2の多重化、双方向ストリーミングのサポートにより、ML推論ワークロードに最適化されています。

解説: 大きなテンソルデータの転送時、Protocol BuffersはJSONと比較して3〜5倍小さいペイロードを生成します。サーバーストリーミングにより、LLMが生成したトークンをリアルタイムにクライアントへ配信できます。また、.protoスキーマでAPIコントラクトが厳密に定義されるため、MLパイプライン統合時の型安全性が保証されます。

Q5. CDNキャッシュ無効化(invalidation)戦略の種類とトレードオフを説明してください。

答え: 主な戦略としてTTLベースの期限切れ、バージョン付きURL、APIによる即時無効化、サロゲートキー(タグ)ベースの無効化があります。

解説:

  • TTLベース: 実装が簡単だが、期限切れまで古いコンテンツが配信される可能性がある
  • バージョン付きURL(例: style.v2.css): キャッシュヒット率を維持しつつ即時更新が可能だが、URL管理が複雑になる
  • 即時無効化API: 素早い反映が可能だがCDNコストが発生し、数十秒の伝播遅延がある
  • サロゲートキー: 関連するコンテンツグループを一括無効化できる(Cloudflare Cache Tagsなど)

実際の運用では、静的アセット(JS/CSS)はバージョン付きURL、APIレスポンスは短いTTLと即時無効化の組み合わせを使用します。