首页 > 解决方案 > Python docker 模块:如何在 python 中为 docker 容器创建终端 shell?

问题描述

我想创建一个 discord/IRC 机器人,让随机的人访问我的服务器上的 bash 控制台。为了让这不那么疯狂,我决定使用一个 docker 容器,所以希望我的服务器不会得到 rm -rf ed。然而,不幸的是,我一直坚持将 IO 传输到 docker 容器上的 /bin/bash 。

import docker
import time
import asyncio 
client = docker.from_env()

c = client.containers.run(
    image='disbox:main',
    command = "neofetch",
    cpu_count = 1,
    mem_limit = "1g",
    hostname="disbox",
    user = "discord",
    entrypoint="/bin/bash",
    ports = {'80': 8080},
    detach = True
)

# wait for container to start
time.sleep(5)
container = client.containers.get(c.id)

while True:
    cmd = input("\n:")
    res = container.exec_run(cmd, stream=True)
    for line in res:
        print(line.output)

这给出了 url 错误的冲突,我不确定这意味着什么。我已经验证它尚未在其他地方运行。我正在使用 root 运行 python(否则它会给我一个 perms 错误)。

Traceback (most recent call last):
  File "/usr/lib/python3/dist-packages/docker/api/client.py", line 261, in _raise_for_status
    response.raise_for_status()
  File "/usr/lib/python3/dist-packages/requests/models.py", line 940, in raise_for_status
    raise HTTPError(http_error_msg, response=self)
requests.exceptions.HTTPError: 409 Client Error: Conflict for url: http+docker://localhost/v1.35/containers/f54feee310d0890b751d9544b020279e1ab35e470b98773f4b160b4c0a470d11/exec

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "<stdin>", line 3, in <module>
  File "/usr/lib/python3/dist-packages/docker/models/containers.py", line 193, in exec_run
    resp = self.client.api.exec_create(
  File "/usr/lib/python3/dist-packages/docker/utils/decorators.py", line 19, in wrapped
    return f(self, resource_id, *args, **kwargs)
  File "/usr/lib/python3/dist-packages/docker/api/exec_api.py", line 80, in exec_create
    return self._result(res, True)
  File "/usr/lib/python3/dist-packages/docker/api/client.py", line 267, in _result
    self._raise_for_status(response)
  File "/usr/lib/python3/dist-packages/docker/api/client.py", line 263, in _raise_for_status
    raise create_api_error_from_http_exception(e)
  File "/usr/lib/python3/dist-packages/docker/errors.py", line 31, in create_api_error_from_http_exception
    raise cls(e, response=response, explanation=explanation)
docker.errors.APIError: 409 Client Error: Conflict ("Container f54feee310d0890b751d9544b020279e1ab35e470b98773f4b160b4c0a470d11 is not running")

此代码片段的目的是在 python 控制台中复制一个 bash 控制台。我哪里做错了?

编辑:图像名称是 100% 正确的,它是一个自定义的 ubuntu SE 图像,带有 neofetch 和一些其他预加载的东西。

标签: pythondockerterminal

解决方案


如果您以交互式 shell 作为其主进程运行容器,但未指定容器的标准输入应保持打开状态,则 shell 将立即退出。(这与docker run --entrypoint /bin/bash disbox:main没有-it选项相同。)创建容器时,您需要包含相关选项:

c = client.containers.run(
  ...,
  stdin_open=True,
  tty=True
)

然后容器对象有一个attach_socket方法。这将返回一个类似套接字的对象,您可以send往返recv;直接连接到容器的标准输入和标准输出。在您的上下文中,您可能可以使用这些直接在客户端进程和容器之间来回中继数据,知道它是面向行的,但不知道它是一个外壳。

您在这里不需要“exec”类型的操作;您直接与容器进程的标准输入和标准输出进行交互。

(还请记住,在容器中使用 shell 可以解决很多问题:对主机本身发起 DoS 攻击、针对主机内核的本地权限提升攻击、加密货币矿工等。 您可以在此处获得一些保护,以免被在一个容器中,但如果您有任何理由不信任您的最终用户,它就不是“安全的”。)


推荐阅读