探索 Kubernetes 多租戶解決方案

簡介

Kubernetes 中的多租戶會帶來各種複雜的挑戰,例如安全性、公平性和資源分配。本博客討論了與多租戶相關的挑戰,以及爲名爲 Labs4grabs.io 的基於 Kubernetes 的學習平臺所做的技術選擇。我將探討兩個關鍵技術 vCluster 和 Kubevirt 的需求、優勢和劣勢。這些技術在開發 Labs4grabs.io 的後端時進行了試驗。儘管 vCluster 非常出色,但我還是決定完全放棄它。

譯自 Exploring Multi-Tenancy Solutions for my Kubernetes Learning Platform。

關於 Labs4grabs.io

我的平臺是一個 Kubernetes 學習平臺,旨在通過實驗環境模擬現實中的問題。但是,除了每個實驗的簡要描述和幾條提示之外,人們需要自行研究和解決問題,指導很少。

實驗內容基於我在 Berops 公司擔任 Kubernetes 工程師時遇到的真實問題,以及我以前在該領域的經驗。

挑戰是直接從 Slack 開始的。

什麼是多租戶?

Kubernetes 的多租戶類似於管理公寓大樓,不同租戶共享空間。每個租戶都需要自己的空間,如浴室、廚房和臥室,以及水、煤氣、電等公共設施。但最重要的是,公寓的租戶不能進入其他人的區域或使用其他人的設施。此外,如果其他租戶進入他們的私人區域,每個租戶都會深感不快。這意味着其他租戶的生活質量降低。

Kubernetes 中的租戶情況也是如此,他們不能訪問其他租戶的資源、網絡帶寬等。這會降低想在我的平臺上提高 Kubernetes 技能的人的體驗質量。此外,與公寓租戶不同的另一個最重要的組成部分是主機系統。

如果租戶能逃出自己的環境進入主機系統,影響其他租戶,使用全部計算能力進行挖礦或其他活動,那將是最大的災難。在爲 Labs4grabs.io 租戶環境選擇正確的多租戶技術時,這是我最擔心的。

主機平臺的注意事項:我選擇了 Kubernetes 作爲託管租戶環境的平臺。Kubernetes 具有調度、網絡、存儲管理、安全等功能,最重要的是背後有強大的社區支持可以解答我可能遇到的任何問題。*

租戶部署在工作節點上的宿主 Kubernetes 集羣。

多租戶的挑戰

在選擇和測試正確的解決方案時,有幾個因素需要考慮:

最簡單的多租戶形式是爲每個學生配置一個新的 Kubernetes 用戶,提供證書和密鑰以訪問主機集羣命名空間。這種解決方案簡單但風險較大,需要學生通過主機集羣的 API 服務器訪問實驗環境。但是,這種方法可能導致問題,例如學生創建過多 NodePort 服務,降低其他學生的體驗,或者向 API 服務器發送數百萬請求,影響合法用戶的性能。

雖然使用 Kyverno、Gatekeeper 等策略引擎有助於防止用戶違反某些規則,但需要通過大量試錯爲每個實驗正確配置策略。此外,這些策略可能會限制學生創建自己的命名空間、訪問根文件系統或部署特權容器等,這些都是 Kubernetes 學習的重要方面。

主機 Kubernetes 集羣中,學生無法直接訪問主機集羣的控制平面,但可以自由訪問他們聲明的環境。

vCluster

vCluster 是在主機 Kubernetes 集羣之上運行的 Kubernetes 集羣。vCluster 不具備自己的節點池或網絡,它會在主機集羣內調度工作負載,同時維護自己的控制平面。

vCluster 是我的多租戶問題的絕佳解決方案。它提供了速度、更好的安全性和易用性。最出色的是 syncer 功能,它可以複製租戶環境中的學生創建的資源到主機集羣上。您可以指定要複製的資源類型和數量。這個功能改變了我可以爲學生提供的內容。

比如在第一個中級實驗 “調試 Python Flask 應用程序” 中使用了 syncer,允許學生創建 ingress 資源並使他們的應用程序在主機集羣上可公開訪問。

vCluster 在租戶上創建了一個指向主機集羣上調度的 pod 的假服務和 pod。

演示

在這個演示中,我們將在student名字空間中創建一個基本的 vCluster 租戶環境。然後我們將在這個租戶環境中創建一個 NGINX pod,並通過 NodePort 服務將其暴露出來。這個服務將被同步到主集羣中,允許從外界通過主機的公網 IP 訪問 NGINX pod。

  1. 使用特權 pod 安全准入創建student名字空間:
apiVersion: v1
kind: Namespace
metadata:
  name: student
  labels:
    pod-security.kubernetes.io/enforce: privileged
  1. 創建 vCluster 租戶環境:
vcluster create tenant -n student --connect=false
  1. 查看 vCluster 租戶環境中運行的所有 pod:
$ vcluster connect tenant --namespace student -- kubectl get pods -A
NAMESPACE     NAME                       READY   STATUS    RESTARTS   AGE
kube-system   coredns-5c96599dbd-fsmwj   1/1     Running   0          116s
  1. 在租戶環境內創建一個 NGINX pod,然後通過 NodePort 服務暴露,並通過主機的公網 IP 訪問:
$ vcluster connect tenant --namespace student -- kubectl run pod nginx --image nginx
service/nginx exposed
$ vcluster connect tenant --namespace student -- kubectl expose pod nginx --type=NodePort --port 80
service/nginx exposed
$ kubectl get service -n student
NAME                                TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)                  AGE
....
nginx-x-default-x-tenant            NodePort    10.43.229.163   <none>        80:31871/TCP             2m36s
$ curl $HOST_PUBLIC_IP:31871
<!DOCTYPE html>
GENERIC NGINX OUTPUT
</html>

您可以不通過vcluster命令連接到 vCluster,而是通過公共 kubeconfig,這是我的實驗要求,因爲不能要求學生安裝 vCluster 命令行並期望獲得 “真正的 Kubernetes 體驗”。

優點

sync:
  services:
    enabled: true
  ingresses:
    enabled: true
  persistentvolumeclaims:
    enabled: true

缺點

總而言之,vCluster 的限制會嚴重限制我想爲學生提供的學習平臺中的某些內容和場景。雖然 syncer 確實啓用了其他解決方案無法實現的某些內容訪問,但它也會阻止比它允許的更多的內容,這與我對該平臺的目標不符。此外,我仍可以探索開發自定義 syncer 以在更小規模上覆制 vCluster 同步器的功能的可能性。然而,我放棄了 vCluster,決定採用虛擬化。

Firecracker 和 Kata 容器

這是關於 Firecracker 和 Kata 容器這兩項技術的概述,它們在 Kubernetes 中啓用了 Firecracker 運行時。我調查並實驗了這些技術,但決定不使用 Kata 容器,因爲它需要爲 Firecracker 運行時進行額外的配置,特別是與設備映射器相關的配置,這讓我感到不太舒服。配置 Firecracker 容器的 SSH 連接也需要額外的步驟,這可能會導致一個非常大的容器,可能無法實現我想要的結果:一個基本的 Kubernetes 集羣,具有完整的操作系統,我可以隨意破壞。

Kubevirt

考慮到 vCluster 在可能缺少的學習內容選項方面的限制,研究指向了虛擬化。這種方法在安全性和與宿主系統的完全隔離方面提供了保證,允許使用完整的操作系統和無限的學習內容。

幸運的是,Kubernetes 中有兩種可用的虛擬化技術:Kubevirt 和 Virtlet。

演示

在查看演示之前,您需要根據此指南安裝 Kubevirt 和 QEMU 虛擬化程序。

與 vCluster 相比,實際實驗環境的安裝更加複雜,因爲需要考慮用戶數據腳本和存儲等因素。但是我將省略這些細節,重點關注最重要的方面。在這個演示中,我將使用通用的容器磁盤鏡像。

下面的 YAML 是每個實驗環境中的三個虛擬機之一的定義,有一個控制平面節點,兩個工作節點,用戶數據略有不同。

  1. 包含向 ubuntu 用戶添加自定義 SSH 密鑰的用戶數據的 Kubevirt 虛擬機定義:
apiVersion: kubevirt.io/v1
kind: VirtualMachine
metadata:
  labels:
    kubevirt.io/vm: myvm
  name: controlplane
  namespace: student
spec:
  running: true
  template:
    metadata:
      labels:
        kubevirt.io/vm: myvm
    spec:
      # 詳細配置
      volumes:
      - name: datavolumedisk1
        containerDisk:
          image: "quay.io/containerdisks/ubuntu:22.04"
      - name: cloudinitdisk
        cloudInitNoCloud:
          userData: | 
            #!/bin/bash
            echo "ssh-rsa public key" >> /home/ubuntu/.ssh/authorized_keys
  1. 創建提供對虛擬機的 SSH 訪問的服務。
apiVersion: v1
kind: Service
metadata:
  name: ssh
  namespace: student
spec:
  type: NodePort
  ports:
  - port: 27017
    targetPort: 22
  selector:
    kubevirt.io/vm: myvm
  1. 使用自定義 SSH 密鑰直接 SSH 訪問:
$ ssh -i ~/.ssh/student ubuntu@$HOST_PUBLIC_IP -p <NODE_PORT> 
...
ubuntu@controlplane:~$ ls /
# 列出根目錄

工作原理

KubeVirt 虛擬機是一種允許在 Kubernetes 生態系統中運行虛擬化實例的工具。從本質上講,KubeVirt 虛擬機是一個 Pod,與 QEMU 虛擬機實例緊密耦合。

Kubevirt 有幾個組件,但我將重點介紹與我的用例相關的兩個重要組件: VirtualMachine 和 VirtualMachineInstance。這兩個組件都作爲自定義資源定義 (CRD) 部署到主機 Kubernetes 集羣中。

當新的VirtualMachine定義被添加到 Kubernetes API 時,Kubevirt 執行以下步驟:

  1. virt-handlerDaemonSet Pod 生成一個virt-launcherPod。

  2. virt-launcher創建一個VirtualMachineInstance對象。

  3. virt-launcher使用來自VirtualMachine定義的轉換定義查詢 libvirtd API。

  4. libvirtd 啓動 QEMU 虛擬機。

  5. VirtualMachine組件的任何更新都反映在 QEMU 虛擬機實例上

更多詳細信息請訪問 Kubevirt 官網。

容器磁盤與持久存儲卷 (PVC)

您可以在 PVC 或容器鏡像磁盤上運行 Kubevirt 實例,這些鏡像是作爲容器安裝的整個操作系統的快照。

最初,我嘗試使用 PVC,但操作很複雜。我必須創建一個包含所有 Kubernetes 組件的通用 “金” PVC,跨命名空間克隆它需要約 3 分鐘。其他平臺的克隆可以實現瞬時完成!

因此我嘗試使用容器磁盤鏡像。一旦這些鏡像被拉取到我的 Kubernetes 集羣上,初始化新環境的速度會更快,大概需要 90 秒左右。

儘管有所改進,時間仍然較長。爲進一步優化,我創建了一組預置就緒的租戶環境緩存。這將初始化環境的時間縮短到不到 30 秒,達到可接受的程度。但我仍計劃在未來進一步加快速度。

緩存由運行在主機集羣的預置就緒環境數據庫組成。學生啓動實驗時,相應的就緒環境會從數據庫中刪除並分配給學生。緩存使用定時任務定期進行補充。

Kubevirt 的資源消耗

爲優化實驗節點的資源管理,我將每個節點的 CPU 上限從 100m 提高到 1000m。這一調優帶來更快的啓動和響應。

內存方面,控制平面分配 1.5G,每個節點分配 1G,每個 Kubevirt 虛擬機實例會額外消耗 100MB 內部容器。每個實驗環境消耗約 3.5-4G 的內存和 3 個 CPU 核。在 64G 機器上,可以同時運行約 12 個並行實驗環境,同時考慮其他組件和虛擬機上沒有挖礦程序。

網絡

網絡安全主要通過限制性的網絡策略來管理。這些策略可以有效防止訪問其他學生環境和其他 Kubernetes 組件。僅允許三個 Kubernetes 組件間相互通信,學生可以通過 SSH 或 kubeconfig 直接與控制平面通信。然後,再從控制平面節點跳轉至 node1 或 node2。

學生視角的網絡拓撲

優勢

劣勢

租戶 Kubernetes 版本

操作系統爲 Ubuntu 22.04。而 Kubernetes 版本,我選擇在租戶環境上使用 Kubeadm 安裝 Kubernetes。最初,我計劃使用 k3s 或其變體,但我意識到,如果故意造成 k3s 故障,k3s 二進制文件就直接無法啓動。因此,調試 k3s 可能需要找到正確的命令,這與我想要創建的內容的重點不符。

我的意圖是通過故意損壞各種層級,從操作系統到 Kubernetes 的各個組件,來教授 Kubernetes 的工作原理。Kubeadm 版本是我能想到的唯一一個可以實現我的願景的選擇。

總結

總的來說,我的決定是基於我可以爲學生提供的內容,而不考慮所使用技術的缺點。儘管 Kubevirt 在資源消耗等方面可能不夠完美,但我可以通過每月分配略高一些的預算來緩解。我可以在 Hetzner 拍賣中輕鬆租用大型裸機服務器,每個月不到 50 歐,提供足夠的內存和計算能力。我最終認爲,Kubevirt 可能有一些劣勢,但它可以提供更加靈活的學習內容,同時通過虛擬化提供更高的安全性。

挑戰

我最大的挑戰一直是,現在也是,平時對各種技術有所瞭解,但在有效集成應用方面艱難前行。我嘗試瞭解熟悉的技術,遇到障礙,然後轉向其他技術,浪費了大量時間。雖然目前一切都運行良好,但我相信仍有很大的改進空間。

例如,在放棄 vCluster 後, 我嘗試使用 Packer 爲 Kubevirt 構建實驗環境的金鏡像。然而,我後來也放棄了 Packer,因爲我已經使用了 Ansible 初始化,不需要再添加新的工具。

當前,我正在根據學生反饋和待辦事項的 Backlog,持續改進基礎架構。然而,進度緩慢,因爲我同時需處理內容、用戶體驗、Slack 機器人、營銷、安全等各種不同任務,時常在它們之間切換。我的任務隊列中積壓了近 70 項任務,這已經成爲一個大型的業餘項目,我還要兼顧全職工作。

我現在意識到,Slack 可能不是構建社區的理想工具。它更適合團隊協作,並且免費賬戶在 API 調用上有限制。此外,Slack 的價格是基於用戶人數,考慮到我 Slack 工作區中不斷增長的學生羣體,如果我想訪問只在付費賬戶可用的 API,成本會很高。

未來計劃

通過我的 syncer 同步學生創建的 NodePort 服務到宿主集羣

目前,緩存的實驗環境只支持一個 Kubernetes 版本,不能用於其他版本。這意味着提供所有學習內容需要 20 到 30 秒,而沙箱是 1.5 分鐘。我將會緩存更多的版本,加速沙箱的初始化速度。

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/s3yMs7moQ9QOy9eKeN5WIQ