共計 7248 個字符,預計需要花費 19 分鐘才能閱讀完成。
這篇文章主要講解了“Kubernetes 的架構怎么使用”,文中的講解內容簡單清晰,易于學習與理解,下面請大家跟著丸趣 TV 小編的思路慢慢深入,一起來研究和學習“Kubernetes 的架構怎么使用”吧!
分布式 TensorFlow
TensorFlow 是一個使用數據流圖進行數值計算的開源軟件庫。圖中的節點代表數學運算,而圖中的邊則代表在這些節點之間傳遞的多維數組(張量)。這種靈活的架構可讓您使用一個 API 將計算工作部署到桌面設備、服務器或者移動設備中的一個或多個 CPU 或 GPU。關于 TensorFlow 的基礎概念,我就不多介紹了。
單機 TensorFlow
下面是一個單機式 TensorFlow 訓練示意圖,通過 Client 提交 Session,定義這個 worker 要用哪個 cpu/gpu 做什么事。
分布式 TensorFlow
2016 年 4 月 TensorFlow 發布了 0.8 版本宣布支持分布式計算,我們稱之為 Distributed TensorFlow。這是非常重要的一個特性,因為在 AI 的世界里,訓練的數據量和模型參數通常會非常大。比如 Google Brain 實驗室今年發表的論文 OUTRAGEOUSLY LARGE NEURAL NETWORKS: THE SPARSELY-GATED MIXTURE-OF-EXPERTS LAYER 中提到一個 680 億個 Parameters 的模型,如果只能單機訓練,那耗時難于接受。通過 Distributed TensorFlow,可以利用大量服務器構建分布式 TensorFlow 集群來提高訓練效率,減少訓練時間。
通過 TensorFlow Replcation 機制,用戶可以將 SubGraph 分布到不同的服務器中進行分布式計算。TensorFlow 的副本機制又分為兩種,In-graph 和 Between-graph。
In-graph Replication 簡單來講,就是通過單個 client session 定義這個 TensorFlow 集群的所有 task 的工作。
與之相對地,Between-graph Replication 就是每個 worker 都有獨立的 client 來定義自己的工作。
下面是抽象出來的分布式 TensorFlow Framework 如下:
我們先來了解里面的幾個概念:
Cluster
一個 TensorFlow Cluster 有一個或多個 jobs 組成,每個 job 又由一個或多個 tasks 構成。Cluster 的定義是通過 tf.train.ClusterSpec 來定義的。比如,定義一個由 3 個 worker 和 2 個 ps 的 TensorFlow Cluster 的 ClusterSpec 如下:
tf.train.ClusterSpec({
worker : [
worker0.example.com:2222 , // 主機名也可以使用 IP
worker1.example.com:2222 ,
worker2.example.com:2222
],
ps : [
ps0.example.com:2222 ,
ps1.example.com:2222
]})
Client
Client 用來 build 一個 TensorFlow Graph,并構建一個 tensorflow::Session 用來與集群通信。一個 Client 可以與多個 TensorFlow Server 交互,一個 Server 能服務多個 Client。
Job
一個 Job 由 tasks list 組成,Job 分 ps 和 worker 兩種類型。ps 即 parameter server,用來存儲和更新 variables 的,而 worker 可以認為是無狀態的,用來作為計算任務的。workers 中,一般都會選擇一個 chief worker(通常是 worker0),用來做訓練狀態的 checkpoint,如果有 worker 故障,那么可以從最新 checkpoint 中 restore。
Task
每個 Task 對應一個 TensorFlow Server,對應一個單獨的進程。一個 Task 屬于某個 Job,通過一個 index 來標記它在對應 Job 的 tasks 中的位置。每個 TensorFlow 均實現了 Master service 和 Worker service。Master service 用來與集群內的 worker services 進行 grpc 交互。Worker service 則是用 local device 來計算 subgraph。
關于 Distributed TensorFlow 的更多內容,請參考官方內容 www.tensorflow.org/deplopy/distributed
分布式 TensorFlow 的缺陷
分布式 TensorFlow 能利用數據中心所有服務器構成的資源池,讓大量 ps 和 worker 能分布在不同的服務器進行參數存儲和訓練,這無疑是 TensorFlow 能否在企業落地的關鍵點。然而,這還不夠,它還存在一下先天不足:
訓練時 TensorFlow 各個 Task 資源無法隔離,很有可能會導致任務間因資源搶占互相影響。
缺乏調度能力,需要用戶手動配置和管理任務的計算資源。
集群規模大時,訓練任務的管理很麻煩,要跟蹤和管理每個任務的狀態,需要在上層做大量開發。
用戶要查看各個 Task 的訓練日志需要找出對應的服務器,并 ssh 過去,非常不方便。
TensorFlow 原生支持的后端文件系統只支持:標準 Posix 文件系統(比如 NFS)、HDFS、GCS、memory-mapped-file。大多數企業中數據都是存在大數據平臺,因此以 HDFS 為主。然而,HDFS 的 Read 性能并不是很好。
當你試著去創建一個大規模 TensorFlow 集群時,發現并不輕松;
TensorFlow on Kubernetes 架構與原理
TensorFlow 的這些不足,正好是 Kubernetes 的強項:
提供 ResourceQuota, LimitRanger 等多種資源管理機制,能做到任務之間很好的資源隔離。
支持任務的計算資源的配置和調度。
訓練任務以容器方式運行,Kubernetes 提供全套的容器 PLEG 接口,因此任務狀態的管理很方便。
輕松對接 EFK/ELK 等日志方案,用戶能方便的查看任務日志。
支持 Read 性能更優秀的分布式存儲(Glusterfs),但目前我們也還沒對接 Glusterfs,有計劃但沒人力。
通過聲明式文件實現輕松快捷的創建一個大規模 TensorFlow 集群。
TensorFlow on Kubernetes 架構
TensorFlow on Kubernetes 原理
在我們的 TensorFlow on Kubernetes 方案中,主要用到以下的 Kubernetes 對象:
Kubernetes Job
我們用 Kubernetes Job 來部署 TensorFlow Worker,Worker 訓練正常完成退出,就不會再重啟容器了。注意 Job 中的 Pod Template restartPolicy 只能為 Never 或者 OnFailure,不能為 Always,這里我們設定 restartPolicy 為 OnFailure,worker 一旦異常退出,都會自動重啟。但是要注意,要保證 worker 重啟后訓練能從 checkpoint restore,不然 worker 重啟后又從 step 0 開始,可能跑了幾天的訓練就白費了。如果你使用 TensorFlow 高級 API 寫的算法,默認都實現了這點,但是如果你是使用底層 core API,一定要注意自己實現。
kind: Job
apiVersion: batch/v1
metadata:
name: {{ name }}-{{ task_type }}-{{ i }}
namespace: {{ name }}
spec:
template:
metadata:
labels:
name: {{ name }}
job: {{ task_type }}
task: {{ i }}
spec:
imagePullSecrets:
- name: harborsecret
containers:
- name: {{ name }}-{{ task_type }}-{{ i }}
image: {{ image }}
resources:
requests:
memory: 4Gi
cpu: 500m
ports:
- containerPort: 2222
command: [/bin/sh , -c , export CLASSPATH=.:/usr/lib/jvm/java-1.8.0/lib/tools.jar:$(/usr/lib/hadoop-2.6.1/bin/hadoop classpath --glob); wget -r -nH -np --cut-dir=1 -R index.html*,*gif {{ script }}; cd ./{{ name }}; sh ./run.sh {{ ps_hosts() }} {{ worker_hosts() }} {{ task_type }} {{ i }} {{ ps_replicas }} {{ worker_replicas }} ]
restartPolicy: OnFailure
Kubernetes Deployment
TensorFlow PS 用 Kubernetes Deployment 來部署。為什么不像 worker 一樣,也使用 Job 來部署呢?其實也未嘗不可,但是考慮到 PS 進程并不會等所有 worker 訓練完成時自動退出 (一直掛起),所以使用 Job 部署沒什么意義。
kind: Deployment
apiVersion: extensions/v1beta1
metadata:
name: {{ name }}-{{ task_type }}-{{ i }}
namespace: {{ name }}
spec:
replicas: 1
template:
metadata:
labels:
name: {{ name }}
job: {{ task_type }}
task: {{ i }}
spec:
imagePullSecrets:
- name: harborsecret
containers:
- name: {{ name }}-{{ task_type }}-{{ i }}
image: {{ image }}
resources:
requests:
memory: 4Gi
cpu: 500m
ports:
- containerPort: 2222
command: [/bin/sh , -c , export CLASSPATH=.:/usr/lib/jvm/java-1.8.0/lib/tools.jar:$(/usr/lib/hadoop-2.6.1/bin/hadoop classpath --glob); wget -r -nH -np --cut-dir=1 -R index.html*,*gif {{ script }}; cd ./{{ name }}; sh ./run.sh {{ ps_hosts() }} {{ worker_hosts() }} {{ task_type }} {{ i }} {{ ps_replicas }} {{ worker_replicas }} ]
restartPolicy: Always
關于 TensorFlow PS 進程掛起的問題,請參考 https://github.com/tensorflow/tensorflow/issues/4713. 我們是這么解決的,開發了一個模塊,watch 每個 TensorFlow 集群的所有 worker 狀態,當所有 worker 對應 Job 都 Completed 時,就會自動去刪除 PS 對應的 Deployment,從而 kill PS 進程釋放資源。
Kubernetes Headless Service
Headless Service 通常用來解決 Kubernetes 里面部署的應用集群之間的內部通信。在這里,我們也是這么用的,我們會為每個 TensorFlow 對應的 Job 和 Deployment 對象都創建一個 Headless Service 作為 worker 和 ps 的通信代理。
kind: Service
apiVersion: v1
metadata:
name: {{ name }}-{{ task_type }}-{{ i }}
namespace: {{ name }}
spec:
clusterIP: None
selector:
name: {{ name }}
job: {{ task_type }}
task: {{ i }}
ports:
- port: {{ port }}
targetPort: 2222
用 Headless Service 的好處,就是在 KubeDNS 中,Service Name 的域名解析直接對應到 PodIp,而沒有 service VIP 這一層,這就不依賴于 kube-proxy 去創建 iptables 規則了。少了 kube-proxy 的 iptables 這一層,帶來的是性能的提升。
在 TensorFlow 場景中,這是不可小覷的,因為一個 TensorFlow Task 都會創建一個 service,幾萬個 service 是很正常的事,如果使用 Normal Service,iptables 規則就幾十萬上百萬條了,增刪一條 iptabels 規則耗時幾個小時甚至幾天,集群早已奔潰。關于 kube-proxy iptables 模式的性能測試數據,請參考華為 PaaS 團隊的相關分享。
KubeDNS Autoscaler
前面提到,每個 TensorFlow Task 都會創建一個 service,都會在 KubeDNS 中有一條對應的解析規則,但 service 數量太多的時候,我們發現有些 worker 的域名解析失敗概率很大,十幾次才能成功解析一次。這樣會影響 TensorFlow 集群內各個 task 的 session 建立,可能導致 TensorFlow 集群起不來。
為了解決這個問題,我們引入了 Kubernetes 的孵化項目 kubernetes-incubator/cluster-proportional-autoscaler 來對 KubeDNS 進行動態伸縮。關于這個問題的具體的細節,有興趣的同學可以查看我的博文 https://my.oschina.net/jxcdwangtao/blog/1581879。
TensorFlow on Kubernetes 實踐
基于上面的方案,我們開發一個 TaaS 平臺,已經實現了基本的功能,包括算法管理、訓練集群的創建和管理、模型的管理、模型上線 (TensorFlow Serving)、一鍵創建 TensorBoard 服務、任務資源監控、集群資源監控、定時訓練管理、任務日志在線查看和批量打包下載等等,這部分內容可以參考之前在 DockOne 上分享的文章 http://dockone.io/article/3036。
這只是剛開始,我正在做下面的特性:
支持基于訓練優先級的任務搶占式調度: 用戶在 TaaS 上創建 TensorFlow 訓練項目時,可以指定項目的優先級為生產 (Production)、迭代 (Iteration)、調研 (PTR),默認為迭代。優先級從高到低依次為 **Production — Iteration — PTR**。但集群資源不足時,按照任務優先級進行搶占式調度。
提供像 Yarn 形式的資源分配視圖,讓用戶對自己的所有訓練項目的資源占用情況變得清晰。
訓練和預測的混合部署,提供數據中心資源利用率。
…
經驗和坑
整個過程中,遇到了很多坑,有 TensorFlow 的,也有 Kubernetes 的,不過問題最多的還是我們用的 CNI 網絡插件 contiv netplugin,每次大問題基本都是這個網絡插件造成的。Kubernetes 是問題最少的,它的穩定性比我預期還要好。
contiv netplugin 的問題,在 DevOps 環境中還是穩定的,在大規模高并發的 AI 場景,問題就層出不窮了,產生大量垃圾 IP 和 Openflow 流表,直接把 Node 都成 NotReady 了,具體的不多說,因為據我了解,現在用這個插件的公司已經很少了,想了解的私下找我。
在我們的方案中,一個 TensorFlow 訓練集群就對應一個 Kubernetes Namespace,項目初期我們并沒有對及時清理垃圾 Namespace,到后來集群里上萬 Namespace 的時候,整個 Kubernetes 集群的相關 API 性能非常差了,導致 TaaS 的用戶體驗非常差。
TensorFlow grpc 性能差,上千個 worker 的訓練集群,概率性的出現這樣的報錯 grpc_chttp2_stream request on server; last grpc_chttp2_stream id=xxx, new grpc_chttp2_stream id=xxx,這是 TensorFlow 底層 grpc 的性能問題,低版本的 grpc 的 Handlergrpc 還是單線程的,只能嘗試通過升級 TensorFlow 來升級 grpc 了,或者編譯 TensorFlow 時單獨升級 grpc 版本。如果升級 TensorFlow 版本的話,你的算法可能還要做 API 適配。目前我們通過增加單個 worker 的計算負載來減少 worker 數量的方法,減少 grpc 壓力。
還有 TensorFlow 自身 OOM 機制的問題等等
感謝各位的閱讀,以上就是“Kubernetes 的架構怎么使用”的內容了,經過本文的學習后,相信大家對 Kubernetes 的架構怎么使用這一問題有了更深刻的體會,具體使用情況還需要大家實踐驗證。這里是丸趣 TV,丸趣 TV 小編將為大家推送更多相關知識點的文章,歡迎關注!