Запустите произвольную команду в контейнере Docker, который выполняется на удаленном хосте после получения некоторых переменных среды из другой команды. - PullRequest
0 голосов
/ 11 декабря 2018

Чтобы показать, что я пытаюсь сделать, это часть скрипта 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 запускаю оболочку в контейнере, чтобы загрузить некоторые переменные среды, с которыми должен работать мой $COMMAND.Важно отметить, что $BAR должно быть значением BAR переменной внутри контейнера .
Так вот, что я пытаюсь достичь в теории.Однако при выполнении этого независимо от того, как я устанавливаю фигурные скобки, кавычки или экранирующие символы - я всегда сталкиваюсь с проблемами, либо синтаксис оболочки неверен, либо он не запускает правильную команду (особенно, когда команда имеет несколько аргументов), либо загружается$BAR значение из моего локального рабочего стола или удаленного хоста, но не из контейнера.

Возможно ли это вообще с одним вкладышем с одной оболочкой?

Ответы [ 2 ]

0 голосов
/ 12 декабря 2018

благодаря великолепному объяснению 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) не на хосте, а на сервере.
тогда следующий уровень экранирования будет 7 раз \.каждый \ экранирует символ справа, поэтому в итоге он равен \\\$ на втором уровне (сервер) и \$ на третьем уровне (контейнер).это гарантирует, что переменная оценивается в контейнере, а не на сервере.тот же принцип с двойными кавычками.Все, что находится между \", выполняется на втором уровне, а все, что между \\\", выполняется на третьем уровне.

0 голосов
/ 11 декабря 2018

Я думаю, мы можем немного упростить вашу команду.

Во-первых, здесь нет необходимости использовать 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, потому что мы хотим, чтобы интерпретация внутри контейнера, а не нашей текущей оболочкой.Теперь нам просто нужно организовать это через ssh.Есть несколько решений для этого.Мы можем просто убедиться, что все в командной строке защищено от дополнительного уровня расширения оболочки, например:

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

Нам нужно заключить всю команду в двойные кавычки, что означает, что мы должны избегать любыхкавычки внутри команды (мы не можем использовать одинарные кавычки, потому что мы действительно хотим развернуть переменную $CONTAINER_DOCKER_NAME локально).Мы потеряем один уровень расширения \, поэтому наша \$BAR станет \\\$BAR.

Если ваша команда не интерактивна, вы можете сделать это немного менее пугающим с помощью конвейера сценария.вместо того, чтобы включать его в командную строку, например:

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

Это упрощает цитирование и экранирование, необходимые для передачи данных в оболочку контейнера.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...