如何用docker-py实现类似docker exec -it的交互式容器命令执行?
问题:通过docker-py实现与容器内命令的标准IO交互
我想实现和以下命令等价的功能:
docker exec -it <some_container> /bin/bash
也就是在容器内运行命令,并将本地程序的stdin和stdout与容器内命令的stdin、stdout建立连接。
根据docker-py官方文档,我写了这段代码:
import docker client = docker.from_env() container, = client.containers.list() container.exec_run(['/usr/local/bin/bash'], stdin=True, tty=True)
(当前环境有一个运行bash:latest的容器)
但实际运行后,本地程序的stdin并没有和容器内命令的stdin连通,输入内容只是在命令行回显,没有任何响应。
我还尝试通过exec_run返回的原始socket交互:
_, s = container.exec_run(['/usr/local/bin/bash'], stdin=True, tty=True, socket=True) s.write('echo hello world')
结果报错:UnsupportedOperation: File or stream is not writable.
请问怎么用docker-py实现让用户和容器内命令的标准IO交互?
解决方案
要实现和docker exec -it完全等价的实时交互,需要处理终端模式并实现双向数据流转发,以下是完整实现:
完整代码
import docker import sys import termios import tty import threading def stream_bidirectional(sock, local_out): """建立容器socket与本地终端的双向数据转发""" def socket_to_local(): while True: data = sock.recv(4096) if not data: break local_out.write(data.decode()) local_out.flush() def local_to_socket(): while True: data = sys.stdin.read(1) if not data: break sock.send(data.encode()) # 启动两个线程分别处理双向数据传递 t1 = threading.Thread(target=socket_to_local) t2 = threading.Thread(target=local_to_socket) t1.start() t2.start() t1.join() t2.join() def main(): client = docker.from_env() # 获取目标容器(这里取第一个运行中的容器,可根据实际修改筛选逻辑) container, = client.containers.list() # 通过底层API创建exec实例并获取交互socket exec_info = client.api.exec_create(container.id, ['/bin/bash'], tty=True, stdin=True) sock = client.api.exec_start(exec_info['Id'], tty=True, socket=True) # 保存终端原始属性,切换到原始模式(禁用行缓冲、自动回显) old_term_attrs = termios.tcgetattr(sys.stdin) try: tty.setraw(sys.stdin.fileno()) tty.setcbreak(sys.stdin.fileno()) # 启动双向流转发 stream_bidirectional(sock, sys.stdout) finally: # 程序退出前恢复终端原始属性 termios.tcsetattr(sys.stdin, termios.TCSADRAIN, old_term_attrs) if __name__ == "__main__": main()
关键说明
- 使用底层API:直接调用
client.api的exec_create和exec_start方法,比高层的exec_run更灵活,能直接获取可交互的socket对象 - 终端模式切换:将本地终端切换为原始模式,避免行缓冲和自动回显,让输入能实时传递到容器内的bash进程
- 双向线程转发:通过两个线程分别处理容器输出到本地、本地输入到容器的数据流,实现实时交互
原代码失败原因
- 高层
exec_run默认会阻塞到命令执行结束,无法支持实时交互;即使开启tty=True,也没有处理本地终端的模式问题 - 直接调用
s.write()失败,是因为返回的socket对象需要配合终端模式使用,且未处理行缓冲,导致输入无法被容器内bash正确接收
内容的提问来源于stack exchange,提问作者Johannes Bauer
相关产品推荐
相关产品推荐

