KEDA を用いた Kubernetes Selenium Grid のスケーリング

KEDA を利用した Kubernetes クラスタ内での Selenium Grid のスケーリング

問題点

Selenium Grid と Kubernetes の経験があれば、スケーリングの問題に遭遇するでしょう。Kubernetes (K8S) は、アプリケーションの CPU およびメモリ使用量に基づいてスケールアップおよびスケールダウンを行うのに優れていますが、Selenium Grid のようなアプリケーションに関しては、それほど簡単ではありません。

この問題は、こちらのブログ記事で非常によく説明されています。 要するに、Kubernetes に組み込まれている Horizontal Pod AutoScaler (HPA) は、(デフォルトで)リソース消費量をチェックして、デプロイメントをスケールアップまたはスケールダウンする必要があるかどうかを判断します。 これは、Selenium Grid にとっていくつかの理由から問題になります。

  1. ブラウザポッドは、現在のテストの需要に応じて可変量のリソースを使用します。 これは、すべてのブラウザポッドが使用中であるにもかかわらず、HPA がスケールアップが必要であると判断するのに十分な CPU 使用率がない可能性があり、テストが不必要にキューで待機することになることを意味します。
  2. Kubernetes がデプロイメントをスケールダウンすることを決定した場合、(ほとんどの場合) ランダムに実行されます。 20 個のポッドで 10 個のテストを実行していて、スケールダウンする必要がある場合があります。 終了を要求されたポッドの少なくとも 1 つがまだテストを実行している可能性が高く、接続エラーが発生します。

KEDA はどのように役立つか

KEDA は、無料およびオープンソースの Kubernetes イベントドリブンオートスケーリングソリューションであり、K8S の HPA の機能セットを拡張します。 これは、コミュニティによって作成されたプラグインを介して行われ、KEDA のメトリクスサーバーに、特定のデプロイメントをスケールアップおよびスケールダウンするために必要な情報を提供します。

Selenium Grid に特化して、必要な情報を取得するためにグリッドに結び付ける プラグイン があります。 使用されるトリガーの例

triggers:
  - type: selenium-grid
    metadata:
      url: 'http://selenium-grid-url-or-ip:4444/graphql'
      browserName: 'chrome'
      platformName: 'Linux'

これらはすべて、次のような Scaled-Object として保存されます。

apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
  name: selenium-chrome-scaledobject
  namespace: <namespace of your browser pods>
  labels:
    deploymentName: selenium-chrome-node-deployment
spec:
  minReplicaCount: 0
  maxReplicaCount: 80
  scaleTargetRef:
    name: selenium-chrome-node-deployment
  triggers:
    - type: selenium-grid
      metadata:
        url: 'http://selenium-grid-url-or-ip:4444/graphql'
        browserName: 'chrome'
        platformName: 'Linux'

リクエストを Grid に送信します。たとえば、Python クライアントで

options = ChromeOptions()
options.set_capability('platformName', 'Linux')
driver = webdriver.Remote(options=options, command_executor='http://selenium-grid-url-or-ip:4444/wd/hub')

追加のボーナスとして、KEDA を使用すると、通常の標準 HPA では許可されていない、使用されていない場合にデプロイメントを 0 までスケールダウンできます。

詳細については、KEDA の Scaled-Object のドキュメント を参照してください。

これを実装する方法の完全な例は記事の後半にありますが、KEDA は 2 つの問題の 1 つを解決します。 これで、Selenium Grid の実際の負荷に基づいて適切にスケールアップおよびスケールダウンできます。 残念ながら、スケールダウンは、ポッドがまだテストを実行していて、完了する前に終了するように指示される可能性が高いという結果になります。

PreStop と Drain の使用

これに対抗するために、K8s の PreStop と Selenium Grid の Drain 機能を組み合わせて使用​​します。

  • PreStop を使用すると、コンテナに停止するように指示される前に、完了まで実行されるコマンドまたはコマンドチェーンを設定できます。
  • Drain は、Selenium ブラウザポッドに現在のテストを完了してからシャットダウンするように指示します。

これらを組み合わせると、ブラウザポッドの yaml は次のようになります。

spec:
  template:
    spec:
      terminationGracePeriodSeconds: 3600
      ...
      ...
      containers:
        lifecycle:
          preStop:
            exec:
              command: ["/bin/sh", "-c", "curl --request POST 'localhost:5555/se/grid/node/drain' --header 'X-REGISTRATION-SECRET;'; tail --pid=$(pgrep -f '[n]ode --bind-host false --config /opt/selenium/config.toml') -f /dev/null; sleep 30s"]

これを分解すると

  • terminationGracePeriodSeconds は、ポッドに強制終了される前に正常に終了するために与える時間として希望する長さに設定されます。 この例では、ポッドに終了を要求されたときにテストを完了するために 60 分を与えます。 これの一部としてクラスターノードもスケーリングしている場合は、クラスターノードの終了猶予期間も長くする必要がある場合があります。
  • ポッドに停止するように指示されると、最初に PreStop コマンドが実行されます。
  • ポッドの localhost を curl して、ドレインするように指示します。 ポッドは新しいセッションリクエストを受け入れなくなり、現在のテストを完了します。 これに関する詳細については、Selenium Grid ドキュメント を参照してください。
  • 次に、ノードがドレインされるまで実行され続ける内部ノードプロセスを tail します。
  • この後、完全な終了コマンドを発行する前に、ポッドに他のことを完了するのに 30 秒を与えます。

これで、アプリケーションは Selenium ブラウザのデプロイメントを安全にスケールダウンできるようになりました!

開始から完了まで

KEDA のインストール

  • バージョン 2.8.0 以降を使用する必要があります。最新のバージョン番号は、Selenium Grid Scaler ドキュメント で確認できます。
  • kubectl apply -f https://github.com/kedacore/keda/releases/download/<バージョン番号>/keda-<バージョン番号>.yaml

Scaled-Object の作成と適用

前述のように、Scaled Object は次のようになります。

apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
  name: selenium-chrome-scaledobject
  namespace: <namespace of your browser pods>
  labels:
    deploymentName: selenium-chrome-node-deployment
spec:
  minReplicaCount: 0
  maxReplicaCount: 80
  scaleTargetRef:
    name: selenium-chrome-node-deployment
  triggers:
    - type: selenium-grid
      metadata:
        url: 'https://selenium-grid-url-or-ip:4444/graphql'
        browserName: 'chrome'

スケールしたいブラウザごとにこれらが必要になります。

編集する項目

  1. namespace は、Selenium ブラウザポッドが存在する名前空間である必要があります
  2. deploymentName は、ブラウザデプロイメントの名前です
  3. (spec 内の)`name` も、ブラウザデプロイメントの名前です
  4. url は、Selenium Grid の URL です
  5. browserName は、使用しているブラウザの名前です
  6. minReplicaCount と `maxReplicaCount` は、必要なポッドの最小数と最大数です

Edge でスケールする場合、KEDA のバージョン `2.8.0` 以降が必要であり、トリガーメタデータに `sessionBrowserName` も含める必要があります

triggers:
    - type: selenium-grid
      metadata:
        url: 'https://selenium-grid-url-or-ip:4444/graphql'
        browserName: 'MicrosoftEdge'
        sessionBrowserName: 'msedge'

これは、キュー内の Edge セッションとアクティブなセッション間の名前の変更によるものであり、このプルリクエストを通じて対処されています。

準備ができたら、yaml ファイルとして保存し、次のように適用します。

  • kubectl apply -f ./<scaled-object-file-name>.yaml --namespace=<browser_namespace>

ブラウザポッドへの PreStop コマンドの追加

  1. デプロイメントの `terminationGracePeriodSeconds` を、ポッドに正常に終了するために与える最大時間に設定します。 K8s プロバイダーによって異なりますが、ノードプールの猶予期間も長くする必要がある場合があります。
  2. PreStop コマンドをコンテナライフサイクルスペックに追加します。
spec:
  template:
    spec:
      terminationGracePeriodSeconds: 3600
      ...
      ...
      containers:
        lifecycle:
          preStop:
            exec:
              command: ["/bin/sh", "-c", "curl --request POST 'localhost:5555/se/grid/node/drain' --header 'X-REGISTRATION-SECRET;'; tail --pid=$(pgrep -f '[n]ode --bind-host false --config /opt/selenium/config.toml') -f /dev/null; sleep 30s"]

以上で、Selenium Grid ポッドはセッションを失うことなく適切にスケールアップおよびスケールダウンするはずです!