从另一个命令获取一些环境变量后,在运行于远程主机上的docker容器中运行任意命令

时间:2018-12-11 14:37:07

标签: bash shell docker ssh

为了展示我正在尝试做的事情,这是我到目前为止bash脚本的一部分:

COMMAND="${@:1}"
CONTAINER_DOCKER_NAME=this-value-is-computed-prior
MY_IP=this-ip-is-computed-prior

ssh user@$MY_IP -t 'bash -c "docker exec -it $( docker ps -a -q -f name='$CONTAINER_DOCKER_NAME' | head -n 1 ) /bin/sh -c "eval $(echo export FOO=$BAR) && $COMMAND""'

所以让我们分解一下长命令:
我将进入一个主机,在其中运行bash并使用docker ps提取正确的容器,然后执行docker exec在容器中运行外壳程序以加载我的{{1 }}需要工作。需要注意的重要一点是,$COMMAND应该是容器内$BAR变量的值
这就是我想要在理论上完成的事情。但是,无论我如何设置大括号,引号或转义字符,运行此命令时-我总是遇到问题,要么Shell语法不正确,要么它运行的命令不正确(特别是当命令具有多个参数时)或加载BAR值来自我的本地桌面或远程主机,但不来自容器。

使用单壳单缸衬套甚至有可能吗?

2 个答案:

答案 0 :(得分:2)

我认为我们可以简化您的命令。

首先,这里不需要使用eval,并且您不需要&& 运算符:

/bin/sh -c "eval $(echo export FOO=$BAR) && $COMMAND"

相反:

/bin/sh -c "FOO=$BAR $COMMAND"

这将设置环境变量FOO的持续时间为 $COMMAND

接下来,您不需要这个复杂的docker ps表达式:

docker ps -a -q -f name="$CONTAINER_DOCKER_NAME"

Docker容器名称是唯一的。如果您有容器名称 存储在$CONTAINER_DOCKER_NAME中,您可以运行:

docker exec -it $CONTAINER_DOCKER_NAME ...

这将docker命令简化为:

docker exec -it $CONTAINER_DOCKER_NAME \
  /bin/sh -c "FOO=\$BAR $COMMAND"

请注意我们如何在$中转义$BAR,因为我们希望那样 在容器内部 解释,而不是通过当前的shell解释。 现在我们只需要安排通过ssh运行它。有一对 解决方案。我们可以确保保护一切 命令行中针对外壳扩展的额外级别,例如 这个:

ssh user@$MY_IP "docker exec -it $CONTAINER_DOCKER_NAME \
  /bin/sh -c \"FOO=\\\$BAR $COMMAND\""

我们需要将整个命令用双引号引起来,这意味着我们 需要在命令中转义任何引号(我们不能使用单引号 引号,因为我们实际上想扩展变量 $CONTAINER_DOCKER_NAME本地)。我们将失去一级 \展开,因此我们的\$BAR变成了\\\$BAR

如果您的命令不是交互式的,则可以减少一些操作 通过将脚本滚动到bash而不是将其包含在 命令行,如下所示:

ssh user@$MY_IP docker exec -i $CONTAINER_DOCKER_NAME /bin/sh <<EOF
  FOO=\$BAR $COMMAND
EOF

这简化了获取商品所需的报价和转义 传递到容器外壳。

答案 1 :(得分:0)

感谢larsks的出色解释,我知道它能正常工作,我的最后一句话是:

ssh -i $ECS_SSH_KEY ec2-user@$EC2_IP -t "bash -c \"docker exec -it \$( docker ps -a -q -f name=$CONTAINER_DOCKER_NAME | head -n 1 ) /bin/sh -c \\\"eval \\\\\\\$(AWS_ENV_PATH=/\\\\\\\$ENVIRONMENT /bin/aws-env) && $COMMAND\\\"\""

因此,基本上,您将所有内容都用双引号引起来,然后在其中也使用双引号,因为我们需要宿主提供的一些变量,例如$DOCKER_CONTAINER_NAME。转义使用\的引号和$符号。
但是因为我们有多个级别的外壳程序(主机,服务器,容器),所以我们还需要使用多个级别的转义。因此第一级只是\ $,它将保护该变量(或诸如docker ps之类的shell命令)不在主机上而是在服务器上运行。
那么下一个转义级别是7倍。每个\都会使字符向右转义,因此最后在第二级(服务器)上是\\\$,在第三级(容器)上是\$。这样可以确保在服务器上而不是容器中评估变量。 双引号的原理相同。 \"之间的所有内容都在第二级上运行,\\\"之间的所有内容都在第三级上运行。