You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用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进程
  • 双向线程转发:通过两个线程分别处理容器输出到本地、本地输入到容器的数据流,实现实时交互

原代码失败原因

  1. 高层exec_run默认会阻塞到命令执行结束,无法支持实时交互;即使开启tty=True,也没有处理本地终端的模式问题
  2. 直接调用s.write()失败,是因为返回的socket对象需要配合终端模式使用,且未处理行缓冲,导致输入无法被容器内bash正确接收

内容的提问来源于stack exchange,提问作者Johannes Bauer

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.28 05:52:38