使用 KEDA 擴展 Kubernetes Selenium Grid

在 Kubernetes 叢集中使用 KEDA 擴展 Selenium Grid

問題

如果您有使用 Selenium Grid 和 Kubernetes 的經驗,您可能會遇到擴展的問題。Kubernetes (K8S) 在根據應用程式的 CPU 和記憶體使用率來擴展和縮減應用程式方面表現出色,但在處理像 Selenium Grid 這樣的應用程式時,就沒有那麼直接了。

這個問題在這篇部落格文章中描述得相當清楚。但簡而言之,Kubernetes 內建的 Horizontal Pod AutoScaler (HPA) 預設會檢查資源消耗,以判斷是否需要擴展或縮減部署。這對於 Selenium Grid 來說會成為一個問題,原因如下:

  1. 瀏覽器 Pod 使用的資源量會根據目前測試的需求而有所不同。這表示您所有的瀏覽器 Pod 可能都在使用中,但 CPU 使用率不足以讓 HPA 判斷需要擴展,導致測試不必要地在佇列中等待。
  2. 當 Kubernetes 決定縮減部署時,它(在很大程度上)是隨機執行的。您可能有 10 個測試在 20 個 Pod 上執行,並且需要縮減規模。很可能至少有一個被要求終止的 Pod 仍在執行測試,導致連線失敗。

KEDA 如何提供協助

KEDA 是一個免費且開源的 Kubernetes 事件驅動自動擴展解決方案,它擴展了 K8S 的 HPA 功能集。這是透過社群編寫的外掛程式來完成的,這些外掛程式將 KEDA 的指標伺服器所需的資訊饋送給它,以擴展和縮減特定的部署。

特別是對於 Selenium Grid,我們有一個外掛程式,它將連接到我們的 Grid 以取得它所需的資訊。以下是使用的觸發器範例:

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

所有這些都將儲存為一個擴展物件,如下所示:

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 允許我們在沒有使用時將我們的部署縮減到 0,這是標準的 HPA 所不允許的。

請查看KEDA 中擴展物件的文件以取得更多詳細資訊。

如何在文章的後面找到完整的實作範例,但 KEDA 解決了我們的兩個問題之一。現在我們可以根據 Selenium Grid 上的實際負載來正確地擴展和縮減。不幸的是,縮減規模仍然可能導致 Pod 仍在執行測試,並且在完成之前被告知終止。

使用 PreStop 和 Drain

為了解決這個問題,我們將結合使用 K8s PreStop 和 Selenium Grid 的 Drain 功能。

  • PreStop 允許我們設定一個命令或一系列命令,這些命令在容器被告知停止之前完成執行。
  • Drain 告訴 Selenium 瀏覽器 Pod 完成其目前的測試,然後關閉。

在我們的瀏覽器 Pod 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 設定為您希望給予 Pod 在被強制終止之前優雅終止的時間長度。在本例中,我給 Pod 60 分鐘的時間在被要求終止時完成其測試。如果您也將叢集節點作為此過程的一部分進行擴展,您可能還需要增加叢集節點的終止寬限期。
  • 當 Pod 被告知停止時,會先執行 PreStop 命令。
  • 我們 curl Pod 的 localhost 以告知它 drain。Pod 將不再接受新的會話請求,並將完成其目前的測試。有關此內容的更多資訊,請可在 Selenium Grid 文件中找到
  • 然後,我們 tail 內部節點進程,該進程將繼續執行,直到節點被 drain 為止。
  • 在此之後,我們給 Pod 30 秒的時間來完成任何其他操作,然後再發出完整的終止命令。

這樣一來,我們的應用程式現在可以安全地縮減 Selenium 瀏覽器部署了!

從開始到結束

安裝 KEDA

  • 您需要使用 2.8.0 或更高版本,您可以在Selenium Grid Scaler 文件中找到最新的版本號碼。
  • kubectl apply -f https://github.com/kedacore/keda/releases/download/<Version_Number_Here>/keda-<Version_Number_Here>.yaml

建立並套用您的擴展物件

如前所述,您的擴展物件將如下所示:

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 瀏覽器 Pod 所在的命名空間
  2. deploymentName 是您的瀏覽器部署的名稱
  3. name(在 spec 內)也是您的瀏覽器部署的名稱
  4. url 是您的 Selenium Grid 的 URL
  5. browserName 是您正在使用的瀏覽器的名稱
  6. minReplicaCountmaxReplicaCount 是您想要擁有的最小和最大 Pod 數量

如果您計畫使用 Edge 進行擴展,您將需要至少 2.8.0 版本的 KEDA,並且還需要在觸發器 metadata 中包含 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 命令新增至您的瀏覽器 Pod

  1. 將您部署的 terminationGracePeriodSeconds 設定為您希望給予 Pod 優雅終止的最大時間。同樣,您可能還需要增加節點池的寬限期,這將因您的 K8s 提供商而異。
  2. PreStop 命令新增至容器生命週期 spec
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 Pod 現在應該可以正確地擴展和縮減,而不會遺失任何會話!