DockePod信號處理機制與僵尸進程優化

Docke&Pod信號處理與僵尸進程優化

容器與信號的關系

  • SIGTERM信號:程序結束(terminate)信號,這是用來終止進程的標準信號,也是 kill 、 killall 、 pkill 命令所發送的默認信號。與SIGKILL不同的是該信號可以被阻塞和處理。通常用來要求程序自己正常退出。shell命令kill缺省產生這個信號。SIGTERM is the default signal sent to a process by the kill or killall commands

  • SIGKILL信號:終止進程,殺死進程。此信號為 “必殺(sure kill)” 信號,處理器程序無法將其阻塞、忽略或者捕獲,故而 “一擊必殺”,總能終止程序

  • SIGHUP信號:當終端斷開(掛機)時,將發送該信號給終端控制進程。SIGHUP 信號還可用于守護進程(比如,init 等)。許多守護進程會在收到 SIGHUP 信號時重新進行初始化并重讀配置文件

  • SIGINT信號:當用戶鍵入終端中斷字符(通常為 Control-C ) 時,終端驅動程序將發送該信號給前臺進程組。該信號的默認行為是終止進程

  • SIGQUIT信號:當用戶在鍵盤上鍵入退出字符(通常為 Control-\ )時,該信號將發往前臺進程組。默認情況下,該信號終止進程,并生成用于調試的核心轉儲文件。進程如果陷入無限循環,或者不再響應時,使用 SIGQUIT 信號就很合適

  • SIGTSTP信號:這是作業控制的停止信號,當用戶在鍵盤上輸入掛起字符(通常為 Control-Z )時,將該信號給前臺進程組,使其停止運行

  • 當你在執行 Docker 容器時,主要執行程序(Process)的 PID 將會是 1,只要這個程序停止,容器就會跟著停止

  • 由于容器中一直沒有像systemd 或sysvinit 這類的初始化系統(init system),少了初始化系統來管理程序,會導致當程序不穩定的時候,無法進一步有效的處理程序的狀態,或是無法有效的控制Signal 處理機制

  • 我們以docker stop 為例,這個命令實質上是對容器中的PID 1 送出一個SIGTERM 訊號如果程序本身并沒有處理Signal 的機制,就會直接忽略這類訊號,這就會導致docker stop 等了10秒之后還不結束,然后Docker Engine 又會對PID 1 送出另一個SIGKILL 訊號,試圖強迫砍掉這個程序,這才會讓容器徹底停下來。但因為 SIGKILL 是無法被捕獲(trapped)地,所以沒有辦法干凈地終止掉子進程比如主程序在被終止時正在寫入文件,那么該文件就會因此損壞。這就像直接拔掉了服務器的電源線一樣殘酷

ENTRYPOINT 與CMD

CMD 有三種格式:

  • CMD [“executable”,“param1”,“param2”] (exec 格式, 推薦使用這種格式)
  • CMD [“param1”,“param2”] (作為 ENTRYPOINT 指令參數)
  • CMD command param1 param2 (shell 格式,默認 /bin/sh -c )

ENTRYPOINT 有兩種格式:

  • ENTRYPOINT [“executable”, “param1”, “param2”] (exec 格式,推薦優先使用這種格式)
  • ENTRYPOINT command param1 param2 (shell 格式)

通常都是因為容器啟動入口使用了 shell,比如使用了類似 /bin/sh -c my-app/docker-entrypoint.sh 這樣的 ENTRYPOINTCMD,這就可能就會導致容器內的業務進程收不到 SIGTERM 信號,原因是:

  • 容器主進程是 shell,業務進程是在 shell 中啟動的,成為了 shell 進程的子進程,不管你 Dockerfile 用其中哪個指令,兩個指令都推薦使用 exec 格式,而不是 shell 格式。原因就是因為使用 shell 格式之后,程序會以 /bin/sh -c 的子命令啟動,并且 shell 格式下不會傳遞任何信號給程序。這也就導致,在 docker stop 容器的時候,以這種格式運行的程序捕捉不到發送的信號,也就談不上優雅的關閉了
  • shell進程默認會處理SIGTERM信號,自己會退出,但是不會將信號傳遞給子進程,所以當我們以子進程啟動業務就會導致業務進程不會觸發停止邏輯
# 在當前進程上啟動一個sleep進程
[root@VM-0-3-centos ~]# echo $$ && sleep 1000
28347# kill 28347,這個28347為父進程,根據下面結果我們可以知道,父進程未把終止信號傳遞給子進程(因為sleep未被關閉)
[root@VM-0-3-centos ~]# kill 28347
[root@VM-0-3-centos ~]# ps -ef | grep slee
root      4922 28347  0 22:55 pts/1    00:00:00 sleep 1000
root      5331 29381  0 22:56 pts/2    00:00:00 grep --color=auto slee# 我們不kill 父進程了,這次我們kill sleep自身進程
[root@VM-0-3-centos ~]# kill 4922# 查看另一個窗口,我們可以發現shell進程本身可以處理終止信號
[root@VM-0-3-centos ~]# echo $$ && sleep 1000
28347
Terminated
  • 當等到 K8S 優雅停止超時時間 (terminationGracePeriodSeconds,默認 30s),發送 SIGKILL 強制殺死 shell 及其子進程

無法處理信號

案例一

# 執行簡單的sleep命令
[root@VM-0-3-centos ~]# docker run -d --rm --name=test ubuntu:22.04 /bin/sh -c "sleep 10000"
ec604a00f360c6b52554455c0b95f638ed5d97ec2578ae03462c65a345bd795a# 查看容器內部進程,可以發現sleep是子進程,子進程是無法接收到中止信號的
[root@VM-0-3-centos ~]# docker exec test ps -ef
UID        PID  PPID  C STIME TTY          TIME CMD
root         1     0  0 10:17 ?        00:00:00 /bin/sh -c sleep 10000   # 父進程
root         7     1  0 10:17 ?        00:00:00 sleep 10000							 # 子進程
root         8     0  0 10:18 ?        00:00:00 ps -ef# 嘗試停止這個容器,此時你會發現要等10秒,容器才會結束,其實是/bin/sh預設并不會處理(handle)訊號,所以他會把所有不認得的訊號忽略,直到作業系統把他SIGKILL為止
[root@VM-0-3-centos ~]# time docker stop test
testreal    0m10.141s
user    0m0.008s
sys     0m0.010s

案例二

  • shell 格式,默認 /bin/sh -c
FROM ubuntu:22.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-shell容器
[root@VM-0-3-centos ~]# docker run -d --name=redis-shell redis:shell# 查看redis日志
[root@VM-0-3-centos ~]# docker logs d5704fc16e2b27cf1fda076ca0eed80100df7f8fd4ef3d0e05487c1a6ee691fa
7:C 14 Aug 2023 10:23:21.682 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
7:C 14 Aug 2023 10:23:21.683 # Redis version=6.0.16, bits=64, commit=00000000, modified=0, pid=7, just started
7:C 14 Aug 2023 10:23:21.683 # Warning: no config file specified, using the default config. In order to specify a config file use /usr/bin/redis-server /path/to/redis.conf
7:M 14 Aug 2023 10:23:21.684 * Running mode=standalone, port=6379.
7:M 14 Aug 2023 10:23:21.684 # WARNING: The TCP backlog setting of 511 cannot be enforced because /proc/sys/net/core/somaxconn is set to the lower value of 128.
7:M 14 Aug 2023 10:23:21.684 # Server initialized
7:M 14 Aug 2023 10:23:21.684 # WARNING overcommit_memory is set to 0! Background save may fail under low memory condition. To fix this issue add 'vm.overcommit_memory = 1' to /etc/sysctl.conf and then reboot or run the command 'sysctl vm.overcommit_memory=1' for this to take effect.
7:M 14 Aug 2023 10:23:21.684 # WARNING you have Transparent Huge Pages (THP) support enabled in your kernel. This will create latency and memory usage issues with Redis. To fix this issue run the command 'echo madvise > /sys/kernel/mm/transparent_hugepage/enabled' as root, and add it to your /etc/rc.local in order to retain the setting after a reboot. Redis must be restarted after THP is disabled (set to 'madvise' or 'never').
7:M 14 Aug 2023 10:23:21.684 * Ready to accept connections# 查看redis進程信息,可以發現redis是子進程方式啟動的
[root@VM-0-3-centos ~]# docker exec redis-shell ps -ef
UID        PID  PPID  C STIME TTY          TIME CMD
root         1     0  0 10:23 ?        00:00:00 /bin/sh -c "/usr/bin/redis-server"
root         7     1  0 10:23 ?        00:00:00 /usr/bin/redis-server *:6379
root        12     0  0 10:25 ?        00:00:00 ps -ef# 手動停止redis-shell進程,等停止完成后,我們查看redis日志,我們可以發現redis并沒有主動關閉服務,而是直接被干掉了
[root@VM-0-3-centos ~]# docker stop redis-shell  			# 這里會停大概10s
redis-shell
[root@VM-0-3-centos ~]# docker logs d5704fc16e2b
7:C 14 Aug 2023 10:23:21.682 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
7:C 14 Aug 2023 10:23:21.683 # Redis version=6.0.16, bits=64, commit=00000000, modified=0, pid=7, just started
7:C 14 Aug 2023 10:23:21.683 # Warning: no config file specified, using the default config. In order to specify a config file use /usr/bin/redis-server /path/to/redis.conf
7:M 14 Aug 2023 10:23:21.684 * Running mode=standalone, port=6379.
7:M 14 Aug 2023 10:23:21.684 # WARNING: The TCP backlog setting of 511 cannot be enforced because /proc/sys/net/core/somaxconn is set to the lower value of 128.
7:M 14 Aug 2023 10:23:21.684 # Server initialized
7:M 14 Aug 2023 10:23:21.684 # WARNING overcommit_memory is set to 0! Background save may fail under low memory condition. To fix this issue add 'vm.overcommit_memory = 1' to /etc/sysctl.conf and then reboot or run the command 'sysctl vm.overcommit_memory=1' for this to take effect.
7:M 14 Aug 2023 10:23:21.684 # WARNING you have Transparent Huge Pages (THP) support enabled in your kernel. This will create latency and memory usage issues with Redis. To fix this issue run the command 'echo madvise > /sys/kernel/mm/transparent_hugepage/enabled' as root, and add it to your /etc/rc.local in order to retain the setting after a reboot. Redis must be restarted after THP is disabled (set to 'madvise' or 'never').
7:M 14 Aug 2023 10:23:21.684 * Ready to accept connections

案例三

  • 以exec方式啟動進程,但是這個進程以非exec方式啟動腳本或程序(腳本和程序實際是等效的)
[root@VM-0-3-centos redis-server-exec]# cat sleep.sh 
#!/bin/bash
sleep 10000 # 等效/bin/sh -c sleep 10000  [root@VM-0-3-centos redis-server-exec]# cat dockerfile 
FROM ubuntu:22.04
COPY sleep.sh /
CMD ["/sleep.sh"]# 啟動容器
[root@VM-0-3-centos redis-server-exec]# docker run -d --name=test redis:son
fe59095f93e2f687c67cb43e13c37011e6032882fea6d00d928eafd968aab737# 查看進程,我們可以發現sleep 也是以子進程方式啟動的,故無法對信號進行處理
[root@VM-0-3-centos redis-server-exec]# docker exec test ps -ef
UID        PID  PPID  C STIME TTY          TIME CMD
root         1     0  0 13:50 ?        00:00:00 /bin/bash /sleep.sh
root         7     1  0 13:50 ?        00:00:00 sleep 10000
root         8     0  0 13:50 ?        00:00:00 ps -ef

正常處理信號

案例一

  • exec 格式, 推薦使用這種格式
FROM ubuntu:22.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容器
[root@VM-0-3-centos ~]# docker run -d --name=redis-exec redis:exec
9bed75cfdd7d9efaf0775abb7f43d1a7c13931c1c959f65c28081e9b89f7d5e8# # 查看redis日志
[root@VM-0-3-centos ~]# docker logs 9bed75cfdd7d9efaf0775abb7f43d1a7c13931c1c959f65c28081e9b89f7d5e8
1:C 14 Aug 2023 13:39:35.582 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
1:C 14 Aug 2023 13:39:35.582 # Redis version=6.0.16, bits=64, commit=00000000, modified=0, pid=1, just started
1:C 14 Aug 2023 13:39:35.582 # Warning: no config file specified, using the default config. In order to specify a config file use /usr/bin/redis-server /path/to/redis.conf
1:M 14 Aug 2023 13:39:35.583 * Running mode=standalone, port=6379.
1:M 14 Aug 2023 13:39:35.583 # WARNING: The TCP backlog setting of 511 cannot be enforced because /proc/sys/net/core/somaxconn is set to the lower value of 128.
1:M 14 Aug 2023 13:39:35.583 # Server initialized
1:M 14 Aug 2023 13:39:35.583 # WARNING overcommit_memory is set to 0! Background save may fail under low memory condition. To fix this issue add 'vm.overcommit_memory = 1' to /etc/sysctl.conf and then reboot or run the command 'sysctl vm.overcommit_memory=1' for this to take effect.
1:M 14 Aug 2023 13:39:35.583 # WARNING you have Transparent Huge Pages (THP) support enabled in your kernel. This will create latency and memory usage issues with Redis. To fix this issue run the command 'echo madvise > /sys/kernel/mm/transparent_hugepage/enabled' as root, and add it to your /etc/rc.local in order to retain the setting after a reboot. Redis must be restarted after THP is disabled (set to 'madvise' or 'never').
1:M 14 Aug 2023 13:39:35.584 * Ready to accept connections# 查看redis進程信息,可以發現redis是在當前進程方式啟動的
root@VM-0-3-centos ~]# docker exec redis-exec ps -ef
UID        PID  PPID  C STIME TTY          TIME CMD
root         1     0  0 13:39 ?        00:00:00 /usr/bin/redis-server *:6379
root        11     0  2 13:40 ?        00:00:00 ps -ef# 手動停止redis-exec進程可以發現立刻停止了,我們查看redis日志,我們可以發現redis并有主動關閉服務,而不是直接被干掉了
[root@VM-0-3-centos ~]# docker stop redis-exec
redis-exec[root@VM-0-3-centos ~]# docker logs redis-exec
1:C 14 Aug 2023 13:39:35.582 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
1:C 14 Aug 2023 13:39:35.582 # Redis version=6.0.16, bits=64, commit=00000000, modified=0, pid=1, just started
1:C 14 Aug 2023 13:39:35.582 # Warning: no config file specified, using the default config. In order to specify a config file use /usr/bin/redis-server /path/to/redis.conf
1:M 14 Aug 2023 13:39:35.583 * Running mode=standalone, port=6379.
1:M 14 Aug 2023 13:39:35.583 # WARNING: The TCP backlog setting of 511 cannot be enforced because /proc/sys/net/core/somaxconn is set to the lower value of 128.
1:M 14 Aug 2023 13:39:35.583 # Server initialized
1:M 14 Aug 2023 13:39:35.583 # WARNING overcommit_memory is set to 0! Background save may fail under low memory condition. To fix this issue add 'vm.overcommit_memory = 1' to /etc/sysctl.conf and then reboot or run the command 'sysctl vm.overcommit_memory=1' for this to take effect.
1:M 14 Aug 2023 13:39:35.583 # WARNING you have Transparent Huge Pages (THP) support enabled in your kernel. This will create latency and memory usage issues with Redis. To fix this issue run the command 'echo madvise > /sys/kernel/mm/transparent_hugepage/enabled' as root, and add it to your /etc/rc.local in order to retain the setting after a reboot. Redis must be restarted after THP is disabled (set to 'madvise' or 'never').
1:M 14 Aug 2023 13:39:35.584 * Ready to accept connections
1:signal-handler (1692020490) Received SIGTERM scheduling shutdown...
1:M 14 Aug 2023 13:41:30.888 # User requested shutdown...
1:M 14 Aug 2023 13:41:30.888 * Saving the final RDB snapshot before exiting.
1:M 14 Aug 2023 13:41:30.896 * DB saved on disk
1:M 14 Aug 2023 13:41:30.896 # Redis is now ready to exit, bye bye...        # 我們可以發現,redis是主動退出了

案例二

  • 以exec方式啟動進程,這個進程也以exec方式啟動腳本或程序(腳本和程序實際是等效的)
  • 腳本中執行二進制
[root@VM-0-3-centos redis-server-exec]# cat sleep.sh 
#!/bin/bash
exec sleep 10000			# 以非子進程啟動[root@VM-0-3-centos redis-server-exec]# cat dockerfile 
FROM ubuntu:22.04
COPY sleep.sh /
CMD ["/sleep.sh"]# 啟動容器
root@VM-0-3-centos redis-server-exec]# docker run -d --name=test redis:son
d9c45f8d3b0f9aa7842b0e3b83a6a716310beff24b0e8c3990820e7d8b54f71a# 查看容器進程,我們可以發現,這是以非子進程啟動的,所以可以接受信號
[root@VM-0-3-centos redis-server-exec]# docker exec test ps -ef 
UID        PID  PPID  C STIME TTY          TIME CMD
root         1     0  0 13:53 ?        00:00:00 sleep 10000
root         7     0  0 13:53 ?        00:00:00 ps -ef

案例三

通常我們一個容器只會有一個進程,也是 Kubernetes 的推薦做法。但有些時候我們不得不啟動多個進程,比如從傳統部署遷移到 Kubernetes 的過渡期間,使用了富容器,即單個容器中需要啟動多個業務進程,這時也只能通過 shell 啟動,但無法使用上面的 exec 方式來傳遞信號,因為 exec只能讓一個進程替代當前 shell 成為主進程

這個時候我們可以在 shell 中使用 trap 來捕獲信號,當收到信號后觸發回調函數來將信號通過 kill 傳遞給業務進程。腳本示例:

[root@VM-0-3-centos redis-server-exec]# cat entrypoint.sh 
#!/bin/bash
sleep 100000 & pid1="$!"  # 啟動一個進程,并記錄pid
echo "sleep1 started with pid $pid1"sleep 100000 & pid2="$!"  # 啟動一個進程,并記錄pid
echo "sleep2 started with pid $pid2"handle_sigterm() {echo "[INFO] Received SIGTERM"kill -SIGTERM $pid1 $pid2 # 傳遞 SIGTERM 給業務進程wait $pid1 $pid2 # 等待所有業務進程完全終止
}
echo "[INFO] sleep1/sleep2 start ok"trap handle_sigterm SIGTERM # 捕獲 SIGTERM 信號并回調 handle_sigterm 函數
wait # 等待回調執行完,主進程再退出[root@VM-0-3-centos redis-server-exec]# cat dockerfile 
FROM ubuntu:22.04
COPY entrypoint.sh  /
CMD ["/entrypoint.sh"]# 啟動容器
[root@VM-0-3-centos redis-server-exec]# docker run -d --name test test:v1
5979b214264d87592c8b3515fb47714b2c51efd2af12f36a1fffa56bc60d5b88# 查看進程,這里我們可以看到,entrypoint.sh 啟動了二個sleep子進程
[root@VM-0-3-centos redis-server-exec]# docker exec 5979b214264d ps -ef 
UID        PID  PPID  C STIME TTY          TIME CMD
root         1     0  0 14:17 ?        00:00:00 /bin/bash /entrypoint.sh
root         7     1  0 14:17 ?        00:00:00 sleep 100000
root         8     1  0 14:17 ?        00:00:00 sleep 100000
root         9     0  0 14:18 ?        00:00:00 ps -ef# 停止的時候我們可以發現,立刻被中止了,說明終止信號被正常處理了
[root@VM-0-3-centos redis-server-exec]# docker stop test
test

僵尸進程

  • 當一個子進程終止后,它首先會變成一個失效(defunct)的進程,也稱為僵尸zombie進程,等待父進程或系統收回(reap)。在Linux內核中維護了關于“僵尸”進程的一組信息(PID,終止狀態,資源使用信息),從而允許父進程能夠獲取有關子進程的信息。如果不能正確回收“僵尸”進程,那么他們的進程描述符仍然保存在系統中,系統資源會緩慢泄露
  • 大多數設計良好的多進程應用可以正確的收回僵尸子進程,比如NGINX master進程可以收回已終止的worker子進程。如果需要自己實現,則可利用如下方法:
    • 利用操作系統的waitpid()函數等待子進程結束并請除它的僵死進程
    • 由于當子進程成為“defunct”進程時,父進程會收到一個SIGCHLD信號,所以我們可以在父進程中指定信號處理的函數來忽略SIGCHLD信號,或者自定義收回處理邏輯
  • 如果父進程已經結束了,那些依然在運行中的子進程會成為孤兒orphaned進程。在Linux中Init進程(PID1)作為所有進程的父進程,會維護進程樹的狀態,一旦有某個子進程成為了“孤兒”進程后,init就會負責接管這個子進程。當一個子進程成為“僵尸”進程之后,如果其父進程已經結束,init會收割這些“僵尸”,釋放PID資源
  • Linux 中,若子進程缺失父進程,其殘留資源會由 init 進程回收。但在 Docker 中,容器并非一個完整的操作系統,不會初始化 init 進程,容器中的第一個進程只是一個普通進程,所以并不會回收僵尸進程

下面我們做幾個試驗來驗證不同的PID1進程對僵尸進程不同的處理能力

FROM ubuntu:22.04
RUN apt-get update && apt-get -y install redis-server && rm -rf /var/lib/apt/lists/*
EXPOSE 6379
CMD ["/usr/bin/redis-server"]			# 以當前進程方式啟動進程# 啟動容器
[root@VM-0-3-centos redis-server-exec]# docker run -d --name=redis redis:v1
daca6972da659b99922df3fe227120b473bc22f4b28e6436e2337979c50ab706# 在redis容器中啟動一個bash進程,并創建子進程“sleep 10000”
[root@VM-0-3-centos redis-server-exec]# docker exec -it redis bash
root@daca6972da65:/# sleep 10000# 查看進程,我們可以發現一個sleep進程是bash進程的子進程
[root@VM-0-3-centos ~]# docker exec redis ps -ef 
UID        PID  PPID  C STIME TTY          TIME CMD
root         1     0  0 14:38 ?        00:00:00 /usr/bin/redis-server *:6379
root        11     0  0 14:38 pts/0    00:00:00 bash
root        19    11  0 14:38 pts/0    00:00:00 sleep 10000
root        20     0  0 14:39 ?        00:00:00 ps -ef# 我們殺死bash進程之后查看進程列表,這時候bash進程已經被殺死。這時候sleep進程(PID為19),雖然已經結束,而且被PID1進程(redis-server)接管,但是其沒有被父進程回收,成為僵尸狀態。這是因為PID1進程“redis-server”沒有考慮過作為init對僵尸子進程的回收的場景
[root@VM-0-3-centos ~]# docker exec redis ps -ef 
UID        PID  PPID  C STIME TTY          TIME CMD
root         1     0  0 14:38 ?        00:00:00 /usr/bin/redis-server *:6379
root        19    11  0 14:38 pts/0    00:00:00 [sleep] <defunct>
root        20     0  0 14:39 ?        00:00:00 ps -ef
FROM ubuntu:22.04
RUN apt-get update && apt-get -y install redis-server && rm -rf /var/lib/apt/lists/*
EXPOSE 6379
CMD "/usr/bin/redis-server"			# 以子進程方式啟動進程# 啟動容器
[root@VM-0-3-centos redis-server-exec]# docker run -d --name=redis redis:v1
8325ff6ad59350249b3f0bdeb24e356b53a897906ae739a603fe594f518c0c38# 在redis容器中啟動一個bash進程,并創建子進程“sleep 10000”
[root@VM-0-3-centos redis-server-exec]# docker exec -it redis bash
root@8325ff6ad593:/# sleep 10000# 查看進程,我們可以發現一個sleep進程是bash進程的子進程
[root@VM-0-3-centos ~]# docker exec redis ps -ef 
UID        PID  PPID  C STIME TTY          TIME CMD
root         1     0  0 14:44 ?        00:00:00 /bin/sh -c "/usr/bin/redis-server"...# ??????????????????????????????
root         7     1  0 14:44 ?        00:00:00 /usr/bin/redis-server *:6379
root        12     0  0 14:45 pts/0    00:00:00 bash
root        20    12  0 14:45 pts/0    00:00:00 sleep 10000
root        21     0  0 14:45 ?        00:00:00 ps -ef# 我們殺死bash進程之后查看進程列表,發現“bash”和“sleep 1000”進程都已經被殺死和回收
[root@VM-0-3-centos ~]# docker exec redis kill -9 12
[root@VM-0-3-centos ~]# docker exec redis ps -ef 
UID        PID  PPID  C STIME TTY          TIME CMD
root         1     0  0 14:44 ?        00:00:00 /bin/sh -c "/usr/bin/redis-server"...# ??????????????????????????????
root         7     1  0 14:44 ?        00:00:00 /usr/bin/redis-server *:6379
root        33     0  2 14:46 ?        00:00:00 ps -ef

這是因為sh/bash等應用可以自動清理僵尸進程。簡單而言,如果在容器中運行多個進程,PID1進程需要有能力接管“孤兒”進程并回收“僵尸”進程。我們可以

  • 利用自定義的init進程來進行進程管理,比如S6,phusion myinit,dumb-init,tini等

  • Bash/sh等缺省提供了進程管理能力,如果需要可以作為PID1進程來實現正確的進程回收

  • 如果我們父進程以bash/sh啟動,能提供進程收割能力,防止容器出現僵尸進程,但是缺無法將終止信號傳遞給子進程,為此我們一般采用:dumb-init、tini作為父進程(提供進程接管能力、信號傳遞能力)

tini&dumb-init進程

  • tini 是一套更簡單的init 系統,專門用來執行一個子程序(spawn a single child),并等待子程序結束,即便子程序已經變成僵尸程序(zombie process)也能捕捉到,同時也能轉送Signal 給子程序
  • Tini一般在容器中運行,用于生成子進程,等待它推出,reap僵尸進程,并執行信號轉發
  • dumb-init 和 tini 都可以作為 init 進程,作為主進程 (PID 1) 在容器中啟動,然后它再運行 shell 來執行我們指定的腳本 (shell 作為子進程),shell 中啟動的業務進程也成為它的子進程,當它收到信號時會將其傳遞給所有的子進程,從而也能完美解決 SHELL 無法傳遞信號問題,并且還有回收僵尸進程的能力。
  • 如果你使用Docker 來跑容器,可以非常簡便的在docker run 的時候用–init 參數,就會自動注入tini 程式(/sbin/docker-init) 到容器中,并且自動取代ENTRYPOINT 設定,讓原本的程式直接跑在tini 程序底下。注意:Docker 1.13 以后的版本才開始支援 --init 參數,并內建 tini 在內
  • init系統有以下幾個特點
    • 它是系統的第一個進程,負責產生其他所有用戶進程
    • init 以守護進程方式存在,是所有其他進程的祖先
    • 它主要負責:啟動守護進程、回收孤兒進程、將操作系統信號轉發給子進程

Tini

  • 當我們docker版本>=1.13時,docker默認集成了tini
# 以init方式啟動容器
[root@VM-0-3-centos ~]# docker run -d --init --name=test ubuntu:22.04 sleep 10000
203e03c6ac46f2806e648d4b3dce9efe3b70f9d239474136e6874f35885a642d# 查看容器進程
[root@VM-0-3-centos ~]# docker exec test ps -ef
UID        PID  PPID  C STIME TTY          TIME CMD
root         1     0  0 01:37 ?        00:00:00 /sbin/docker-init -- sleep 10000
root         7     1  0 01:37 ?        00:00:00 sleep 10000
root         8     0  0 01:38 ?        00:00:00 ps -ef# 停止容器,我們可以發現,正常情況下,sleep是作為子進程,無法正常處理信號的,需要等待10s,這里立馬就停止了,說明docker-init將信號傳遞給子進程了
[root@VM-0-3-centos ~]# docker stop test
test
  • Dockerfile集成tini
FROM ubuntu:22.04
ENV TINI_VERSION v0.19.0
ADD https://github.com/krallin/tini/releases/download/${TINI_VERSION}/tini /tini
RUN chmod +x /tini
# [-vvv/-vv/-v] 設置日志級別,可選參數
ENTRYPOINT ["/tini", "-vvv","--"]# 啟動程序,這里僅測試,啟動命令由docker run傳遞進來
# CMD ["/your/program", "-and", "-its", "arguments"]# 啟動容器
[root@VM-0-3-centos ~]# docker run -d --name=test test:tini sleep 10000
ca4f0d14c341223d530bbac9a5beb0907e633e23b095eb2168ade9b6df3e6d70# 查看容器進程
[root@VM-0-3-centos ~]# docker exec test ps -ef
UID        PID  PPID  C STIME TTY          TIME CMD
root         1     0  0 01:45 ?        00:00:00 /tini -vvv -- sleep 10000
root         7     1  0 01:45 ?        00:00:00 sleep 10000
root         8     0  0 01:46 ?        00:00:00 ps -ef# 測試僵尸進程回收,我們另起一個窗口,啟動sleep,然后將其父進程bash kill掉
[root@VM-0-3-centos ~]# docker exec -it test /bin/bash
root@ca4f0d14c341:/# sleep 10000# 查看此時進程信息,sleep的父進程IP為14
[root@VM-0-3-centos ~]# docker exec test ps -ef
UID        PID  PPID  C STIME TTY          TIME CMD
root         1     0  0 01:45 ?        00:00:00 /tini -vvv -- sleep 10000
root         7     1  0 01:45 ?        00:00:00 sleep 10000
root        14     0  0 01:47 pts/0    00:00:00 /bin/bash
root        22    14  0 01:47 pts/0    00:00:00 sleep 10000
root        23     0  0 01:48 ?        00:00:00 ps -ef# 我們可以發現tini進程將子進程回收了,并沒有產生僵尸進程
[root@VM-0-3-centos ~]# docker exec test kill -9 14
[root@VM-0-3-centos ~]# docker exec test ps -ef
UID        PID  PPID  C STIME TTY          TIME CMD
root         1     0  0 01:45 ?        00:00:00 /tini -vvv -- sleep 10000
root         7     1  0 01:45 ?        00:00:00 sleep 10000
root        35     0  0 01:48 ?        00:00:00 ps -ef

參考鏈接:https://blog.miniasp.com/post/2021/07/09/Use-dumb-init-in-Docker-Container
參考鏈接:http://www.oschina.net/translate/docker-and-the-pid-1-zombie-reaping-problem

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/37426.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/37426.shtml
英文地址,請注明出處:http://en.pswp.cn/news/37426.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

k8s和docker簡單介紹

當涉及到容器技術和容器編排時&#xff0c;Docker和Kubernetes是兩個重要的概念。我將更詳細地介紹它們以及它們之間的關系。 Docker&#xff1a; Docker是一種容器化技術&#xff0c;它允許你將應用程序及其依賴項打包到一個稱為"容器"的封閉環境中。每個容器都包…

rust包跨平臺編譯,macbook ,linux

在 MacBook 上編譯 Rust 項目并生成 Linux 包需要一些步驟。以下是一般的步驟概述&#xff1a; 1. **安裝所需工具&#xff1a;** 首先&#xff0c;確保您的 MacBook 上已經安裝了所需的工具。您需要 Rust 編程語言的工具鏈以及一些用于交叉編譯到 Linux 的工具。 - 安裝 R…

【BASH】回顧與知識點梳理(二十一)

【BASH】回顧與知識點梳理 二十一 二十一. Linux 的文件權限與目錄配置21.1 使用者與群組屬主(文件擁有者)屬組(群組概念)其他人的概念root(萬能的天神)Linux 用戶身份與群組記錄的文件 21.2 Linux 文件權限概念Linux 文件屬性Linux 文件權限的重要性 21.3 如何改變文件屬性與權…

組合模式(C++)

定義 將對象組合成樹形結構以表示部分-整體’的層次結構。Composite使得用戶對單個對象和組合對象的使用具有一致性(穩定)。 應用場景 在軟件在某些情況下&#xff0c;客戶代碼過多地依賴于對象容器復雜的內部實現結構&#xff0c;對象容器內部實現結構(而非抽象接口)的變化…

Redis數據結構——鏈表list

鏈表是一種常用的數據結構&#xff0c;提供了順序訪問的方式&#xff0c;而且高效地增刪操作。 Redis中廣泛使用了鏈表&#xff0c;例如&#xff1a;列表的底層實現之一就是鏈表。 在Redis中&#xff0c;鏈表分為兩部分&#xff1a;鏈表信息 鏈表節點。 鏈表節點用來表示鏈表…

PyTorch深度學習實踐---筆記

PyTorch深度學習實踐---筆記 2.線性模型&#xff08;Linear Model&#xff09;2.exercise 3. 梯度下降算法&#xff08;Gradient Descent&#xff09;3.1梯度下降&#xff08;Gradient Descent&#xff09;3.2 隨機梯度下降&#xff08;Stochastic Gradient Descent&#xff09…

亞馬遜測評工作室怎么做?

亞馬遜是全球最大的電商平臺之一&#xff0c;任何一個賣家想要提升自己店鋪的知名度和銷量&#xff0c;都需要關注自己Listing的Review數量和星級評價&#xff0c;而測評對于賣家賬號的評定和產品曝光量有著重要影響&#xff0c;可以用于店鋪提升銷量&#xff0c;留評等 在進行…

【GC日志分析】-對象晉升到老年代分析

[GC (Allocation Failure) [PSYoungGen: 123456K->65432K(262144K)] 123456K->765432K(1048576K), 0.1234567 secs] [Times: user0.10 sys0.02, real0.13 secs] 從這段GC日志中&#xff0c;可以看到兩個年輕代和老年代的堆內存容量變化情況。 首先&#xff0c;我們來看…

百度坐標(BD09)、國測局坐標(火星坐標,GCJ02)、和WGS84坐標系之間的轉換

<!DOCTYPE html> <html><head><meta charset"UTF-8"><title></title></head><body><script>/*** * 百度坐標&#xff08;BD09&#xff09;、國測局坐標&#xff08;火星坐標&#xff0c;GCJ02&#xff09;、和…

c51單片機串口通信(中斷方式接收數據)(單片機--單片機通信)示例代碼 附proteus圖

單片機一般采用中斷方式接受數據&#xff0c;這樣便于及時處理 #include "reg51.h" #include "myheader.h" #define uchar unsigned char int szc[10]{0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90}; int bufferc[6]{0}; int sza[6]{0x01,0x02,0x0…

HOT96-只出現一次的數字

leetcode原題鏈接&#xff1a;只出現一次的數字 題目描述 給你一個 非空 整數數組 nums &#xff0c;除了某個元素只出現一次以外&#xff0c;其余每個元素均出現兩次。找出那個只出現了一次的元素。你必須設計并實現線性時間復雜度的算法來解決此問題&#xff0c;且該算法只使…

TEXTure環境配置,跑通inference的demo

TEXTure 環境配置安裝kaolin這個包,這里可能會遇到各種問題配置huggingface的訪問令牌 運行Text Conditioned Texture Generation指令報錯1報錯2成功運行 查看結果查看貼圖后的三維網格模型 環境配置 # 創建一個名為texture的環境 conda create -n texture python3.9 -y# 激活…

EasyLLM:簡化語言模型處理,實現 OpenAI 和 Hugging Face 客戶端的無縫切換

前言 在這短短不到一年的時間里&#xff0c;國內外涌現的大型語言模型&#xff08;LLM&#xff09;可謂是百花齊放&#xff0c;不管是開源還是閉源都出現了一些非常優秀的模型&#xff0c;然而在利用LLM進行應用開發的時候&#xff0c;會發現每個模型從部署、到訓練、微調、AP…

人工智能任務1-【NLP系列】句子嵌入的應用與多模型實現方式

大家好&#xff0c;我是微學AI&#xff0c;今天給大家介紹一下人工智能任務1-【NLP系列】句子嵌入的應用與多模型實現方式。句子嵌入是將句子映射到一個固定維度的向量表示形式&#xff0c;它在自然語言處理&#xff08;NLP&#xff09;中有著廣泛的應用。通過將句子轉化為向量…

ASP.NET Core - 緩存之分布式緩存

分布式緩存是由多個應用服務器共享的緩存&#xff0c;通常作為訪問它的應用服務器的外部服務進行維護。 分布式緩存可以提高 ASP.NET Core 應用的性能和可伸縮性&#xff0c;尤其是當應用由云服務或服務器場托管時。 與其他將緩存數據存儲在單個應用服務器上的緩存方案相比&am…

沁恒ch32V208處理器開發(三)GPIO控制

目錄 GPIO功能概述 CH32V2x 微控制器的GPIO 口可以配置成多種輸入或輸出模式&#xff0c;內置可關閉的上拉或下拉電阻&#xff0c;可以配置成推挽或開漏功能。GPIO 口還可以復用成其他功能。端口的每個引腳都可以配置成以下的多種模式之一&#xff1a; 1 浮空輸入 2 上拉輸入…

AMEYA360:DNB1101大唐恩智浦工規級電池管理芯片

大唐恩智浦作為全球領先的半導體供應商&#xff0c;一直致力于為全球客戶提供高質量的解決方案。在電池管理芯片領域&#xff0c;大唐恩智浦推出的DNB1101可謂是一款工規級的電池管理芯片&#xff0c;其卓越的性能和可靠性成為市場上備受全球領先的半導體供應商&#xff0c;一直…

c#編碼技巧(十四):全面總結delegate、Func委托的寫法演變

delegate委托對于初學者來說不太好理解。 按筆者的經驗&#xff0c;delegate本質是函數指針&#xff0c;可以把它理解為某一類方法的入口&#xff0c;把他翻譯為&#xff1a;“長得像XXX的函數方法&#xff08;入參是什么、返回值是什么&#xff09;”可能更容易理解。 以下示例…

2023杭電多校第7場M題-M. Minimal and Maximal XOR Sum

題目鏈接&#xff1a;csoj | M. Minimal and Maximal XOR Sum (scnu.edu.cn) 解題思路&#xff1a; 最小值&#xff1a;每次操作的區間長度為2&#xff0c;即交換兩個相鄰數&#xff0c;每次異或2(10)&#xff0c;故最小值肯定為2(10)或0(00)&#xff0c;如果是偶排序最小值是…

Java接口壓力測試—如何應對并優化Java接口的壓力測試

導言 在如今的互聯網時代&#xff0c;Java接口壓力測試是評估系統性能和可靠性的關鍵一環。一旦接口不能承受高并發量&#xff0c;用戶體驗將受到嚴重影響&#xff0c;甚至可能導致系統崩潰。因此&#xff0c;了解如何進行有效的Java接口壓力測試以及如何優化接口性能至關重要…