日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

容器中的一号进程

發(fā)布時(shí)間:2024/3/24 编程问答 40 豆豆
生活随笔 收集整理的這篇文章主要介紹了 容器中的一号进程 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

微信公眾號(hào):運(yùn)維開發(fā)故事,作者:夏老師

如何理解 init 進(jìn)程?

linux 進(jìn)程在樹中排序。每個(gè)進(jìn)程都可以產(chǎn)生子進(jìn)程,并且除了最頂層的進(jìn)程之外,每個(gè)進(jìn)程都有一個(gè)父進(jìn)程。

一旦我們啟動(dòng)了多個(gè)進(jìn)程,那么容器里就會(huì)出現(xiàn)一個(gè) pid 1,也就是我們常說的 1 號(hào)進(jìn)程或者 init 進(jìn)程,然后由這個(gè)進(jìn)程創(chuàng)建出其他的子進(jìn)程。接下來,我?guī)闶崂硪幌?init 進(jìn)程是怎么來的。

一個(gè) Linux 操作系統(tǒng),在系統(tǒng)打開電源,執(zhí)行 BIOS/boot-loader 之后,就會(huì)由 boot-loader 負(fù)責(zé)加載 Linux 內(nèi)核。Linux 內(nèi)核執(zhí)行文件一般會(huì)放在 /boot 目錄下,文件名類似 vmlinuz*。在內(nèi)核完成了操作系統(tǒng)的各種初始化之后,這個(gè)程序需要執(zhí)行的第一個(gè)用戶態(tài)程就是 init 進(jìn)程。

內(nèi)核代碼啟動(dòng) 1 號(hào)進(jìn)程的時(shí)候,在沒有外面參數(shù)指定程序路徑的情況下,一般會(huì)從幾個(gè)缺省路徑嘗試執(zhí)行 1 號(hào)進(jìn)程的代碼。這幾個(gè)路徑都是 Unix 常用的可執(zhí)行代碼路徑。

系統(tǒng)啟動(dòng)的時(shí)候先是執(zhí)行內(nèi)核態(tài)的代碼,然后在內(nèi)核中調(diào)用 1 號(hào)進(jìn)程的代碼,從內(nèi)核態(tài)切換到用戶態(tài)。

目前主流的 Linux 發(fā)行版,無論是 RedHat 系的還是 Debian 系的,都會(huì)把 /sbin/init 作為符號(hào)鏈接指向 Systemd。Systemd 是目前最流行的 Linux init 進(jìn)程,在它之前還有 SysVinit、UpStart 等 Linux init 進(jìn)程。

docker中的init

在 Linux 上有了容器的概念之后,一旦容器建立了自己的 Pid Namespace(進(jìn)程命名空間),這個(gè) Namespace 里的進(jìn)程號(hào)也是從 1 開始標(biāo)記的。所以,容器的 init 進(jìn)程也被稱為 1 號(hào)進(jìn)程。你只需要記住:1 號(hào)進(jìn)程是第一個(gè)用戶態(tài)的進(jìn)程,由它直接或者間接創(chuàng)建了 Namespace 中的其他進(jìn)程。

每個(gè)Docker容器都是一個(gè)PID命名空間,這意味著容器中的進(jìn)程與主機(jī)上的其他進(jìn)程是隔離的。PID命名空間是一棵樹,從PID 1開始,通常稱為init。

注意:當(dāng)你運(yùn)行一個(gè)Docker容器時(shí),鏡像的ENTRYPOINT就是你的根進(jìn)程,即PID 1(如果你沒有ENTRYPOINT,那么CMD就會(huì)作為根進(jìn)程,你可能配置了一個(gè)shell腳本,或其他的可執(zhí)行程序,容器的根進(jìn)程具體是什么,完全取決于你的配置)。

PID 1在處理kill信號(hào)的特別之處

與其他進(jìn)程不同的是:

  • PID 1它會(huì)忽略具有默認(rèn)操作的任何信號(hào)。因此除非經(jīng)過編碼,否則應(yīng)用沒有監(jiān)聽 SIGTERM 信號(hào),或者應(yīng)用中沒有實(shí)現(xiàn)處理 SIGTERM 信號(hào)的邏輯,應(yīng)用就不會(huì)停止。比如默認(rèn)的Bash與C語言的程序,是沒有注冊(cè)SIGTERM 信號(hào)的handler;

  • PID 1永遠(yuǎn)不會(huì)響應(yīng) SIGKILL 和 SIGSTOP 這兩個(gè)特權(quán)信號(hào);

  • 對(duì)于其他的信號(hào),如果用戶自己注冊(cè)了 handler,1 號(hào)進(jìn)程可以響應(yīng)。

把Bash當(dāng)作PID 1呢?

每個(gè)基礎(chǔ)鏡像都有這個(gè)是Bash 。Bash 正確地收割了采用的子進(jìn)程。Bash 可以運(yùn)行任何東西。所以在你的Dockerfile中,你肯定會(huì)用這個(gè):

CMD ["/bin/bash", "-c", "/path-to-your-app"]

Bash默認(rèn)不會(huì)處理SIGTERM信號(hào),因此這將會(huì)導(dǎo)致如下的問題:第一個(gè)問題是:如果將Bash作為PID 1運(yùn)行,那么發(fā)送到Docker容器docker stop的信號(hào),最終都是將 SIGTERM信號(hào)發(fā)送到Bash,但是Bash默認(rèn)不會(huì)處理SIGTERM信號(hào),也不會(huì)將它們轉(zhuǎn)發(fā)到任何地方(除非您自己編寫代碼實(shí)現(xiàn))。docker stop命令執(zhí)行后,容器會(huì)有一個(gè)關(guān)閉的時(shí)限,默認(rèn)為10秒,超過十秒則用kill強(qiáng)制關(guān)閉。換句話說,給 Bash發(fā)送SIGTERM信號(hào)終止時(shí),會(huì)等待十秒鐘,然后被內(nèi)核強(qiáng)制終止包含所有進(jìn)程的整個(gè)容器。這些進(jìn)程通過 SIGKILL 信號(hào)不正常地終止。SIGKILL是特權(quán)信號(hào),無法被捕獲,因此進(jìn)程無法干凈地終止。假設(shè)服務(wù)正在運(yùn)行的應(yīng)用程序正忙于寫入文件;如果應(yīng)用程序在寫入過程中不干凈地終止,文件可能會(huì)損壞。不干凈的終止是不好的。這幾乎就像從服務(wù)器上拔下電源插頭一樣。

第二個(gè)問題是:一旦進(jìn)程退出,Bash也會(huì)繼續(xù)退出。如果程序出了bug退出了,Bash會(huì)退出,退出代碼為0,而進(jìn)程實(shí)際上崩潰了(但0表示“一切正常”;這將導(dǎo)致Docker或者k8s上重啟策略不符合預(yù)期)。因?yàn)檎嬲胍目赡苁荁ash返回與的進(jìn)程相同的退出代碼。

請(qǐng)注意,我們對(duì)bash進(jìn)行修改,編寫一個(gè) EXIT 處理程序,它只是向子進(jìn)程發(fā)送信號(hào):

#!/bin/bash function cleanup() {local pids=`jobs -p`if [[ "$pids" != "" ]]; thenkill $pids >/dev/null 2>/dev/nullfi }trap cleanup EXIT /path-to-your-app

不幸的是,這并不能解決問題。向子進(jìn)程發(fā)送信號(hào)是不夠的:init 進(jìn)程還必須等待子進(jìn)程終止,然后才能終止自己。如果 init 進(jìn)程過早終止,那么所有子進(jìn)程都會(huì)被內(nèi)核不干凈地終止。

很明顯,需要一個(gè)更復(fù)雜的解決方案,但是像 Upstart、Systemd 和 SysV init 這樣的完整 init 系統(tǒng)對(duì)于輕量級(jí) Docker 容器來說太過分了。幸運(yùn)的是,我們有很多在容器中使用的init程序。我們這里推薦使用簡(jiǎn)單的tini。

tini當(dāng)作PID 1

我們?cè)谌萜髦袉?dòng)一個(gè)init 系統(tǒng)有很多種,這里推薦使用 tini,它是專用于容器的輕量級(jí) init 系統(tǒng),使用方法也很簡(jiǎn)單:

FROM openjdk8:8u201-jdk-alpine3.9 RUN apk add --no-cache tini wget \&& mkdir -p /opt/arthas \&& cd /opt/arthas \&& wget https://arthas.aliyun.com/arthas-boot.jar ENTRYPOINT ["/sbin/tini", "--"]

請(qǐng)注意,Tini中還有一些額外的功能,在Bash或Java中很難實(shí)現(xiàn)(例如,Tini可以注冊(cè)為“子收割者”,因此它實(shí)際上不需要作為PID 1運(yùn)行來完成“僵尸進(jìn)程”收割工作),但是這些功能對(duì)于一些高級(jí)應(yīng)用場(chǎng)景來說非常有用。

為什么docker中會(huì)有僵尸進(jìn)程?

使用容器的理想境界是一個(gè)容器只啟動(dòng)一個(gè)進(jìn)程,但這在現(xiàn)實(shí)應(yīng)用中有時(shí)是做不到的。

比如說,在一個(gè)容器中除了主進(jìn)程之外,我們可能還會(huì)啟動(dòng)輔助進(jìn)程,做監(jiān)控或者 rotate logs;再比如說,我們需要把原來運(yùn)行在虛擬機(jī)(VM)的程序移到容器里,這些原來跑在虛擬機(jī)上的程序本身就是多進(jìn)程的。

一旦我們啟動(dòng)了多個(gè)進(jìn)程,那么容器里就會(huì)出現(xiàn)一個(gè) pid 1,也就是我們常說的 1 號(hào)進(jìn)程或者 init 進(jìn)程,然后由這個(gè)進(jìn)程創(chuàng)建出其他的子進(jìn)程。比如我們?cè)诓渴餵ava服務(wù)的時(shí)候,我們需要部署一個(gè)Arthas(阿爾薩斯),來做為java程序的診斷工具。

總結(jié)

第一個(gè)概念是 Linux 1 號(hào)進(jìn)程。它是第一個(gè)用戶態(tài)的進(jìn)程。它直接或者間接創(chuàng)建了 Namespace 中的其他進(jìn)程。第二個(gè)概念是容器里 1 號(hào)進(jìn)程對(duì)信號(hào)處理的三個(gè)要點(diǎn):

  • PID 1沒有默認(rèn)的信號(hào)處理程序。如果應(yīng)用沒有監(jiān)聽 SIGTERM 信號(hào),或者應(yīng)用中沒有實(shí)現(xiàn)處理 SIGTERM 信號(hào)的邏輯,應(yīng)用就不會(huì)停止,容器也不會(huì)終止。

  • 在容器中,1 號(hào)進(jìn)程永遠(yuǎn)不會(huì)響應(yīng) SIGKILL 和 SIGSTOP 這兩個(gè)特權(quán)信號(hào);

  • 對(duì)于其他的信號(hào),如果用戶自己注冊(cè)了 handler,1 號(hào)進(jìn)程可以響應(yīng)。

第三個(gè)概念是tini作為1號(hào)進(jìn)程可以給子進(jìn)程傳遞SIGTERM信號(hào)和收割僵尸進(jìn)程。

總結(jié)

以上是生活随笔為你收集整理的容器中的一号进程的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。