Author: xidianwangtao@gmail.comnode
摘要:一些公共服務組件在追求性能過程當中,與業務耦合太緊,形成在製做基礎鏡像時,都會把這些基礎組件都打包進去,所以當業務鏡像啓動後,容器裏面一大堆進程,這讓Kubernetes對Pod的管理存在很大隱患。爲了讓業務容器瘦身,更是爲了基礎組件自身的管理更獨立和方便,將基礎組件從業務鏡像中剝離並DaemonSet容器化部署。然而一些基礎組件Agent與業務Pod之間經過共享內存的方式進行通訊,同一Node中跨Pod的共享內存方案是首先要解決的問題。docker
自研的公共基礎組件,好比服務路由組件、安全組件等,一般以進程方式部署在Node上並同時爲Node上全部的業務提供服務,微服務及容器化以後,服務數量成百上千的增加,若是以sidecar或者打包到業務Image中繼續Per Pod Per Agent的方式部署, 那麼基礎組件的Server端的壓力可能也會成百上千的增加,風險是很大的。所以,咱們但願能以DaemonSet方式部署這些組件的Agents。api
先說說Kubernetes大行其道的今天,若是不將這些基礎組件從業務Pod中剝離,存在哪些問題:安全
業務容器中存在一大堆進程,咱們在爲Pod申請資源(cpu/mem request and limit)時,不只要考慮業務應用自己的資源消耗,還要考慮這些基礎組件的資源消耗。並且一旦某些Agent有Bug,好比內存泄漏,這將致使Pod牽連被重建,甚至Cgroup OOM在kill進程時,可能將業務進程kill了。架構
違背了Kubernetes&微服務的部署最佳實踐:Per Process Per Contaienr,而且業務進程在前臺運行,使其與容器共生死,否則這將致使Kubernetes沒法根據業務進程狀態關聯到容器狀態,進而進行高可用管理。併發
一個Node上運行10個Pod,那麼就會有x10的基礎組件數量在Node上。沒有容器化以前,一個Node只要部署一個組件進程便可,容器化以後,集羣中組件Agents數量要幾十倍的增加,若是業務進行了微服務拆分,這個指數會更大,這些基礎組件服務端是否能承受比以往高几十倍上百倍的通訊請求,這是未知的。app
若是你要全網升級某個基礎組件Agent,那你可能會瘋掉,你須要從新打全部業務鏡像,而後全網業務要進行灰度升級。由於一個Agent的升級,致使你不得不重建業務Pod。你可能會說,基礎組件Agents都會有本身的熱升級方案,咱們經過它們的方案升級就行了呀,那你將引入很大麻煩:Agents的熱升級由於沒法被Kubernetes感知,將引起Kubernetes中集羣中的數據不一致問題,那就真的要回到虛擬機或者物理機部署的玩法了。固然,這樣的需求,咱們也想過經過Operator也實現,但代價太大了,並且很不CloudNative!ide
將基礎組件Agents從業務Pod中剝離,以上的問題都能解決了,架構上的解耦帶來的好處無需多言。並且咱們能夠經過Kubernetes管理這些基礎組件Agents了,享受其自愈、滾動升級等好處。微服務
然而,理想很美好,現實很殘酷。首先要解決的問題是,有些組件Agent與業務Pod之間是經過共享內存通訊的,這跟Kubernetes&微服務的最佳實踐背道而馳。高併發
你們都知道,Kubernetes單個Pod內是共享IPC的,而且能夠經過掛載Medium爲Memory的EmptyDir Volume共享同一塊內存Volume。
首先咱們來了解一下Linux共享內存的兩種機制:
POSIX共享內存(shm_open()、shm_unlink()
)
System V共享內存(shmget()、shmat()、shmdt()
)
其中,System V共享內存歷史悠久,通常的UNIX系統上都有這套機制;而POSIX共享內存機制接口更加方便易用,通常是結合內存映射mmap使用。
mmap和System V共享內存的主要區別在於:
sysv shm是持久化的,除非被一個進程明確的刪除,不然它始終存在於內存裏,直到系統關機;
mmap映射的內存在不是持久化的,若是進程關閉,映射隨即失效,除非事先已經映射到了一個文件上。
/dev/shm 是Linux下sysv共享內存的默認掛載點。
POSIX共享內存是基於tmpfs來實現的。實際上,更進一步,不只PSM(POSIX shared memory),並且SSM(System V shared memory)在內核也是基於tmpfs實現的。
從這裏能夠看到tmpfs主要有兩個做用:
用於SYSV共享內存,還有匿名內存映射;這部分由內核管理,用戶不可見;
用於POSIX共享內存,由用戶負責mount,並且通常mount到/dev/shm ;依賴於CONFIG_TMPFS;
雖然System V與POSIX共享內存都是經過tmpfs實現,可是受的限制卻不相同。也就是說 /proc/sys/kernel/shmmax只會影響SYS V共享內存,/dev/shm只會影響Posix共享內存 。實際上,System V與Posix共享內存原本就是使用的兩個不一樣的tmpfs實例(instance)。
SYS V共享內存可以使用的內存空間只受/proc/sys/kernel/shmmax限制;而用戶經過掛載的/dev/shm,默認爲物理內存的1/2。
歸納一下:
- POSIX共享內存與SYS V共享內存在內核都是經過tmpfs實現,但對應兩個不一樣的tmpfs實例,相互獨立。
- 經過/proc/sys/kernel/shmmax能夠限制SYS V共享內存的最大值,經過/dev/shm能夠限制POSIX共享內存的最大值(全部之和)。
基礎組件Agents DaemonSet部署後,Agents和業務Pod分別在同一個Node上不一樣的Pod,那麼Kubernetes該如何支持這兩種類型的共享內存機制呢?
固然,安全性上作出了犧牲,但在非容器化以前IPC的隔離也是沒有的,因此這一點是能夠接受的。
對於集羣中的存量業務,以前都是將Agents與業務打包在同一個docker image,所以須要有灰度上線方案,以保證存量業務不受影響。
首先建立好對應的Kubernetes ClusterRole, SA, ClusterRoleBinding, PSP Object。關於PSP 的內容,請參考官方文檔介紹pod-security-policy。
在集羣中任意選擇部分Node,給Node打上Label(AgentsDaemonSet:YES)和Taint(AgentsDaemonSet=YES:NoSchedule)。
$ kubectl label node $nodeName AgentsDaemonSet=YES $ kubectl taint node $nodeName AgentsDaemonSet=YES:NoSchedule
apiVersion: apps/v1 kind: DaemonSet metadata: name: demo-agent namespace: kube-system labels: k8s-app: demo-agent spec: selector: matchLabels: name: demo-agent template: metadata: annotations: scheduler.alpha.kubernetes.io/critical-pod: "" labels: name: demo-agent spec: tolerations: - key: "AgentsDaemonSet" operator: "Equal" value: "YES" effect: "NoSchedule" hostNetwork: true hostIPC: true nodeSelector: AgentsDaemonSet: "YES" containers: - name: demo-agent image: demo_agent:1.0 volumeMounts: - mountPath: /dev/shm name: shm resources: limits: cpu: 200m memory: 200Mi requests: cpu: 100m memory: 100Mi volumes: - name: shm hostPath: path: /dev/shm type: Directory
在高併發業務下,尤爲仍是以C/C++代碼實現的基礎組件,常常會使用共享內存通訊機制來追求高性能,本文給出了Kubernetes Pod間Posix/SystemV共享內存方式的折中方案,以犧牲必定的安全性爲代價,請知悉。固然,若是微服務/容器化改造後,基礎服務的Server端肯定不會有壓力,那麼建議以SideCar Container方式將基礎服務的Agents與業務Container部署在同一Pod中,利用Pod的共享IPC特性及Memory Medium EmptyDir Volume方式共享內存。