返回文章列表

Kubernetes 服務暴露與機器學習最佳實踐

本文探討如何將 Kubernetes 服務暴露給外部應用程式,並深入研究在 Kubernetes 上執行機器學習的最佳實踐。涵蓋 NodePort、負載平衡器、跨叢集服務共用等方法,並提供程式碼範例和 YAML 組態檔案,演示如何佈署和訓練機器學習模型,以及如何使用 MNIST

Kubernetes 機器學習

Kubernetes 提供多種方式將服務暴露給外部,例如使用 NodePort、LoadBalancer 或將外部機器整合到 Kubernetes 叢集中。NodePort 會在每個節點上開啟一個靜態埠,外部應用程式可透過此埠存取服務。LoadBalancer 則利用雲端供應商提供的負載平衡器,將流量導向服務。更進階的作法是將外部機器加入 Kubernetes 叢集,使其參與服務發現和網路機制。此外,跨叢集服務共用可透過虛擬 IP 位址整合,實作不同叢集間的服務互通。選擇合適的暴露方式取決於應用程式的需求和基礎架構環境。

將 Kubernetes 服務暴露給外部應用程式

在前面的章節中,我們討論瞭如何將外部服務匯入 Kubernetes,以及如何將 Kubernetes 服務暴露給外部應用程式。其中一種方法是使用 NodePort 型別的服務。

使用 NodePort 暴露服務

以下是一個 NodePort 服務的 YAML 檔案範例:

apiVersion: v1
kind: Service
metadata:
  name: my-node-port-service
spec:
  type: NodePort
  ...

建立 NodePort 型別的服務後,Kubernetes 會自動選擇一個埠號,您可以從服務的 spec.ports[*].nodePort 欄位中取得該埠號。如果您想要自行選擇埠號,可以在建立服務時指定,但 NodePort 必須在叢集組態的範圍內。預設情況下,此範圍是 30000 到 30999 之間的埠號。

內容解密:

  • apiVersionkind 定義了 Kubernetes 資源的版本和型別。
  • metadata.name 指定了服務的名稱。
  • spec.type 設定為 NodePort,表示這是一個 NodePort 型別的服務。
  • Kubernetes 自動選擇的埠號可以從 spec.ports[*].nodePort 中取得。

將 NodePort 服務暴露給外部應用程式

Kubernetes 的工作在服務暴露於此埠號上已經完成。要將其匯出到叢集外部的現有應用程式,您(或您的網路管理員)需要使其可被發現。根據您的應用程式組態方式,您可以為應用程式提供 ${node}:${port} 對的列表,應用程式將執行客戶端負載平衡。或者,您可能需要在網路中組態實體或虛擬負載平衡器,將流量從虛擬 IP 位址導向到 ${node}:${port} 後端列表。

將外部機器整合到 Kubernetes

如果上述解決方案都不適合您的需求,也許是因為您需要更緊密的動態服務發現整合,那麼將執行應用程式的機器直接整合到 Kubernetes 叢集的服務發現和網路機制中是最後一個選擇。這種方法比前兩種方法更具侵入性和複雜性,只有在必要時才應使用。

將 kubelet 節點加入叢集

  1. 在要加入叢集的機器上執行 kubelet。
  2. 使用 kubectl cordon 命令將節點標記為不可排程,以防止額外的工作被排程到該節點上。

較輕量級的方法

  1. 在機器上以程式方式執行 kube-proxy。
  2. 調整機器的 DNS 伺服器,使其指向 Kubernetes 叢集 DNS 伺服器。

在 Kubernetes 叢集之間共用服務

另一種重要的使用案例是在 Kubernetes 叢集之間連線服務。這可能是為了實作不同區域 Kubernetes 叢集之間的東西向容錯移轉,或者是為了連結不同團隊執行的服務。

跨叢集服務共用的步驟

  1. 在第一個 Kubernetes 叢集中暴露服務,使網路流量能夠流動。
  2. 將虛擬 IP 位址整合到第二個 Kubernetes 叢集中,以實作服務發現。

第三方工具

本章描述了將服務匯入、匯出和連線跨 Kubernetes 叢集和某些外部資源的各種方法。如果您之前有使用服務網格技術的經驗,這些概念可能相當熟悉。有許多第三方工具和專案可以用於互連 Kubernetes 中的服務和任意應用程式及機器。這些工具通常提供了很多功能,但操作上也明顯比前面描述的方法更複雜。

Kubernetes 服務暴露方法比較

圖表翻譯: 此圖示呈現了不同的 Kubernetes 服務暴露方法的比較,包括 NodePort、LoadBalancer、外部機器整合和跨叢集服務共用。每種方法都有其特定的實作方式和適用場景。圖中展示了這些方法之間的關係和差異,有助於讀者理解不同方法的優缺點和適用情況。

在 Kubernetes 中執行機器學習的最佳實踐

在微服務、分散式系統和雲端運算的時代,機器學習模型和工具的普及已經成為可能。大規模的基礎設施現在已經商品化,機器學習生態系統周圍的工具也日趨成熟。Kubernetes 已成為開發人員、資料科學家和開源社群中最受歡迎的平台之一,為機器學習工作流程和生命週期提供了最佳環境。大型機器學習模型如 GPT-4 和 DALL·E 將機器學習推向了聚光燈下,像 OpenAI 這樣的組織也公開了他們使用 Kubernetes 來支援這些模型的事實。

為什麼 Kubernetes 適合機器學習?

Kubernetes 已經迅速成為深度學習快速創新的首選平台。TensorFlow 等工具和函式庫的匯聚使這項技術更容易被廣大的資料科學家所接受。Kubernetes 為什麼成為執行深度學習工作負載的最佳場所?讓我們來看看 Kubernetes 提供了什麼:

無所不在

Kubernetes 無處不在。所有主要的公有雲都支援它,並且有適用於私有雲和基礎設施的發行版。根據像 Kubernetes 這樣的平台構建生態系統工具,允許使用者在任何地方執行深度學習工作負載。

可擴充套件性

深度學習工作流程通常需要存取大量的計算能力,以便有效地訓練機器學習模型。Kubernetes 具有原生自動擴充套件功能,使資料科學家能夠輕鬆實作和調整他們訓練模型所需的規模。

可擴充性

有效地訓練機器學習模型通常需要存取專門的硬體。Kubernetes 允許叢集管理員快速輕鬆地向排程器公開新的硬體型別,而無需更改 Kubernetes 原始碼。它還允許自定義資源和控制器無縫整合到 Kubernetes API 中,以支援專門的工作流程,例如超引數調優。

自助服務

資料科學家可以使用 Kubernetes 按需執行自助式機器學習工作流程,而無需具備 Kubernetes 本身的專業知識。

可移植性

只要工具根據 Kubernetes API,機器學習模型就可以在任何地方執行。這使得機器學習工作負載能夠跨 Kubernetes 提供商移植。

機器學習工作流程

要有效地瞭解深度學習的需求,您必須瞭解完整的機器學習工作流程。下圖表示了一個簡化的工作流程。

@startuml
skinparam backgroundColor #FEFEFE
skinparam componentStyle rectangle

title Kubernetes 服務暴露與機器學習最佳實踐

package "Kubernetes Cluster" {
    package "Control Plane" {
        component [API Server] as api
        component [Controller Manager] as cm
        component [Scheduler] as sched
        database [etcd] as etcd
    }

    package "Worker Nodes" {
        component [Kubelet] as kubelet
        component [Kube-proxy] as proxy
        package "Pods" {
            component [Container 1] as c1
            component [Container 2] as c2
        }
    }
}

api --> etcd : 儲存狀態
api --> cm : 控制迴圈
api --> sched : 調度決策
api --> kubelet : 指令下達
kubelet --> c1
kubelet --> c2
proxy --> c1 : 網路代理
proxy --> c2

note right of api
  核心 API 入口
  所有操作經由此處
end note

@enduml

圖表翻譯: 此圖示展示了機器學習開發工作流程的各個階段,從資料收集到模型監控,每個階段都是緊密相連且不可或缺的。

內容詳解

此圖表清晰地展示了機器學習開發的完整流程。首先,資料收集是整個流程的起點,資料科學家需要從各種來源收集資料。接著,資料預處理階段對收集到的資料進行清理和格式化,以確保資料品質。隨後,模型訓練階段使用預處理後的資料來訓練機器學習模型。訓練完成後,模型評估階段會評估模型的效能,以確定是否需要進一步最佳化。最後,模型佈署階段將經過評估的模型佈署到生產環境中,而模型監控階段則持續監控模型的表現,以確保其持續有效運作。

連線叢集和外部服務的最佳實踐

在實際應用中,並非所有應用程式都是雲原生(cloud native)。建構生產就緒的應用程式通常涉及將現有的系統與較新的應用程式連線起來。本章節描述瞭如何將 Kubernetes 與舊式應用程式整合,以及如何整合執行在多個不同 Kubernetes 叢集中的不同服務。

  • 建立叢集與本地之間的網路連線。網路連線在不同的站點、雲端和叢集組態之間可能有所不同,但首先要確保 Pod 能夠與本地機器通訊,反之亦然。
  • 要存取叢集外部的服務,可以使用無選擇器(selector-less)服務,直接在機器的 IP 位址(例如資料函式庫)中進行程式設計。如果沒有固定的 IP 位址,可以使用 CNAME 服務重定向到 DNS 名稱。如果既沒有 DNS 名稱也沒有固定的服務,可能需要編寫動態運算元,定期將外部服務 IP 位址與 Kubernetes 伺服器端點同步。
  • 要從 Kubernetes 輸出服務,請使用內部負載平衡器或 NodePort 服務。內部負載平衡器通常在公有雲環境中更容易使用,可以直接繫結到 Kubernetes 服務本身。當這種負載平衡器不可用時,NodePort 服務可以在叢集中的所有機器上公開該服務。
  • 可以透過結合這兩種方法實作 Kubernetes 叢集之間的連線,在另一個 Kubernetes 叢集中公開一個服務,然後將其作為無選擇器服務使用。

程式碼範例:使用 Kubernetes 佈署機器學習模型

from kubernetes import client, config

# 載入 Kubernetes 組態
config.load_kube_config()

# 建立一個 Deployment 物件
deployment = client.AppsV1Api().create_namespaced_deployment(
    namespace="default",
    body={
        "apiVersion": "apps/v1",
        "kind": "Deployment",
        "metadata": {"name": "machine-learning-model"},
        "spec": {
            "replicas": 3,
            "selector": {"matchLabels": {"app": "machine-learning-model"}},
            "template": {
                "metadata": {"labels": {"app": "machine-learning-model"}},
                "spec": {
                    "containers": [
                        {
                            "name": "machine-learning-model",
                            "image": "tensorflow:latest",
                            "ports": [{"containerPort": 8501}],
                        }
                    ]
                },
            },
        },
    },
)

print("Deployment 建立成功:", deployment.metadata.name)

內容解密:

此範例程式碼展示瞭如何使用 Python 的 Kubernetes 使用者端函式庫建立一個 Deployment 物件,用於佈署機器學習模型。首先,我們載入了 Kubernetes 組態,然後定義了一個 Deployment 物件,其中包含了模型的容器規格,包括使用的 Docker 映象和容器埠。最後,我們使用 create_namespaced_deployment 方法在指定的名稱空間中建立了這個 Deployment。這個範例展示瞭如何透過程式設計方式自動化佈署機器學習模型到 Kubernetes 叢集。

在 Kubernetes 上進行機器學習工作負載的管理與訓練

Kubernetes 中的機器學習工作流程階段

機器學習工作流程包含了多個重要階段,每個階段都有其特定的需求和挑戰。以下是主要的四個階段:

資料集準備

在這個階段,需要處理資料集的儲存、索引、編目以及相關的元資料。對於本文的目的,我們主要關注儲存方面。資料集的大小差異極大,從幾百 MB 到幾百 TB 甚至 PB 級別。為了滿足這些需求,必須考慮提供適當屬性的儲存解決方案。通常需要大規模的區塊儲存和物件儲存,並且這些儲存需要能夠透過 Kubernetes 原生的儲存抽象層或直接存取的 API 來存取。

模型開發

在這個階段,資料科學家編寫、分享並協作機器學習演算法。開源工具如 JupyterHub 很容易安裝在 Kubernetes 上,因為它們通常像其他工作負載一樣運作。

訓練

為了讓模型能夠使用資料集學習執行其設計的任務,必須對其進行訓練。訓練過程的結果通常是訓練好的模型狀態的檢查點。訓練過程是利用 Kubernetes 所有能力的關鍵部分,包括排程、存取專用硬體、資料集卷管理、擴充套件和網路等都會同時被呼叫以完成此任務。

服務

這是使訓練好的模型能夠接受客戶端服務請求的過程,以便根據客戶端提供的資料進行推理。例如,如果您有一個已經訓練好的影像識別模型,可以檢測狗和貓,客戶端可能會提交一張狗的圖片,而模型應該能夠以一定的準確度確定它是狗。

為 Kubernetes 叢集管理員提供的機器學習

在 Kubernetes 叢集上執行機器學習工作負載之前,有幾個主題需要考慮。本文特別針對叢集管理員。作為負責一組資料科學家的叢集管理員,您將面臨的最大挑戰是理解相關術語。您需要隨著時間的推移熟悉無數的新術語,但請放心,這是可行的。

Kubernetes 上的模型訓練

在 Kubernetes 上訓練機器學習模型需要傳統的 CPU 和圖形處理單元(GPU)。通常,投入的資源越多,訓練完成的速度就越快。在大多數情況下,模型訓練可以在具有所需資源的單一機器上實作。許多雲提供商提供多 GPU 的虛擬機器(VM)型別,因此我們建議在考慮分散式訓練之前,將 VM 縱向擴充套件到四到八個 GPU。資料科學家在訓練模型時使用一種稱為超引數調優的技術。超引數是在訓練過程開始之前具有設定值的引數。超引數調優是為模型訓練找到最佳超引數集的過程。這種技術涉及執行許多相同的訓練作業,但使用不同的超引數集。

在 Kubernetes 上訓練您的第一個模型

使用 MNIST 資料集進行影像分類別模型訓練

在本例中,您將使用 MNIST 資料集來訓練影像分類別模型。MNIST 資料集是公開可用的,並且常用於影像分類別。

首先,您需要確認您的 Kubernetes 叢集具有可用的 GPU。以下命令顯示了 Kubernetes 叢集中可用的 GPU 數量:

$ kubectl get nodes -o yaml | grep -i nvidia.com/gpu
nvidia.com/gpu: "1"
nvidia.com/gpu: "1"
nvidia.com/gpu: "1"
nvidia.com/gpu: "1"

建立並執行訓練作業

建立一個名為 mnist-demo.yaml 的檔案,並將以下內容儲存到您的檔案系統中:

apiVersion: batch/v1
kind: Job
metadata:
  labels:
    app: mnist-demo
  name: mnist-demo
spec:
  template:
    metadata:
      labels:
        app: mnist-demo
    spec:
      containers:
      - name: mnist-demo
        image: lachlanevenson/tf-mnist:gpu
        args: ["--max_steps", "500"]
        imagePullPolicy: IfNotPresent
        resources:
          limits:
            nvidia.com/gpu: 1
      restartPolicy: OnFailure

建立此資源在您的 Kubernetes 叢集上:

$ kubectl create -f mnist-demo.yaml
job.batch/mnist-demo created

檢查您剛建立的作業狀態:

$ kubectl get jobs
NAME         COMPLETIONS   DURATION   AGE
mnist-demo   1/1           31s        49s

檢視 Pod,您應該可以看到正在執行的訓練作業:

$ kubectl get pods
NAME              READY   STATUS    RESTARTS   AGE
mnist-demo-8lqrn   1/1     Running   0          63s

檢視 Pod 日誌,您可以看到正在進行的訓練:

$ kubectl logs mnist-demo-8lqrn
2023-02-10 23:14:42.007518: I tensorflow/core/platform/cpu_feature_guard.cc:137] Your CPU supports instructions that this TensorFlow binary was not compiled to use: SSE4.1 SSE4.2 AVX AVX2 FMA
2023-02-10 23:14:42.205555: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1030] Found device 0 with properties:
name: Tesla K80 major: 3 minor: 7 memoryClockRate(GHz): 0.8235
pciBusID: 0001:00:00.0
totalMemory: 11.17GiB freeMemory: 11.12GiB
2023-02-10 23:14:42.205596: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1120] Creating TensorFlow device (/device:GPU:0) -> (device: 0, name: Tesla K80, pci bus id: 0001:00:00.0, compute capability: 3.7)
...
Accuracy at step 0: 0.0886

程式碼解析:

上述 YAML 組態檔案定義了一個 Kubernetes Job,用於執行 MNIST 資料集上的 TensorFlow 訓練任務。主要組態包括:

  • 使用 lachlanevenson/tf-mnist:gpu 映象,該映象內含 TensorFlow 和 MNIST 資料集的訓練程式碼。
  • 設定 args["--max_steps", "500"],表示將訓練過程限制在 500 步以內。
  • 指定 resources.limits.nvidia.com/gpu1,表示該作業需要使用一個 GPU 資源。
  • 設定 restartPolicyOnFailure,表示當容器失敗時會自動重啟。

圖表說明:

此圖示呈現了 Kubernetes 中執行機器學習工作流程的主要階段,包括資料準備、模型開發、訓練和服務四個階段。每個階段都有其特定的需求和挑戰。 圖表翻譯: 此圖表展示了在 Kubernetes 環境中進行機器學習工作的流程。首先,在資料準備階段,需要處理和儲存大量的資料集。接著,在模型開發階段,資料科學家會編寫和測試機器學習演算法。在訓練階段,使用準備好的資料集對模型進行訓練,以使其能夠執行特定的任務。最後,在服務階段,已經訓練好的模型會被佈署,以接受客戶端的請求並提供預測結果。每個階段都需要特定的資源和技術支援,例如 GPU 加速和高效的儲存解決方案。