如您所知,OCI 容器(通常称为 Docker 容器)一旦其入口点进程(也称为容器命名空间内的 PID1)退出,就会终止。
但是,我很好奇,如果您在此容器内拥有 shell 访问权限和 root 权限,是否有办法热替换此 PID 1 进程?
想象一下 PID1 是一个[/bin/sleep, 99999]
进程,有没有一种方法可以在不导致容器终止的情况下替换它,例如使用一个全新的进程,例如[/bin/sleep, 100]
同时保留其 pid(就好像它是从进程本身执行的一样)?
请注意,这个问题不是关于替换可执行文件,而是关于用另一个进程替换正在运行的进程本身。
答案1
就好像它是从进程本身执行的
方法是让进程调用execve。
这就是调试器的工作。
您至少需要拥有运行该进程的用户的权限。取决于设置kernel.yama.ptrace_scope
,您可能需要成为容器内的 root,这通常是相同的事情。我不确定容器外部适用什么权限:我不知道如何ptrace访问检查规则与 Docker 交互。
这是一个使用 sh 和 gdb 的脚本。您可以从容器内部(传递 1 作为 PID)或外部(传递容器 init 进程的 PID)运行它。
#!/bin/sh
set -e
help_and_exit () {
cat <<EOF
Usage: $0 PID COMMAND [ARG...]
Use gdb to replace the running process PID by the specified command.
EOF
exit $1
}
if [ "$1" = "--help" ]; then
help_and_exit
elif [ $# -lt 2 ]; then
help_and_exit 120 >&2
fi
pid=$1; shift
# Quote the command path and the arguments as a C string.
args=
add_arg () {
args="$args\""
while case "$1" in *[\\\"]*) true;; *) false;; esac; do
set -- "${1#*[\\\"]}" "${1%"${1#*[\\\"]}"}"
args="$args${2%?}\\${2#"${2%?}"}"
done
args="$args$1\", "
}
add_arg "$1"
args="$args$args"; shift
for x; do
add_arg "$x"
done
args="${args}(char*)0"
gdb -n -pid "$pid" -batch -ex "call execlp($args)"
如果您不想安装 gdb,您可以用 Python 或 C(或您喜欢的任何语言)编写一个小程序,以实现必要的安装ptrace
来电。
请注意,如果容器的 PID 1 是一个打算用作 init 的程序,它可能有一种方法告诉它重新执行自身,您应该使用它。程序可以在执行之前进行清理,并且可以传递其后继可执行附加数据,例如在文件或环境变量中。