- Authors

- Name
- Youngju Kim
- @fjvbn20031
目次
- ネットワーク基礎: OSI & TCP/IP
- ソケットプログラミング: asyncio & aiohttp
- サービスメッシュ: Istio & Envoy
- ネットワークセキュリティ: TLS & Zero Trust
- CDN & エッジコンピューティング
- AIサービングネットワーク: gRPC & SSE
- パケット解析の実践
- クイズ
1. ネットワーク基礎
OSI 7レイヤーモデル
OSI(Open Systems Interconnection)モデルは、ネットワーク通信を7つの層に抽象化した参照モデルです。
| 層 | 名称 | プロトコル例 | PDU |
|---|---|---|---|
| 7 | アプリケーション | HTTP, DNS, SMTP | Message |
| 6 | プレゼンテーション | TLS, JPEG, ASCII | Message |
| 5 | セッション | NetBIOS, RPC | Message |
| 4 | トランスポート | TCP, UDP | Segment |
| 3 | ネットワーク | IP, ICMP | Packet |
| 2 | データリンク | Ethernet, 802.11 | Frame |
| 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 を解決する際のフロー:
- ブラウザキャッシュの確認
- OSキャッシュの確認 (
/etc/hosts) - 再帰リゾルバ(ISP DNS)へのクエリ
- ルートNS →
.comTLD NS →example.com権威NSへの階層的解決 - 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 の比較
| 項目 | gRPC | REST |
|---|---|---|
| プロトコル | HTTP/2 | HTTP/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 認可コードフロー:
- クライアントがユーザーを認可サーバーにリダイレクト
- ユーザー認証後、認可コードを発行
- バックエンドでコードをアクセストークンに交換
- アクセストークンでリソースサーバーの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ウェイハンドシェイク(接続確立):
- Client → Server: SYN (seq=x)
- Server → Client: SYN-ACK (seq=y, ack=x+1)
- Client → Server: ACK (ack=y+1)
4ウェイ終了(接続切断):
- Client → Server: FIN
- Server → Client: ACK
- Server → Client: FIN
- 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と即時無効化の組み合わせを使用します。