前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >线上的执行器收不到信号,险些酿成悲剧...

线上的执行器收不到信号,险些酿成悲剧...

作者头像
米开朗基杨
发布2021-12-06 15:28:02
5060
发布2021-12-06 15:28:02
举报

记录一个今天遇到的小问题。这是继 Debug 一个在 uWSGI 下使用 subprocess 卡住的问题[1] 之后又一次遇到信号问题。

我写的 chaos engineering 平台支持一个功能:立即中断正在进行的实验,并且执行 rollback 操作复原注入的操作。每一个实验都是由一个进程负责的,终止的方法是向进程发送一个 SIGINT 信号,让进程停止注入并且切换到 rollback 开始清理。

最近的一个改动从 asyncio.create_subprocess_exec[2] 切换到了 asyncio.[3]create_subprocess_shell 导致了一个 bug,现象是线上的执行器根本收不到停止的信号了,刹车失灵,险些酿成悲剧。

经过警方调查发现,asyncio.[4]create_subprocess_shell 其实会开一个新的 shell 来执行命令,默认使用的是 sh,而 sh 默认是不转发它收到的信号的。(这里我是用了 killsnoop[5] 来发现 sh 确实收到了信号,但是执行 chaos 的进程没有收到,然后查阅文档并通过实验复现确认 sh 不会转发信号。)

但是这个问题我在开发环境(Mac)并没有测试出来,因为开发环境工作的好好的。

我写了一个最小的 case 可以复现这个场景:

import asyncio
 
 
async def subshell():
    print("start sleep...")
    process = await asyncio.create_subprocess_shell(
        "sleep 23",
    )
    print("sub process create, my id {}".format(process, process.pid))
    await asyncio.sleep(23)
 
 
loop = asyncio.get_event_loop()
loop.run_until_complete(subshell())

在 Mac 上的表现是,python 进程的子进程就直接是 sleep 进程,并没有一个中间的 sh 进程。

$ pstree -p 39656
-+= 00001 root /sbin/launchd
 \-+= 01751 xintao.lai tmux
   \-+= 38831 xintao.lai -zsh
     \-+= 39640 xintao.lai /Users/xintao.lai/.pyenv/versions/3.8.5/bin/python3 asy.py
       \--- 39656 xintao.lai sleep 23

而在 Linux 上的表现是:python 进程的子进程是 sh 进程,然后 sh 的子进程才是 sleep 进程。

$ python3 asy.py
start sleep...
sub process create: <Process 13275>, 13275

$ pstree -lasp 13275
systemd,1
  └─sshd,2096
      └─sshd,13174
          └─sshd,13213
              └─bash,13214
                  └─python3,13274 asy.py
                      └─sh,13275 -c sleep 13
                          └─sleep,13277 13

经过 ./grey 指点,发现在 Mac 上 sh -c "sleep 99" 之后,sh 自己也不见了,只有 sleep 99 这个进程,父进程是我自己的 zsh shell.

这里就真相大白了。中间进程的这个 sh 并不会转发 signal,所以在线上的 Linux 系统上收不到信号;在开发电脑上由于没有中间的 sh ,所以直接将 signal 发给了子进程。

那么 sh 在两个系统上到底有怎么样的不同呢?

在我的 Mac 上,man sh 说:

sh is a POSIX-compliant command interpreter (shell). It is implemented by re-execing as either bash(1), dash(1), or zsh(1) as determined by the symbolic link located at /private/var/select/sh. If /private/var/select/sh does not exist or does not point to a valid shell, sh will use one of the supported shells.

经过查看,可以发现其实 sh 在 Mac 上是 bash:

$ ls -l /private/var/select/sh
lrwxr-xr-x 1 root wheel 9 Jan  1  2020 /private/var/select/sh -> /bin/bash

对于 bash -c "sleep 99" 这个命令,bash 有一些优化,为了节省资源,bash 会直接通过 execve() 去执行 sleep,这样在系统上就可以少存在一个 bash 进程。详细解释[6]

而在 ubuntu 上,sh 其实是 dash:

$ realpath $(which sh)
/usr/bin/dash

dash (至少我们使用的版本)还没有这个优化,所以在 Python 的 subprocess shell[7] (经过 linw1995[8] 指点)中就会有两层进程,一个是 dash,dash 的子进程才是运行的命令。

在 ubuntu 上 bash -c "sleep 99" 可以看到 bash 本身也是会消失的。说明这个确实是 bash 的行为。

bash 进程消失不太准确,它其实是换了一个形式存在而已。strace可以证明它存在过:

$ strace  bash -c 'sleep 99'
execve("/usr/bin/bash", ["bash", "-c", "sleep 99"], 0x7ffff8ff9f90 /* 27 vars */) = 0
brk(NULL)                               = 0x5614ac6ae000
...
execve("/usr/bin/sleep", ["sleep", "99"], 0x5614ac6b8930 /* 27 vars */) = 0

反思一下这个问题,有以下几点可以做的更好:

  • 换成 Linux 开发;
  • 写测试用例,CI 完全可以发现这个问题;
  • 还是尽量使用 asyncio.create_subprocess_exec[9] 来执行命令吧!

引用链接

[1]Debug 一个在 uWSGI 下使用 subprocess 卡住的问题: https://www.kawabangga.com/posts/4558

[2]asyncio.create_subprocess_exec: https://docs.python.org/3/library/asyncio-subprocess.html#asyncio.create_subprocess_exec

[3]asyncio.: https://docs.python.org/3/library/asyncio-subprocess.html#asyncio.create_subprocess_shell

[4]asyncio.: https://docs.python.org/3/library/asyncio-subprocess.html#asyncio.create_subprocess_shell

[5]killsnoop: https://github.com/brendangregg/perf-tools/blob/master/killsnoop

[6]详细解释: https://unix.stackexchange.com/questions/466496/why-is-there-no-apparent-clone-or-fork-in-simple-bash-command-and-how-its-done/466523

[7]subprocess shell: https://github.com/python/cpython/blob/3.10/Lib/subprocess.py#L1708

[8]linw1995: https://github.com/linw1995

[9]asyncio.create_subprocess_exec: https://docs.python.org/3/library/asyncio-subprocess.html#asyncio.create_subprocess_exec

原文链接:https://www.kawabangga.com/posts/4617

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2021-11-29,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 云原生实验室 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 引用链接
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档