簡(jiǎn)介
在大數(shù)據(jù)迅速發(fā)展的今天,很大一部分支持來自于底層技術(shù)的不斷發(fā)展,其中非常重要的一點(diǎn)就是系統(tǒng)資源的管理和控制,大數(shù)據(jù)平臺(tái)的核心就是對(duì)資源的調(diào)度管理,在調(diào)度和管理之后如何對(duì)這些資源進(jìn)行控制便成了另一個(gè)重要的問題。大數(shù)據(jù)系統(tǒng)中用戶成千上萬的作業(yè)進(jìn)程跑在集群中,如果不能對(duì)這些進(jìn)程的資源進(jìn)行控制,那么大數(shù)據(jù)平臺(tái)將變得舉步維艱,整個(gè)集群便會(huì)隨時(shí)崩潰。同時(shí),大數(shù)據(jù)作業(yè)的調(diào)度也是基于資源的配額進(jìn)行分配,大數(shù)據(jù)的作業(yè)本身就承載了資源配額的屬性,但是這些作業(yè)是否按照配額進(jìn)行運(yùn)行和計(jì)算,是否超過了指定的配額導(dǎo)致overuse,是否達(dá)不到指定的配額導(dǎo)致資源浪費(fèi),這一直以來都是大數(shù)據(jù)平臺(tái)面對(duì)和要解決的問題。
本文針對(duì)大數(shù)據(jù)平臺(tái)中資源控制這個(gè)層面來詳細(xì)介紹資源控制在不同操作系統(tǒng)上的具體技術(shù)實(shí)現(xiàn),以及大數(shù)據(jù)平臺(tái)和資源控制的集成。
資源控制使用的系統(tǒng)功能
cgroup簡(jiǎn)介
cgroup是Linux內(nèi)核的一部分,cgroup可以為一組進(jìn)程定義組群分配資源,這個(gè)組群分配資源可以包含CPU時(shí)間,內(nèi)存,網(wǎng)絡(luò)帶寬,并且定義的這些資源分配可以動(dòng)態(tài)修改。cgroup以一種層級(jí)結(jié)構(gòu)(hierarchical)聚合和管理進(jìn)程,將所有任務(wù)進(jìn)程以文件夾的形式組成一個(gè)控制族群樹,子控制組自動(dòng)繼承父節(jié)點(diǎn)的特定屬性,子控制組還可以有自己特定的屬性。
cgroup提供一些subsystem作為控制族群樹的根節(jié)點(diǎn),所有的任務(wù)進(jìn)程都以這些子系統(tǒng)為入口按樹狀結(jié)構(gòu)設(shè)置資源配額。Red Hat Linux 7.3 提供 12 個(gè) cgroup 子系統(tǒng),根據(jù)名稱和功能列出如下。
cgroup各子系統(tǒng)功能
可通過以下命令查看操作系統(tǒng)支持的cgroup子系統(tǒng),同時(shí)顯示各個(gè)子系統(tǒng)掛載的根目錄(也可以查看系統(tǒng)文件/proc/mount或者使用命令lssubsys -a):
cgroup的操作沒有提供系統(tǒng)API調(diào)用或者命令行,而是直接訪問cgroup mount的這個(gè)文件系統(tǒng),舉個(gè)例子描述下cgroup接口的使用方式。
1. 創(chuàng)建一個(gè)目錄用于指定需要控制的作業(yè)進(jìn)程,創(chuàng)建之后系統(tǒng)在會(huì)每一級(jí)自動(dòng)生成所有的配置文件,可以將該目錄認(rèn)為是一個(gè)資源控制組。
2. 添加需要的進(jìn)程到該資源控制組,可以添加多個(gè)進(jìn)程ID
3. 設(shè)置該資源控制組的物理內(nèi)存使用配額
如果不進(jìn)行設(shè)置,默認(rèn)情況下,繼承根目錄的內(nèi)存配置,即系統(tǒng)內(nèi)存。
下面介紹下大數(shù)據(jù)系統(tǒng)中常用的配額設(shè)置。
內(nèi)存:物理內(nèi)存的設(shè)置文件為memory子系統(tǒng)下的memory.limit_in_bytes,虛擬內(nèi)存為memory.memsw.limit_in_bytes。如果同時(shí)設(shè)置這兩個(gè)參數(shù),需要先設(shè)置memory.limit_in_bytes,因?yàn)樘摂M內(nèi)存的配額只有在物理內(nèi)存用完后開始生效。在Linux系統(tǒng)上,只有當(dāng)物理內(nèi)存消耗完后才開始消耗虛擬內(nèi)存,超過配額后再申請(qǐng)的話就會(huì)觸發(fā)OOM kill掉進(jìn)程。注意:OOM killer也可以關(guān)閉,需要向memory.oom_control中寫入1,這樣當(dāng)進(jìn)程嘗試申請(qǐng)的內(nèi)存超過允許,那么它就會(huì)被暫停,直到額外的內(nèi)存被釋放。
CPU:對(duì)CPU的配額控制是通過CPU子系統(tǒng)下的cpu.cfs_period_us和cpu.cfs_quota_us兩個(gè)參數(shù)控制。cpu.cfs_period_us表示重新分配CPU時(shí)間的周期,默認(rèn)為 100000,即百毫秒。cpu.cfs_quota_us就是在這期間內(nèi)可使用的 cpu 時(shí)間,默認(rèn) -1,即無限制。所以默認(rèn)情況下CPU的使用為100%。如果需要將CPU的使用設(shè)置為50%,可以將 cpu.cfs_quota_us設(shè)為 50000,cpu.cfs_period_us保持100000,表示每隔100毫秒分配CPU時(shí)間,持續(xù)使用50毫秒。對(duì)CPU的限制不像內(nèi)存,超過配額后再申請(qǐng)的話就會(huì)觸發(fā)OOM kill掉進(jìn)程,CPU設(shè)置配額后進(jìn)程不會(huì)超過該配額的使用。
JobObjects簡(jiǎn)介
Windows平臺(tái)也有對(duì)應(yīng)的內(nèi)核對(duì)象用來控制作業(yè)對(duì)系統(tǒng)資源的訪問,而且控制的范圍比Linux廣,包括剪切板,關(guān)閉Windows的權(quán)限,窗口權(quán)限等。不同于Linux,Windows通過系統(tǒng)API來實(shí)現(xiàn)對(duì)作業(yè)對(duì)象的訪問。
Windows JobObjects支持的列表
Windows上使用內(nèi)核作業(yè)對(duì)象的流程大概如下:
- 創(chuàng)建內(nèi)核作業(yè)對(duì)象:調(diào)用::CreateJobObject()創(chuàng)建一個(gè)內(nèi)核對(duì)象,剛創(chuàng)建的對(duì)象沒有和進(jìn)程關(guān)聯(lián)。
- 把限制屬性設(shè)置到作業(yè)對(duì)象:調(diào)用:: SetInformationJobObject()可以設(shè)置如上列表中的限制屬性到該作業(yè)對(duì)象。
- 將進(jìn)程加入到作業(yè)對(duì)象:調(diào)用:: AssignProcessToJobObject()將進(jìn)程加入到作業(yè)中,如果該進(jìn)程產(chǎn)生子進(jìn)程,那么該子進(jìn)程會(huì)自動(dòng)成為作業(yè)的一部分。
- 關(guān)閉作業(yè)對(duì)象:調(diào)用:: CloseHandle()關(guān)閉作業(yè)對(duì)象的句柄。
需要注意以下幾點(diǎn):
- 一個(gè)進(jìn)程屬于一個(gè)作業(yè)對(duì)象之后,不能再assign給另一個(gè)作業(yè)對(duì)象。
- 在Windows開啟UAC的系統(tǒng)中,沒有提示權(quán)限的進(jìn)程會(huì)被加入到一個(gè)默認(rèn)的兼容性系統(tǒng)作業(yè)對(duì)象中,所以必須使用CREATE_BREAKAWAY_FROM_JOB參數(shù)創(chuàng)建進(jìn)程使該進(jìn)程脫離默認(rèn)的作業(yè)對(duì)象。
- 新啟動(dòng)的進(jìn)程最好使用CREATE_SUSPEND參數(shù)這樣可以在進(jìn)程啟動(dòng)之前加入到作業(yè)對(duì)象中,防止起啟動(dòng)的新的子進(jìn)程逃離作業(yè)對(duì)象。
Windows對(duì)于內(nèi)存的管理與Linux不同,Windows上的物理內(nèi)存指的是WorkingSet,虛擬內(nèi)存指的是committed memory,在Windows任務(wù)管理器中看的話物理內(nèi)存指的是“工作設(shè)置(內(nèi)存)”,虛擬內(nèi)存指的是“提交大小”。CPU通過CpuRate設(shè)置,CpuRate的含義是線程在每10000個(gè)處理器調(diào)度周期內(nèi)被調(diào)度的周期數(shù),比如需要限制到20%,就設(shè)置CpuRate為2000。
下面直接以C++代碼為例來說明如果創(chuàng)建和管理作業(yè)對(duì)象,同時(shí)包含如何與ACE進(jìn)程對(duì)象如何集成。
Docker容器資源控制
目前在Linux生態(tài)圈,用Docker發(fā)布和運(yùn)行程序基本已經(jīng)成為一個(gè)標(biāo)準(zhǔn),同時(shí)用Docker管理本地私有云也越來越流行,尤其對(duì)于用Kubernetes管理的容器云,如何限制容器資源變得非常重要。
在RedHat上,Docker擁有自己的cgroup控制目錄,位于各個(gè)子系統(tǒng)下的system.slice的文件夾里面。當(dāng)我們啟動(dòng)一個(gè)docker容器之后,就會(huì)產(chǎn)生這個(gè)容器ID開頭的一個(gè)子目錄,用來配置這個(gè)容器里面的所有進(jìn)程對(duì)系統(tǒng)資源的使用。
其中task目錄中存放的為容器中進(jìn)程的PID,以我們這個(gè)示例來說,我們?cè)谌萜髦袉?dòng)了 /bin/sh 進(jìn)程,這個(gè)進(jìn)程ID為2730。
云計(jì)算中Docker容器的資源收集
目前通過Docker容器部署大數(shù)據(jù)平臺(tái)也比較流行,但是大數(shù)據(jù)平臺(tái)需要獲取每個(gè)節(jié)點(diǎn)運(yùn)行環(huán)境的資源配額,對(duì)于已經(jīng)運(yùn)行在Docker容器里面的進(jìn)程,如何判斷自己擁有多少系統(tǒng)資源也可以通過cgroup文件系統(tǒng)獲取。但是Docker容器里面看到的cgroup的文件目錄和宿主機(jī)不同,docker容器里面沒有system.slice文件夾,直接以/sys/fs/cgroup/開頭,可以通過命令查看。所以可以通過這個(gè)目錄下的memory.limit_in_bytes獲取容器自身的物理內(nèi)存配額。對(duì)于容器中CPU core數(shù)目的獲取,可以通過這個(gè)公式獲取到近似的core數(shù):min(1, (int)ceil(cpu.cfs_quota_us/cpu.cfs_period_us))。
用Kubernetes部署的容器平臺(tái)需要提前定義資源配額,否則容器可以使用到宿主機(jī)的所有資源,資源配額在YAML文件的resources中定義:
YARN容器管理
作為容器管理的平臺(tái),Kubernetes主要用來在容器中部署分布式應(yīng)用程序,YARN作為一個(gè)資源管理平臺(tái)也支持容器的管理,主要用來以容器的方式運(yùn)行大數(shù)據(jù)作業(yè)。像Spark將YARN作為資源管理器運(yùn)行Spark job。
YARN支持對(duì)現(xiàn)有容器大小的調(diào)整(cgroup和jobobjects都支持修改資源配額),當(dāng)用戶從YARN申請(qǐng)了一些固定大小的容器,想改變?nèi)萜髻Y源配額的大小的時(shí)候不需要釋放掉這些容器重新申請(qǐng),YARN支持動(dòng)態(tài)改變已經(jīng)分配的容器的大小。
結(jié)束語
隨著大數(shù)據(jù)和云計(jì)算技術(shù)的發(fā)展,資源控制和管理作為底層技術(shù)已經(jīng)非常成熟,掌握這些技術(shù)便可以在大數(shù)據(jù)處理中游刃有余。