使用asyncio在K8s Pod中执行命令时触发FileNotFoundError异常的求助
看起来你遇到的问题核心是asyncio.create_subprocess_exec和你之前用的subprocess.Popen参数传递逻辑不一样,我来帮你拆解下原因和解决办法:
错误根源
asyncio.create_subprocess_exec默认是不通过shell解析命令的,它要求你把命令和每个参数拆分成独立的参数传入。你现在把完整的kubectl命令行当作一个单一字符串传进去,Python会把这个整串当成要执行的程序名——显然不存在叫kubectl exec -n ns1 ...的可执行文件,所以就抛出了FileNotFoundError。
而你之前用subprocess.Popen能成功,大概率是因为你要么把命令拆成了参数列表,要么给Popen加了shell=True参数(让shell帮你解析整串命令),只是你没特意留意这点。
两种解决方案
方案1:拆分命令为参数列表(推荐,更安全)
把kubectl命令拆解成一个个独立的参数,直接传给create_subprocess_exec:
process = await asyncio.create_subprocess_exec( "kubectl", "exec", "-n", "ns1", "devpod-654fcd84dd-r7spk", "--", "/bin/sh", "-c", "llamafactory-cli train /home/llama-factory-runtime/.tmpfiles/e2ece994-7f10-442c-aae4-e938381fb67a.yaml > .tmpfiles/e2ece994-7f10-442c-aae4-e938381fb67a.log 2>&1", # 根据需要配置stdout/stderr,比如stdout=asyncio.subprocess.PIPE )
这里要注意,--后面的整串shell命令要作为一个参数传给/bin/sh -c,所以要把它放在同一个字符串里。
方案2:使用create_subprocess_shell(兼容原命令写法)
如果你想保留整串命令的写法,可以用asyncio.create_subprocess_shell,它和subprocess.Popen(shell=True)逻辑一致,会通过shell解析整个命令字符串:
process = await asyncio.create_subprocess_shell( "kubectl exec -n ns1 devpod-654fcd84dd-r7spk -- /bin/sh -c llamafactory-cli train /home/llama-factory-runtime/.tmpfiles/e2ece994-7f10-442c-aae4-e938381fb67a.yaml > .tmpfiles/e2ece994-7f10-442c-aae4-e938381fb67a.log 2>&1", # 同样可以按需配置stdout/stderr )
不过这种方式要注意shell注入风险,如果你的命令里包含用户输入的内容,尽量用方案1更安全。
关于额外的AttributeError: characters_written
这个错误是调试器(debugpy)的问题,不是你的代码逻辑错误,只要解决了前面的FileNotFoundError,这个错误应该就会消失。
备注:内容来源于stack exchange,提问作者Positron

