开启shareProcessNamespace后容器异常

  1. 背景
  2. 复现
  3. 分析
  4. 方案
  5. 参考

https://qingwave.github.io/cotainer-init/

背景

目前k8s不支持容器启动顺序,部分业务通过开启shareProcessNamespace监控某些进程状态。当开启共享pid后,有用户反馈某个容器主进程退出,但是容器并没有重启,执行exec会卡住,现象参考issue

复现

  1. 创建deployment
  • apiVersion: apps/v1
    kind: Deployment
    metadata:
    labels:
    app: nginx
    name: nginx
    spec:
    selector:
    matchLabels:
    app: nginx
    template:
    metadata:
    labels:
    app: nginx
    name: nginx
    spec:
    shareProcessNamespace: true
    containers:

    • image: nginx:alpine
      name: nginx
  • 查看进程信息
    由于开启了shareProcessNamespace, pause变为pid 1, nginx daemonpid为6, ppid为containerd-shim

  • # 查看容器内进程
    / # ps -efo “pid,ppid,comm,args”
    PID PPID COMMAND COMMAND
    1 0 pause /pause
    6 0 nginx nginx: master process nginx -g daemon off;
    11 6 nginx nginx: worker process
    12 6 nginx nginx: worker process
    13 6 nginx nginx: worker process
    14 6 nginx nginx: worker process
    15 0 sh sh
    47 15 ps ps -efo pid,ppid,comm,args

  • 删除主进程
    子进程被pid 1回收, 有时也会被containerd-shim回收

  1. / # kill -9 6
    / #
    / # ps -efo “pid,ppid,comm,args”
    PID PPID COMMAND COMMAND
    1 0 pause /pause
    11 1 nginx nginx: worker process
    12 1 nginx nginx: worker process
    13 1 nginx nginx: worker process
    14 1 nginx nginx: worker process
    15 0 sh sh
    48 15 ps ps -efo pid,ppid,comm,args

  2. docker hang
    此时对此容器执行docker命令(inspect, logs, exec)将卡住, 同样通过kubectl执行会超时。

分析

在未开启shareProcessNamespace的容器中,主进程退出pid 1, 此pid namespace销毁,系统会kill其下的所有进程。开启后,pid 1pause进程,容器主进程退出,由于共享pid namespace,其他进程没有退出变成孤儿进程。此时调用docker相关接口去操作容器,docker首先去找主进程,但主进程已经不存在了,导致异常(待确认)。

清理掉这些孤儿进程容器便会正常退出,可以kill掉这些进程或者killpause进程,即可恢复。

方案

有没有优雅的方式解决此种问题,如果主进程退出子进程也一起退出便符合预期,这就需要进程管理工具来实现,在宿主机中有systemdgod,容器中也有类似的工具即init进程(传递信息,回收子进程),常见的有

  1. docker init, docker自带的init进程(即tini)
  2. tini, 可回收孤儿进程/僵尸进程,kill进程组等
  3. dumb-init, 可管理进程,重写信号等

经过测试,tini进程只能回收前台程序,对于后台程序则无能为力(例如nohup, &启动的程序),dumb-init在主进程退出时,会传递信号给子进程,符合预期。

开启dumb-init进程的dockerfile如下,tini也类似

FROM nginx:alpine

# tini
# RUN apk add –no-cache tini
# ENTRYPOINT [“/sbin/tini”, “-s”, “-g”, “–”]

# dumb-init
RUN wget -O /usr/bin/dumb-init https://github.com/Yelp/dumb-init/releases/download/v1.2.2/dumb-init\_1.2.2\_amd64
RUN chmod +x /usr/bin/dumb-init
ENTRYPOINT [“/usr/bin/dumb-init”, “-v”, “–”]

CMD [“nginx”, “-g”, “daemon off;”]

init方式对于此问题是一种临时的解决方案,需要docker从根本上解决此种情况。容器推荐单进程运行,但某些情况必须要运行多进程,如果不想处理处理传递回收进程等,可以通过init进程,无需更改代码即可实现。

参考

[1] https://github.com/Yelp/dumb-init
[2] https://github.com/krallin/tini
[3] https://github.com/kubernetes/kubernetes/issues/92214


转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。也可以邮件至 1209453173@qq.com