• 容器下进程退出产生僵尸进程的过程与原因


    前言

    最近容器部署标准化,本来很简单,也没什么特殊的要求,但是在容器里,因为临时需要启动进程,结果停止后出现了僵尸进程

    Java应用

    以Java应用为例,这里以 adoptopenjdk/openjdk11为模板

    docker pull adoptopenjdk/openjdk11

    Java代码示例,简单处理,没有包名,通过java Demo运行 

    1. public class Demo {
    2. public static void main(String[] args) throws InterruptedException {
    3. System.out.println("start>>>>>>>>>>>>");
    4. Thread.sleep(500000000000l);
    5. }
    6. }

    编写Dockerfile

    1. FROM adoptopenjdk/openjdk11:latest
    2. WORKDIR /opt
    3. ADD Demo.class .
    4. ENTRYPOINT ["java","Demo","> /opt/demo.log"]

    docker build . -t java-demo:1.0

    编译后

    docker run -d --name java-demo java-demo:1.0

     

    docker exec -it java-demo bash

     

    模拟需要临时启动一些进程,在虚拟机上非常常见。 

    echo 'java Demo' > start.sh

    执行chmod +x start.sh 

    ./start.sh &

    通过另外的bash进入容器,可以看到进程的父子关联,出现僵尸进程

     僵尸进程产生跟父进程ID为1有关,父进程id为1,在容器下kill会产生僵尸进程。

     

    rust应用

    为了验证是否产生僵尸进程跟语言的关系,又写了一个rust代码

    1. fn main() {
    2. println!("Hello, world!》》》》》》》》》》");
    3. use std::{thread, time};
    4. let ten_millis = time::Duration::from_millis(10000000000000);
    5. thread::sleep(ten_millis);
    6. }

    安装交叉编译环境,以macOS为例

    # macOS
    rustup target add x86_64-unknown-linux-musl
    brew install filosottile/musl-cross/musl-cross

    #win

    rustup target add x86_64-pc-windows-gnu
    brew install mingw-w64

    配置config

    vim ~/.cargo/config 或者在项目的.cargo/config,一般就是编译Linux环境,本地环境不需要交叉编译

    [target.x86_64-unknown-linux-musl]

    linker = "x86_64-linux-musl-gcc" 

    cargo build --release --target x86_64-unknown-linux-musl

     

    在target下

     

     在ubuntu运行

    镜像准备,控制变量,还是用原来的基底。

    1. FROM adoptopenjdk/openjdk11:latest
    2. WORKDIR /opt
    3. ADD hello .
    4. RUN chmod +x hello
    5. ENTRYPOINT ["./hello","> /opt/demo.log"]

     执行额外进程操作

     再进行exec

     执行kill -9,出现僵尸进程。

     

     结论是僵尸进程的产生实际上跟语言没关系。

     

    产生原因

    在容器下进程id为1的进程是不能被kill的,而且id为0是所有进程的父进程id

    当kill时,实际上kill -15一样会出现,跟-9没关系

    1. kill进程,当父进程id为1时,出现僵尸进程

    2. kill进程,如果父进程先被kill(包括正常退出),当前进程的父进程id在容器下会被设置为1,即基底容器的运行进程

     

    解决方法

    根据总结的2条结果,要解决也很简单,kill的过程安装创建进程的逆过程即可

    先kill子进程,再kill父进程,知道全部kill掉。

    尝试java-demo,果然创建的逆过程无僵尸进程产生,这个设计实际上在Kubernetes的发布过程经常出现

     rust-demo

     

    总结

    在容器下进程id为1的进程是不能kill掉的,当容器下需要创建进程,运行时启动,那么创建进程的过程非常重要,因为销毁需要逆过程,否则会出现僵尸进程。容器下当新创建的进程(id为1的进程除外)的父进程被kill或者自然退出,当前进程的父进程id会被设为1,即容器下不能被kill的进程,导致僵尸进程的产生。

  • 相关阅读:
    下班路上捡了一部手机,我用8年开发知识主动找到了失主
    C++内存管理
    SpringCloud 2022有哪些变化
    Docker基础知识
    6种限流实现,附代码![通俗易懂]
    软件流程和管理(四):Communication Management
    关于new Set( )还有哪些是你不知道的
    算法基础(一)| 快速排序和归并排序详解
    Lua位或操作
    【MetaGPT】单智能体多动作实践——AI小说家
  • 原文地址:https://blog.csdn.net/fenglllle/article/details/127591851