Что именно означает "устройство ввода не является TTY" в выводе "docker run"?

Это команда, которая работает:

$ echo 'hi there' | docker run -i ubuntu cathi there

Это команда, которая выдает сообщение об ошибке:

$ echo 'hi there' | docker run -it ubuntu catthe input device is not a TTY

Я хотел бы точно выяснить, что здесь происходит. Не просто "удалите -t, и это будет исправлено".

Я знаю, что docker run-t опция расшифровывается как "Выделить псевдо-TTY", и я прочитал исторические обзоры того, что означает TTY, но это не помогло мне понять, какой контракт здесь нарушен.

Поздний ответ, но может кому-то помочь

docker run/exec -i подключит STDIN команды внутри контейнера к STDIN команды docker run/exec сам.

Так

  • docker run -i alpine cat выдает вам пустую строку, ожидающую ввода. Введите "привет", вы получите эхо "привет". Контейнер не завершится, пока вы не отправите CTRL + D, потому что основной процесс cat ожидает ввода из бесконечного потока, который является терминальным входом docker run.
  • С другой стороны echo "hello" | docker run -i alpine cat напечатает "привет" и немедленно завершит работу, потому что cat замечает, что входной поток закончился, и завершает сам себя.

Если вы попытаетесь docker ps после того, как вы завершите любой из вышеперечисленных действий, вы не найдете никаких запущенных контейнеров. В обоих случаях, cat сам завершается, таким образом, docker завершает работу контейнера.

Теперь для "-t" это сообщает основному процессу внутри docker, что его ввод является терминальным устройством.

Так

  • docker run -t alpine cat выдаст вам пустую строку, но если вы попытаетесь ввести "привет", вы не получите никакого эха. Это происходит потому, что в то время как cat подключен к входу терминала, этот вход не подключен к вашему входу. Введенное вами "привет" не дошло до ввода cat. cat ожидает ввода, который никогда не поступает.
  • echo "hello" | docker run -t alpine cat также выдаст вам пустую строку и не выйдет из контейнера при нажатии CTRL-D, но вы не получите эхо "привет", потому что вы не прошли -i

Если вы отправите CTRL + C, вы получите свою оболочку обратно, но если вы попытаетесь docker ps теперь вы видите, что cat контейнер все еще работает. Это потому что cat все еще ожидает входного потока, который никогда не был закрыт. Я не нашел никакого полезного применения для -t один, не будучи объединенным с -i.

Теперь, для -it вместе. Это сообщает cat, что его вход является терминалом, и в то же время подключает этот терминал к входу docker run который является терминалом. docker run/exec удостоверится, что его собственный ввод на самом деле является tty, прежде чем передавать его в cat. Вот почему вы получите input device is not a TTY если вы попытаетесь echo "hello" | docker run -it alpine cat потому что в этом случае ввод docker run сам по себе является каналом из предыдущего echo, а не терминалом, где docker run выполняется

Наконец, зачем вам нужно проходить -t если -i сделает трюк с подключением вашего ввода к catчей вклад? Это связано с тем, что команды обрабатывают ввод по-разному, если это терминал. Это также лучше всего иллюстрируется примером

  • docker run -e MYSQL_ROOT_PASSWORD=123 -i mariadb mysql -uroot -p выдаст вам запрос на ввод пароля. Если вы вводите пароль, символы печатаются на видном месте.
  • docker run -i alpine sh даст вам пустую строку. Если вы введете команду типа ls вы получите вывод, но вы не получите подсказку или цветной вывод.

В последних двух случаях вы получаете такое поведение, потому что mysql так же как shell не обрабатывали входные данные как tty и, следовательно, не использовали специфичное для tty поведение, такое как маскирование входных данных или раскрашивание выходных данных.

Этот ответ помог мне собраться с мыслями:

  • по умолчанию (без каких-либо -i ни -t опции) контейнер Docker отправляет свои выходные данные только в Stdout,
  • с -i опция включает подключение к STDIN,
  • -t опция подключает драйвер интерфейса терминала, который работает поверх STDIN / STDOUT. И когда подключается драйвер терминала, связь с контейнером должна соответствовать протокол интерфейса терминала. Трубопроводная передача строки этого не делает.

Tty указывает, что у вас есть терминал, который может быть предоставлен xterm или одним из многих интерфейсов командной строки Linux. Ему нужна клавиатура и связанный с ней интерфейс вывода текста. Типичные причины, по которым это необходимо, - поддержка вывода цветного текста, обработка различных комбинаций клавиш (например, клавиш со стрелками) и возможность перемещения курсора по экрану.

Когда вы передаете команду в docker, как ваш echo пример показывает, что канал является входным, и этот канал не имеет интерфейса tty, это просто поток текста. Попытка создать tty с этим завершится неудачей, как указано в сообщении об ошибке.

Могу ли я запустить tty в docker? У меня есть какое-то приложение, которое перестает работать, если я не запускаю docker с помощью -t, но я не могу изменить команду запуска docker в рабочей среде. Поэтому мне нужно заставить приложение думать, что оно было запущено с `-t’.

Это не лишнее, docker может создать TTY, ничего к нему не привязывая. Ваш вывод будет содержать символы для цвета и т.д., Но ваш вывод терминала не будет передаваться по каналу на вход контейнера. Таким образом, вводимые вами символы будут поставлены в очередь для следующей команды, которую вы запустите после завершения работы команды docker.