首先我們需要創建一個 K3S Master 節點。
Demo 項目作者編寫了快速啟動腳本(當然我們也可以通過 K3S 官方指導文檔安裝)。K3S的安裝比較簡單。
git clone https:./example/scripts/edge-k3s-setup.sh當執行以下命令正確返回時,說明 K3S master 已經安裝成功。
kubectl cluster-infoKubernetes master is running at https://3.230.166.4:6443
To further debug and diagnose cluster problems, use 'kubectl cluster-info dump'我們將會在後面需要這個 Master endpoint 的連接信息。
注意這裡的 K3S Master 僅僅部署了 Master 角色,實際上是通過下述參數啟動了 K3S Master。
/usr/local/bin/k3s server --docker --no-flannel --no-deploy coredns --no-deploy traefik --disable-agent在這裡我們可以看到,項目沒有使用 K3S 默認的 Flannel 網絡方案,那將採用什麼網絡方案呢?這正是這個 Demo 項目的有趣之處。
Master節點啟動後,就可以將我們的樹莓派以 Agent 節點加入這個 K3S 集群了。
關於 Smarter-CNI 網絡和邊緣計算框架的討論
我們在這裡稍微中斷一下實踐安裝步驟,來討論一下 Kubernetes 網絡方案和邊緣計算框架。
Smarter 項目在這裡有一個特別的地方,它並沒有採用 K3S 默認的 Flannel 網絡,而是採用了自己開發的 Smarter-cni 網絡,這個網絡插件是專門為 IoT 和邊緣場景 Kubernetes 跨雲邊設計的網絡方案。
在邊緣計算場景中,IoT Endpoint 終端可能沒有IP連接,它們直接與邊緣網關通信。邊緣網關作為一個中繼,與雲端應用進行通信。邊緣網關側則進行低延遲的計算處理,同時也考慮了安全問題,不必要將未經處理的數據發送到雲端。
在這種邊緣場景中,邊緣網關是不需要和其他邊緣網關節點通信的,它只需要和雲端通信,這是這個 Smarter 項目沒有採用複雜且要求較高的 Flannel 網絡方案的原因。Smarter-CNI 網絡方案在雲端建設 Kubernetes Master,只需要邊緣計算設備能向雲端 Master 發起連接。Kubernetes Node 之內的 Pod 能夠福相通信,與其他節點則不能(也可能不需要)。
那麼 Smarter-CNI 具體怎麼實現呢?實際上Smarter-CNI 採用了 Docker User-defined Network 來作為 Kubernetes Pod 的網絡實現,節點上所有的 Pod 分配得這個 Docker User-defined Network 的一個虛擬接口和 IP 地址。
Smarter-CNI 也將在節點上啟用一個 DNS 伺服器,來為本節點上的 Pod 容器提供互相的服務發現和外部地址的解析。
Smarter-CNI 網絡方案看上去非常簡單,但是非常有趣,以一個非常簡單且解耦的方式解決了 Kubernetes 集群跨雲邊構建的問題。它涉及到我們常提及的將 Kubernetes 運用在邊緣計算的方案問題。我們這裡簡單作一些討論。
#########
KubeEdge 是華為開源的邊緣計算框架,比較早開源出來,主要架構特點是雲邊協同。
K3S 是 Rancher 提出的輕量級 Kubernetes 發行版,大量削減 Kubernetes 非核心代碼,保留核心能力,增強邊緣能力。
OpenYurt 是阿里巴巴最近開源,還沒有全部組件開源出來。主要架構特點是雲邊一體化。
1、邊緣資源上的處理
邊緣計算,首先考慮點之一是邊緣資源有限,需要儘量減少基礎架構對資源的消耗。KubeEdge 和 OpenYurt 的雲邊協同或一體化架構,只在邊緣部署 Agent,這樣的方式降低資源消耗。
K3S 的方案是在邊緣運行完整的輕量版的 Kubernetes 集群。
2、邊緣網絡上的處理
將 Kubernetes 應用於邊緣,一個比較大的挑戰是 Kubernetes 集群對網絡的要求比較高,邊緣容器方案需要考慮集群網絡的問題。KubeEdge 的方案是通過 WebSocket 和消息機制降低網絡要求,並通過邊緣 EdgeHub 本地化Kubernetes數據。OpenYurt 有類似的方案,通過 Tunnel Server 和 Agent 構建網絡通道,以邊緣 YurtHub 處理本地 Kubernetes 資源配置。
K3S 與 KubeEdge 和 OpenYurt 不同,因為其在邊緣是獨立的 Kubernetes 集群,與雲端的聯繫通過 Rancher Server 進行。K3S 本身不處理雲邊統一網絡的問題。
有趣的是,本文實踐的 K3S + Smarter-CNI 方案是一個將單 K3S 集群跨雲邊的邊緣方案,通過 K3S 的 Tunnel 設計和簡單的 Docker User-defined Network 實現了雲邊網絡的處理。
3、邊緣自治的處理
如第 2 點提到,KubeEdge 通過 CloudCore 和 EdgeCore 實現節點邊緣自治,而 OpenYurt 通過 Yurt Controller 和 YurtHub 實現邊緣自治,OpenYurt 有個 Shadow APIserver 的概念。K3S 本身是一個Kubernetes,故其邊緣自治是利用 Kubernetes 集群本身的能力。KubeEdge 也提供了 EdgeSite 輕量級 Kubernetes 的方案。 K3S + Smarter-CNI 方案沒有邊緣自治的能力。
4、邊緣設備的處理
KubeEdge 對邊緣設備的管理採用的是 Device CRD 和 Device Controller Manager 的方式,通過 Kubernetes 的CRD 對 DeviceModel 和 DeviceInstance/ DeviceTwin 進行定義。OpenYurt 有個 UnitController 的設計似乎是處理這部分的,不過還沒有 Release ,不確定其能力。K3S + Smarter-CNI 的方案,本身不處理設備管理,在本文的實踐中,ARM 團隊提供了一個 Device Manager (本身是一個 Pod 形式部署)來實現本地設備的發現和管理。當然這個設備管理與 KubeEdge 裡 DeviceMapper 等的概念是有差別的。
另外, AWS IoT 也有 Shadow 的概念,在 Azure IoT 有 DeviceTwin 的概念。
#########
回到本文的實踐內容。
Smarter-CNI 安裝非常簡便。在樹莓派上執行下列命令。
git clone https://gitlab.com/arm-research/smarter/smarter-cni.gitsudo ./install.sh執行後,節點上會生成一個名為 mynet 的 Docker network,以及運行一個名為 mydns 的 Docker 容器。
CNI 插件安裝完成後,即可以開始將樹莓派 加入的該 K3S 集群。樹莓派我們採用 Ubuntu 系統,可支持 ARM64 位。
export K3S_VERSION="v1.18.3+k3s1"curl -sfL https://get.k3s.io | INSTALL_K3S_VERSION=$K3S_VERSION K3S_URL=https://<myserver>:6443 K3S_TOKEN=XXX sh -s - --docker --no-flannelK3S_URL 、TOKEN 可以在上述安裝 K3S Master 中獲取,腳本會在 Master 本地節點生成 TOKEN、Kubeconfig 等文件。(熟悉 K3S 的朋友也可以在默認 K3S 保存對應信息的目錄獲取)。
$ kubectl get nodesNAME STATUS ROLES AGE VERSIONubuntu Ready <none> 6h38m v1.18.3+k3s1安裝無誤,則能查看得樹莓派 ubuntu 已成為 K3S Node。
這樣,我們就 Setup 起一個跨雲邊的 K3S 集群。
在開始第二部分部署邊緣圖像識別應用之前,我們需要部署 Cloud Data Node,這個節點將部署 K3S 服務,並通過 K3S 運行 ElasticSearch、Kibanfa、InFluxDB 和 Grafana, 作為我們剛剛建立的運行邊緣應用的業務 K3S 提供監控和日誌服務。
作者提供了一鍵安裝的腳本,執行一下命令即可。
git clone https:export MY_EMAIL=<A_VALID_EMAIL>
cd example./scripts/cloud-data-setup.sh安裝無誤,能得到如下輸出:
=========================================================================Grafana initial admin password has been set to eSBa0shDNENOTE: If using kubectl from this machine, make sure to run 'export KUBECONFIG=/home/ubuntu/example/cloud-kube.config'REMEMBER: To access grafana use https://grafana-3-236-47-70.nip.ioREMEMBER: To access kibana use https://kibana-3-236-47-70.nip.ioREMEMBER: To access prometheus dashboard use https://prometheus-3-236-47-70.nip.ioNOTE: on your dev machine export SMARTER_DATA_DOMAIN=3-236-47-70.nip.ioYour elasticsearch and kibana username is 'elastic' with password vC2sLOfbuRPsZovBg0K6=========================================================================我們將在後續安裝中使用到 Grafana 的 Endpint 地址。
通過 K3S 部署邊緣圖像識別應用
首先是在通過 K3S Master 在 樹莓派上運行一些基本的工作負載。
在 K3S Master 上執行一下命令。參數在文章第一部分的實踐中可以獲得。
export KUBECONFIG=<YOUR_KUBECONFIG>export NODE_TO_USE=<YOUR_NODE_NAME>export SMARTER_DATA_DOMAIN=<YOUR_K3S_DATA_NODE_IP (DASH SEPARATED)>.nip.ioexport SMARTER_EDGE_DOMAIN=<YOUR_K3S_EDGE_MASTER_IP>然後執行
git clone https://gitlab.com/arm-research/smarter/example.gitcd example/demo./start_device_mgmt.sh
kubectl get secrets/fluentd-credentials --template={{.data.password}} | base64 -dkubectl create secret generic fluentd-credentials --from-literal=password=<YOUR FLUENTD PASSWORD>kubectl label node ${NODE_TO_USE} nodetype=rpi
kubectl get nodes -o widekubectl get node ${NODE_TO_USE} -o wide --show-labels
kubectl apply -f ../yaml/fluent-bit/envsubst < ../yaml/fluent-bit/fluent-bit-ds.yaml | kubectl apply -f -kubectl get ds
kubectl get pods --field-selector spec.nodeName=${NODE_TO_USE} -o wide
kubectl label node ${NODE_TO_USE} fluent-bit=enabledkubectl get node ${NODE_TO_USE} -o wide --show-labels
kubectl get pods --field-selector spec.nodeName=${NODE_TO_USE} -o wide
kubectl apply -f ../yaml/netdata-slavekubectl label node ${NODE_TO_USE} netdata-monitor=enabled
kubectl apply -f ../yaml/faas/edgefaas-ds.yaml
kubectl label node ${NODE_TO_USE} edgefaas=enabled
kubectl get pods --field-selector spec.nodeName=${NODE_TO_USE} -o wide
kubectl apply -f ../yaml/pulseaudio/
kubectl label node ${NODE_TO_USE} pulse=enabled
kubectl apply -f ../yaml/faas/install-squasher.yamlkubectl label node ${NODE_TO_USE} squasher=enabled
kubectl apply -f ../yaml/faas/list.yamlkubectl label node ${NODE_TO_USE} faas-list=enabledkubectl logs edgefaas-lis上述腳本將會在節點上部署devicemanger、 fluentbit、edgefaas、net-data等基礎組件。
#########
DeviceManger
DeviceManger 是 ARM 提供的本地設備管理應用。
它能幫助發現和管理節點上的設備。
Allocated resources: (Total limits may be over 100 percent, i.e., overcommitted.) Resource Requests Limits --- --- - cpu 560m (14%) 850m (21%) memory 365Mi (48%) 365Mi (48%) ephemeral-storage 0 (0%) 0 (0%) smarter-devices/gpiochip0 0 0 smarter-devices/gpiochip1 0 0 smarter-devices/gpiochip2 0 0 smarter-devices/gpiomem 0 0 smarter-devices/i2c-1 0 0 smarter-devices/snd 0 0 smarter-devices/vchiq 1 1 smarter-devices/vcs 0 0 smarter-devices/vcsm 1 1 smarter-devices/vcsm-cma 0 0 smarter-devices/video10 0 0 smarter-devices/video11 0 0 smarter-devices/video12 0 0 smarter-devices/video4 2 2Events: <none>Netdata
這裡我們注意到監控沒有使用 Prometheus,因 Prometheus 主要是 Pull 模型,而邊緣設備地址是不暴露的。這裡採用了 NetData(https://github.com/netdata/netdata),採用 Push 的方式向雲端 InfluxDB 發送數據。
FluentBit
FlunentBit 可以說是一個輕量版的 Fluentd,比較適合邊緣,用來收集邊緣節點上所有容器的日誌,並發送給雲端 ElasticSearch。FluentBit 也接收後續部署的圖像識別程序發送的 MQTT 消息,並將消息過濾後發送給 InfluxDB,最終由 Grafana 展示圖像識別結果的統計圖表。
#########
然後我們再部署圖像識別應用。
kubectl apply -f ../yaml/triton/triton-ds.yaml
kubectl label node ${NODE_TO_USE} triton=enabled
if kubectl get node ${NODE_TO_USE} --show-labels | grep -q nodetype=rpi; then kubectl apply -f ../yaml/ambient-sound-classifier/ambient-sound-classifier-ds.yaml
kubectl label node ${NODE_TO_USE} ambient-sound-classifier=enabledfi
kubectl apply -f ../yaml/image-detector/image-detector-ds.yamlkubectl apply -f ../yaml/audio-client/audio-client-ds.yaml
kubectl label node ${NODE_TO_USE} image-detector=enabled
kubectl get pods --field-selector spec.nodeName=${NODE_TO_USE} -o wide
kubectl label node ${NODE_TO_USE} audio-client=enabled
kubectl get pods --field-selector spec.nodeName=${NODE_TO_USE} -o wide
kubectl apply -f ../yaml/image-detector/image-detector-extended-ds.yamlkubectl get pods --field-selector spec.nodeName=${NODE_TO_USE} -o wide如安裝無誤,能夠獲取如下返回。
$ kubectl get pods --field-selector spec.nodeName=${NODE_TO_USE} -o wide --watchNAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATESsmarter-device-manager-9d4mk 1/1 Running 0 6h42m 192.168.125.112 ubuntu <none> <none>fluent-bit-4fqmc 1/1 Running 0 6h41m 172.38.0.4 ubuntu <none> <none>pulseaudio-t6v8q 1/1 Running 0 6h41m 172.38.0.8 ubuntu <none> <none>edgefaas-5gpqm 1/1 Running 0 6h41m 172.38.0.7 ubuntu <none> <none>netdata-slave-zr8jr 1/1 Running 0 6h41m 192.168.125.112 ubuntu <none> <none>edgefaas-list 0/1 Completed 0 6h41m 172.38.0.6 ubuntu <none> <none>edgefaas-install-squasher 0/1 Completed 0 6h41m 172.38.0.5 ubuntu <none> <none>ambient-sound-classifier-qqgdn 1/1 Running 0 6h37m 172.38.0.6 ubuntu <none> <none>triton-armnn-b2tbw 1/1 Running 0 6h37m 172.38.0.5 ubuntu <none> <none>audio-client-sxdt4 0/1 Running 81 6h37m 172.38.0.10 ubuntu <none> <none>image-detector-hbflg 0/1 Running 0 55m 172.38.0.9 ubuntu <none> <none>圖像識別的測試
上述所有步驟成功執行的話,到這裡我們就已經 Setup 起完整的 Smarter Demo 項目實驗環境,可以開始測試了。
Demo 項目在本地啟用了 Web 伺服器,通過樹莓派 8080 埠可以訪問 Web,實時查看攝像頭拍攝圖像和識別的結果(識別為車將顯示綠色框框、人則顯示紅色框框)。
將攝像頭指向人或汽車。可以從樹莓派地址 http://192.168.125.112:8080/ 中直觀獲得是識別結果,結果將在畫面中以紅框或綠框圈出識別出的人或車。查看 image-detector 容器日誌也可得到相應的識別記錄。
訪問 Grafana 地址 https://grafana-3-236-47-70.nip.io ,按照 Github 指導方式導入 json 定義圖表,可以獲得 Grafana 識別結果動態統計圖表。
至此,我們重複了 Smarter Demo 項目的 實踐內容,並藉此簡單討論了邊緣計算方案的特點。
下面,將在 Demo 項目基礎上進行修改,將邊緣計算結果發送到雲端 IoT 平臺進行處理。
將邊緣圖像識別結果發送到 AWS IoT
為什麼要發送到 IoT 平臺處理?
從上述的項目代碼中可以看到,Image-detector 程序通過 TensorFlow 框架持續對輸入圖像進行識別,並將識別結果向 FluentBit 發送了 topic 為 demo/car_count 和 demo/person_count 的 MQTT 消息,FluentBit 通過 Fluent-plugin-influxdb 向雲端 influxDB 轉發了過濾後的 MQTT 消息,最終由 Grafana 進行相應的 car 和 person 的實時豎狀圖表展示。
我個人認為上述處理過程雖比較巧妙地利用了雲原生的相關工具來處理 IoT 設備數據,但有失優雅。我更傾向於使用 FluentBit、Netdata、Grafana 來處理基礎架構的問題,IoT 數據的處理、清洗、分析等涉及更複雜的內容,應該由其他方式進行。主流公有雲廠商提供了非常多的雲服務,可以對數據進行更方便和豐富的處理和分析,甚至利用人工智慧雲服務的能力。
這部分的內容與上一篇文章簡單玩一下 AWS IoT Python SDK 和 MQTT 相關,下面將結合這篇文章中提到的 AWS IoT Python SDK 進行 MQTT Publish 到 AWS IoT Core 平臺。
我們對 image-detector 中的代碼進行修改,引入 AWS IoT Python SDK,使其將識別結果通過 MQTT 發送到 AWS IoT Core,並由 AWS IoT 進行後續數據處理。
下面為一段 AWS IoT MQTT 連接代碼,需要我們傳入證書:
mqtt_connection = mqtt_connection_builder.mtls_from_path( endpoint=args.endpoint, cert_filepath=args.cert, pri_key_filepath=args.key, client_bootstrap=client_bootstrap, ca_filepath=args.root_ca, on_connection_interrupted=on_connection_interrupted, on_connection_resumed=on_connection_resumed, client_id=args.client_id, clean_session=False, keep_alive_secs=6)
print("Connecting to {} with client ID '{}'...".format( args.endpoint, args.client_id))
connect_future = mqtt_connection.connect()
connect_future.result() print("Connected!")將完整代碼打包成鏡像:yejianhonghong/smarter-image-detector-aws:v1.3
部署更新 DaemonSet/image-detector 為上述鏡像。
也可以將 K3S 納管到 Rancher 平臺,在平臺上進行部署更新。
在 AWS IoT Core Test 功能頁 Subscribe 主題 smarter/test (代碼中定義)。可見 AWS IoT 已經收到了樹莓派上圖像識別的結果信息。
我們還可以通過 AWS IoT Action 將數據存儲到 AWS DynamoDB 資料庫,方便後續的數據處理和分析。
完畢。
IoT 設備數據發送到雲端 IoT 平臺只是 物聯網 方案的第一步,更能體現 IoT 價值的應該是數據的處理、分析和呈現,甚至 AI 分析等。
後續我們將介紹通過 AWS 各類雲服務對 IoT 設備數據進行數據的存儲、處理、清洗、轉換,和可視化呈現。