使用 k3s 在 Fedora IoT 上執行 K8S

2019-05-28 09:41:00

Fedora IoT 是一個即將發布的、面向物聯網的 Fedora 版本。去年 Fedora Magazine 的《》一文第一次介紹了它。從那以後,它與 Fedora Silverblue 一起不斷改進,以提供針對面向容器的工作流的不可變基礎作業系統。

Kubernetes 是一個頗受歡迎的容器編排系統。它可能最常用在那些能夠處理巨大負載的強勁硬體上。不過,它也能在像樹莓派 3 這樣輕量級的裝置上執行。讓我們繼續閱讀,來了解如何執行它。

為什麼用 Kubernetes?

雖然 Kubernetes 在雲端計算領域風靡一時,但讓它在小型單板機上執行可能並不是常見的。不過,我們有非常明確的理由來做這件事。首先,這是一個不需要昂貴硬體就可以學習並熟悉 Kubernetes 的好方法;其次,由於它的流行性,市面上有大量應用進行了預先打包,以用於在 Kubernetes 叢集中執行。更不用說,當你遇到問題時,會有大規模的社群使用者為你提供幫助。

最後但同樣重要的是,即使是在家庭實驗室這樣的小規模環境中,容器編排也確實能夠使事情變得更加簡單。雖然在學習曲線方面,這一點並不明顯,但這些技能在你將來與任何叢集打交道的時候都會有幫助。不管你面對的是一個單節點樹莓派叢集,還是一個大規模的機器學習場,它們的操作方式都是類似的。

K3s - 輕量級的 Kubernetes

一個“正常”安裝的 Kubernetes(如果有這麼一說的話)對於物聯網來說有點沉重。K8s 的推薦記憶體設定,是每台機器 2GB!不過,我們也有一些替代品,其中一個新人是 k3s —— 一個輕量級的 Kubernetes 發行版。

K3s 非常特殊,因為它將 etcd 替換成了 SQLite 以滿足鍵值儲存需求。還有一點,在於整個 k3s 將使用一個二進位制檔案分發,而不是每個元件一個。這減少了記憶體占用並簡化了安裝過程。基於上述原因,我們只需要 512MB 記憶體即可執行 k3s,極度適合小型單板電腦!

你需要的東西

  1. Fedora IoT 執行在虛擬機器或實體裝置中執行的。在這裡可以看到優秀的入門指南。一台機器就足夠了,不過兩台可以用來測試向叢集新增更多節點。
  2. 設定防火牆,允許 6443 和 8372 埠的通訊。或者,你也可以簡單地執行 systemctl stop firewalld 來為這次實驗關閉防火牆。

安裝 k3s

安裝 k3s 非常簡單。直接執行安裝指令碼:

curl -sfL https://get.k3s.io | sh -

它會下載、安裝並啟動 k3s。安裝完成後,執行以下命令來從伺服器獲取節點列表:

kubectl get nodes

需要注意的是,有幾個選項可以通過環境變數傳遞給安裝指令碼。這些選項可以在文件中找到。當然,你也完全可以直接下載二進位制檔案來手動安裝 k3s。

對於實驗和學習來說,這樣已經很棒了,不過單節點的叢集也不能算一個叢集。幸運的是,新增另一個節點並不比設定第一個節點要難。只需要向安裝指令碼傳遞兩個環境變數,它就可以找到第一個節點,而不用執行 k3s 的伺服器部分。

curl -sfL https://get.k3s.io | K3S_URL=https://example-url:6443 \  K3S_TOKEN=XXX sh -

上面的 example-url 應被替換為第一個節點的 IP 地址,或一個完全限定域名。在該節點中,(用 XXX 表示的)令牌可以在 /var/lib/rancher/k3s/server/node-token 檔案中找到。

部署一些容器

現在我們有了一個 Kubernetes 叢集,我們可以真正做些什麼呢?讓我們從部署一個簡單的 Web 伺服器開始吧。

kubectl create deployment my-server --image nginx

這會從名為 nginx 的容器映象中建立出一個名叫 my-server部署(預設使用 docker hub 註冊中心,以及 latest 標籤)。

kubectl get pods

為了存取到 pod 中執行的 nginx 伺服器,首先通過一個 服務 來暴露該部署。以下命令將建立一個與該部署同名的服務。

kubectl expose deployment my-server --port 80

服務將作為一種負載均衡器和 Pod 的 DNS 記錄來工作。比如,當執行第二個 Pod 時,我們只需指定 my-server(服務名稱)就可以通過 curl 存取 nginx 伺服器。有關如何操作,可以看下面的範例。

# 啟動一個 pod,在裡面以互動方式執行 bashkubectl run debug --generator=run-pod/v1 --image=fedora -it -- bash# 等待 bash 提示符出現curl my-server# 你可以看到“Welcome to nginx!”的輸出頁面

Ingress 控制器及外部 IP

預設狀態下,一個服務只能獲得一個 ClusterIP(只能從叢集內部存取),但你也可以通過把它的型別設定為 LoadBalancer 為該服務申請一個外部 IP。不過,並非所有應用都需要自己的 IP 地址。相反,通常可以通過基於 Host 請求頭部或請求路徑進行路由,從而使多個服務共用一個 IP 地址。你可以在 Kubernetes 使用 Ingress 完成此操作,而這也是我們要做的。Ingress 也提供了額外的功能,比如無需設定應用即可對流量進行 TLS 加密。

Kubernetes 需要 Ingress 控制器來使 Ingress 資源工作,k3s 包含 Traefik 正是出於此目的。它還包含了一個簡單的服務負載均衡器,可以為叢集中的服務提供外部 IP。這篇文件描述了這種服務:

k3s 包含一個使用可用主機埠的基礎服務負載均衡器。比如,如果你嘗試建立一個監聽 80 埠的負載均衡器,它會嘗試在叢集中尋找一個 80 埠空閒的節點。如果沒有可用埠,那麼負載均衡器將保持在 Pending 狀態。

k3s README

Ingress 控制器已經通過這個負載均衡器暴露在外。你可以使用以下命令找到它正在使用的 IP 地址。

$ kubectl get svc --all-namespacesNAMESPACE     NAME         TYPE           CLUSTER-IP      EXTERNAL-IP   PORT(S)                      AGE default       kubernetes   ClusterIP      10.43.0.1               443/TCP                      33d default       my-server    ClusterIP      10.43.174.38            80/TCP                       30m kube-system   kube-dns     ClusterIP      10.43.0.10              53/UDP,53/TCP,9153/TCP       33d kube-system   traefik      LoadBalancer   10.43.145.104   10.0.0.8      80:31596/TCP,443:31539/TCP   33d

找到名為 traefik 的服務。在上面的例子中,我們感興趣的 IP 是 10.0.0.8。

路由傳入的請求

讓我們建立一個 Ingress,使它通過基於 Host 頭部的路由規則將請求路由至我們的伺服器。這個例子中我們使用 xip.io 來避免必要的 DNS 記錄設定工作。它的工作原理是將 IP 地址作為子域包含,以使用 10.0.0.8.xip.io 的任何子域來達到 IP 10.0.0.8。換句話說,my-server.10.0.0.8.xip.io 被用於存取叢集中的 Ingress 控制器。你現在就可以嘗試(使用你自己的 IP,而不是 10.0.0.8)。如果沒有 Ingress,你應該會存取到“預設後端”,只是一個寫著“404 page not found”的頁面。

我們可以使用以下 Ingress 讓 Ingress 控制器將請求路由到我們的 Web 伺服器的服務。

apiVersion: extensions/v1beta1kind: Ingressmetadata:  name: my-serverspec:  rules:    - host: my-server.10.0.0.8.xip.io      http:        paths:          - path: /            backend:              serviceName: my-server              servicePort: 80

將以上片段儲存到 my-ingress.yaml 檔案中,然後執行以下命令將其加入叢集:

kubectl apply -f my-ingress.yaml

你現在應該能夠在你選擇的完全限定域名中存取到 nginx 的預設歡迎頁面了。在我的例子中,它是 my-server.10.0.0.8.xip.io。Ingress 控制器會通過 Ingress 中包含的資訊來路由請求。對 my-server.10.0.0.8.xip.io 的請求將被路由到 Ingress 中定義為 backend 的服務和埠(在本例中為 my-server80)。

那麼,物聯網呢?

想象如下場景:你的家或農場周圍有很多的裝置。它是一個具有各種硬體功能、感測器和執行器的物聯網裝置的異構集合。也許某些裝置擁有攝像頭、天氣或光線感測器。其它裝置可能會被連線起來,用來控制通風、燈光、百葉窗或閃爍的 LED。

這種情況下,你想從所有感測器中收集資料,在最終使用它來制定決策和控制執行器之前,也可能會對其進行處理和分析。除此之外,你可能還想設定一個儀表盤來視覺化那些正在發生的事情。那麼 Kubernetes 如何幫助我們來管理這樣的事情呢?我們怎麼保證 Pod 在合適的裝置上執行?

簡單的答案就是“標籤”。你可以根據功能來標記節點,如下所示:

kubectl label nodes <node-name> <label-key>=<label-value># 舉例kubectl label nodes node2 camera=available

一旦它們被打上標籤,我們就可以輕鬆地使用 nodeSelector 為你的工作負載選擇合適的節點。拼圖的最後一塊:如果你想在所有合適的節點上執行 Pod,那應該使用 DaemonSet 而不是部署。換句話說,應為每個使用唯一感測器的資料收集應用程式建立一個 DaemonSet,並使用 nodeSelector 確保它們僅在具有適當硬體的節點上執行。

服務發現功能允許 Pod 通過服務名稱來尋找彼此,這項功能使得這類分散式系統的管理工作變得易如反掌。你不需要為應用設定 IP 地址或自定義埠,也不需要知道它們。相反,它們可以通過叢集中的命名服務輕鬆找到彼此。

充分利用空閒資源

隨著叢集的啟動並執行,收集資料並控制燈光和氣候,可能使你覺得你已經把它完成了。不過,叢集中還有大量的計算資源可以用於其它專案。這才是 Kubernetes 真正出彩的地方。

你不必擔心這些資源的確切位置,或者去計算是否有足夠的記憶體來容納額外的應用程式。這正是編排系統所解決的問題!你可以輕鬆地在叢集中部署更多的應用,讓 Kubernetes 來找出適合執行它們的位置(或是否適合執行它們)。

為什麼不執行一個你自己的 NextCloud 範例呢?或者執行 gitea?你還可以為你所有的物聯網容器設定一套 CI/CD 流水線。畢竟,如果你可以在叢集中進行本地構建,為什麼還要在主計算機上構建並交叉編譯它們呢?

這裡的要點是,Kubernetes 可以更容易地利用那些你可能浪費掉的“隱藏”資源。Kubernetes 根據可用資源和容錯處理規則來排程 Pod,因此你也無需手動完成這些工作。但是,為了幫助 Kubernetes 做出合理的決定,你絕對應該為你的工作負載新增資源請求設定。

總結

儘管 Kuberenetes 或一般的容器編排平台通常不會與物聯網相關聯,但在管理分散式系統時,使用一個編排系統肯定是有意義的。你不僅可以使用統一的方式來處理多樣化和異構的裝置,還可以簡化它們的通訊方式。此外,Kubernetes 還可以更好地對閒置資源加以利用。

容器技術使構建“隨處執行”應用的想法成為可能。現在,Kubernetes 可以更輕鬆地來負責“隨處”的部分。作為構建一切的不可變基礎,我們使用 Fedora IoT。