2021/08/17

[GKE]nodeAffinityを活用してDBとその他アプリケーションを別々のノードにデプロイする

gkek8s

GKEには、「自動スケーリング」という便利な機能がありますが、以下の点で注意が必要です。

GKEのノードが負荷を検知して、自動スケーリングする際に、既存のnodeを頻繁にシャットダウンしたり、新しいnodeを作成したりします。その際に、自動スケーリングに対応しているpoolにデプロイされているpodが再起動します。

Rest API Serverなどのステートレスなアプリケーションは、そのようなnode数の変更(再起動)にうまく対応してくれますが、PostgresqlやRedisなどのステートフルなアプリケーションが頻繁に再起動するのには(かなり)問題があります。

なので、ステートレスなアプリケーションをデプロイするノードプールとステートフルなアプリケーションをデプロイするノードプールとを分ける必要があり、それらに以下のようにして対応しました。

  • 2つのノードプールを作成する
    • 自動スケーリングがオフのノードプール
    • 自動スケーリングがオンのノードプール
  • affinity.nodeAffinityを使って、アプリケーションをデプロイする
    • ステートフルなアプリケーションは自動スケーリングがオフのノードプールのノードにデプロイ
    • ステートレスなアプリケーションは自動スケーリングがオンのノードプールのノードにデプロイ

※ 規模が大きいアプリケーションの場合は、Postgresql専用のノードプールを作るなど、アプリケーション毎に分けるのが最適かなと思いますが、私がよく対応する小〜中規模のものはステートレスかステートフルかを分けるくらいが管理が楽でちょうど良いのでそうしています。

概要図

以下のような感じになります

gke-cluster-pool-min.png

ノードプールの作成

  • “fixed-sized-pool” という名前で、自動スケーリングがオフのノードプールを作成します。
  • “app-pool” という名前で、自動スケーリングがオンのノードプールを作成します。

※ マシンタイプは実際に稼働させてみて、メモリ使用率やCPU使用率などをみて最適なものを選ぶと良いと思います。私の場合は、fixed-sized-poolにはちょっとスペックの良いものを選び、メモリがちょっと多めのものをよく選びます。逆にapp-poolではCPUが少し多めで、スペック自体はやや悪いものを選びます。(負荷が高まると複数のnodeが立ち上がるので、一つ一つはスペック悪めですが、負荷に応じてスケールアウトするような感じにしています。)

アプリケーションのデプロイ

ステートフルアプリケーションの場合

affinity.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecutionを活用してfixed-size-poolという名前のノードプールのノードにしかデプロイされないように指定します。

以下のような感じです。

apiVersion: apps/v1
kind: StatefulSet
spec:
  ..中略..
  template:
    ..中略..
    spec:
      affinity:
        nodeAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
            nodeSelectorTerms:
              - matchExpressions:
                  - key: "cloud.google.com/gke-nodepool"
                    operator: In
                    values:
                      - fixed-size-pool

ステートレスアプリケーションの場合

affinity.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecutionを活用してapp-poolという名前のノードプールのノードにしかデプロイされないように指定します。

apiVersion: apps/v1
kind: Deployment
spec:
  replicas: 3
  ..中略..
  template:
    ..中略..
    spec:
      affinity:
        nodeAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
            nodeSelectorTerms:
              - matchExpressions:
                  - key: "cloud.google.com/gke-nodepool"
                    operator: In
                    values:
                      - app-pool

Tips

ちなみにk8sクラスターのnodeをラベル付きで表示する場合は、以下のコマンドをうって確かめます。

$ kubectl get nodes --show-labels

上で記載した、cloud.google.com/gke-nodepoolのようなkey名もこのコマンドで確認できます。

こちらの構成で、運用がかなり安定しました。

以上です。