5

Python noob here, I'm trying to use the exec_run function from docker-py to send commands to a detached docker container and have the output hitting stdout in real time. Here's a MWE:

import docker, sys
client = docker.from_env()
# start a detached container
box = client.containers.run(image = "ubuntu",
                            remove = True,
                            detach = True,
                            tty = True,
                            command = "/bin/bash")
# send a test command
box.exec_run(cmd = "find /") # gives no output in the terminal when run

# same again, but save the output
run = box.exec_run(cmd = "find /")
print(run.output.decode("utf-8"))

box.stop()
sys.exit()

I can grab the output after the fact by storing it in a variable, but I can't seem to get the real time output. I actually want to give it long running processes, so it's preferable to see the output as it happens (as well as saving it). Is this difficult to do, or am I missing something really basic here?

drgibbon
  • 405
  • 1
  • 4
  • 11

2 Answers2

1

I implemented the desired behavior like that:

    def install_model(self, language: str) -> bool:
    if self.container:
        _, stream = self.container.exec_run(
            cmd=f"lima_models.py -l {language}", stream=True)
        for data in stream:
            print(data.decode(), end='')
        print()

As you can see, I use stream=True and then iterate over the result. I print the decoded data but you could also store it for later use. The executed command produces a progress bar using \r at the beginning of each output line. This is why add the end='' parameter.

Here is your MWE modified with the above solution:

import docker, sys
client = docker.from_env()
# start a detached container
box = client.containers.run(image = "ubuntu",
                            remove = True,
                            detach = True,
                            tty = True,
                            command = "/bin/bash")
# send a test command
_, stream = box.exec_run(cmd = "find /", stream=True)
for data in stream:
    print(data.decode())

box.stop()
sys.exit()
Kleag
  • 642
  • 7
  • 14
  • Would it be possible to have the same real-time log output for a client.images.build operation? I can only see them after the blocking call is complete? On the other note, why do we need tty and detach? Would not detach make the call asynchronous by default and allow streaming out of the box? – d56 Dec 10 '21 at 11:45
0

The command parameter in the client.containers.run function is that what you're looking for. It may take even a list of commands, doc (link):

command (str or list) – The command to run in the container.

So, simply replace command = "/bin/bash" with bash -c "echo $(find /)".


In case if you want to keep the docker container alive and execute commands one by one - create detached container and simply use exec_run:

container = client.containers.run(image='ubuntu', auto_remove=False, stdin_open=True, detach=True)
container.exec_run(cmd='echo $(find /)')
Kirill Rud
  • 217
  • 3
  • 9