dumb-init
目录:
测试脚本
测试脚本
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <stdlib.h>
static struct sigaction siga;
static void signal_handler(int sig, siginfo_t *siginfo, void *context) {
pid_t sender_pid = siginfo->si_pid;
if(sig == SIGTERM) {
printf("received sign: [term] , the sender is [%d]\n", (int)sender_pid);
return;
}
return;
}
void main(int argc, char *argv[]) {
printf("process [%d] started...\n", getpid());
siga.sa_sigaction = *signal_handler;
siga.sa_flags |= SA_SIGINFO;
sigaction(SIGTERM, &siga, NULL);
while(1) {
sleep(10);
}
}
编译二进制程序
$ gcc -static -o get_term_signal get_term_signal.c
如果以下遇到错误可以yum install glibc-static
/usr/bin/ld: cannot find -lc
collect2: error: ld returned 1 exit status
在第一个终端启动编译好的二进制程序
$ ./get_term_signal
process [12037] started...
在另一个终端进行kill -15
$ echo $$
31374
$ kill -15 12037
在第一个终端看一下
$ ./get_term_signal
process [12037] started...
received sign: [term] , the sender is [31374]
程序能监听到发送到程序的SIGTERM信号,并且获取发送者
SIGTERM是kill的默认信号,为了让程序更好的退出,信号是可以被捕获的,可能被堵塞,只有当前进程收到信号,子进程不会收到,如果当前进程被kill了,那么它的子进程的父进程将会是init,也就是pid为1的进程
相对SIGKILL不能被捕获,程序收到这个信号一定会退出
相对SIGINT可以被捕获,与字符ctrl+c
关联,只能结束前台进程
容器测试
容器的dockerfile
FROM alpine
COPY get_term_signal /
CMD ["/get_term_signal"]
编译容器
$ docker build -t signal:v1 .
Sending build context to Docker daemon 865.3 kB
Step 1/3 : FROM reg.why.com/alpine
--->
Step 2/3 : COPY get_term_signal /
---> Using cache
---> c2bde9b59984
Step 3/3 : CMD /get_term_signal
---> Running in 43c71c94f381
---> 28dbf2aa5d4e
Removing intermediate container 43c71c94f381
Successfully built 28dbf2aa5d4e
在第一个终端启动容器
$ docker run --rm -it --name signal_v1 signal:v1 sh
/ # /get_term_signal
process [8] started...
在另一个终端进入容器ps看一下
$ docker exec -it signal_v1 sh
/ # ps -ef
PID USER TIME COMMAND
1 root 0:00 sh
7 root 0:00 /get_term_signal
8 root 0:00 sh
14 root 0:00 ps -ef
停止
$ docker stop signal_v1
signal_v1
可以看到进程没有收到SIGTERM信号
$ docker run --rm -it --name signal_v1 signal:v1 sh
/ # /get_term_signal
process [8] started...
docker官网介绍是
The main process inside the container will receive SIGTERM, and after a grace period, SIGKILL.
dumb-init
dumb-init的git地址
下载dumb-init
$ wget https://github.com/Yelp/dumb-init/releases/download/v1.2.2/dumb-init_1.2.2_amd64 -O dumb-init
$ chmod +x dumb-init
更新dockerfile
FROM reg.why.com/alpine
COPY get_term_signal /
COPY dumb-init /usr/bin/
ENTRYPOINT ["/usr/bin/dumb-init", "--"]
CMD ["sh", "-c", "/get_term_signal"]
构建镜像
$ docker build --no-cache -t signal:v2 .
Sending build context to Docker daemon 920.6 kB
Step 1/5 : FROM reg.why.com/alpine
---> 3fd9065eaf02
Step 2/5 : COPY get_term_signal /
---> 180577f479f7
Removing intermediate container e8f830648b83
Step 3/5 : COPY dumb-init /usr/bin/
---> d9fab79f16b5
Removing intermediate container c16fcb06e4c4
Step 4/5 : ENTRYPOINT /usr/bin/dumb-init --
---> Running in f793e8a9bcdb
---> 4063e4e32260
Removing intermediate container f793e8a9bcdb
Step 5/5 : CMD sh -c /get_term_signal
---> Running in d8990968f491
---> 7e757474af14
Removing intermediate container d8990968f491
Successfully built 7e757474af14
在一个终端启动容器
$ docker run --rm -it --name signal_v2 signal:v2
process [8] started...
在另一个终端看一下
$ docker exec -it signal_v2 sh
/ # ps -ef
PID USER TIME COMMAND
1 root 0:00 /usr/bin/dumb-init -- sh -c /get_term_signal
7 root 0:00 sh -c /get_term_signal
8 root 0:00 /get_term_signal
9 root 0:00 sh
15 root 0:00 ps -ef
/ # exit
$ docker stop signal_v2
signal_v2
可以看到由dumb-init来进行接管
停止容器后,再看第一个终端
$ docker run --rm -it --name signal_v2 signal:v2
process [8] started...
received sign: [term] , the sender is [1]
可以看到接收到了来自1号进程的term信号
其他操作
使用exec启动
在shell中启动二进制的命令前加一个 exec 即可让该二进制启动的进程代替当前 shell 进程,即让新启动的进程成为主进程:
#! /bin/bash
...
exec /bin/yourapp # 脚本中执行二进制
然后业务进程就可以正常接收所有信号了
多进程场景
#! /bin/bash
/bin/app1 & pid1="$!" # 启动第一个业务进程并记录 pid
echo "app1 started with pid $pid1"
/bin/app2 & pid2="$!" # 启动第二个业务进程并记录 pid
echo "app2 started with pid $pid2"
handle_sigterm() {
echo "[INFO] Received SIGTERM"
kill -SIGTERM $pid1 $pid2 # 传递 SIGTERM 给业务进程
wait $pid1 $pid2 # 等待所有业务进程完全终止
}
trap handle_sigterm SIGTERM # 捕获 SIGTERM 信号并回调 handle_sigterm 函数
wait # 等待回调执行完,主进程再退出