drgibbon
drgibbon

Reputation: 455

Following the exec_run() output from docker-py in realtime

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?

Upvotes: 5

Views: 4834

Answers (2)

Kleag
Kleag

Reputation: 650

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()

Upvotes: 1

Kirill Rud
Kirill Rud

Reputation: 265

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 /)')

Upvotes: 0

Related Questions