賀宗平, 張曉東, 劉 玉
1(南京審計大學 信息化辦公室,南京 211815)
2(南京審計大學 信息工程學院,南京 211815)
3(南京審計大學 實驗中心,南京 211815)
Jupyter Notebook是當前在數據科學領域非常受歡迎的交互式分析軟件,支持Python、R、Julia、Scala、SQL等多種編程語言內核,具備即時編譯、可視化和markdown語法編輯等功能[1],為數據分析、數值計算、統計建模、機器學習等方面提供了便捷. 盡管基于web的設計實現方式有許多局限性,Jupyter Notebook在數據科學領域得到了極為廣泛應用,主要原因就在于其能夠進行交互式、可視化的數據探索性分析,從最簡單的點線圖、地圖以及復雜的D3.js可視化等,甚至可以在某些場景中替代BI工具的功能,這些特點相對于傳統IDE編輯器具有顛覆性優勢. 但是Jupyter Notebook是傳統的單體服務架構模式,缺少多用戶管理和訪問認證等方面的功能,無法直接部署于計算集群上,難以充分利用和調度計算中心的計算資源.
當今的科研學術論文向著越來越復雜的方式演變,各個研究領域和研究內容對程序依賴度高,需要通過編程來處理數據、繪制圖表以及統計分析等. 研究中的程序代碼繁瑣復雜,直接導致了研究結論難以被他人重復實現,科研的真正價值無法得到交流傳播[2].Jupyter Notebook提供了一種混合的編輯方式,將程序代碼運行、文字圖表編輯等功能糅合在同一電子筆記中,打破了兩者之間的隔離界限,將研究中的論述表達和實驗分析等過程有機結合起來. 對于科研結論的交流、分享和傳播的形式方法上,具有里程碑式的重要意義.
目前,國外一些大型研究機構、實驗室以及超算中心開始提供面向多用戶的Jupyter Notebook服務[3],目的就是將高性能計算集群資源充分利用起來,為研究人員提供一個性能優異、操作便捷的工具[4-6].
美國國家能源研究科學計算中心(NERSC)在其Cori超算集群上部署Jupyter,為其6000多用戶提供了更便捷化的計算資源訪問方式,NERSC在“Science Gateway”節點設置多用戶管理環境,當用戶登錄后可以啟動一個專屬的項目環境,并且能夠訪問計算中心其他服務、文件和作業,直接訪問大規模數據集、作業隊列系統,允許用戶提交作業,查詢作業批處理運行情況. OpenMSI[7]是NERSC目前正在運行的基于云計算平臺和Jupyter的質譜成像項目,研究人員可以在web瀏覽器中對質譜成像數據進行分析、展示和操作.
科羅拉多大學波爾得分校(CU-Boulder)計算研究中心為研究人員提供了大規模計算資源、海量存儲等服務,此外還提供計算科學和數據管理方面的咨詢服務. 為了更好地提供優質服務,CU-Boulder計算研究中心在其HPC集群上部署了支持并行計算的Jupyter環境,能夠支持ipyparallel和MPI for Python.
歐洲原子能研究機構(CERN)開發了SWAN[8](Service for Web based ANalysis)交互式數據分析平臺,在集群中通過Docker鏡像構建Jupyter Notebook應用,并且支持其內部開發的CERNBox數據云存儲服務,使得代碼、數據集、文檔在其組織內部安全無縫地實現同步和共享,如圖1.

圖1 SWAN體系架構示意
本文研究的重心關注Jupyter體系的架構優化與拓展,通過容器和微服務化改造Jupyter體系架構,研究基于Kubernetes集群構建面向多用戶的交互式數據分析平臺.
Jupyter Notebook是一個典型Web架構的應用,客戶端部分負責筆記代碼的運行、存儲和輸出等功能,并通過markdown語法進行標記,以JSON格式發送給服務器端存儲,服務器端負責存取筆記代碼、調用編譯內核等功能.
Jupyter Notebook (如圖2)作為最基本的交互式分析程序,缺少平臺級的資源調度與管理的能力,不具備面向多用戶的服務管理功能,后臺運行環境無法做到隔離. 這種狀況限制了Jupyter Notebook在計算集群上的部署應用.

圖2 Jupyter Notebook架構示意
Jupyter Hub在Jupyter Notebook基礎上實現了多用戶的管理,用于創建、管理、代理多個Jupyter Notebook實例. Jupyter Hub包含3個組件:多用戶的Hub管理器、可配置的http代理、多個單用戶Jupyter Notebook服務器. Jupyter Hub架構如圖3所示.

圖3 Jupyter Hub架構示意
Jupyter Hub的功能流程[9]包括:首先,Hub服務啟動一個代理; 然后,代理默認將所有訪問請求轉發給Hub; Hub處理用戶登錄認證并根據需求啟動一個單用戶的Jupyter Notebook服務器; 最后,Hub配置代理將URL前綴轉發給單用戶的Jupyter Notebook服務器.Jupyter Hub實現多用戶的服務管理、具備訪問認證機制,但仍然無法實現集群調度、分布式計算、彈性擴容等功能,特別是無法直接部署在大型計算集群. 對應用系統進行微服務化改造、運用分布式架構是解決問題的根本途徑.
微服務將單體應用(Monolithic)先拆分成多個單元應用,服務間通過HTTP API或消息中間件進行通信,再聚合組成實際應用,這個過程顯著改善了系統架構的彈性,同時通過容器技術可以降低系統持續集成與持續部署(CI/CD)的復雜度. 容器作為微服務的一種基礎運行方式,具備成熟和強大的工具、平臺以及開發生態圈. Jupyter 微服務化重構示意圖如圖4.

圖4 Jupyter微服務化重構示意
2.3.1 微服務架構特點
微服務之父Martin Fowler總結微服務具有3個典型特征[10]:微小化(small),獨立化(independently deployable)和自動化(automated deployment). “微小化”是“獨立化”的基礎,“微小化”代表微服務的設計應當遵循“高內聚、低耦合”的原則,功能邊界定義清晰無重疊,“自動化”代表系統組裝、部署和運維可以通過工具實現高度的自動化.
康威定律[9](Conway's Law)認為組織結構決定系統結構. 隨著組織規模的不斷擴大,必然會演化出更多的小型組織. 從這個角度來看,傳統單體應用拆分轉換為微服務的過程,是系統結構適應平臺功能擴展的演變. 微服務降低了系統架構的復雜性,通過將單體應用分解為一組服務,在保持功能不變的條件下,強化了系統模塊化的組織水平,使得開發更易于維護.
2.3.2 Jupyter的微服務拆分
單體應用的主要架構特征是系統模塊間的“緊耦合”,模塊運行在同一進程中. 在系統部分模塊更新升級、故障掉線等情況下,需要重新啟動整個應用進程,導致系統可靠性和可維護性水平低下. 微服務架構通過對單體應用的不同功能模塊進行拆分重構,轉換為多個獨立的微服務,運行為多個獨立的進程,微服務間通過REST、RPC等遠程接口通信,實現分布式架構部署[11].
微服務架構包括了無狀態化和容器化兩個前提準備環節,從架構角度來看,單體架構對狀態信息的讀寫在本地的內存或存儲中,從而導致難以進行橫向擴展.無狀態化的過程是將業務邏輯無狀態部分與數據讀寫存儲的有狀態部分做分離,業務邏輯經過無狀態化處理能夠實現橫向擴展,狀態部分則通過中間件、分布式數據庫等進行存儲,從而實現單體架構向分布式架構的轉化改造. Jupyter Hub的3個主要組成部分:Configurable HTTP Proxy、Hub、Notebook,3個模塊間功能界限清晰,完全可以做到微服務化拆分,符合能夠獨立運行的標準,其中的Hub和Notebook模塊需要進行無狀態化(stateless)處理. Configurable HTTP Proxy是可配置的訪問代理模塊,負責代理轉發用戶的訪問請求; Hub中主要包括了認證功能模塊(Authenticator)和Notebook生成模塊(Spawner),Authenticator提供web安全訪問認證的接口,Spawner負責生成和啟動各個隔離的Notebook容器.
Kubernetes(簡稱K8S)作為一個分布式集群管理平臺,同時也是一個容器編排系統,用于在主機集群之間自動部署、擴展和運行應用程序容器,提供以容器為中心的基礎架構. K8S具有如服務命名與發現、負載均衡、運行狀況檢查、橫向彈性伸縮和滾動更新等功能[12],適合在生產環境中部署應用程序. 基于 Kubernetes的Jupyter 微服務架構如圖5所示.
Pod:Pod是Kubernetes的基本單元,由一個或多個容器組成,并在同一個物理主機內,共享相同的資源.在pod內部署的所有容器都可以通過localhost看到其他容器. 每個Pod在集群中具有唯一的IP地址.
Service:Service是一組Pod的邏輯區分,是Pod在集群內部被公開訪問的策略,同時允許在集群外部IP地址上訪問,主要通過ClusterIP、NodePort、Load Balancer和ExternalName等四種方式進行地址暴露.
Replication Controller:Kubernetes的一種控制器,能夠在集群中運行指定數目的Pod副本,實現Pod的數量伸縮(scale)操作.

圖5 基于Kubernetes的Jupyter微服務架構
3.2.1 訪問認證
交互式數據分析平臺通常部署在計算中心,向組織內部提供計算服務,其訪問認證的形式應當能夠與組織內部現有賬戶信息做到無縫對接和集成. LDAP是輕量目錄訪問協議(Lightweight Directory Access Protocol)的縮寫,是從X.500目錄訪問協議的基礎上發展而來,是一種集中賬號管理架構的實現協議.LDAP通常應用于組織內部的賬戶密碼管理[13],構建集中的身份驗證系統,可以降低管理成本,增強安全性.
Jupyter Hub中訪問認證功能可通過插件的方式配置LDAP訪問認證功能,與組織內部現有用戶訪問認證體系完整集成. 這種方式對于大型科研機構、高校等組織,可以顯著降低用戶管理和安全運維成本.
3.2.2 持久化存儲
Kubernetes持久化存儲通過API資源管理,包括PersistentVolume和PersistentVolumeClaim. Kubernetes中的存儲組件支持NFS、EBS等多種后端存儲,存儲具有獨立于Pod的生命周期. NFS (Network File System)作為FreeBSD支持的網絡文件系統,NFS節點之間通過TCP/IP協議訪問,NFS的客戶端應用可以透明地讀寫位于遠端NFS服務器上的文件,跟本地文件訪問相同.
Jupyter Hub配置Kubernetes中使用NFS,文件系統將被掛載在Pod中. NFS允許多個Pod同時進行寫操作,這些Pod使用相同的PersistentVolumeClaim. 通過使用NFS卷,相同的數據可以在多個Pod之間共享.
3.2.3 用戶資源分配
Jupyter Hub用戶資源分配內容一般包括了CPU、內存和存儲,對于高性能計算還需要重點關注GPU資源的分配. CPU、內存和存儲等資源可以直接在Kubernetes中彈性配置,對于GPU資源需要通過驅動掛載的方式,將宿主機上的GPU資源提供給容器使用. Jupyter Hub的資源定制化配置能力,關系到滿足不同用戶群體的不同層次需求,以及用戶體驗的改進. 用戶資源分配主要有兩種方式:資源保證(resource guarantees)和資源限制(resource limits).
資源保證的方式是確保所有用戶在任何情況下至少可以使用的資源量,如果有多余的資源仍然可以在加配. 例如,如果一個用戶得到了1 GB內存的資源保證,如果系統存在閑置多余的內存資源,此用戶可以分配到高于1 GB的內存量.
資源限制的方式設置了用戶可用資源的上限,如果一個用戶只給了1 GB內存的資源限制,那么此用戶在任何時間、任何系統資源富余的情況下,都不能使用超過1 GB內存的資源量.
資源調度是Jupyter Hub充分利用計算集群資源和具備面向多用戶開放的關鍵. Kubernetes內置了默認的調度規則為Pod分配運行主機,并且開放自定義編寫調度算法. 調度規則區分為預選(predicates)和優選(priorities)兩個階段規則. 系統首先通過預選初步排除不符合Pod運行要求的主機,然后再對預選后的主機進行量化評分,分值高低代表主機狀態優劣,最后將分值最高的主機調度為Pod的運行主機.
3.3.1 預選規則與節點篩選
預選規則是在集群節點中排除不滿足基本運行條件主機的規則集,Kubernetes集群排除不符合預定條件的節點. Kubernetes 默認預選規則集見表1.
定義Kubernetes集群的工作節點集合為:

預選算法的規則集合為:

定義規則的運算結果:

當節點不滿足預選算法規則集合中的任一規則,則節點被篩選掉,最終通過預選規則過濾得到的集合為:


表1 Kubernetes默認預選規則集
3.3.2 優選算法與節點評分
在篩選出符合基本要求的候選節點后,通過優選算法的節點評分決定Pod容器具體調度運行的集群宿主機. 優選算法通過不同的評分函數以及其權重,得出候選節點的綜合分值為評分函數加權求和,最后根據得分情況,將容器運行在最佳節點上.其中,s(k)為優選算法的節點綜合評分; wi為權重系數,權重系數一般為均勻等比例分配,也可以由用戶根據主觀情況進行自定義分配調整,pj(k)為評分函數.

Kubernetes中默認內置了幾種典型評分函數:
(1) LeastRequestedPriority,當新的Pod被調度到節點上,節點的優先級取決于節點空閑資源與節點總容量的比值,比值越高的節點得分越高,CPU和內存的權重相同,具有參照資源消耗跨節點調度Pod的作用.
(2) BalancedResourceAllocation,嘗試分析部署Pod后集群節點的CPU和內存利用率,以達到集群均衡狀態為目標,可以避免出現CPU、內存負載不均衡的情況.
(3) SelectorSpreadPriority,主要針對多實例的場景下使用,將優先級交給運行Pod實例數量少的節點.
為了對比Jupyter“微服務化”分布式架構改進性能,將Jupyter單體服務架構應用與基于Kubernetes集群架構的兩種不同部署方式進行測試,測試內容包括了兩個部分:并發訪問負載測試、Pod運行數量負載均衡測試. 其中,訪問負載測試的是為了對比兩種架構方式下后臺服務的響應能力; Pod運行數量負載均衡測試是通過觀察集群中Pod運行數量變化與節點計算資源使用率間的變化關系,分析驗證集群計算資源調度分配的有效性.
本文研究重點關注資源調度和橫向擴容測試,為了方便實驗,可以控制測試節點規模,本實驗中搭建了3個節點(節點配置如表2所示)的Kubernetes集群,其中1個master集群控制節點、2個Node工作節點.

表2 節點配置
Apache Bench (AB)是一種簡單有效的壓力負載測試工具包,通常用于對服務器的HTTP請求性能測試. 本研究通過AB工具分別對基于Kubernetes微服務架構和單體架構的Jupyter負載能力進行測試,測試模擬100個用戶共20 000次服務請求的響應情況,測試結果主要關注“平均響應時間(time per request)”[13,14]指標,其計算公式如下:

其中,T代表請求測試花費的總時間,N代表總請求數量,C代表并發用戶數.
如圖6測試結果數據統計分析表明,在相同的訪問量和用戶數條件下,在訪問請求總數的各個百分比分位點上,基于微服務架構的Jupyter在“平均響應時間”指標上優于單體架構.

圖6 訪問負載測試結果數據統計分析
基于Kubernetes分布式微服務架構的Jupyter,通過為不同的用戶啟動獨立的Pod提供Notebook服務,其Pod運行數量即代表用戶運行數量. Pod運行數量負載均衡測試模擬隨著用戶運行數量增加,集群節點的資源使用率變化情況,主要關注“內存使用率(Memory Usage)”和“CPU使用率(CPU Usage)”兩個指標,計算公式見式(8)和式(9):

其中,假設集群節點node(j)共計運行m 個容器實例,代表節點node(j)序號為i的容器實例,memory_container_usage[15]為節點某個容器內存使用率,cpu_container_usage[15]為節點某個容器在Δt時間間隔內的CPU使用率,node_memory為某節點總計內存資源量,node_cores為某節點CPU計算資源總量.
如圖7所示測試結果數據分析表明,隨著用戶數量的增加,集群不同節點的資源使用率呈現總體同步提高的變化趨勢,證明了計算資源的負載被均衡分配到集群各個宿主節點上. 通過資源分配算法,Kubernetes在集群上啟動Pod單元時,優先考慮宿主機的剩余計算資源,將負載優先分配到計算資源、環境條件相對優越的節點上,從而實現了在整個集群上的負載均衡.

圖7 Pod運行負載測試結果數據分析,node1、node2為測試集群中的兩個計算節點,為Pod的運行宿主機.
Jupyter作為集合了代碼運行、數據分析、論文編寫等功能為一體的優秀平臺工具,逐漸走向大型計算中心的后端,向著基于容器技術的分布式微服務架構方向演進. 本文研究通過對Jupyter的微服務化重構,并結合Kubernetes資源調度分配算法,實現了在計算集群上多用戶資源需求的負載均衡,能夠充分優化利用計算中心的集群資源[16],具備了橫向動態擴容的能力,為用戶提供更為便捷、高效的計算資源訪問方式.在下一步的研究中,將重點關注面向HPC高性能計算的Jupyter分布式并行架構,以優化計算資源利用率為主要目標[17],并融合主流大數據存儲計算框架,為計算中心和用戶構建易于管理操作的交互式分析計算平臺.