從一個實例,一窺docker進程管理

在Docker中,進程管理的基礎是Linux內核的PID命名空間技術。在不一樣的PID命名空間下,能夠有相同的PID。redis

Linux內核爲全部的PID命名空間維護了一個樹狀的數據結構,最頂層是系統初始化時建立的root namespace(根命名空間), 父節點能夠看到子節點中的進程,並能夠經過信號等方式對子節點中的進程產生影響。反過來,子節點不能看到父節點名空間中的任何內容,也不可能經過kill或ptrace影響父節點或其餘名空間中的進程。docker

在docker中有一個很特殊的進程——PID爲1的進程,這也是docker的主進程,經過Dockerfile中的 ENTRYPOINT 和/或 CMD指令指定。當主進程退出的時候,容器所擁有的PIG命名空間就會被銷燬,容器的生命週期也會結束docker最佳實踐建議的是一個container一個service,並不強制要你一個container一個線程。有的服務,會催生更多的子進程,好比Apache和uwsgi,這是徹底OK的。shell

PID1進程須要對本身建立的子進程負責,當主進程沒有設計好,不能優雅地讓子進程退出,就會照成不少問題,好比數據庫container,若是處理數據的進程沒有優雅地退出,可能會照成數據丟失。若是很不幸,你的主進程就是這種管理不了子進程的那種,docker提供了一個小工具,幫助你來完成這部份內容。你只須要在run建立container的時候提供一個—init flag就行,docker就會手動爲你處理好這些問題。數據庫

來看一個實例

在docker中,對於CMD和 ENTRYPOINT,支持兩種進程執行方式:exec和shell。ubuntu

shell的格式是:bash

CMD "executable param1 param2"
複製代碼

最終PID1進程將是: /bin/sh -c 」executable param1 param2」微信

Exec的格式是:數據結構

CMD ["executable","param1","param2"]
複製代碼

最終的PID1進程是: executable param1 param2工具

如今有兩個鏡像,Dockerfile分別以下:ui

  • 鏡像redis:shell
FROM ubuntu:14.04
RUN apt-get update && apt-get -y install redis-server && rm -rf /var/lib/apt/lists/* EXPOSE 6379
CMD "/usr/bin/redis-server" 複製代碼
  • 鏡像redis:exec
FROM ubuntu:14.04
RUN apt-get update && apt-get -y install redis-server && rm -rf /var/lib/apt/lists/* EXPOSE 6379
CMD ["/usr/bin/redis-server"] 複製代碼
docker run -d --name myredis1 redis:shell
docker run -d --name myredis2 redis:exec
複製代碼

那個docker鏡像更好一點呢?

咱們前面講過,PID1進程(主進程)須要對本身的子進程負責,對於redis:shell,它產生的PID1進程是

/bin/sh -c "/usr/bin/redis-server"
複製代碼

也就是說,是/bin/sh這個進程,不是/usr/bin/redis-server!/usr/bin/redis-server只是它建立的一個子進程!

執行命令

docker exec myredis1 ps -ef
複製代碼

能夠驗證這種猜想

經過exec方式運行的container的主進程則是咱們所指望的。

你可能會以爲,這有什麼大不了的呢,問題出現當咱們中止container的時候。

中止redis:shell

docker stop myredis1
docker logs myredis1
複製代碼

Stop的時候,docker明顯停頓了一段時間,並且查看日誌能夠看出,redis沒有作任何保存數據庫的操做,直接被強制退出了。這期間發生了什麼?首先,運行stop命令會向容器發送 SIGTERM信號,告訴主進程:你該退出了,感受收拾收拾。可是,這裏的主進程是/bin/sh啊,它怎麼可能會有處理redis進程退出的機制?因此redis進程不會立刻退出。 Docker Daemon等待一段時間以後(默認是10s),發現容器尚未徹底退出,這時候就會發送 SIGKILL,將容器強行殺死。在這過程當中,redis進程徹底不知道本身該退出了,因此他沒有作任何收尾的工做。

中止redis:exec

docker stop myredis2
docker logs myredis2
複製代碼

這一次stop的時候是當即生效了,沒有卡頓延遲現象,從輸出來看,redis進行了shutdown的操做,把該持久化的數據都保存到磁盤了。由於這時候的PID1進程是

/usr/bin/redis-server
複製代碼

它是可以正確處理SIGTERM信號的。這纔是咱們所指望的。

總結一下

Docker的主進程(PID1進程)是一個很特殊的存在,它的生命週期就是docker container的生命週期,它得對產生的子進程負責,在寫Dockerfile的時候,務必明確PID1進程是什麼。

關注個人微信公衆號

相關文章
相關標籤/搜索