2021-02-26 分類: 網(wǎng)站建設(shè)
Go 語言現(xiàn)在的一個主要應(yīng)用領(lǐng)域就是云原生技術(shù),包括容器(以 Docker 為代表)、Kubernetes、Prometheus 等。后面將寫一系列文章來介紹一下云原生技術(shù)棧中的關(guān)鍵技術(shù)。
過去兩年很多大公司的一個主要技術(shù)方向就是將應(yīng)用上云,在這個過程中的一個典型錯誤用法就是將容器當成虛擬機來使用,將一堆進程啟動在一個容器內(nèi)。但是容器和虛擬機對進程的管理能力是有著巨大差異的。不管在容器中還是虛擬機中都有一個一號進程,虛擬機中是 systemd 進程,容器中是 entrypoint 啟動進程,然后所有的其他線程都是一號進程的子進程,或者子進程的子進程,遞歸下去。這里的主要差異就體現(xiàn)在 systemd 進程對僵尸進程回收的能力。如果你想和更多容器技術(shù)專家交流,可以加我微信liyingjiese,備注『加群』。群里每周都有全球各大公司的好實踐以及行業(yè)最新動態(tài)。
僵尸進程
說到僵尸進程,這里簡單介紹一下 Linux 系統(tǒng)中的進程狀態(tài),我們可以通過 ps 或者 top 等命令查看系統(tǒng)中的進程,比如通過 ps aux 在我的 ecs 虛擬機上面得到如下的輸出。
- [root@emr-header-1 ~]# ps aux
- USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
- root 1 0.1 0.0 190992 3568 ? Ss Mar16 289:04 /usr/lib/systemd/systemd --switched-root --system --de
- root 2 0.0 0.0 0 0 ? S Mar16 0:05 [kthreadd]
- root 3 0.0 0.0 0 0 ? S Mar16 13:01 [ksoftirqd/0]
- root 5 0.0 0.0 0 0 ? S< Mar16 0:00 [kworker/0:0H]
- root 7 0.0 0.0 0 0 ? S Mar16 14:41 [migration/0]
- root 8 0.0 0.0 0 0 ? S Mar16 0:00 [rcu_bh]
- root 9 0.0 0.0 0 0 ? S Mar16 243:19 [rcu_sched]
- root 10 0.0 0.0 0 0 ? S Mar16 0:50 [watchdog/0]
- root 11 0.0 0.0 0 0 ? S Mar16 0:39 [watchdog/1]
- root 12 0.0 0.0 0 0 ? S Mar16 23:51 [migration/1]
- root 13 0.0 0.0 0 0 ? S Mar16 15:44 [ksoftirqd/1]
- root 15 0.0 0.0 0 0 ? S< Mar16 0:00 [kworker/1:0H]
我們可以看到排在第一位的就是前面說到的 1 號進程 systemd。其中的 STAT 那一列就是進程狀態(tài),這里的狀態(tài)都是和 S 有關(guān)的,但是正常還有 R、D、Z 等狀態(tài)。各個狀態(tài)的含義簡單描述如下:
關(guān)于僵尸進程,這里繼續(xù)討論一下。對于正常的使用情況,子進程的創(chuàng)建一般需要父進程通過系統(tǒng)調(diào)用 wait() 或者 waitpid() 來等待子進程結(jié)束,從而回收子進程的資源。除了這種方式外,還可以通過異步的方式來進行回收,這種方式的基礎(chǔ)是子進程結(jié)束之后會向父進程發(fā)送 SIGCHLD 信號,基于此父進程注冊一個 SIGCHLD 信號的處理函數(shù)來進行子進程的資源回收就可以了。記住這兩種方式,后面還會涉及到。
僵尸進程的大危害是對資源的一種永久性占用,比如進程號,系統(tǒng)會有一個大的進程數(shù) n 的限制,也就意味一旦 1 到 n 進程號都被占用,系統(tǒng)將不能創(chuàng)建任何進程和線程(進程和線程對于 OS 而言,使用同一種數(shù)據(jù)結(jié)構(gòu)來表示,task_struct)。這個時候?qū)τ谟脩舻囊粋€直觀感受就是 shell 無法執(zhí)行任何命令,這個原因是 shell 執(zhí)行命令的本質(zhì)是 fork。
- [root@emr-header-1 ~]# ulimit -a
- core file size (blocks, -c) 0
- data seg size (kbytes, -d) unlimited
- scheduling priority (-e) 0
- file size (blocks, -f) unlimited
- pending signals (-i) 63471
- max locked memory (kbytes, -l) 64
- max memory size (kbytes, -m) unlimited
- open files (-n) 131070
- pipe size (512 bytes, -p) 8
- POSIX message queues (bytes, -q) 819200
- real-time priority (-r) 0
- stack size (kbytes, -s) 8192
- cpu time (seconds, -t) unlimited
- max user processes (-u) 63471
- virtual memory (kbytes, -v) unlimited
- file locks (-x) unlimited
孤兒進程
前面說到如果子進程先于父進程退出,并且父進程沒有對子進程殘留的資源進行回收的話將會產(chǎn)生僵尸進程。這里引申另外一種情況,父進程先于子進程退出的話,那么子進程的資源誰來回收呢?
父進程先于子進程退出,這個時候我們一般將還在運行的子進程稱為孤兒進程,但是實際上孤兒進程并沒有一個明確的定義,他的狀態(tài)還是處于上面討論的幾種進程狀態(tài)中。那么孤兒進程的資源誰來回收呢?類 Unix 系統(tǒng)針對這種情況會將這些孤兒進程的父進程置為 1 號進程也就是 systemd 進程,然后由 systemd 來對孤兒進程的資源進行回收。
單進程模型的本質(zhì)
看完上面兩節(jié)大家應(yīng)該知道了虛擬機或者一個完整的 OS 是如何避免僵尸進程的。但是,在容器中,1 號進程一般是 entry point 進程,針對上面這種 將孤兒進程的父進程置為 1 號進程進而避免僵尸進程 處理方式,容器是處理不了的。進而就會導(dǎo)致容器中在孤兒進程這種異常場景下僵尸進程無法徹底處理的窘境。
所以說,容器的單進程模型的本質(zhì)其實是容器中的 1 號進程并不具有管理多進程、多線程等復(fù)雜場景下的能力。如果一定在容器中處理這些復(fù)雜情況的,那么需要開發(fā)者對 entry point 進程賦予這種能力。這無疑是加重了開發(fā)者的心智負擔,這是任何一項大眾技術(shù)或者平臺框架都不愿看到的尷尬之地。
如何避免
除了第二節(jié)討論的開發(fā)者自己賦予 entrypoint 進程管理多進程的能力,這里我更推薦借助 Kubernetes 來做這件事情。我想現(xiàn)在應(yīng)該也沒有人對容器進行人工管理了,大部分人應(yīng)該都轉(zhuǎn)向了容器編排和調(diào)度工具 Kubernetes 陣營了(對于那些還在使用 Swarm 的一小波人,我勸你們早日棄暗投明 :))。
Kubernetes 中可以將多個容器編排到一個 Pod 里面,共享同一個 Linux NameSpace。這項技術(shù)的本質(zhì)是使用 Kubernetes 提供一個 pause 鏡像,展開來說就是先用 pause 鏡像實例化出 NameSpace,然后其他容器加入這個 NameSpace 從而實現(xiàn) NameSpace 共享。突然意識到這塊需要有容器和 NameSpace 的技術(shù)背景,限于篇幅,希望你可以自行搜索這種技術(shù)背景?;蛘呶蚁乱黄恼掠懻撘幌氯萜骷夹g(shù)的本質(zhì)。
言歸正傳,我們來介紹一下 pause。pause 是 Kubernetes 在 1.16 版本引入的技術(shù),要使用 pause,我們只需要在 Pod 創(chuàng)建的 yaml 中指定 shareProcessNamespace 參數(shù)為 true,如下:
- apiVersion: v1
- kind: Pod
- metadata:
- name: nginx
- spec:
- shareProcessNamespace: true
- containers:
- - name: nginx
- image: nginx
- - name: shell
- image: busybox
- securityContext:
- capabilities:
- add:
- - SYS_PTRACE
- stdin: true
- tty: true
創(chuàng)建 Pod:
- kubectl apply -fshare-process-namespace.yaml
attach 到 Pod 中,ps 查看進程列表:
- / # ps ax
- PID USER TIME COMMAND
- 1 root 0:00 /pause
- 8 root 0:00 nginx: master process nginx -g daemon off;
- 14 101 0:00 nginx: worker process
- 15 root 0:00 sh
- 21 root 0:00 ps ax
我們可以看到 Pod 中的 1 號進程變成了 /pause,其他容器的 entrypoint 進程都變成了 1 號進程的子進程。這個時候開始逐漸逼近事情的本質(zhì)了:/pause 進程是如何處理將孤兒進程的父進程置為 1 號進程進而避免僵尸進程的呢?我們看一下源碼,git repo: pause.c:
重點關(guān)注一下 35 行和 13 行,這個不就是我們上面說的。
SIGCHLD 信號的處理函數(shù)核心就是這一行 while (waitpid(-1, NULL, WNOHANG) > 0) ,其中 WNOHANG 參數(shù)是為了讓父進程直接返回不阻塞。
總結(jié)
容器化改造的路非常漫長,對于很多業(yè)務(wù)同學在改造的過程中由于一些思維的慣性就想把容器當成一個虛擬機來使用,這個可能會導(dǎo)致非常多的問題?;蛟S我們可以探究一些容器的設(shè)計模式,以便進行更好的實踐。
分享標題:為什么說容器是單進程模型
網(wǎng)頁網(wǎng)址:http://jinyejixie.com/news43/103143.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供面包屑導(dǎo)航、做網(wǎng)站、網(wǎng)站制作、用戶體驗、網(wǎng)站營銷、微信公眾號
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)
猜你還喜歡下面的內(nèi)容