じゃあ、おうちで学べる

本能を呼び覚ますこのコードに、君は抗えるか

Istioによるトラフィック制御で仮想待合室システムを目指すけどもやな

この記事では仮想待合室システムを目指すけども結局はできておらずIstioのトラフィック制御までがメインです。方針は決まったが睡魔に負けたのでこの記事はここまで

はじめに

登壇で抽象度の高いことを人前で喋ってとても心が疲れたので技術者として手を動かしたいです。深夜ノリなのでガバガバである。今回は、Istio を用いて仮想待合室が作りたくなってたのでKubernetes環境でIstioを使用してトラフィック制御を実装する方法について、解説します。この記事では、将来的な仮想待合室システムの構築を視野に入れつつ、まずはトラフィック制御の基本的な実装に焦点を当てます。Kindを使用したローカル開発環境の構築から、Istioによるトラフィック管理の設定、そして実際のテストを実行します。

1. 環境セットアップ

まず、必要なツールをインストールしていることを確認してください。

  • Docker
  • Kind (Kubernetes in Docker)
  • kubectl
  • Istioctl

1.1 Kindクラスターの作成

Kindを使用してローカルKubernetesクラスターを作成します。以下の内容でkind-config.yamlファイルを作成してください。

kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
  kubeadmConfigPatches:
  - |
    kind: InitConfiguration
    nodeRegistration:
      kubeletExtraArgs:
        node-labels: "ingress-ready=true"
  extraPortMappings:
  - containerPort: 80
    hostPort: 80
    protocol: TCP
  - containerPort: 443
    hostPort: 443
    protocol: TCP

この設定ファイルは、Kindクラスターにポート80と443のマッピングを追加し、Ingressコントローラーの準備をします。

次に、以下のコマンドでKindクラスターを作成します。

kind create cluster --name traffic-control --config kind-config.yaml

1.2 Istioのインストール

Istioをインストールし、デフォルトの名前空間にIstio injectionを有効化します。

istioctl install --set profile=demo -y
kubectl label namespace default istio-injection=enabled

2. アプリケーションの準備

2.1 Goアプリケーションの作成

以下の内容でmain.goファイルを作成します。

package main

import (
    "fmt"
    "net/http"
)

func main() {
    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        fmt.Fprintf(w, "Welcome to the main content!")
    })
    http.ListenAndServe(":8080", nil)
}

2.2 Dockerfileの作成

FROM golang:1.22

WORKDIR /app

COPY go.mod go.sum ./
RUN go mod download

COPY main.go .
RUN go build -o main .

CMD ["./main"]

2.3 イメージのビルドとロード

docker build -t waiting-room-app:latest .
kind load docker-image waiting-room-app:latest --name traffic-control

3. Kubernetesリソースの定義

3.1 Kubernetes リソースの作成

waiting-room.yaml を作成する

apiVersion: apps/v1
kind: Deployment
metadata:
  name: waiting-room-app
spec:
  replicas: 1
  selector:
    matchLabels:
      app: waiting-room-app
  template:
    metadata:
      labels:
        app: waiting-room-app
    spec:
      containers:
      - name: waiting-room-app
        image: waiting-room-app:latest
        imagePullPolicy: Never
        ports:
        - containerPort: 8080
---
apiVersion: v1
kind: Service
metadata:
  name: waiting-room-app
spec:
  selector:
    app: waiting-room-app
  ports:
    - protocol: TCP
      port: 80
      targetPort: 8080

3.2 Istioリソースの定義

istio-rules.yamlファイルには、Istioの主要な3つのリソース(VirtualService、DestinationRule、Gateway)が定義されています。これらのリソースは、仮想待合室システムのトラフィック制御と負荷管理を実現する上で重要な役割を果たします。

  1. VirtualService: VirtualServiceは、トラフィックのルーティングルールを定義します。
   apiVersion: networking.istio.io/v1alpha3
   kind: VirtualService
   metadata:
     name: waiting-room-vs
   spec:
     hosts:
     - "*"
     gateways:
     - waiting-room-gateway
     http:
     - route:
       - destination:
           host: waiting-room-app
           port:
             number: 80
       timeout: 1s
       retries:
         attempts: 3
         perTryTimeout: 500ms
       fault:
         delay:
           percentage:
             value: 80
           fixedDelay: 5s
  • hosts: "*": すべてのホストからのトラフィックに適用されます。
  • gateways: - waiting-room-gateway: 特定のゲートウェイを通過するトラフィックにのみ適用されます。
  • route: トラフィックwaiting-room-app サービスの80ポートにルーティングします。
  • timeout: 1s: リクエストの最大待機時間を1秒に設定します。
  • retries: 失敗したリクエストを最大3回、500ミリ秒間隔で再試行します。
  • fault: 80%のトラフィックに5秒の遅延を導入します。これにより、仮想待合室の「待ち時間」をシミュレートします。

  • DestinationRule: DestinationRuleは、トラフィックのロードバランシングと接続プールの設定を定義します。

   apiVersion: networking.istio.io/v1alpha3
   kind: DestinationRule
   metadata:
     name: waiting-room-dr
   spec:
     host: waiting-room-app
     trafficPolicy:
       connectionPool:
         tcp:
           maxConnections: 10
         http:
           http1MaxPendingRequests: 1
           maxRequestsPerConnection: 1
       outlierDetection:
         consecutive5xxErrors: 1
         interval: 1s
         baseEjectionTime: 3m
         maxEjectionPercent: 100
  • host: waiting-room-app: このルールが適用されるサービスを指定します。
  • connectionPool: 同時接続数を10に制限し、保留中のリクエストと接続あたりのリクエスト数を1に制限します。これにより、サーバーの過負荷を防ぎます。
  • outlierDetection: 連続して5xxエラーが発生した場合、そのインスタンスを3分間トラフィックから除外します。これにより、問題のあるインスタンスを自動的に切り離し、システムの安定性を維持します。

  • Gateway: Gatewayは、メッシュへの入口となる外部トラフィックの受け入れ口を定義します。

   apiVersion: networking.istio.io/v1alpha3
   kind: Gateway
   metadata:
     name: waiting-room-gateway
   spec:
     selector:
       istio: ingressgateway
     servers:
     - port:
         number: 80
         name: http
         protocol: HTTP
       hosts:
       - "*"

これらのリソースを組み合わせることで、以下のような仮想待合室の動作を実現します。

  1. 外部からのトラフィックGatewayを通じて受け入れられます。
  2. VirtualServiceにより、80%のトラフィックに5秒の遅延が導入され、待ち時間が発生します。
  3. DestinationRuleにより、同時接続数が制限され、システムの過負荷が防止されます。
  4. 問題が発生した場合、自動的にトラフィックが健全なインスタンスにリダイレクトされます。

4. リソースの適用

以下のコマンドでKubernetesリソースを適用します。

kubectl apply -f waiting-room.yaml
kubectl apply -f istio-rules.yaml

5. アクセスの設定

Istio IngressGatewayにアクセスするために、kubectl port-forwardコマンドを使用します。

kubectl port-forward -n istio-system svc/istio-ingressgateway 8080:80

このコマンドにより、ローカルマシンの8080ポートがIstio IngressGatewayの80ポートに転送されます。これにより、http://localhost:8080でアプリケーションにアクセスできるようになります。

6. 動作確認とテスト

6.1 基本的な動作確認

新しいターミナルウィンドウを開き、以下のコマンドでアプリケーションにアクセスしてみましょう:

curl http://localhost:8080

6.2 負荷テストスクリプト

以下の内容でload_test.shスクリプトを作成します。

#!/bin/bash

CONCURRENT=10
TOTAL_REQUESTS=50
RESULTS_DIR="access_test_results"
mkdir -p "$RESULTS_DIR"

make_request() {
    local id=$1
    local start_time=$(date +%s.%N)
    local http_code=$(curl -s -o /dev/null -w "%{http_code}" http://localhost:8080)
    local end_time=$(date +%s.%N)
    local duration=$(echo "$end_time - $start_time" | bc)
    echo "$id,$http_code,$duration" >> "$RESULTS_DIR/results.csv"
}

echo "RequestID,HTTPCode,Duration" > "$RESULTS_DIR/results.csv"

for i in $(seq 1 $TOTAL_REQUESTS); do
    make_request $i &
    if (( i % CONCURRENT == 0 )); then
        wait
    fi
done

wait

echo "All requests completed. Results saved in $RESULTS_DIR/results.csv"

echo "===============================
テスト結果サマリー
==============================="
echo "総リクエスト数: $TOTAL_REQUESTS"
echo "同時接続数: $CONCURRENT"

echo -e "\nHTTPステータスコード分布:"
status_codes=$(sort "$RESULTS_DIR/results.csv" | cut -d',' -f2 | sort | uniq -c | sort -nr)
total_success=$(echo "$status_codes" | grep " 200" | awk '{print $1}')
total_success=${total_success:-0}
echo "$status_codes" | while read count code; do
    if [ "$code" != "HTTPCode" ]; then
        percentage=$(echo "scale=2; $count / $TOTAL_REQUESTS * 100" | bc)
        printf "%s: %s (%.2f%%)\n" "$code" "$count" "$percentage"
        bar=$(printf '%0.s#' $(seq 1 $(echo "$percentage/2" | bc)))
        printf "  %s\n" "$bar"
    fi
done

success_rate=$(echo "scale=2; $total_success / $TOTAL_REQUESTS * 100" | bc)
echo -e "\n成功率(200 OKの割合): ${success_rate}%"

echo -e "\n応答時間統計:"
awk -F',' '
    NR>1 {
        sum+=$3; 
        sumsq+=$3*$3; 
        if(NR==2 || $3<min) min=$3; 
        if(NR==2 || $3>max) max=$3;
    } 
    END {
        avg=sum/NR; 
        std=sqrt(sumsq/NR - avg*avg);
        printf "最小: %.2f秒\n", min;
        printf "最大: %.2f秒\n", max;
        printf "平均: %.2f秒\n", avg;
        printf "標準偏差: %.2f秒\n", std;
    }
' "$RESULTS_DIR/results.csv"

echo -e "\n注: 詳細な結果は $RESULTS_DIR/results.csv に保存されています。"

このスクリプトに実行権限を付与し、実行します。

chmod +x load_test.sh
./load_test.sh

7. 結果の分析

テストスクリプトの実行結果を分析しましょう。以下は典型的な結果の例です。

All requests completed. Results saved in access_test_results/results.csv
===============================
テスト結果サマリー
===============================
総リクエスト数: 50
同時接続数: 10

HTTPステータスコード分布:
503: 36 (72.00%)
  ####################################
200: 14 (28.00%)
  ##############

成功率(200 OKの割合): 28.00%

応答時間統計:
最小: 0.00秒
最大: 5.00秒
平均: 4.02秒
標準偏差: 1.99秒

注: 詳細な結果は access_test_results/results.csv に保存されています。

結果の説明

  1. アクセス制限の効果: 72%のリクエストが503 (Service Unavailable) エラーを返しており、設定したアクセス制限が効果的に機能していることがわかります。

  2. 遅延の導入: 平均応答時間が4.02秒、最大が5.00秒となっており、VirtualServiceで設定した5秒の遅延が適切に適用されていることが確認できます。

  3. 成功率: 28%のリクエストのみが成功(200 OK)しており、トラフィック制御システムが効果的にリクエストを制限していることを示しています。

  4. 応答時間のばらつき: 標準偏差が1.99秒と大きいことから、一部のリクエストは即座に処理され、他は遅延が適用されていることがわかります。これは、設定した80%のトラフィックへの遅延導入が機能していることを示しています。

8. まとめ

この記事では、Kubernetes上でトラフィック制御を実装し、スケーラブルな待機システムを構築する基礎部分を解説しました。Kindを使用したローカル開発環境の構築から、Istioによるトラフィック管理、負荷テストの実施までを紹介しました。この手法は、大規模イベントやセール時のトラフィック急増への対策として有効です。

しかし、実際の運用では、継続的なモニタリングやビジネス側との調整に加え、本格的な仮想待合室システムの構築には、待ち行列管理やユーザーインターフェースの実装が不可欠になります。これらの要素については、次回の記事ではRedisやEnvoyFilterなどを活用した具体的な実装方法と合わせて詳しく解説していく予定です。お楽しみに!おやすみなさい😪

9. 参考リンク