使用docker-py创建Python容器时遭遇EOFError问题的解决咨询
解决docker-py运行交互式Python脚本的EOFError问题
我来帮你搞定这个EOFError的问题~
首先得搞清楚错误的原因:你用container.exec_run()执行foobar.py时,这个方法默认是非交互式的——它没有给脚本分配一个真正的终端环境,所以input()尝试读取输入时,发现没有可用的输入流,直接就抛出了EOFError。加上你之前注释掉了tty=True,这也让容器失去了终端交互的基础环境。
结合你提到的dockerpty思路,以及后续要实现TCP管道传输stdin/stdout的需求,我给你两个针对性的解决方案:
方案1:用dockerpty实现本地交互式终端(适合测试场景)
这完全符合你想到的伪终端思路,它能把本地的终端和容器内的终端关联起来,让input()正常读取输入。
步骤:
- 先安装dockerpty:
pip install dockerpty
- 修改你的代码,开启
tty=True并使用dockerpty执行脚本:
import docker import dockerpty base = "python:3.7.12" limit = {"ram": "1g"} # 注意这里必须开启tty=True,bash和交互式脚本都依赖终端环境 container = docker.from_env().containers.run( base, command="bash", detach=True, ports=port[0], name=assignee, tty=True, # 取消注释并开启 stdin_open=True, stdout=True, stderr=True, **dict(limit) ) # 依赖安装这类非交互式操作,依然可以用exec_run container.exec_run("echo 'test' > test.txt") # 用dockerpty启动交互式脚本,关联本地终端 client = docker.from_env() dockerpty.start(client, container, command="python foobar.py")
这样运行后,你就能在本地终端里输入内容,foobar.py的input()就能正常读取了。
方案2:通过TCP管道转发stdin/stdout(适合你的长期需求)
如果你后续要实现TCP传输输入输出,那就不用依赖本地终端了,可以通过套接字转发数据流:
import docker import socket import threading base = "python:3.7.12" limit = {"ram": "1g"} container = docker.from_env().containers.run( base, command="bash", detach=True, ports=port[0], name=assignee, tty=True, stdin_open=True, stdout=True, stderr=True, **dict(limit) ) # 先完成依赖安装等操作 container.exec_run("echo 'test' > test.txt") # 获取容器的attach套接字,用于数据流交互 sock = container.attach_socket( params={"stdin": 1, "stdout": 1, "stderr": 1, "stream": 1} ) # 启动TCP服务端,监听指定端口(这里用12345示例) tcp_server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) tcp_server.bind(("0.0.0.0", 12345)) tcp_server.listen(1) print("等待TCP客户端连接...") client_conn, addr = tcp_server.accept() # 定义数据流转发函数,用线程分别处理输入和输出 def forward_to_container(): while True: data = client_conn.recv(1024) if not data: break sock.send(data) def forward_to_client(): while True: data = sock.recv(1024) if not data: break client_conn.send(data) # 启动转发线程 threading.Thread(target=forward_to_container, daemon=True).start() threading.Thread(target=forward_to_client, daemon=True).start() # 执行交互式脚本,注意开启tty参数 container.exec_run("python foobar.py", tty=True)
之后你可以用任何TCP客户端连接到127.0.0.1:12345,发送的内容会作为容器内input()的输入,容器的输出也会通过TCP返回给客户端,完美匹配你的后续需求。
关键注意点
- 不管用哪种方案,必须开启
tty=True和stdin_open=True:这两个参数是容器支持交互式终端的基础,缺一不可。 exec_run默认是非交互式的,所以即使容器开了tty,直接用exec_run执行带input()的脚本还是会报错,必须通过伪终端或者数据流转发来关联输入流。
内容的提问来源于stack exchange,提问作者Peyang
相关产品推荐
相关产品推荐

