dumb-init

时间:April 19, 2019 分类:

目录:

测试脚本

测试脚本

#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 # 等待回调执行完,主进程再退出