【问题标题】:How to execute command from one docker container to another如何从一个 docker 容器执行命令到另一个
【发布时间】:2020-03-20 23:07:57
【问题描述】:

我正在创建一个应用程序,允许用户上传视频文件,然后进行一些处理。

我有两个容器。

  1. Nginx 为用户上传视频文件的网站提供服务的容器。
  2. 安装了FFmpeg 和其他一些处理内容的视频处理容器。

我想要达到的目标。我需要容器 1 才能在容器 2 上运行 bash 脚本。

据我所知,一种可能性是让它们通过 API 通过 HTTP 进行通信。但是我需要在容器 2 中安装一个 Web 服务器并编写一个 API,这似乎有点矫枉过正。 我只想执行一个 bash 脚本。

有什么建议吗?

【问题讨论】:

  • 您可以只使用共享卷并观察更改。您还可以共享/var/run/docker.sock 并从容器运行 docker 命令。
  • 这并不是一个好的设计。解决此问题所需的技术与两个容器在物理上独立的系统上运行的技术相同。

标签: docker docker-container docker-networking docker-command


【解决方案1】:

从容器运行docker 命令并不简单,也不是一个好主意(在我看来),因为:

  1. 您需要在容器上安装 docker(并在 docker stuff 中执行 docker)
  2. 您需要共享 unix 套接字,如果您不知道自己在做什么,这不是一件好事。

所以,这给我们留下了两个解决方案:

  1. 在你的容器上安装 ssh 并通过 ssh 执行命令
  2. 共享一个卷并有一个进程来监视触发您的批处理的东西

【讨论】:

  • 我正在考虑共享 docker 套接字。但我不喜欢在我的 docker 镜像中安装 docker 的想法。这似乎有点危险,特别是因为我没有完全掌握它可能会打开哪些安全漏洞。监视文件夹中的文件也不起作用,因为我需要根据某些参数运行不同的脚本。对于每种情况,我都必须有许多不同的文件夹。所以这让我们可以通过 SSH 执行命令。这可能是最好的解决方案。我试试看,谢谢!
  • +1 表示共享卷的想法。对 touchfile 进行轮询通常不是我最喜欢的方法,但它确实有效,并且可能是最直接的解决方案,具体取决于具体情况。
  • github.com/msoap/shell2http 也是一个很棒的项目。它甚至允许通过 HTTP-Post 发送文件。因此,甚至不需要共享卷。
【解决方案2】:

您有几个选择,但最先想到的两个是:

  1. 在容器 1 中,安装 Docker CLI 并绑定挂载 /var/run/docker.sock(您需要从 启动容器时的主机)。然后,在容器内,你 应该能够对绑定安装使用docker 命令 就像你从主机执行它们一样(你也可以 需要chmod容器内的socket允许非root 用户来执行此操作。
  2. 您可以在容器 2 上安装 SSHD,然后从容器 1 中安装 ssh 并运行您的脚本。这里的优点是您不需要在容器内进行任何更改来说明它们在 Docker 中运行而不是裸机的事实。缺点是您需要将 SSHD 设置添加到 Dockerfile 或启动脚本中。

我能想到的大多数其他想法只是选项 (2) 的变体,将 SSHD 替换为其他工具。

还要注意 Docker 网络有点奇怪(至少在 Mac 主机上),因此您需要确保容器使用相同的 docker-network 并且能够通过它进行通信。

编辑添加:
要完全清楚,不要在实验室或非常受控的开发环境之外使用选项 1。它采用对 host 上的 Docker 运行时具有完全权限的安全套接字,并从 container 授予对它的未经检查的访问权限。这样做可以很容易地突破 Docker 沙箱并破坏主机系统。我认为唯一可以接受的地方是作为全栈集成测试设置的一部分,该设置只能由开发人员临时运行。这是一种技巧,在某些非常特殊的情况下可能是一种有用的捷径,但其缺点怎么强调都不过分。

【讨论】:

  • 我考虑过使用 docker 套接字的选项 1,但似乎有点危险。我也不喜欢在 docker 中安装 docker 的想法。我会选择 SSHD 选项,因为它似乎是最佳选择。感谢您的帮助,非常感谢
  • 很多人会同意你关于套接字选项的观点。真的取决于你在做什么,但 SSHD 绝对是更安全的选择,尤其是对于 prod 系统。很高兴我能帮忙:)
  • @NicolasBuch 我希望你不介意我问,但为什么这对你来说似乎很危险?我明白,如果集装箱向公众开放,那么是的,那是非常危险的。但是我有一个需要在容器中执行命令的 cron 容器。 cron 容器本身并没有与外界连接。
  • @ChristianGröber 非常不安全,它允许容器中运行的任何进程对主机进行 root。谨慎行事...
【解决方案3】:

我专门为这个用例编写了一个 python 包。

Flask-Shell2HTTP 是一个 Flask 扩展,只需 5 行代码即可将命令行工具转换为 RESTful API。

示例代码

from flask import Flask
from flask_executor import Executor
from flask_shell2http import Shell2HTTP

app = Flask(__name__)
executor = Executor(app)
shell2http = Shell2HTTP(app=app, executor=executor, base_url_prefix="/commands/")

shell2http.register_command(endpoint="saythis", command_name="echo")
shell2http.register_command(endpoint="run", command_name="./myscript")

可以很容易地调用,

$ curl -X POST -H 'Content-Type: application/json' -d '{"args": ["Hello", "World!"]}' http://localhost:4000/commands/saythis

您可以使用它来创建 RESTful 微服务,这些微服务可以异步执行带有动态参数的预定义 shell 命令/脚本并获取结果。

它支持文件上传、回调 fn、反应式编程等。我建议您查看Examples

【讨论】:

  • 听起来很酷!难道没有django版本的吗?
  • 这很有创意,在很多情况下可能是一个很好的解决方案。主要是使用包含 Python 但没有 sshd 的基本映像的任何东西(这是 很多 个基本映像)。在生产环境中我会对此持谨慎态度,因为它似乎在没有任何身份验证的情况下暴露了这些端点。但是对于本地测试设置,这似乎很容易。
  • @Z4-tier 您可以将端点包装在装饰器中以实现authenticationlogging 等基本上任何东西。
【解决方案4】:

这里之前提到过,但是一个合理的、半hacky的选择是在两个容器中安装SSH,然后使用ssh在另一个容器上执行命令:

# install SSH, if you don't have it already
sudo apt install openssh-server

# start the ssh service
sudo service start ssh

# start the daemon
sudo /usr/sbin/sshd -D &

假设您不想一直是 root,您可以添加默认用户(在本例中为“foobob”):

useradd -m --no-log-init --system  --uid 1000 foobob -s /bin/bash -g sudo -G root

#change password
echo 'foobob:foobob' | chpasswd

在源容器和目标容器上都这样做。现在您可以执行从 container_1 到 container_2 的命令了。

# obtain container-id of target container using 'docker ps'
ssh foobob@<container-id> << "EOL"
echo 'hello bob from container 1' > message.txt
EOL

您可以使用 ssh-agent 自动输入密码,或者您可以使用 sshpass 的更多技巧(首先使用 sudo apt install sshpass 安装它):

sshpass -p 'foobob' ssh foobob@<container-id>

【讨论】:

  • 嗨,感谢您分享此解决方案。我试过了,想知道 message.txt 文件在容器 2 上的什么位置?我在 ssh cmd 中放置了一个详细标志,最后它显示:已传输:发送 2836,接收 2644 字节,在 0.1 秒内每秒字节数:发送 30489.4,接收 28425.2。文件好像被转移了?
  • 这应该只是在“container_2”的主目录中创建文本文件message.txt,其中包含一行文本“hello bob ...”。应该没有文件传输。 ssh 的这份报告仅告诉您它作为通信开销发送和接收的文本
  • 谢谢,我发现我的工作目录被设置到另一个位置,所以我没有看到它。我检查了主目录并看到了文件。感谢您分享此解决方案!
  • sshpass 应该带有警告(在手册页中确实如此):ssh 故意使从脚本输入密码变得困难,因为这种模式几乎总是会导致不安全的密码管理实践.如果可能,最好使用您构建到在目标容器上运行的映像中的公钥。
【解决方案5】:

我相信

docker exec -it <container_name> <command>

应该可以工作,即使在容器内。

您也可以尝试在您尝试执行命令的容器中挂载到docker.sock

docker run -v /var/run/docker.sock:/var/run/docker.sock ...

【讨论】:

  • 从容器内部调用 docker 将无法单独工作。你需要两件事,1:/var/run/docker.sock 共享,2:在你的容器上运行的 docker 版本,如果容器和主机都是 linux,那么一些用户在那里共享主机 docker 二进制文件。但更好的选择是安装一个带有 docker 的容器镜像,这样宿主机就可以是 windows 或 linux。
  • 很公平,没有考虑在运行容器中安装 docker 的要求。
  • 没问题,。另外值得注意的是,您只需要容器内的 docker cli。错过了那一点:)
  • 请记住,这也允许容器以 root 身份对主机上的任意文件进行任意更改,并且通常会接管整个系统。我不会随便这样做的。
猜你喜欢
  • 2018-10-08
  • 1970-01-01
  • 2021-04-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-03-19
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多