GCP Ubuntu 18.04LTS 上的进程所有权自动更改为 init 进程

GCP Ubuntu 18.04LTS 上的进程所有权自动更改为 init 进程

我们最近将开发基础设施从运行 Ubuntu 12.04 的旧机器迁移到运行 Ubuntu 18.04 的 Google Cloud 实例。

开发人员通常会启动一些屏幕并在这些屏幕中运行 django 服务器。

例如,可以创建一个屏幕screen -S webserver_5552并在屏幕内运行其 django 开发应用程序python manage.py runserver 0.0.0.0:5552

在我们以前的机器上,我们可以分离屏幕 ( ctrl+a d) 并稍后返回 ( screen -r xxxx.webserver_5552):django 服务器进程仍会在这里,启动并运行,并由屏幕的 bash 进程拥有。

然而,在 Google Cloud 机器上,这却以不同的方式工作,并且一直让我们抓狂。我们仍然可以分离屏幕,但是如果我们稍后再回来,django 进程将不再属于 bash 进程!相反,它由 init 进程拥有(ppid 从 ps 设置为 1)。

通常,在 django 进程收到一些信号并更改其所有权之前,我们会从 django 进程获得回溯,但这就是我们所得到的一切,我们无法弄清楚根本原因是什么以及如何防止它:

Traceback (most recent call last):
  File "manage.py", line 65, in <module>
    execute_from_command_line(sys.argv)
  File "/home/testing/env/local/lib/python2.7/site-packages/django/core/management/__init__.py", line 367, in execute_from_command_line
    utility.execute()
  File "/home/testing/env/local/lib/python2.7/site-packages/django/core/management/__init__.py", line 359, in execute
    self.fetch_command(subcommand).run_from_argv(self.argv)
  File "/home/testing/env/local/lib/python2.7/site-packages/django/core/management/base.py", line 294, in run_from_argv
    self.execute(*args, **cmd_options)
  File "/home/testing/env/local/lib/python2.7/site-packages/django/core/management/commands/runserver.py", line 58, in execute
    super(Command, self).execute(*args, **options)
  File "/home/testing/env/local/lib/python2.7/site-packages/django/core/management/base.py", line 345, in execute
    output = self.handle(*args, **options)
  File "/home/testing/env/local/lib/python2.7/site-packages/django/core/management/commands/runserver.py", line 97, in handle
    self.run(**options)
  File "/home/testing/env/local/lib/python2.7/site-packages/django/core/management/commands/runserver.py", line 106, in run
    autoreload.main(self.inner_run, None, options)
  File "/home/testing/env/local/lib/python2.7/site-packages/django/utils/autoreload.py", line 333, in main
    reloader(wrapped_main_func, args, kwargs)
  File "/home/testing/env/local/lib/python2.7/site-packages/django/utils/autoreload.py", line 304, in python_reloader
    exit_code = restart_with_reloader()
  File "/home/testing/env/local/lib/python2.7/site-packages/django/utils/autoreload.py", line 290, in restart_with_reloader
    exit_code = os.spawnve(os.P_WAIT, sys.executable, args, new_environ)
  File "/home/testing/odesk_android/../env/lib/python2.7/os.py", line 575, in spawnve
    return _spawnvef(mode, file, args, env, execve)
  File "/home/testing/odesk_android/../env/lib/python2.7/os.py", line 548, in _spawnvef
    wpid, sts = waitpid(pid, 0)
OSError: [Errno 4] Interrupted system call

这是一个特别烦人的行为,我们无法找出根本原因(它是否来自 ubuntu、GCP、某些配置错误,...?)。

编辑:

我通过启动屏幕和启动屏幕进行了测试:

testing@whova-qa-01:/home/simon_ninon_whova_com$ ps -ejf | grep 55530
testing  20764 19638 20764 19638  4 00:12 pts/18   00:00:01 ../env/bin/python manage.py runserver 0.0.0.0:55530
testing  20769 20764 20764 19638 12 00:12 pts/18   00:00:04 /home/testing/appium_android/../env/bin/python manage.py runserver 0.0.0.0:55530

正如你所看到的,我启动了一个../env/bin/python manage.py runserver 0.0.0.0:55530PID=20764 和 PPID=19638 的 django 进程(bash 进程)

这个 django 进程创建了一个/home/testing/appium_android/../env/bin/python manage.py runserver 0.0.0.0:55530PID=20769 和 PPID=20764 的子进程(我生成的原始进程)

现在,今天早上,当我重新登录机器时,在我重新连接屏幕之前,一切还是一样:

simon_ninon_whova_com@whova-qa-01:~$ ps -ejf | grep 55530
simon_n+  9026  9011  9025  9011  0 09:09 pts/9    00:00:00 grep --color=auto 55530
testing  20764 19638 20764 19638  0 00:12 pts/18   00:00:01 ../env/bin/python manage.py runserver 0.0.0.0:55530
testing  20769 20764 20764 19638  2 00:12 pts/18   00:13:56 /home/testing/appium_android/../env/bin/python manage.py runserver 0.0.0.0:55530

因此,当我重新连接屏幕时,我预计问题不会出现。然而,当我重新连接屏幕时:繁荣,进程被杀死了!

testing@whova-qa-01:~$ ps -ejf | grep 55530
testing   9085  9031  9084  9011  0 09:10 pts/9    00:00:00 grep --color=auto 55530
testing  20769     1 20764 19638  2 00:12 pts/18   00:13:59 /home/testing/appium_android/../env/bin/python manage.py runserver 0.0.0.0:55530

正如您所看到的,父进程被杀死,而子进程仍然存在,由 init 进程拥有。

有趣的是,检查原始 bash 进程是否仍然存在,结果证明:

simon_ninon_whova_com@whova-qa-01:~$ ps aux | grep 19638
simon_n+  9315  0.0  0.0  14664  1016 pts/9    S+   09:16   0:00 grep --color=auto 19638
testing  19638  0.0  0.0  25360  7692 pts/18   Ss+  Feb27   0:00 /bin/bash

因此,重新连接屏幕似乎保留了 bash 进程,但导致父 django 进程因某种原因被终止。

不确定这一步会发生什么?请注意,如果我启动服务器,在短时间内断开并重新连接屏幕,则不会触发问题,只会在一段时间后发生。

答案1

我找到了根本原因。

当重新连接屏幕时,SIGWINCH 信号被发送到父 django 进程。该进程不处理它,只是崩溃,让孩子成为孤儿。

然后可以通过调整术语大小或使用 轻松重新触发kill -28 PID

我不确定为什么它只发生在 GCP 实例上,但环境中可能有不同的东西(python 版本?),无论如何,这给了我更多关于在哪里找到解决方案的线索。

编辑:

经过一段时间的搜索,根本原因是我们readline在源代码中导入了一个 django 依赖项。

python readline 是与库的绑定gnureadline,并且似乎信号处理gnureadline干扰了 python/django 中完成的信号处理。

考虑到它只发生在GCP机器上,而不是我们以前的机器上,我怀疑我们GCP机器上安装的gnureadline不同(无论是在版本方面,还是在使用的编译选项方面),导致不同的信号处理行为。

相关内容