热替换容器内的 PID 1 进程而不崩溃

热替换容器内的 PID 1 进程而不崩溃

如您所知,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 的程序,它可能有一种方法告诉它重新执行自身,您应该使用它。程序可以在执行之前进行清理,并且可以传递其后继可执行附加数据,例如在文件或环境变量中。

相关内容