GKE に Redis クラスタをデプロイする

このチュートリアルでは、ステートフル アプリケーションを作成し、アプリケーションを実行する Google Kubernetes Engine(GKE)クラスタをアップグレードする際のおすすめの方法を説明します。このチュートリアルでは、ステートフル アプリケーションをデプロイする例として Redis を使用しますが、GKE にデプロイされた他のタイプのステートフル アプリケーションにも同じコンセプトを適用できます。

リリース チャンネルに登録される GKE クラスタを作成する

GKE クラスタを作成するには、次の手順を行います。

  1. 3 つのノードを持つ redis-test という名前のクラスタを作成します。

    gcloud container clusters create redis-test \
        --location CONTROL_PLANE_LOCATION \
        --num-nodes=3 \
        --release-channel regular
    

    CONTROL_PLANE_LOCATION は、クラスタのコントロール プレーンの Compute Engine のロケーションに置き換えます。リージョン クラスタの場合はリージョン、ゾーンクラスタの場合はゾーンを指定します。

    クラスタが作成されると、次のような出力が表示されます。

      NAME: redis-test
      LOCATION: us-central1-c
      MASTER_VERSION: 1.22.10-gke.600
      MASTER_IP: 34.69.67.7
      MACHINE_TYPE: e2-medium
      NODE_VERSION: 1.22.10-gke.600
      NUM_NODES: 3
      STATUS: RUNNING
    
  2. クラスタと通信を行うように kubectl を構成します。

    gcloud container clusters get-credentials redis-test
    

GKE に Redis クラスタを作成する

このセクションでは、ConfigMapStatefulSetヘッドレス サービスをデプロイし、以前に作成した GKE クラスタの上に Redis クラスタを追加します。

Redis クラスタを作成するには、次の手順を完了します。

  1. Redis 構成が格納されている ConfigMap ファイル(redis-configmap.yaml)を参照します。以下のスニペットは、readinessProbe と livenessProbe のスクリプトを示しています。

    readiness.sh: |-
      #!/bin/sh
    
      pingResponse="$(redis-cli -h localhost ping)"
      if [ "$?" -eq "124" ]; then
        echo "PING timed out"
        exit 1
      fi
    
      if [ "$pingResponse" != "PONG"]; then
        echo "$pingResponse"
        exit 1
      fi
    liveness.sh: |-
      #!/bin/sh
    
      pingResponse="$(redis-cli -h localhost ping | head -n1 | awk '{print $1;}')"
      if [ "$?" -eq "124" ]; then
        echo "PING timed out"
        exit 1
      fi
    
      if [ "$pingResponse" != "PONG"] && [ "$pingResponse" != "LOADING" ] && [ "$pingResponse" != "MASTERDOWN" ]; then
        echo "$pingResponse"
        exit 1
      fi

    readiness.sh スクリプトと liveness.sh スクリプトは、redis-cli ping を使用して、Redis サーバーが実行中かどうかを確認します。PONG が返された場合、Redis サーバーは稼働しています。これらのスクリプトは redis-cluster.yaml で使用されます。

    この ConfigMap の Redis パラメータの詳細については、Redis クラスタ チュートリアルで Redis クラスタ構成パラメータのセクションをご覧ください。

  2. ConfigMap をデプロイします。

    kubectl apply -f redis-configmap.yaml
    
  3. readinessProbe と livenessProbe の使用方法を示す StatefulSet(redis-cluster.yaml)スニペットをご覧ください。

    Kubernetes でプローブを構成する方法については、プローブを構成するをご覧ください。

    startupProbe:
      periodSeconds: 5
      timeoutSeconds: 5
      successThreshold: 1
      failureThreshold: 20
      tcpSocket:
        port: redis
    livenessProbe:
      periodSeconds: 5
      timeoutSeconds: 5
      successThreshold: 1
      failureThreshold: 5
      exec:
        command: ["sh", "-c", "/probes/liveness.sh"]
    readinessProbe:
      periodSeconds: 5
      timeoutSeconds: 1
      successThreshold: 1
      failureThreshold: 5
      exec:
        command: ["sh", "-c", "/probes/readiness.sh"]

    ノードプールをアップグレードする場合は、readinessProbe と livenessProbe を使用することを強くおすすめします。これにより、アップグレード中に Pod の準備ができます。

  4. StatefulSet をデプロイします。

    kubectl apply -f redis-cluster.yaml
    
  5. redis-service.yaml という名前のヘッドレス Service は、Redis ノードの接続用です。ヘッドレス Service を作成するために、clusterIP フィールドは None に設定されています。

    サービスをデプロイします。

    kubectl apply -f redis-service.yaml
    
  6. 約 2 分待ってから次のコマンドを使用して、すべての Pod が実行されていることを確認します。

    kubectl get pods
    

    出力は次の例のようになります。

    NAME      READY   STATUS              RESTARTS   AGE
    redis-0   1/1     Running             0          2m29s
    redis-1   1/1     Running             0          2m8s
    redis-2   1/1     Running             0          107s
    redis-3   1/1     Running             0          85s
    redis-4   1/1     Running             0          54s
    redis-5   1/1     Running             0          23s
    
  7. 次のコマンドを実行して、永続ボリュームが作成されたことを確認します。

    kubectl get pv
    

    出力は次の例のようになります。

    NAME       CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM                  STORAGECLASS   REASON   AGE
    pvc-HASH   1Gi        RWO            Delete           Bound    default/data-redis-5   standard                75s
    pvc-HASH   1Gi        RWO            Delete           Bound    default/data-redis-1   standard                2m59s
    pvc-HASH   1Gi        RWO            Delete           Bound    default/data-redis-3   standard                2m16s
    pvc-HASH   1Gi        RWO            Delete           Bound    default/data-redis-2   standard                2m38s
    pvc-HASH   1Gi        RWO            Delete           Bound    default/data-redis-0   standard                3m20s
    pvc-HASH   1Gi        RWO            Delete           Bound    default/data-redis-4   standard                104s
    

    この出力で、HASH は各永続ボリューム名に関連付けられたハッシュを表します。

Redis クラスタにロールを割り当てる

構成が完了したら、Redis クラスタにロールを割り当てます。

次のスクリプトは、Pod の IP アドレスを取得し、各 Pod IP アドレスをコマンドに渡してリーダーとフォロワーのロールを割り当てます。

#!/bin/bash
# Usage: ./roles.sh

urls=$(kubectl get pods -l app=redis -o jsonpath='{range.items[*]}{.status.podIP} ')
command="kubectl exec -it redis-0 -- redis-cli --cluster create --cluster-replicas 1 "

for url in $urls
do
    command+=$url":6379 "
done

echo "Executing command: " $command
$command

Redis クラスタにロールを割り当てるには、次の手順を完了します。

  1. スクリプトを実行します。

    chmod +x ./roles.sh
    ./roles.sh
    
  2. プロンプトが表示されたら、「yes」と入力します。

  3. Redis ノードにログインしてロールを確認します。たとえば、redis-0 にリーダーのロールが割り当てられていることを確認するには、次のコマンドを実行します。

    kubectl exec -it redis-0 -- redis-cli role
    

    出力は次の例のようになります。

    1) "master"
    2) (integer) 574
    3) 1) 1) "10.28.2.3"
           2) "6379"
           3) "574"
    

Redis クライアント アプリケーションをデプロイする

作成した GKE クラスタにアプリケーションをデプロイするには、アプリケーション用の Deployment を定義します。app-deployment.yaml という名前のファイルには、アプリケーションのデプロイメント定義が含まれています。

この Deployment で使用される Probe と Pod のアフィニティ ルールの詳細については、GKE のベスト プラクティス: 高可用性クラスタの設計と構築をご覧ください。

Deployment を作成するには、次の手順を行います。

  1. Deployment を適用します。

    kubectl apply -f app-deployment.yaml
    
  2. ロードバランサ経由でアプリケーションを公開します。

    kubectl expose deployment hello-web \
        --type=LoadBalancer \
        --port 80 \
        --target-port 8080
    
  3. 約 1 分待ってから次のコマンドを実行して、アプリケーションの外部 IP アドレスを取得します。

    kubectl get service
    

    出力から、hello-web's EXTERNAL-IP 列に表示されている値をコピーします。

    NAME             TYPE           CLUSTER-IP    EXTERNAL-IP    PORT(S)              AGE
    hello-web        LoadBalancer   10.13.10.55   EXTERNAL_IP   80:30703/TCP         166m
    
  4. ウェブブラウザに EXTERNAL_IP を貼り付けて、アプリケーションが機能していることを確認します。出力は次の例のようになります。

    I have been hit [1] times since deployment!
    

    アクセス数をメモしておきます。これは、アプリケーションの中断テストで必要になります。

  5. コピーした EXTERNAL_IP の変数を設定します。この値は、次のセクションでアプリケーションのテスト スクリプトを作成するときに使用します。

    export IP=EXTERNAL_IP
    

ノードプールのアップグレードに関するベスト プラクティスを構成する

ステートフル アプリケーションに対して以下のベスト プラクティスを実施し、ノードプールのアップグレード中の可用性を最適化します。

Pod Disruption Budget(PDB)を設定する

Pod Disruption Budget を作成して、自発的な中断中に同時に停止される複製 Pod の数を制限します。これは、アップグレード中に使用可能なレプリカの数のクォーラムが必要なステートフル アプリケーションに役立ちます。

apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
  name: redis-pdb
spec:
  minAvailable: 3
  selector:
    matchLabels:
      app: redis

PDB の定義では:

  • app は、この PDB を適用するアプリケーションを指定します。
  • minAvailable は、中断時に使用できる Pod の最小数を設定します。値または割合(例: 30%)を指定できます。
  • maxUnavailable は、中断中に使用できない Pod の最大数を設定します。値または割合を指定できます。

PDB を設定するには、次の手順を完了します。

  1. PDB をデプロイします。

    kubectl apply -f pdb-minavailable.yaml
    
  2. PDB が作成されていることを確認します。

    kubectl get pdb
    

メンテナンスの時間枠と除外を設定する

ノードの自動アップグレードによってアップグレード プロセスが効率化され、ユーザーに代わってコントロール プレーンがアップグレードされるときに、クラスタ内のノードが最新の状態に保たれます。この機能はデフォルトで有効になっています。詳細は、ノードの自動アップグレードをご覧ください。

メンテナンスの時間枠とメンテナンスの除外を使用すると、時間枠を設定して、GKE クラスタでのメンテナンスの実行のタイミングを制御できます。

  1. 2022 年 8 月 19 日の午前 2 時(UTC)に開始し、4 時間後に終了するようにメンテナンスの時間枠を設定します。このメンテナンスの時間枠は毎日実行されます。この期間中は、自動メンテナンスが許可されます。

    gcloud container clusters update redis-test \
       --maintenance-window-start 2022-08-19T02:00:00Z \
       --maintenance-window-end 2022-08-19T06:00:00Z \
       --maintenance-window-recurrence FREQ=DAILY
    
  2. 除外時間枠を設定して、年末年始にメンテナンスが行われないようにします。このメンテナンスの除外は、no_upgrades スコープを使用します。この期間中は、あらゆる種類の自動メンテナンスが禁止されます。詳細については、除外するメンテナンスの範囲をご覧ください。

    gcloud container clusters update redis-test \
       --add-maintenance-exclusion-name new-year \
       --add-maintenance-exclusion-start 2022-12-26T00:00:00Z \
       --add-maintenance-exclusion-end 2023-01-02T02:00:00Z \
       --add-maintenance-exclusion-scope no_upgrades
    
  3. メンテナンスの時間枠と除外が適用されていることを確認します。maintenancePolicy: の下を確認します。

    gcloud container clusters describe redis-test
    

詳細については、メンテナンスの時間枠と除外を構成するをご覧ください。

ノードのアップグレード戦略を構成する

GKE クラスタ内のノードプールには、Blue/Green アップグレードサージ アップグレードという 2 つのノード アップグレード戦略があります。詳細については、ノードのアップグレード戦略をご覧ください。

Blue/Green アップグレード

ワークロードの中断を許容できず、リソース使用量の増加によって一時的な費用の増加が許容される場合は、Blue/Green アップグレードを選択します。

次のコマンドを実行して、現在のノードプールを Blue/Green アップグレード戦略に変更します。

gcloud container node-pools update default-pool \
--cluster=redis-test \
--enable-blue-green-upgrade \
--location CONTROL_PLANE_LOCATION \
--node-pool-soak-duration=120s

このチュートリアルの目的上、ノードプール ソーク期間は、ノードプールのソークフェーズで時間を節約するために 2 分間に設定しています。このフェーズは、Blue プールノードがドレインされた後、ワークロードの正常性を確認するために使用されます。ノードプール ソーク期間を 1 時間(3,600 秒)またはアプリケーションに最適な期間に設定することをおすすめします。

Pod の割り当ての管理の詳細については、Pod を特定のノードプールにデプロイする特定のノードプールに Service をデプロイするをご覧ください。

Blue/Green アップグレードの構成の詳細については、Blue/Green アップグレードを構成するをご覧ください。

サージ アップグレード

コストの最適化が重要であり、ワークロードが 60 分未満で正常なシャットダウンを許容できる場合は、サージ アップグレードを選択します(GKE では PDB が最大 60 分間適用されます)。

次のコマンドを実行して、現在のノードプールをサージ アップグレード戦略に変更します。

gcloud container node-pools update default-pool \
--max-surge-upgrade=1 \
--max-unavailable-upgrade=0 \
--cluster=redis-test

この構成(maxSurge=1maxUnavailable=0)では、アップグレード中にノードプールにサージノードを 1 つだけ追加できます。このため、一度にアップグレードできるノードは 1 つだけです。この設定では、アップグレード中の Pod の再起動が速くなります。

サージ アップグレードの構成の詳細については、サージ アップグレードを構成するをご覧ください。

現在のノードプールの構成を確認します。

   gcloud container node-pools describe default-pool \
   --cluster redis-test \
   --location CONTROL_PLANE_LOCATION

ノードプールの表示の詳細については、クラスタ内のノードプールを表示するをご覧ください。

アプリケーションをテストする

このセクションでは、アプリケーションにリクエストを送信するスクリプトと、リクエストの成功率を測定する 2 つのスクリプトを使用します。これらのスクリプトを使用して、クラスタをアップグレードしたときに何が起こるかを測定します。

スクリプトを作成するには:

  1. スクリプトが含まれているディレクトリに移動します。

    cd
    cd kubernetes-engine-samples/quickstarts/hello-app-redis/scripts
    
  2. generate_load.sh という名前のスクリプトを参照します。このスクリプトは、秒間クエリ数(QPS)リクエストをアプリケーションに送信します。このスクリプトは、現在のディレクトリの output という名前のファイルに HTTP レスポンス コードを保存します。output の値は、次の手順で作成するスクリプトで使用します。

    #!/bin/bash
    # Usage: ./generate_load.sh <IP> <QPS>
    
    IP=$1
    QPS=$2
    
    while true
      do for N in $(seq 1 $QPS)
        do curl -I -m 5 -s -w "%{http_code}\n" -o /dev/null http://${IP}/ >> output &
        done
      sleep 1
    done
  3. generate_load.sh によって生成される出力に基づいて成功率を計算する print_error_rate.sh という名前のスクリプトを参照してください。

    #!/bin/bash
    # Usage: watch ./print_error_rate.sh
    
    TOTAL=$(cat output | wc -l);
    SUCCESS=$(grep "200" output |  wc -l);
    ERROR1=$(grep "000" output |  wc -l)
    ERROR2=$(grep "503" output |  wc -l)
    ERROR3=$(grep "500" output |  wc -l)
    SUCCESS_RATE=$(($SUCCESS * 100 / TOTAL))
    ERROR_RATE=$(($ERROR1 * 100 / TOTAL))
    ERROR_RATE_2=$(($ERROR2 * 100 / TOTAL))
    ERROR_RATE_3=$(($ERROR3 * 100 / TOTAL))
    echo "Success rate: $SUCCESS/$TOTAL (${SUCCESS_RATE}%)"
    echo "App network Error rate: $ERROR1/$TOTAL (${ERROR_RATE}%)"
    echo "Resource Error rate: $ERROR2/$TOTAL (${ERROR_RATE_2}%)"
    echo "Redis Error rate: $ERROR3/$TOTAL (${ERROR_RATE_3}%)"
  4. スクリプトの実行権限を付与します。

    chmod u+x generate_load.sh print_error_rate.sh
    
  5. QPS の数を格納する変数を設定します。この値は、EXTERNAL_IP に設定した変数と同様に generate_load.sh スクリプトで使用されます。値に 40 を設定することをおすすめします。

    export QPS=40
    
  6. generate_load.sh スクリプトを実行して QPS の送信を開始します。

    ./generate_load.sh $IP $QPS 2>&1
    
  7. generate_load.sh スクリプトを実行したまま、新しいターミナルを開きます。新しいターミナルで、print_error_rate.sh スクリプトを実行してエラー率を確認します。

    cd
    cd kubernetes-engine-samples/quickstarts/hello-app-redis/scripts
    watch ./print_error_rate.sh
    

    QPS が発生すると 100% の成功率と 0% エラー率が表示されます。

  8. 両方のスクリプトを実行したまま、次のセクション用に 3 つ目のターミナルを開きます。

クラスタをアップグレードする

クラスタをアップグレードするには、次の手順を完了します。

  1. redis-test クラスタが使用している GKE のバージョンを確認します。

    V=$(gcloud container clusters describe redis-test | grep "version:" | sed "s/version: //")
    echo $V
    

    出力は 1.22.9-gke.2000 のようになります。

  2. 使用可能な Kubernetes バージョンのリストを取得します。

    gcloud container get-server-config
    
  3. バージョンのリストで validMasterVersions: セクションに移動し、前の手順で取得した redis-test バージョンを探します。ノードと互換性のないバージョンを選択して GKE バージョン スキュー ポリシーに違反しないようにするには、redis-test バージョンの直前にリストされているバージョンをコピーします。

  4. クラスタのコントロール プレーンを選択したバージョンにアップグレードします。プロンプトが表示されたら「y」と入力します。

    gcloud container clusters upgrade redis-test \
        --master \
        --cluster-version VERSION
    

    VERSION は、前のステップで選択したバージョンに置き換えます。

    コントロール プレーンのアップグレードには数分かかります。

  5. クラスタのノードを選択したバージョンにアップグレードします。プロンプトが表示されたら「y」と入力します。

    gcloud container clusters upgrade redis-test \
        --cluster-version=VERSION \
        --node-pool=default-pool
    

    VERSION は、リストで選択したバージョンに置き換えます。

ワークロードの中断をテストする

このセクションでは、アプリケーションのステータスをテストして、ワークロードの中断を観察します。

  1. ./print_error_rate.sh を実行しているターミナル ウィンドウに戻り、アップグレード中の成功率の変化を確認します。アップグレードのためにノードが停止すると、成功率が若干低下し、アプリのネットワーク エラー率がわずかに増加します。

    Success rate フィールドには、ウェブサイトに対して成功したアクセス数が表示されます。この値をメモします。

  2. 関連するターミナルで「CTRL+C」と入力して、両方のスクリプトの実行を停止します。

  3. アプリケーションの IP アドレス(Redis クライアント アプリケーションのデプロイでコピーした EXTERNAL_IP)をブラウザに入力して、アプリケーションのウェブサイトに戻ります。

  4. アプリのアクセス数を確認します。表示された数値は次のようになるはずです。

    ORIGINAL_VISIT_NUMBER + SUCCESSFUL_VISIT_NUMBER

    ORIGINAL_VISIT_NUMBER は、Redis クライアント アプリケーションをデプロイするの最後で記録した数字です。SUCCESSFUL_VISIT_NUMBER は、このセクションの最初のステップで記録した数値です。