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

前言

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

Java应用

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

docker pull adoptopenjdk/openjdk11

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

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

编写Dockerfile

FROM adoptopenjdk/openjdk11:latest

WORKDIR /opt

ADD Demo.class .

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代码

fn main() {
    println!("Hello, world!》》》》》》》》》》");
    use std::{thread, time};
    let ten_millis = time::Duration::from_millis(10000000000000);
    thread::sleep(ten_millis);
}

安装交叉编译环境,以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运行

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

FROM adoptopenjdk/openjdk11:latest

WORKDIR /opt

ADD hello .

RUN chmod +x hello

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的进程,导致僵尸进程的产生。

猜你喜欢

转载自blog.csdn.net/fenglllle/article/details/127591851
今日推荐