Dumb-Init是一個簡單的過程主管和初始化系統,旨在在最小容器環境(例如Docker)中以PID 1運行。它被部署為用C的小靜態二進制二進制。
輕巧的容器已經普及了沒有正常的初始化系統(例如SystemD或sysvinit)運行單個過程或服務的想法。但是,省略初始化系統通常會導致對流程和信號的處理不正確,並且可能導致無法優雅停止的容器,或者洩漏應該被銷毀的容器。
dumb-init使您只需將命令帶有dumb-init 。它充當PID 1,並立即將您的命令作為兒童流程產生,並在收到的情況下要注意正確處理和轉發信號。
通常,當您啟動Docker容器時,要執行的過程將變為PID 1,使其成為容器的初始化系統所帶來的怪癖和職責。
這有兩個常見的問題:
在大多數情況下,信號無法正確處理。
Linux內核將特殊信號處理應用於以PID 1運行的過程。
當在普通Linux系統上發送流程信號時,內核將首先檢查該過程已註冊該信號的任何自定義處理程序,否則會落後於默認行為(例如,在SIGTERM上殺死該過程)。
但是,如果接收信號的過程為PID 1,則可以通過內核進行特殊處理。如果尚未註冊信號的處理程序,則內核將不會落後於默認行為,也不會發生任何事情。換句話說,如果您的流程沒有明確處理這些信號,則將其SIGTERM無效。
一個常見的例子是CI作業, docker run my-container script :將SIGTERM發送到docker run流程通常會殺死docker run命令,但將容器運行在後台。
孤立的殭屍過程沒有得到適當的收穫。
當過程退出時,過程將變成一個殭屍,並保持殭屍,直到其父母調用wait()系統調用的一些變化為止。它仍然是過程表中的“已停產”過程。通常,父進程會立即致電wait() ,並避免長壽殭屍。
如果父母在孩子麵前退出,則孩子是“孤兒”的,並根據PID 1進行了重新分配。因此,Init Systems負責在孤立的殭屍過程中wait() 。
當然,大多數過程都不會在恰好與之相連的隨機過程上wait() ,因此容器通常以數十個植根於PID 1的殭屍結束。
dumb-init有什麼作用dumb-init以PID 1的形式運行,就像一個簡單的Init系統。它啟動了一個過程,然後代理都收到了紮根於該兒童過程的會話的信號。
由於您的實際過程不再是PID 1,當它從dumb-init接收信號時,將應用默認信號處理程序,並且您的過程將按照您的預期行事。如果您的流程死亡, dumb-init也會死亡,請注意清理可能仍然存在的任何其他過程。
在默認模式下, dumb-init建立了一個植根於孩子的會話,並將信號發送給整個過程組。如果您的行為不佳的孩子(例如殼牌腳本)通常不會在死前表示其孩子,這將很有用。
實際上,這在常規過程主管(例如Daemontools或Supervisord)中用於監督Shell腳本的常規過程主管中的Docker容器之外很有用。通常,外殼接收到的SIGTERM之類的信號不會轉發給子過程。相反,只有外殼過程死亡。使用Dumb-Init,您只需在Shebang中寫下帶有Dumb-Init的外殼腳本:
#!/usr/bin/dumb-init /bin/sh
my-web-server & # launch a process in the background
my-other-server # launch another process in the foreground
通常,發送給外殼的SIGTERM會殺死外殼,但使這些過程運行(背景和前景!)。使用Dumb-Init,您的子流程將收到您的外殼相同的信號。
如果您希望僅將信號發送到直接的孩子,則可以使用--single-child參數運行,或者在運行dumb-init時設置環境變量DUMB_INIT_SETSID=0 。在這種模式下,愚蠢的內部是完全透明的。您甚至可以將多個串在一起(例如dumb-init dumb-init echo 'oh, hi' )。
愚蠢的內部允許在代理傳入信號之前重寫傳入信號。在您擁有碼頭主管(例如Mesos或Kubernetes)的情況下,這很有用它總是發送標准信號(例如Sigterm)。一些應用需要不同的停止信號才能進行優雅的清理。
例如,要將信號sigterm(編號15)重寫為sigquit(數字3),只需在命令行上添加--rewrite 15:3即可。
要完全刪除信號,您可以將其重寫為特殊編號0 。
在setSID模式下運行時,在大多數情況下不足以向SIGTSTP / SIGTTIN / sigttou轉發sigtstp / SIGTTOU ,因為如果該過程沒有為這些信號添加自定義信號處理程序,則該內核將不應用默認信號處理行為(由於它是孤兒流過程組的成員,因此將不應用默認信號處理行為。因此,我們將默認的重寫設置為從這三個信號中的SIGSTOP 。如果需要,您可以通過將信號重寫回原始值來選擇退出此行為。
具有此功能的一個警告:對於工作控制信號( SIGTSTP , SIGTTIN , SIGTTOU ),Dumb-Init在收到信號後總是會暫停自身,即使您將其重寫為其他東西。
您有一些使用dumb-init的選擇:
現在,許多流行的Linux發行版(包括debian(自stretch )和烏本圖(Ubuntu)等迪比安(Debian)衍生品(自bionic )中)現在在其官方存儲庫中包含愚蠢的包裹。
在基於Debian的發行版中,您可以像安裝其他任何軟件包一樣,運行apt install dumb-init來安裝啞巴。
注意:與我們提供的版本不同,大多數dumb-init的發行版本都沒有靜態鏈接(請參見下面的其他選項)。通常這是完全可以的,但是意味著,這些版本的啞巴內部版本通常在復製到其他Linux發行版時不起作用,這與我們提供的靜態鏈接版本不同。
如果您有內部APT服務器,則將.deb上傳到服務器是使用dumb-init推薦方法。在您的Dockerfiles中,您可以簡單地apt install dumb-init ,並且可以使用。
Debian軟件包可從GitHub發布選項卡中獲得,也可以自己運行自行make builddeb 。
.deb軟件包(Debian/Ubuntu)如果您沒有內部APT服務器,則可以使用dpkg -i安裝.deb軟件包。您可以選擇將.deb放在容器上的方式(安裝目錄或wget -ing是某些選項)。
一種可能性是在您的Dockerfile中使用以下命令:
RUN wget https://github.com/Yelp/dumb-init/releases/download/v1.2.5/dumb-init_1.2.5_amd64.deb
RUN dpkg -i dumb-init_*.deb由於Dumb-Init以靜態鏈接的二進制發布,因此通常可以將其插入圖像中。這是在Dockerfile中這樣做的一個示例:
RUN wget -O /usr/local/bin/dumb-init https://github.com/Yelp/dumb-init/releases/download/v1.2.5/dumb-init_1.2.5_x86_64
RUN chmod +x /usr/local/bin/dumb-init儘管dumb-init完全寫在C中,但我們還提供了一個python軟件包,該軟件包可以編譯和安裝二進製文件。可以使用pip從PYPI安裝。您需要先安裝C編譯器(在Debian/Ubuntu上, apt-get install gcc就足夠了),然後只需pip install dumb-init即可。
從1.2.0開始,PYPI的包裝可以作為預先構建的車輪存檔,並且不需要在常見的Linux分佈上編譯。
安裝在Docker容器中後,只需將您的命令帶有dumb-init命令(並確保您正在使用推薦的JSON語法)。
在Dockerfile中,最好將Dumb-Init用作容器的入口處。 “入口點”是一個部分命令,可預先到您的CMD指令,非常適合啞巴:
# Runs "/usr/bin/dumb-init -- /my/script --with --args"
ENTRYPOINT [ "/usr/bin/dumb-init" , "--" ]
# or if you use --rewrite or other cli flags
# ENTRYPOINT ["dumb-init", "--rewrite", "2:3", "--"]
CMD [ "/my/script" , "--with" , "--args" ]如果您在基本圖像中聲明入口點,則任何從中下降的圖像也不需要聲明愚蠢的內部。他們只能像往常一樣設置CMD 。
對於交互式的一次性用法,您可以手動進行預處理:
$ docker run my_container dumb-init python -c 'while True: pass'
在沒有dumb-init下運行同一命令將導致沒有SIGKILL容器無法停止容器,但是使用dumb-init ,您可以將其發送更多的人道信號,例如SIGTERM 。
必須將JSON語法用於CMD和ENTRYPOINT很重要。否則,Docker會調用外殼來運行您的命令,從而將Shell作為PID 1而不是Dumb-Init。
通常,容器想做一些在建築時間內無法完成的啟動前工作。例如,您可能需要根據環境變量模板將一些配置文件模板。
將其與啞巴納入的最佳方法是這樣:
ENTRYPOINT [ "/usr/bin/dumb-init" , "--" ]
CMD [ "bash" , "-c" , "do-some-pre-start-thing && exec my-server" ]通過仍將愚蠢的內部用作入口點,您始終擁有適當的初始化系統。
BASH命令的exec部分很重要,因為它用您的服務器替換了BASH進程,因此Shell僅在開始時暫時存在。
構建愚蠢的二進製文件需要一個工作的編譯器和LIBC標題,並默認為GLIBC。
$ make
由於glibc,靜態編譯的啞巴內部的啞巴內部超過700kb,但現在可以選擇Musl。在debian/ubuntu apt-get install musl-tools以安裝源和包裝器,然後僅:
$ CC=musl-gcc make
當靜態地與Musl編譯時,二進制尺寸約為20kb。
我們使用標準的Debian慣例來指定構建依賴性(以debian/control為單位)。入門的一種簡單方法是apt-get install build-essential devscripts equivs ,然後是sudo mk-build-deps -i --remove以自動安裝所有丟失的構建依賴項。然後,您可以使用make builddeb來構建愚蠢的Debian軟件包。
如果您喜歡使用Docker自動化的Debian軟件包構建,則只需運行make builddeb-docker即可。這很容易,但是要求您讓Docker在計算機上運行。