Запутался в опции Docker -t для выделения псевдо-TTY

Что именно делает этот вариант? Я много читаю на TTY и все еще в замешательстве. Я играл с не имея -t и просто -i и кажется, что программы, которые ожидают пользовательского ввода, выдают ошибку без -t, Почему важно включить псевдо-TTY?

9 ответов

Опция "-t" описывает, как Unix/Linux обрабатывает терминальный доступ. Раньше терминалом было жесткое соединение, позднее - модемное соединение. У них были драйверы физических устройств (они были настоящими частями оборудования). Как только обобщенные сети начали использоваться, был разработан драйвер псевдо-терминала. Это потому, что это создает разделение между пониманием того, какие возможности терминала могут быть использованы, без необходимости записывать их непосредственно в вашу программу (читайте справочные страницы на stty, curses).

Итак, с этим в качестве фона, запустите контейнер без параметров, и по умолчанию у вас есть поток stdout (так docker run | <cmd> работает); запустить с "-i", и вы получите поток стандартного ввода (так <cmd> | docker run -i работает); используйте "-t", обычно в комбинации "-it", и у вас есть добавленный драйвер терминала, который, если вы взаимодействуете с процессом, скорее всего то, что вам нужно. Это в основном делает начало контейнера похожим на сеанс терминального соединения.

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

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

Так

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

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

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

Так

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

Аргумент "-t" НЕ документирован, или часто упоминается многими, согласно поиску в Google.

Он даже не отображается, когда вы отображаете список (что должно быть) всех аргументов клиента Docker, набрав "docker" в приглашении Bash (с последней версией 1.8.1).

На самом деле, если вы попытаетесь получить конкретную помощь по этому аргументу, набрав "docker -t --help", если вы получите удивительно смутный ответ:

"флаг указан, но не определен: -t"

Таким образом, вы не можете быть обвинены в том, что запутались в этом аргументе!

В онлайновой документации Docker есть упоминание о том, что оно "выделяет псевдо-tty" и часто используется с -i:

https://docs.docker.com/reference/run/

Я видел, как он использовался в документации для потрясающего контейнера док-станции jwilder / nginx-proxy следующим образом:

docker run -d -p 80:80 --name nginx -v /tmp/nginx:/etc/nginx/conf.d -t nginx

В этом случае он отправляет вывод в "виртуальный" tty (командная строка / терминал Bash) в этом контейнере Docker. Затем вы можете увидеть эти выходные данные, выполнив команду docker "docker logs CONTAINER", где CONTAINER - это первая пара символов идентификатора этого контейнера. Этот идентификатор контейнера можно найти, набрав "docker ps -a"

Я видел этот аргумент "-t", кратко упомянутый в следующей ссылке, где говорится: "Флаги -t и -i выделяют псевдо-tty и оставляют stdin открытым, даже если он не подключен. Это позволит вам использовать Контейнер, как традиционная виртуальная машина, пока работает приглашение bash. "

https://coreos.com/os/docs/latest/getting-started-with-docker.html

Надеюсь, это поможет! Я не уверен, почему это не задокументировано или не используется много. Возможно, это экспериментально и будет реализовано как документированная функция в следующих версиях.

Большинство ответов здесь являются отличными концептуальными ответами, но я обнаружил, что они упустили слишком много деталей, чтобы я мог использовать информацию, сидя за компьютером. Ответ Ахмеда Гномина постепенно превращается в программный, но давайте попробуем продвинуть его еще на один шаг.

Сначала немного теории

Два изображения в The TTY Demystified являются ключевыми:

Я не могу утверждать, что полностью понимаю эту картину, но интересная взаимосвязь здесь заключается в том, что когда открывается xterm (или gnome-terminal в ubuntu; представленный одним из пузырьков "пользовательского процесса" на изображении выше), он запускает bash (или любую другую оболочку по умолчанию), а затем отправляет ему ввод с клавиатуры через главный и подчиненный псевдотерминал ядра (PTY):

xterm -> ptmx (pty master) -> pts (pty slave) -> bash

Второе изображение представляет процессы, участвующие в этом коротком сеансе bash:

      >>> cat
>>> ls | sort
...

Ключевыми битами информации являются строки TTY и stdin, stdout, stderr. Это показывает, что каждый процесс связан с TTY (терминал телетайпа), и что их 3 потока (stdin, stdout, stderr) вполне естественно связаны с этим TTY, за исключением случаев каналов или перенаправлений (обратите внимание, что канал ls | sort связывает стандартный вывод ls со стандартным вводом сортировки).

Теперь немного проверки теории

Мы можем найти псевдотерминал, используемый bash, набрав tty:

      >>> tty
/dev/pts/2

Таким образом, Bash связан с ведомым устройством PTY номер 2 (это, вероятно, означает, что есть еще один открытый терминал, связанный с парой 1 ведущий / ведомый). Мы также можем получить потоки stdin, stdout и stderr bash:

      >>> ls -l /proc/$$/fd
lrwx------ 1 samlaf samlaf 64 Jun 17 21:50 0 -> /dev/pts/2
lrwx------ 1 samlaf samlaf 64 Jun 17 21:50 1 -> /dev/pts/2
lrwx------ 1 samlaf samlaf 64 Jun 17 21:50 2 -> /dev/pts/2

В самом деле, все они связаны с естественным подчиненным телетайпом bash. ( $$это переменная bash, которая возвращает PID bash. Мы также можем найти его, используя ps и набрав от руки).

И, наконец, используя эту теорию, чтобы ответить на первоначальный вопрос Докера.

Мы воспроизводим описанные выше шаги, но на этот раз внутри контейнера докеров:

      >>> docker run --rm -t ubuntu tty
/dev/pts/0
>>> docker run --rm ubuntu tty
not a tty

что имеет смысл, так как -t .

Связанные команды труднее интерпретировать.

      >>> docker run --rm ubuntu bash -c "ls -l /proc/\$\$/fd"
lrwx------ 1 root root 64 Jun 18 02:37 0 -> /dev/null
l-wx------ 1 root root 64 Jun 18 02:37 1 -> pipe:[9173789]
l-wx------ 1 root root 64 Jun 18 02:37 2 -> pipe:[9173790]
>>> docker run --rm -t ubuntu bash -c "ls -l /proc/\$\$/fd"
lrwx------ 1 root root 64 Jun 18 02:39 0 -> /dev/pts/0
lrwx------ 1 root root 64 Jun 18 02:39 1 -> /dev/pts/0
lrwx------ 1 root root 64 Jun 18 02:39 2 -> /dev/pts/0
>>> docker run --rm -it ubuntu bash -c "ls -l /proc/\$\$/fd"
lrwx------ 1 root root 64 Jun 18 02:39 0 -> /dev/pts/0
lrwx------ 1 root root 64 Jun 18 02:39 1 -> /dev/pts/0
lrwx------ 1 root root 64 Jun 18 02:39 2 -> /dev/pts/0

Я до сих пор не могу понять, что именно ... Мне нужна помощь! Единственная интересная команда, которую я смог найти, в которой она, кажется, делает различие:

      >>> docker run --rm -a stdout -i ubuntu bash -c "ls -l /proc/\$\$/fd"
lr-x------ 1 root root 64 Jun 18 02:43 0 -> pipe:[9199896]
l-wx------ 1 root root 64 Jun 18 02:43 1 -> pipe:[9199897]
l-wx------ 1 root root 64 Jun 18 02:43 2 -> pipe:[9199898]
>>> docker run --rm -a stdout ubuntu bash -c "ls -l /proc/\$\$/fd"
lrwx------ 1 root root 64 Jun 18 02:43 0 -> /dev/null
l-wx------ 1 root root 64 Jun 18 02:43 1 -> pipe:[9197938]
l-wx------ 1 root root 64 Jun 18 02:43 2 -> pipe:[9197939]

В выделяет псевдотерминалдокументации Docker упоминается, что -a «присоединяется к потоку, переданному как вход», но я не смог найти объяснения того, что это означает, и как это связано с -i параметры.

В -itкомбинированные параметры известны как интерактивный режим.

По умолчанию контейнеры имеют только поток stdout (т.е. docker run | CMD работает), для взаимодействия с нашим контейнером нам понадобятся следующие параметры:

  • -i добавляет поток stdin (т.е. CMD | docker run работает)
  • -t выделяет пару псевдо-TTY главный / подчиненный, при этом подчиненная часть связана с запущенным процессом в контейнере, а главная часть связана с вашим эмулятором терминала.

Поток stdin прикрепляет контейнер к stdin вашей оболочки (поскольку Docker наследует поток stdin вашей оболочки), в то время как дисциплина линии TTY дает вам возможность взаимодействовать с контейнером в стиле клавиатуры.

Что я знаю о -t является следующим:

docker exec -ti CONTAINER bash - позволяет мне "войти" в контейнер. Это похоже на ssh-ing (это не так).

Но беда была, когда я захотел восстановить базу данных.

Обычно я делаюdocker exec -ti mysql.5.7 mysql - Здесь я выполняю команду mysql в контейнере и получаю интерактивный терминал.

я добавил <dump.sql на предыдущую команду, чтобы я мог восстановить БД. Но это не удалось с cannot enable tty mode on non tty input,

Удаление -t помог. Все еще не понимаю, почему:

docker exec -i mysql.5.7 mysql < dump.sql

Последний работает. Надеюсь, что это помогает людям.

Каждый процесс имеет три потока данных, т.е. STDIN/ STDOUT/ STDERR. Когда процесс выполняется в контейнере, по умолчанию терминал подключается к потоку STDOUT процесса, запущенного в контейнере. Следовательно, все выходные потоки будут видны во время работыdocker runкоманда в терминале. Но если вы хотите предоставить входные данные для запущенного процесса в контейнере, вам необходимо подключиться к каналу STDIN процесса, которого нет по умолчанию и выполняется с помощьюdocker run -i команда.

-t используется для интерактивных / форматированных операций ввода.

В Linux, когда вы запускаете команду, вам нужен терминал (tty) для ее выполнения.

Поэтому, когда вы хотите подключиться к докеру (или запустить команду в контейнере докера), вы должны указать параметр -t, который учитывает терминал внутри контейнера докера.

-It указывает Docker выделить псевдо-TTY, связанный с stdin контейнера, создавая интерактивную оболочку bash в контейнере.

--interactive, -i   false   Keep STDIN open even if not attached

--tty, -t   false   Allocate a pseudo-TTY

https://docs.docker.com/engine/reference/commandline/run/

Другие вопросы по тегам