保障IDC安全:分布式HIDS集群架构设计
背景
近年來,互聯(lián)網(wǎng)上安全事件頻發(fā),企業(yè)信息安全越來越受到重視,而IDC服務(wù)器安全又是縱深防御體系中的重要一環(huán)。保障IDC安全,常用的是基于主機(jī)型入侵檢測系統(tǒng)Host-based Intrusion Detection System,即HIDS。在HIDS面對幾十萬臺甚至上百萬臺規(guī)模的IDC環(huán)境時(shí),系統(tǒng)架構(gòu)該如何設(shè)計(jì)呢?復(fù)雜的服務(wù)器環(huán)境,網(wǎng)絡(luò)環(huán)境,巨大的數(shù)據(jù)量給我們帶來了哪些技術(shù)挑戰(zhàn)呢?
需求描述
對于HIDS產(chǎn)品,我們安全部門的產(chǎn)品經(jīng)理提出了以下需求:
分析需求
首先,服務(wù)器業(yè)務(wù)進(jìn)程優(yōu)先級高,HIDS Agent進(jìn)程自己可以終止,但不能影響宿主機(jī)的主要業(yè)務(wù),這是第一要點(diǎn),那么業(yè)務(wù)需要具備熔斷功能,并具備自我恢復(fù)能力。
其次,進(jìn)程保活、維持心跳、實(shí)時(shí)獲取新指令能力,百萬臺Agent的全量控制時(shí)間一定要短。舉個(gè)極端的例子,當(dāng)Agent出現(xiàn)緊急情況,需要全量停止時(shí),那么全量停止的命令下發(fā),需要在1-2分鐘內(nèi)完成,甚至30秒、20秒內(nèi)完成。這些將會是很大的技術(shù)挑戰(zhàn)。
還有對配置動態(tài)更新,日志級別控制,細(xì)分精確控制到每個(gè)Agent上的每個(gè)HIDS子進(jìn)程,能自由地控制每個(gè)進(jìn)程的啟停,每個(gè)Agent的參數(shù),也能精確的感知每臺Agent的上線、下線情況。
同時(shí),Agent本身是安全Agent,安全的因素也要考慮進(jìn)去,包括通信通道的安全性,配置管理的安全性等等。
最后,服務(wù)端也要有一致性保障、可用性保障,對于大量Agent的管理,必須能實(shí)現(xiàn)任務(wù)分?jǐn)?#xff0c;并行處理任務(wù),且保證數(shù)據(jù)的一致性。考慮到公司規(guī)模不斷地?cái)U(kuò)大,業(yè)務(wù)不斷地增多,特別是美團(tuán)和大眾點(diǎn)評合并后,面對的各種操作系統(tǒng)問題,產(chǎn)品還要具備良好的兼容性、可維護(hù)性等。
總結(jié)下來,產(chǎn)品架構(gòu)要符合以下特性:
技術(shù)難點(diǎn)
在列出產(chǎn)品需要實(shí)現(xiàn)的功能點(diǎn)、技術(shù)點(diǎn)后,再來分析下遇到的技術(shù)挑戰(zhàn),包括不限于以下幾點(diǎn):
- 資源限制,較小的CPU、內(nèi)存。
- 五十萬甚至一百萬臺服務(wù)器的Agent處理控制問題。
- 量級大了后,集群控制帶來的控制效率,響應(yīng)延遲,數(shù)據(jù)一致性問題。
- 量級大了后,數(shù)據(jù)傳輸對整個(gè)服務(wù)器內(nèi)網(wǎng)帶來的流量沖擊問題。
- 量級大了后,運(yùn)行環(huán)境更復(fù)雜,Agent異常表現(xiàn)的感知問題。
- 量級大了后,業(yè)務(wù)日志、程序運(yùn)行日志的傳輸、存儲問題,被監(jiān)控業(yè)務(wù)訪問量突增帶來監(jiān)控?cái)?shù)據(jù)聯(lián)動突增,對內(nèi)網(wǎng)帶寬,存儲集群的爆發(fā)壓力問題。
我們可以看到,技術(shù)難點(diǎn)幾乎都是服務(wù)器到達(dá)一定量級帶來的,對于大量的服務(wù),集群分布式是業(yè)界常見的解決方案。
架構(gòu)設(shè)計(jì)與技術(shù)選型
對于管理Agent的服務(wù)端來說,要實(shí)現(xiàn)高可用、容災(zāi)設(shè)計(jì),那么一定要做多機(jī)房部署,就一定會遇到數(shù)據(jù)一致性問題。那么數(shù)據(jù)的存儲,就要考慮分布式存儲組件。 分布式數(shù)據(jù)存儲中,存在一個(gè)定理叫CAP定理:
CAP的解釋
關(guān)于CAP定理,分為以下三點(diǎn):
- 一致性(Consistency):分布式數(shù)據(jù)庫的數(shù)據(jù)保持一致。
- 可用性(Availability):任何一個(gè)節(jié)點(diǎn)宕機(jī),其他節(jié)點(diǎn)可以繼續(xù)對外提供服務(wù)。
- 分區(qū)容錯(cuò)性(網(wǎng)絡(luò)分區(qū))Partition Tolerance:一個(gè)數(shù)據(jù)庫所在的機(jī)器壞了,如硬盤壞了,數(shù)據(jù)丟失了,可以添加一臺機(jī)器,然后從其他正常的機(jī)器把備份的數(shù)據(jù)同步過來。
根據(jù)定理,分布式系統(tǒng)只能滿足三項(xiàng)中的兩項(xiàng)而不可能滿足全部三項(xiàng)。理解CAP定理的最簡單方式是想象兩個(gè)節(jié)點(diǎn)分處分區(qū)兩側(cè)。允許至少一個(gè)節(jié)點(diǎn)更新狀態(tài)會導(dǎo)致數(shù)據(jù)不一致,即喪失了Consistency。如果為了保證數(shù)據(jù)一致性,將分區(qū)一側(cè)的節(jié)點(diǎn)設(shè)置為不可用,那么又喪失了Availability。除非兩個(gè)節(jié)點(diǎn)可以互相通信,才能既保證Consistency又保證Availability,這又會導(dǎo)致喪失Partition Tolerance。
參見:CAP Theorem
CAP的選擇
為了容災(zāi)上設(shè)計(jì),集群節(jié)點(diǎn)的部署,會選擇的異地多機(jī)房,所以 「Partition tolerance」是不可能避免的。那么可選的是 AP 與 CP。
在HIDS集群的場景里,各個(gè)Agent對集群持續(xù)可用性沒有非常強(qiáng)的要求,在短暫時(shí)間內(nèi),是可以出現(xiàn)異常,出現(xiàn)無法通訊的情況。但最終狀態(tài)必須要一致,不能存在集群下發(fā)關(guān)停指令,而出現(xiàn)個(gè)別Agent不聽從集群控制的情況出現(xiàn)。所以,我們需要一個(gè)滿足 CP 的產(chǎn)品。
滿足CP的產(chǎn)品選擇
在開源社區(qū)中,比較出名的幾款滿足CP的產(chǎn)品,比如etcd、ZooKeeper、Consul等。我們需要根據(jù)幾款產(chǎn)品的特點(diǎn),根據(jù)我們需求來選擇符合我們需求的產(chǎn)品。
插一句,網(wǎng)上很多人說Consul是AP產(chǎn)品,這是個(gè)錯(cuò)誤的描述。既然Consul支持分布式部署,那么一定會出現(xiàn)「網(wǎng)絡(luò)分區(qū)」的問題, 那么一定要支持「Partition tolerance」。另外,在consul的官網(wǎng)上自己也提到了這點(diǎn) Consul uses a CP architecture, favoring consistency over availability.
Consul is opinionated in its usage while Serf is a more flexible and general purpose tool. In CAP terms, Consul uses a CP architecture, favoring consistency over availability. Serf is an AP system and sacrifices consistency for availability. This means Consul cannot operate if the central servers cannot form a quorum while Serf will continue to function under almost all circumstances.
etcd、ZooKeeper、Consul對比
借用etcd官網(wǎng)上etcd與ZooKeeper和Consul的比較圖。
在我們HIDS Agent的需求中,除了基本的服務(wù)發(fā)現(xiàn) 、配置同步 、配置多版本控制 、變更通知等基本需求外,我們還有基于產(chǎn)品安全性上的考慮,比如傳輸通道加密、用戶權(quán)限控制、角色管理、基于Key的權(quán)限設(shè)定等,這點(diǎn) etcd比較符合我們要求。很多大型公司都在使用,比如Kubernetes、AWS、OpenStack、Azure、Google Cloud、Huawei Cloud等,并且etcd的社區(qū)支持非常好。基于這幾點(diǎn)因素,我們選擇etcd作為HIDS的分布式集群管理。
選擇etcd
對于etcd在項(xiàng)目中的應(yīng)用,我們分別使用不同的API接口實(shí)現(xiàn)對應(yīng)的業(yè)務(wù)需求,按照業(yè)務(wù)劃分如下:
- Watch機(jī)制來實(shí)現(xiàn)配置變更下發(fā),任務(wù)下發(fā)的實(shí)時(shí)獲取機(jī)制。
- 腦裂問題在etcd中不存在,etcd集群的選舉,只有投票達(dá)到 N/2+1 以上,才會選做Leader,來保證數(shù)據(jù)一致性。另外一個(gè)網(wǎng)絡(luò)分區(qū)的Member節(jié)點(diǎn)將無主。
- 語言親和性,也是Golang開發(fā)的,Client SDK庫穩(wěn)定可用。
- Key存儲的數(shù)據(jù)結(jié)構(gòu)支持范圍性的Key操作。
- User、Role權(quán)限設(shè)定不同讀寫權(quán)限,來控制Key操作,避免其他客戶端修改其他Key的信息。
- TLS來保證通道信息傳遞安全。
- Txn分布式事務(wù)API配合Compare API來確定主機(jī)上線的Key唯一性。
- Lease租約機(jī)制,過期Key釋放,更好的感知主機(jī)下線信息。
- etcd底層Key的存儲為BTree結(jié)構(gòu),查找時(shí)間復(fù)雜度為O(㏒n),百萬級甚至千萬級Key的查找耗時(shí)區(qū)別不大。
etcd Key的設(shè)計(jì)
前綴按角色設(shè)定:
- Server配置下發(fā)使用 /hids/server/config/{hostname}/master。
- Agent注冊上線使用 /hids/agent/master/{hostname}。
- Plugin配置獲取使用 /hids/agent/config/{hostname}/plugin/ID/conf_name。
Server Watch /hids/server/config/{hostname}/master,實(shí)現(xiàn)Agent主機(jī)上線的瞬間感知。Agent Watch /hids/server/config/{hostname}/來獲取配置變更,任務(wù)下發(fā)。Agent注冊的Key帶有Lease Id,并啟用keepalive,下線后瞬間感知。 (異常下線,會有1/3的keepalive時(shí)間延遲)
關(guān)于Key的權(quán)限,根據(jù)不同前綴,設(shè)定不同Role權(quán)限。賦值給不同的User,來實(shí)現(xiàn)對Key的權(quán)限控制。
etcd集群管理
在etcd節(jié)點(diǎn)容災(zāi)考慮,考慮DNS故障時(shí),節(jié)點(diǎn)會選擇部署在多個(gè)城市,多個(gè)機(jī)房,以我們服務(wù)器機(jī)房選擇來看,在大部分機(jī)房都有一個(gè)節(jié)點(diǎn),綜合承載需求,我們選擇了N臺服務(wù)器部署在個(gè)別重要機(jī)房,來滿足負(fù)載、容災(zāi)需求。但對于etcd這種分布式一致性強(qiáng)的組件來說,每個(gè)寫操作都需要N/2-1的節(jié)點(diǎn)確認(rèn)變更,才會將寫請求寫入數(shù)據(jù)庫中,再同步到各個(gè)節(jié)點(diǎn),那么意味著節(jié)點(diǎn)越多,需要確認(rèn)的網(wǎng)絡(luò)請求越多,耗時(shí)越多,反而會影響集群節(jié)點(diǎn)性能。這點(diǎn),我們后續(xù)將提升單個(gè)服務(wù)器性能,以及犧牲部分容災(zāi)性來提升集群處理速度。
客戶端填寫的IP列表,包含域名、IP。IP用來規(guī)避DNS故障,域名用來做Member節(jié)點(diǎn)更新。最好不要使用Discover方案,避免對內(nèi)網(wǎng)DNS服務(wù)器產(chǎn)生較大壓力。
同時(shí),在配置etcd節(jié)點(diǎn)的地址時(shí),也要考慮到內(nèi)網(wǎng)DNS故障的場景,地址填寫會混合IP、域名兩種形式。
我們在設(shè)計(jì)產(chǎn)品架構(gòu)時(shí),為了安全性,開啟了TLS證書認(rèn)證,當(dāng)節(jié)點(diǎn)變更時(shí),證書的生成也同樣要考慮到上面兩種方案的影響,證書里需要包含固定IP,以及DNS域名范圍的兩種格式。
etcd Cluster節(jié)點(diǎn)擴(kuò)容
節(jié)點(diǎn)擴(kuò)容,官方手冊上也有完整的方案,etcd的Client里實(shí)現(xiàn)了健康檢測與故障遷移,能自動的遷移到節(jié)點(diǎn)IP列表中的其他可用IP。也能定時(shí)更新etcd Node List,對于etcd Cluster的集群節(jié)點(diǎn)變更來說,不存在問題。需要我們注意的是,TLS證書的兼容。
分布式HIDS集群架構(gòu)圖
集群核心組件高可用,所有Agent、Server都依賴集群,都可以無縫擴(kuò)展,且不影響整個(gè)集群的穩(wěn)定性。即使Server全部宕機(jī),也不影響所有Agent的繼續(xù)工作。
在以后Server版本升級時(shí),Agent不會中斷,也不會帶來雪崩式的影響。etcd集群可以做到單節(jié)點(diǎn)升級,一直到整個(gè)集群升級,各個(gè)組件全都解耦。
編程語言選擇
考慮到公司服務(wù)器量大,業(yè)務(wù)復(fù)雜,需求環(huán)境多變,操作系統(tǒng)可能包括各種Linux以及Windows等。為了保證系統(tǒng)的兼容性,我們選擇了Golang作為開發(fā)語言,它具備以下特點(diǎn):
產(chǎn)品架構(gòu)大方向
HIDS產(chǎn)品研發(fā)完成后,部署的服務(wù)都運(yùn)行著各種業(yè)務(wù)的服務(wù)器,業(yè)務(wù)的重要性排在第一,我們產(chǎn)品的功能排在后面。為此,確定了幾個(gè)產(chǎn)品的大方向:
- 高可用,數(shù)據(jù)一致,可橫向擴(kuò)展。
- 容災(zāi)性好,能應(yīng)對機(jī)房級的網(wǎng)絡(luò)故障。
- 兼容性好,只維護(hù)一個(gè)版本的Agent。
- 依賴低,不依賴任何動態(tài)鏈接庫。
- 侵入性低,不做Hook,不做系統(tǒng)類庫更改。
- 熔斷降級可靠,寧可自己掛掉,也不影響業(yè)務(wù) 。
產(chǎn)品實(shí)現(xiàn)
篇幅限制,僅討論框架設(shè)計(jì)、熔斷限流、監(jiān)控告警、自我恢復(fù)以及產(chǎn)品實(shí)現(xiàn)上的主進(jìn)程與進(jìn)程監(jiān)控。
框架設(shè)計(jì)
如上圖,在框架的設(shè)計(jì)上,封裝常用類庫,抽象化定義Interface,剝離etcd Client,全局化Logger,抽象化App的啟動、退出方法。使得各模塊(以下簡稱App)只需要實(shí)現(xiàn)自己的業(yè)務(wù)即可,可以方便快捷的進(jìn)行邏輯編寫,無需關(guān)心底層實(shí)現(xiàn)、配置來源、重試次數(shù)、熔斷方案等等。
沙箱隔離
考慮到子進(jìn)程不能無限的增長下去,那么必然有一個(gè)進(jìn)程包含多個(gè)模塊的功能,各App之間既能使用公用底層組件(Logger、etcd Client等),又能讓彼此之間互不影響,這里進(jìn)行了沙箱化處理,各個(gè)屬性對象僅在各App的sandbox里生效。同樣能實(shí)現(xiàn)了App進(jìn)程的性能熔斷,停止所有的業(yè)務(wù)邏輯功能,但又能具有基本的自我恢復(fù)功能。
IConfig
對各App的配置抽象化處理,實(shí)現(xiàn)IConfig的共有方法接口,用于對配置的函數(shù)調(diào)用,比如Check的檢測方法,檢測配置合法性,檢測配置的最大值、最小值范圍,規(guī)避使用人員配置不在合理范圍內(nèi)的情況,從而避免帶來的風(fēng)險(xiǎn)。
框架底層用Reflect來處理JSON配置,解析讀取填寫的配置項(xiàng),跟Config對象對比,填充到對應(yīng)Struct的屬性上,允許JSON配置里只填寫變化的配置,沒填寫的配置項(xiàng),則使用Config對應(yīng)Struct的默認(rèn)配置。便于靈活處理配置信息。
type IConfig interface {Check() error //檢測配置合法性 }func ConfigLoad(confByte []byte, config IConfig) (IConfig, error) { ... //反射生成臨時(shí)的IConfigvar confTmp IConfigconfTmp = reflect.New(reflect.ValueOf(config).Elem().Type()).Interface().(IConfig) ...//反射 confTmp 的屬性confTmpReflect := reflect.TypeOf(confTmp).Elem()confTmpReflectV := reflect.ValueOf(confTmp).Elem()//反射config IConfigconfigReflect := reflect.TypeOf(config).Elem()configReflectV := reflect.ValueOf(config).Elem() ...for i = 0; i < num; i++ {//遍歷處理每個(gè)FieldenvStructTmp := configReflect.Field(i)//根據(jù)配置中的項(xiàng),來覆蓋默認(rèn)值if envStructTmp.Type == confStructTmp.Type {configReflectV.FieldByName(envStructTmp.Name).Set(confTmpReflectV.Field(i)) 復(fù)制代碼Timer、Clock調(diào)度
在業(yè)務(wù)數(shù)據(jù)產(chǎn)生時(shí),很多地方需要記錄時(shí)間,時(shí)間的獲取也會產(chǎn)生很多系統(tǒng)調(diào)用。尤其是在每秒鐘產(chǎn)生成千上萬個(gè)事件,這些事件都需要調(diào)用獲取時(shí)間接口,進(jìn)行clock_gettime等系統(tǒng)調(diào)用,會大大增加系統(tǒng)CPU負(fù)載。 而很多事件產(chǎn)生時(shí)間的準(zhǔn)確性要求不高,精確到秒,或者幾百個(gè)毫秒即可,那么框架里實(shí)現(xiàn)了一個(gè)顆粒度符合需求的(比如100ms、200ms、或者1s等)間隔時(shí)間更新的時(shí)鐘,即滿足事件對時(shí)間的需求,又減少了系統(tǒng)調(diào)用。
同樣,在有些Ticker場景中,Ticker的間隔顆粒要求不高時(shí),也可以合并成一個(gè)Ticker,減少對CPU時(shí)鐘的調(diào)用。
Catcher
在多協(xié)程場景下,會用到很多協(xié)程來處理程序,對于個(gè)別協(xié)程的panic錯(cuò)誤,上層線程要有一個(gè)良好的捕獲機(jī)制,能將協(xié)程錯(cuò)誤拋出去,并能恢復(fù)運(yùn)行,不要讓進(jìn)程崩潰退出,提高程序的穩(wěn)定性。
抽象接口
框架底層抽象化封裝Sandbox的Init、Run、Shutdown接口,規(guī)范各App的對外接口,讓App的初始化、運(yùn)行、停止等操作都標(biāo)準(zhǔn)化。App的模塊業(yè)務(wù)邏輯,不需要關(guān)注PID文件管理,不關(guān)注與集群通訊,不關(guān)心與父進(jìn)程通訊等通用操作,只需要實(shí)現(xiàn)自己的業(yè)務(wù)邏輯即可。App與框架的統(tǒng)一控制,采用Context包以及Sync.Cond等條件鎖作為同步控制條件,來同步App與框架的生命周期,同步多協(xié)程之間同步,并實(shí)現(xiàn)App的安全退出,保證數(shù)據(jù)不丟失。
限流
網(wǎng)絡(luò)IO
- 限制數(shù)據(jù)上報(bào)速度。
- 隊(duì)列存儲數(shù)據(jù)任務(wù)列表。
- 大于隊(duì)列長度數(shù)據(jù)丟棄。
- 丟棄數(shù)據(jù)總數(shù)計(jì)數(shù)。
- 計(jì)數(shù)信息作為心跳狀態(tài)數(shù)據(jù)上報(bào)到日志中心,用于數(shù)據(jù)對賬。
磁盤IO
程序運(yùn)行日志,對日志級別劃分,參考 /usr/include/sys/syslog.h:
- LOG_EMERG
- LOG_ALERT
- LOG_CRIT
- LOG_ERR
- LOG_WARNING
- LOG_NOTICE
- LOG_INFO
- LOG_DEBUG
在代碼編寫時(shí),根據(jù)需求選用級別。級別越低日志量越大,重要程度越低,越不需要發(fā)送至日志中心,寫入本地磁盤。那么在異常情況排查時(shí),方便參考。
日志文件大小控制,分2個(gè)文件,每個(gè)文件不超過固定大小,比如20M、50M等。并且,對兩個(gè)文件進(jìn)行來回寫,避免日志寫滿磁盤的情況。
IRetry
為了加強(qiáng)Agent的魯棒性,不能因?yàn)槟承㏑PC動作失敗后導(dǎo)致整體功能不可用,一般會有重試功能。Agent跟etcd Cluster也是TCP長連接(HTTP2),當(dāng)節(jié)點(diǎn)重啟更換或網(wǎng)絡(luò)卡頓等異常時(shí),Agent會重連,那么重連的頻率控制,不能是死循環(huán)般的重試。假設(shè)服務(wù)器內(nèi)網(wǎng)交換機(jī)因內(nèi)網(wǎng)流量較大產(chǎn)生抖動,觸發(fā)了Agent重連機(jī)制,不斷的重連又加重了交換機(jī)的負(fù)擔(dān),造成雪崩效應(yīng),這種設(shè)計(jì)必須要避免。 在每次重試后,需要做一定的回退機(jī)制,常見的指數(shù)級回退,比如如下設(shè)計(jì),在規(guī)避雪崩場景下,又能保障Agent的魯棒性,設(shè)定最大重試間隔,也避免了Agent失控的問題。
//網(wǎng)絡(luò)庫重試Interface type INetRetry interface {//開始連接函數(shù)Connect() errorString() string//獲取最大重試次數(shù)GetMaxRetry() uint... } // 底層實(shí)現(xiàn) func (this *Context) Retry(netRetry INetRetry) error { ...maxRetries = netRetry.GetMaxRetry() //最大重試次數(shù)hashMod = netRetry.GetHashMod() for {if c.shutting {return errors.New("c.shutting is true...")}if maxRetries > 0 && retries >= maxRetries {c.logger.Debug("Abandoning %s after %d retries.", netRetry.String(), retries)return errors.New("超過最大重試次數(shù)")} ...if e := netRetry.Connect(); e != nil {delay = 1 << retriesif delay == 0 {delay = 1}delay = delay * hashInterval ...c.logger.Emerg("Trying %s after %d seconds , retries:%d,error:%v", netRetry.String(), delay, retries, e)time.Sleep(time.Second * time.Duration(delay))} ... } 復(fù)制代碼事件拆分
百萬臺IDC規(guī)模的Agent部署,在任務(wù)執(zhí)行、集群通訊或?qū)λ拗鳈C(jī)產(chǎn)生資源影響時(shí),務(wù)必要錯(cuò)峰進(jìn)行,根據(jù)每臺主機(jī)的唯一特征取模,拆分執(zhí)行,避免造成雪崩效應(yīng)。
監(jiān)控告警
古時(shí)候,行軍打仗時(shí),提倡「兵馬未動,糧草先行」,無疑是冷兵器時(shí)代決定勝負(fù)走向的重要因素。做產(chǎn)品也是,尤其是大型產(chǎn)品,要對自己運(yùn)行狀況有詳細(xì)的掌控,做好監(jiān)控告警,才能確保產(chǎn)品的成功。
對于etcd集群的監(jiān)控,組件本身提供了Metrics數(shù)據(jù)輸出接口,官方推薦了Prometheus來采集數(shù)據(jù),使用Grafana來做聚合計(jì)算、圖標(biāo)繪制,我們做了Alert的接口開發(fā),對接了公司的告警系統(tǒng),實(shí)現(xiàn)IM、短信、電話告警。
Agent數(shù)量感知,依賴Watch數(shù)字,實(shí)時(shí)準(zhǔn)確感知。
如下圖,來自產(chǎn)品剛開始灰度時(shí)的某一時(shí)刻截圖,Active Streams(即etcd Watch的Key數(shù)量)即為對應(yīng)Agent數(shù)量,每次灰度的產(chǎn)品數(shù)量。因?yàn)樵摬僮?#xff0c;是Agent直接與集群通訊,并且每個(gè)Agent只Watch一個(gè)Key。且集群數(shù)據(jù)具備唯一性、一致性,遠(yuǎn)比心跳日志的處理要準(zhǔn)確的多。
etcd集群Members之間健康狀況監(jiān)控
用于監(jiān)控管理etcd集群的狀況,包括Member節(jié)點(diǎn)之間數(shù)據(jù)同步,Leader選舉次數(shù),投票發(fā)起次數(shù),各節(jié)點(diǎn)的內(nèi)存申請狀況,GC情況等,對集群的健康狀況做全面掌控。
程序運(yùn)行狀態(tài)監(jiān)控告警
全量監(jiān)控Aagent的資源占用情況,統(tǒng)計(jì)每天使用最大CPU\內(nèi)存的主機(jī)Agent,確定問題的影響范圍,及時(shí)做策略調(diào)整,避免影響到業(yè)務(wù)服務(wù)的運(yùn)行。并在后續(xù)版本上逐步做調(diào)整優(yōu)化。
百萬臺服務(wù)器,日志告警量非常大,這個(gè)級別的告警信息的篩選、聚合是必不可少的。減少無用告警,讓研發(fā)運(yùn)維人員疲于奔命,也避免無用告警導(dǎo)致研發(fā)人員放松了警惕,前期忽略個(gè)例告警,先解決主要矛盾。
- 告警信息分級,告警信息細(xì)分ID。
- 根據(jù)告警級別過濾,根據(jù)告警ID聚合告警,來發(fā)現(xiàn)同類型錯(cuò)誤。
- 根據(jù)告警信息的所在機(jī)房、項(xiàng)目組、產(chǎn)品線等維度來聚合告警,來發(fā)現(xiàn)同類型錯(cuò)誤。
數(shù)據(jù)采集告警
- 單機(jī)數(shù)據(jù)數(shù)據(jù)大小、總量的歷史數(shù)據(jù)對比告警。
- 按機(jī)房、項(xiàng)目組、產(chǎn)品線等維度的大小、總量等維度的歷史數(shù)據(jù)對比告警。
- 數(shù)據(jù)采集大小、總量的對賬功能,判斷經(jīng)過一系列處理流程的日志是否丟失的監(jiān)控告警。
熔斷
- 針對單機(jī)Agent使用資源大小的閾值熔斷,CPU使用率,連續(xù)N次觸發(fā)大于等于5%,則進(jìn)行保護(hù)性熔斷,退出所有業(yè)務(wù)邏輯,以保護(hù)主機(jī)的業(yè)務(wù)程序優(yōu)先。
- Master進(jìn)程進(jìn)入空閑狀態(tài),等待第二次時(shí)間Ticker到來,決定是否恢復(fù)運(yùn)行。
- 各個(gè)App基于業(yè)務(wù)層面的監(jiān)控熔斷策略。
灰度管理
在前面的配置管理中的etcd Key設(shè)計(jì)里,已經(jīng)細(xì)分到每個(gè)主機(jī)(即每個(gè)Agent)一個(gè)Key。那么,服務(wù)端的管理,只要區(qū)分該主機(jī)所屬機(jī)房、環(huán)境、群組、產(chǎn)品線即可,那么,我們的管理Agent的顆粒度可以精確到每個(gè)主機(jī),也就是支持任意緯度的灰度發(fā)布管理與命令下發(fā)。
數(shù)據(jù)上報(bào)通道
組件名為 log_agent ,是公司內(nèi)部統(tǒng)一日志上報(bào)組件,會部署在每一臺VM、Docker上。主機(jī)上所有業(yè)務(wù)均可將日志發(fā)送至該組件。 log_agent會將日志上報(bào)到Kafka集群中,經(jīng)過處理后,落入Hive集群中。(細(xì)節(jié)不在本篇討論范圍)
主進(jìn)程
主進(jìn)程實(shí)現(xiàn)跟etcd集群通信,管理整個(gè)Agent的配置下發(fā)與命令下發(fā);管理各個(gè)子模塊的啟動與停止;管理各個(gè)子模塊的CPU、內(nèi)存占用情況,對資源超標(biāo)進(jìn)行進(jìn)行熔斷處理,讓出資源,保證業(yè)務(wù)進(jìn)程的運(yùn)行。
插件化管理其他模塊,多進(jìn)程模式,便于提高產(chǎn)品靈活性,可更簡便的更新啟動子模塊,不會因?yàn)閭€(gè)別模塊插件的功能、BUG導(dǎo)致整個(gè)Agent崩潰。
進(jìn)程監(jiān)控
方案選擇
我們在研發(fā)這產(chǎn)品時(shí),做了很多關(guān)于linux進(jìn)程創(chuàng)建監(jiān)控的調(diào)研,不限于安全產(chǎn)品,大約有下面三種技術(shù)方案:
| cn_proc | 不支持Docker | 一般 | 存在內(nèi)核拿到的PID,在/proc/下丟失的情況 | 無 |
| Audit | 不支持Docker | 一般 | 同cn_proc | 弱,但依賴Auditd |
| Hook | 定制 | 高 | 精確 | 強(qiáng) |
對于公司的所有服務(wù)器來說,幾十萬臺都是已經(jīng)在運(yùn)行的服務(wù)器,新上的任何產(chǎn)品,都盡量避免對服務(wù)器有影響,更何況是所有服務(wù)器都要部署的Agent。 意味著我們在選擇系統(tǒng)侵入性來說,優(yōu)先選擇最小侵入性的方案。
對于Netlink的方案原理,可以參考這張圖(來自:kernel-proc-connector-and-containers)
系統(tǒng)侵入性比較
- cn_proc跟Autid在「系統(tǒng)侵入性」和「數(shù)據(jù)準(zhǔn)確性」來說,cn_proc方案更好,而且使用CPU、內(nèi)存等資源情況,更可控。
- Hook的方案,對系統(tǒng)侵入性太高了,尤其是這種最底層做HOOK syscall的做法,萬一測試不充分,在特定環(huán)境下,有一定的概率會出現(xiàn)Bug,而在百萬IDC的規(guī)模下,這將成為大面積事件,可能會造成重大事故。
兼容性上比較
- cn_proc不兼容Docker,這個(gè)可以在宿主機(jī)上部署來解決。
- Hook的方案,需要針對每種Linux的發(fā)行版做定制,維護(hù)成本較高,且不符合長遠(yuǎn)目標(biāo)(收購?fù)獠抗緯r(shí)遇到各式各樣操作系統(tǒng)問題)
數(shù)據(jù)準(zhǔn)確性比較
在大量PID創(chuàng)建的場景,比如Docker的宿主機(jī)上,內(nèi)核返回PID時(shí),因?yàn)镻ID返回非常多非常快,很多進(jìn)程啟動后,立刻消失了,另外一個(gè)線程都還沒去讀取/proc/,進(jìn)程都丟失了,場景常出現(xiàn)在Bash執(zhí)行某些命令。
最終,我們選擇Linux Kernel Netlink接口的cn_proc指令作為我們進(jìn)程監(jiān)控方案,借助對Bash命令的收集,作為該方案的補(bǔ)充。當(dāng)然,仍然存在丟數(shù)據(jù)的情況,但我們?yōu)榱讼到y(tǒng)穩(wěn)定性,產(chǎn)品侵入性低等業(yè)務(wù)需求,犧牲了一些安全性上的保障。
對于Docker的場景,采用宿主機(jī)運(yùn)行,捕獲數(shù)據(jù),關(guān)聯(lián)到Docker容器,上報(bào)到日志中心的做法來實(shí)現(xiàn)。
遇到的問題
內(nèi)核Netlink發(fā)送數(shù)據(jù)卡住
內(nèi)核返回?cái)?shù)據(jù)太快,用戶態(tài)ParseNetlinkMessage解析讀取太慢,導(dǎo)致用戶態(tài)網(wǎng)絡(luò)Buff占滿,內(nèi)核不再發(fā)送數(shù)據(jù)給用戶態(tài),進(jìn)程空閑。對于這個(gè)問題,我們在用戶態(tài)做了隊(duì)列控制,確保解析時(shí)間的問題不會影響到內(nèi)核發(fā)送數(shù)據(jù)。對于隊(duì)列的長度,我們做了定值限制,生產(chǎn)速度大于消費(fèi)速度的話,可以丟棄一些數(shù)據(jù),來保證業(yè)務(wù)正常運(yùn)行,并且來控制進(jìn)程的內(nèi)存增長問題。
疑似“內(nèi)存泄露”問題
在一臺Docker的宿主機(jī)上,運(yùn)行了50個(gè)Docker實(shí)例,每個(gè)Docker都運(yùn)行了復(fù)雜的業(yè)務(wù)場景,頻繁的創(chuàng)建進(jìn)程,在最初的產(chǎn)品實(shí)現(xiàn)上,啟動時(shí)大約10M內(nèi)存占用,一天后達(dá)到200M的情況。
經(jīng)過我們Debug分析發(fā)現(xiàn),在ParseNetlinkMessage處理內(nèi)核發(fā)出的消息時(shí),PID頻繁創(chuàng)建帶來內(nèi)存頻繁申請,對象頻繁實(shí)例化,占用大量內(nèi)存。同時(shí),在Golang GC時(shí),掃描、清理動作帶來大量CPU消耗。在代碼中,發(fā)現(xiàn)對于linux/connector.h里的struct cb_msg、linux/cn_proc.h里的struct proc_event結(jié)構(gòu)體頻繁創(chuàng)建,帶來內(nèi)存申請等問題,以及Golang的GC特性,內(nèi)存申請后,不會在GC時(shí)立刻歸還操作系統(tǒng),而是在后臺任務(wù)里,逐漸的歸還到操作系統(tǒng),見:debug.FreeOSMemory
FreeOSMemory forces a garbage collection followed by an attempt to return as much memory to the operating system as possible. (Even if this is not called, the runtime gradually returns memory to the operating system in a background task.)
但在這個(gè)業(yè)務(wù)場景里,大量頻繁的創(chuàng)建PID,頻繁的申請內(nèi)存,創(chuàng)建對象,那么申請速度遠(yuǎn)遠(yuǎn)大于釋放速度,自然內(nèi)存就一直堆積。
從文檔中可以看出,FreeOSMemory的方法可以將內(nèi)存歸還給操作系統(tǒng),但我們并沒有采用這種方案,因?yàn)樗螛?biāo)不治本,沒法解決內(nèi)存頻繁申請頻繁創(chuàng)建的問題,也不能降低CPU使用率。
為了解決這個(gè)問題,我們采用了sync.Pool的內(nèi)置對象池方式,來復(fù)用回收對象,避免對象頻繁創(chuàng)建,減少內(nèi)存占用情況,在針對幾個(gè)頻繁創(chuàng)建的對象做對象池化后,同樣的測試環(huán)境,內(nèi)存穩(wěn)定控制在15M左右。
大量對象的復(fù)用,也減少了對象的數(shù)量,同樣的,在Golang GC運(yùn)行時(shí),也減少了對象的掃描數(shù)量、回收數(shù)量,降低了CPU使用率。
項(xiàng)目進(jìn)展
在產(chǎn)品的研發(fā)過程中,也遇到了一些問題,比如:
方法一定比困難多,但方法不是拍腦袋想出來的,一定要深入探索問題的根本原因,找到系統(tǒng)性的修復(fù)方法,具備高可用、高性能、監(jiān)控告警、熔斷限流等功能后,對于出現(xiàn)的問題,能夠提前發(fā)現(xiàn),將故障影響最小化,提前做處理。在應(yīng)對產(chǎn)品運(yùn)營過程中遇到的各種問題時(shí),逢山開路,遇水搭橋,都可以從容的應(yīng)對。
經(jīng)過我們一年的努力,已經(jīng)部署了除了個(gè)別特殊業(yè)務(wù)線之外的其他所有服務(wù)器,數(shù)量達(dá)幾十萬臺,產(chǎn)品穩(wěn)定運(yùn)行。在數(shù)據(jù)完整性、準(zhǔn)確性上,還有待提高,在精細(xì)化運(yùn)營上,需要多做改進(jìn)。
本篇更多的是研發(fā)角度上軟件架構(gòu)上的設(shè)計(jì),關(guān)于安全事件分析、數(shù)據(jù)建模、運(yùn)營策略等方面的經(jīng)驗(yàn)和技巧,未來將會由其他同學(xué)進(jìn)行分享,敬請期待。
總結(jié)
我們在研發(fā)這款產(chǎn)品過程中,也看到了網(wǎng)上開源了幾款同類產(chǎn)品,也了解了他們的設(shè)計(jì)思路,發(fā)現(xiàn)很多產(chǎn)品都是把主要方向放在了單個(gè)模塊的實(shí)現(xiàn)上,而忽略了產(chǎn)品架構(gòu)上的重要性。
比如,有的產(chǎn)品使用了syscall hook這種侵入性高的方案來保障數(shù)據(jù)完整性,使得對系統(tǒng)侵入性非常高,Hook代碼的穩(wěn)定性,也嚴(yán)重影響了操作系統(tǒng)內(nèi)核的穩(wěn)定。同時(shí),Hook代碼也缺少了監(jiān)控熔斷的措施,在幾十萬服務(wù)器規(guī)模的場景下部署,潛在的風(fēng)險(xiǎn)可能讓安全部門無法接受,甚至是致命的。
這種設(shè)計(jì),可能在服務(wù)器量級小時(shí),對于出現(xiàn)的問題多花點(diǎn)時(shí)間也能逐個(gè)進(jìn)行維護(hù),但應(yīng)對幾十萬甚至上百萬臺服務(wù)器時(shí),對維護(hù)成本、穩(wěn)定性、監(jiān)控熔斷等都是很大的技術(shù)挑戰(zhàn)。同時(shí),在研發(fā)上,也很難實(shí)現(xiàn)產(chǎn)品的快速迭代,而這種方式帶來的影響,幾乎都會導(dǎo)致內(nèi)核宕機(jī)之類致命問題。這種事故,使用服務(wù)器的業(yè)務(wù)方很難進(jìn)行接受,勢必會影響產(chǎn)品的研發(fā)速度、推進(jìn)速度;影響同事(SRE運(yùn)維等)對產(chǎn)品的信心,進(jìn)而對后續(xù)產(chǎn)品的推進(jìn)帶來很大的阻力。
以上是筆者站在研發(fā)角度,從可用性、可靠性、可控性、監(jiān)控熔斷等角度做的架構(gòu)設(shè)計(jì)與框架設(shè)計(jì),分享的產(chǎn)品研發(fā)思路。
筆者認(rèn)為大規(guī)模的服務(wù)器安全防護(hù)產(chǎn)品,首先需要考慮的是架構(gòu)的穩(wěn)定性、監(jiān)控告警的實(shí)時(shí)性、熔斷限流的準(zhǔn)確性等因素,其次再考慮安全數(shù)據(jù)的完整性、檢測方案的可靠性、檢測模型的精確性等因素。
九層之臺,起于累土。只有打好基礎(chǔ),才能運(yùn)籌帷幄,決勝千里之外。
參考資料
作者簡介
陳馳,美團(tuán)點(diǎn)評技術(shù)專家,2017年加入美團(tuán),十年以上互聯(lián)網(wǎng)產(chǎn)品研發(fā)經(jīng)驗(yàn),專注于分布式系統(tǒng)架構(gòu)設(shè)計(jì),目前主要從事安全防御產(chǎn)品研發(fā)工作。
關(guān)于美團(tuán)安全
美團(tuán)安全部的大多數(shù)核心開發(fā)人員,擁有多年互聯(lián)網(wǎng)以及安全領(lǐng)域?qū)嵺`經(jīng)驗(yàn),很多同學(xué)參與過大型互聯(lián)網(wǎng)公司的安全體系建設(shè),其中也不乏全球化安全運(yùn)營人才,具備百萬級IDC規(guī)模攻防對抗的經(jīng)驗(yàn)。安全部也不乏CVE“挖掘圣手”,有受邀在Black Hat等國際頂級會議發(fā)言的講者,當(dāng)然還有很多漂亮的運(yùn)營妹子。
目前,美團(tuán)安全部涉及的技術(shù)包括滲透測試、Web防護(hù)、二進(jìn)制安全、內(nèi)核安全、分布式開發(fā)、大數(shù)據(jù)分析、安全算法等等,同時(shí)還有全球合規(guī)與隱私保護(hù)等策略制定。我們正在建設(shè)一套百萬級IDC規(guī)模、數(shù)十萬終端接入的移動辦公網(wǎng)絡(luò)自適應(yīng)安全體系,這套體系構(gòu)建于零信任架構(gòu)之上,橫跨多種云基礎(chǔ)設(shè)施,包括網(wǎng)絡(luò)層、虛擬化/容器層、Server 軟件層(內(nèi)核態(tài)/用戶態(tài))、語言虛擬機(jī)層(JVM/JS V8)、Web應(yīng)用層、數(shù)據(jù)訪問層等,并能夠基于“大數(shù)據(jù)+機(jī)器學(xué)習(xí)”技術(shù)構(gòu)建全自動的安全事件感知系統(tǒng),努力打造成業(yè)界最前沿的內(nèi)置式安全架構(gòu)和縱深防御體系。
隨著美團(tuán)的高速發(fā)展,業(yè)務(wù)復(fù)雜度不斷提升,安全部門面臨更多的機(jī)遇和挑戰(zhàn)。我們希望將更多代表業(yè)界最佳實(shí)踐的安全項(xiàng)目落地,同時(shí)為更多的安全從業(yè)者提供一個(gè)廣闊的發(fā)展平臺,并提供更多在安全新興領(lǐng)域不斷探索的機(jī)會。
招聘信息
美團(tuán)安全部正在招募Web&二進(jìn)制攻防、后臺&系統(tǒng)開發(fā)、機(jī)器學(xué)習(xí)&算法等各路小伙伴。如果你想加入我們,歡迎簡歷請發(fā)至郵箱zhaoyan17@meituan.com
具體職位信息可參考這里:mp.weixin.qq.com/s/ynEq5LqQ2…
美團(tuán)安全應(yīng)急響應(yīng)中心MTSRC主頁:security.meituan.com
總結(jié)
以上是生活随笔為你收集整理的保障IDC安全:分布式HIDS集群架构设计的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 常用python模块
- 下一篇: 文档和帮助创作工具提供商Innovasy