深度學習大熱之後受到大量關注,大部分剛接觸深度學習的同學,注意力大都集中在如何調整引數/資料/網路結構,如何達到預期的精度/召回率/準確率等。
然而,深度學習模型應用的整個流程裡面還有一個重要的環節,就是模型部署上線。一個模型只有部署上線了,這個模型的價值才能得到體現。
所以我們今天不討論如何訓練模型,我們關注一下如何將訓練好的模型部署上線。
模型生命週期
模型部署上線,就是將模型封裝成一個線上服務,並分配相應的資源,將線上服務執行在伺服器上,實時接收請求,返回predict結果。封裝線上服務的方式有很多,可以選擇一些成熟的框架比如http / thrift / gRPC等,選擇一款適合自己專案的即可,我們重點關注怎麼部署。
模型部署 v0.1 - 裸機部署
顧名思義,就是直接在物理伺服器上安裝配置好相應的環境,直接將工程程式碼/模型檔案部署到物理伺服器上執行,看起來好像很簡單直接,但是實際操作起來問題很多。實際伺服器可能會有2塊GPU甚至4塊GPU,所以一臺伺服器通常需要部署多個模型應用,而在同一臺伺服器上部署多個應用碰到的最常見的問題,就是環境問題。
環境問題的複雜性表現在以下幾個方面:
- 依賴管理複雜:由於各種原因,線上伺服器的作業系統版本可能會不一致,比如系統有CentOS6/7、Debian8/9,甚至核心版本也會有一些差異,依賴包管理工作變得非常複雜;
- 容易產生依賴衝突:各個模型應用依賴的包版本很容易產生衝突,比如一個應用依賴了opencv2,另一個應用依賴了opencv3,在沒有很好的隔離的情況下,會出現各種相容性問題;
- 配置/部署效率低下:某一臺伺服器部署了A應用,某一天A應用下線了,想要部署B應用,可能會面臨遺留環境衝突問題,甚至需要重灌系統從零開始。碰上線上服務需要緊急擴容的情況,這種環境問題更是容易讓人手忙腳亂。
模型部署複雜的環境/依賴
模型部署v0.2 - 容器技術
為了將開發運維從複雜的環境管理工作中解放出來,我們引入了容器技術。容器是一種輕量級、可移植、自包含的軟體打包技術,使應用程式可以在幾乎任何地方以相同的方式執行。
簡單的說,容器包含兩部分:
每個程式都自帶依賴環境,並且相互隔離,這很好的解決了裸機部署帶來的各個應用之間的依賴衝突問題,對宿主機的環境依賴很小,真正實現了 “Build Once, Run Anywhere” 的口號。藉助nvidia官方提供的 nvidia-docker 元件,將nvidia裝置對映到容器內部,我們可以很容易的實現快速的模型應用部署上線。
容器化環境隔離
但容器技術並沒有解決所有問題,其中一個問題就是資源管理和排程問題,由於一臺伺服器需要部署多個應用,每個應用都需要分配1塊GPU,這就要求我們手動維護好分配記錄,例如:
- 應用 A 部署在 Server0 伺服器上,分配了第0塊GPU
- 應用B部署在Server0 / Server1伺服器上,分配了第1塊GPU
- ...
這種管理工作在叢集規模小的時候可以手動管理,但是在叢集規模大了之後就很混亂、很複雜了,而且也容易錯——尤其是當今天易盾日均請求量達到十億量級以上的時候,僅靠手動管理已無法想象。
更為嚴峻的是,因為沒有對應用的資源做隔離,透過這種方式部署的應用,相互之間會產生資源競爭問題。例如一臺伺服器上執行了 A / B / C 應用,此時A應用由於程式Bug將CPU資源耗盡,此時在同一臺伺服器上的B / C應用也會受到牽連影響。
模型部署v0.3 - Kubernetes
為了解決資源排程問題,我們引入了Kubernetes。Kubernetes 是一個開源系統,用於容器化應用的自動部署、擴縮和管理。在Kubernetes裡有一個“資源”的概念,比如CPU和記憶體都是資源型別,我們可以透過Kubernetes對執行的容器進行資源管理,例如:
apiVersion: v1
kind: Pod
metadata:
name: nginx
spec:
containers:
- name: nginx
image: nginx
resources:
requests:
memory: "64Mi"
cpu: "1"
limits:
memory: "128Mi"
cpu: "2"
我們對執行的nginx容器申請了64M記憶體和1核CPU,並且限制這個容器最多隻能使用128M記憶體和2核CPU,如果nginx程式嘗試申請超過128M記憶體,就會被kubernetes系統kill掉重新拉起來,確保不會影響其他應用。藉助nvidia官方提供的 k8s-device-plugin 外掛,nvidia GPU也被抽象成一種資源型別,這時候我們就可以像申請CPU/記憶體一樣申請GPU資源。
apiVersion: v1
kind: Pod
metadata:
name: model-app
spec:
containers:
- name: model-app
image: model-app:v0.3
resources:
requests:
cpu: 4
memory: "8Gi"
nvidia.com/gpu: "1"
limits:
cpu: 4
memory: "10Gi"
nvidia.com/gpu: "1"
上述示例中我們為 model-app這個應用申請了4核CPU、8G記憶體、1塊GPU,申請提交之後,Kubernetes會分配好對應的資源,並自動排程。開發運維人員的視角從“某臺服務部署了xx應用”轉換為“這個kubenetes叢集的資源使用情況”,這極大的簡化了運維管理工作。
Kubernetes自帶了dashboard元件,監控方案可以用prometheus+grafana,基本的運維和監控管理都可以實現,但畢竟是開源版本,部分功能還不能滿足我們的運維開發需求,經過一番定製開發,我們基於Kubernetes開發出了適合自己的集開發運維於一體的一站式管理平臺。
易盾某專案的資源彙總資訊
總結
由於易盾業務發展迅猛,網易安全部商業化易盾僅僅三年多一點的時間,網易易盾就已擁有超過26萬的開發者,服務客戶達數千家。這也使得這塊技術,從最早的裸機手動部署,到半自動的容器部署,再到全自動的kubernetes部署。
每一次進步都伴隨著效率的提升,解放了大量的開發運維人員精力,讓開發運維人員可以投入更多的精力到值得關注的產品功能上。與此同時,也使得易盾的機器學習模型,從訓練完成到上線應用,僅需要數分鐘就可以實現落地,從而及時幫助某些有緊急需求的客戶。
參考資料
https://www.docker.com/
https://github.com/NVIDIA/nvidia-docke
rhttps://github.com/NVIDIA/k8s-device-plugin
https://kubernetes.io/zh/
點選免費體驗網易易盾安全服務。