Even without docker exec -t, the output is there - docker

I understood docker's -t as a kind of virtual terminal that seems to access the terminal through /dev/pts. So, if I do echo "hello, tty" > /dev/pts/1 , I see that it is output to the connected terminal. Since the -i option is STDIN, the container understands it as an option to receive text as input. So, who does the input go to when only the -i option is applied?
Below is the result of the command given only the -i option.
~ $ docker exec -i mysql-container bash
tty
not a tty
ls
bin
boot
dev
docker-entrypoint-initdb.d
entrypoint.sh
etc
home
lib
lib64
media
mnt
opt
proc
root
run
sbin
srv
sys
tmp
usr
var
I didn't give the -t option, so I was expecting no results back. But it exactly missed my expectations. Why is it running like this?

The difference is subtle, but when you only use -i, you communicate with it using stdin and stdout. But there's no terminal in the container.
When you use -it you attach a terminal and that lets you do 'terminal stuff'. For instance, with -it
you get a prompt
you can send programs to the background with stuff like tail -f /dev/null &
ctrl-c works as expected
etc
The difference is hard to spot, because with -i you usually take stdin from a terminal on the host and send stdout to the a terminal on the host.
Usually, when you want to run commands interactively, you'll use -it. A scenario where you might use only -i is when you pipe the commands into the container. Something like this
echo -e 'tty\nls' | docker exec -i mysql-container bash
which will run the tty and ls commands and give you the output.

Related

What does -it exactly do in docker? [duplicate]

This question already has answers here:
what is docker run -it flag?
(3 answers)
Closed 22 days ago.
When executing this command, I can't just leave out neither i nor t to get the bash to work.
sudo docker exec -it 69e937450dab bash
What does it exactly do? When do I need the command without these parameters?
The flags -i and -t are required to run an interactive shell session in the container:
-i makes the session interactive by keeping STDIN open even if not attached
-t allocates a pseudo-TTY, allowing you to interact with the container using a terminal
I will answer it myself:
Normal execution without any flags:
[ec2-user#ip-172-31-109-14 ~]$ sudo docker exec 69e937450dab ls
bin
boot
dev
docker-entrypoint.d
docker-entrypoint.sh
etc
If your command needs an input like cat, you can try:
[ec2-user#ip-172-31-109-14 ~]$ echo test | sudo docker exec 69e937450dab cat
Nothing will show, because there is no input stream going to the docker container. This can be achieved with the -i flag.
[ec2-user#ip-172-31-109-14 ~]$ echo test | sudo docker exec -i 69e937450dab cat
test
Now, let us suppose, you want the bash to start as process:
sudo docker exec 69e937450dab bash
You will see nothing, because the process started in the container. Adding the flag will do the deal:
[ec2-user#ip-172-31-109-14 ~]$ sudo docker exec -t 69e937450dab bash
root#69e937450dab:/#
But this does not really help, because we need an input stream, which takes our commands and can be received by the bash. Therefore, we need to combine the two:
[ec2-user#ip-172-31-109-14 ~]$ sudo docker exec -i -t 69e937450dab bash
root#69e937450dab:/# ls
bin boot dev docker-entrypoint.d docker-entrypoint.sh etc hi home lib lib64 media mnt opt proc root run sbin srv sys tmp usr var
root#69e937450dab:/#
small recap:
-t for attaching the bash process to our terminal
-i for being able to send inputs via STDIN for example with the keyboard to the bash in the container
Without -i can be used for commands, that don't need inputs. Without -t and bash can be used, when you just want to use one command.

Write to stdin of running docker container

Say I run a docker container as a daemon:
docker run -d foo
is there a way to write to the stdin of that container? Something like:
docker exec -i foo echo 'hi'
last time I checked the -i and -d flags were mutually exclusive when used with the docker run command.
According to another answer on ServerFault, you can use socat to pipe input to a docker container like this:
echo 'hi' | socat EXEC:"docker attach container0",pty STDIN
Note that the echo command includes a newline at the end of the output, so the line above actually sends hi\n. Use echo -n if you don't want a newline.
Let's see how this looks like with the example script from David's answer:
# Create a new empty directory
mkdir x
# Run a container, in the background, that copies its stdin
# to a file in that directory
docker run -itd --rm -v $PWD/x:/x --name cattainer busybox sh -c 'cat >/x/y'
# Send some strings in
echo 'hi' | socat EXEC:"docker attach cattainer",pty STDIN
echo 'still there?' | socat EXEC:"docker attach cattainer",pty STDIN
# Stop container (cleans up itself because of --rm)
docker stop cattainer
# See what we got out
cat x/y
# should output:
# hi
# still there?
You could also wrap it in a shell function:
docker_send() {
container="$1"; shift
echo "$#" | socat EXEC:"docker attach $container",pty STDIN
}
docker_send cattainer "Hello cat!"
docker_send cattainer -n "No newline here:" # flag -n is passed to echo
Trivia: I'm actually using this approach to control a Terraria server running in a docker container, because TerrariaServer.exe only accepts server commands (like save or exit) on stdin.
In principle you can docker attach to it. CTRL+C will stop the container (by sending SIGINT to the process); CTRL+P, CTRL+Q will detach from it and leave it running (if you started the container with docker run -it).
The one trick here is that docker attach expects to be running in a terminal of some sort; you can do something like run it under script to meet this requirement. Here's an example:
# Create a new empty directory
mkdir x
# Run a container, in the background, that copies its stdin
# to a file in that directory
docker run -itd -v $PWD/x:/x --name cat busybox sh -c 'cat >/x/y'
# Send a string in
echo foo | script -q /dev/null docker attach cat
# Note, EOF here stops the container, probably because /bin/cat
# exits normally
# Clean up
docker rm cat
# See what we got out
cat x/y
In practice, if the main way a program communicates is via text on its standard input and standard output, Docker isn't a great packaging mechanism for it. In higher-level environments like Docker Compose or Kubernetes, it becomes progressively harder to send content this way, and there's frequently an assumption that a container can run completely autonomously. Just invoking the program gets complicated quickly (as this question hints at). If you have something like, say, the create-react-app setup tool that asks a bunch of interactive questions then writes things to the host filesystem, it will be vastly easier to run it directly on the host and not in Docker.

Why Docker exec need --interactive? --tty should be enough?

I new to docker, when i play with docker exec. I have below question:
Command docker exec -t 26b318e534c0 bash, already have tty, this should be interactive? Why must use docker exec **-it** 26b318e534c0 bash?
You can create a pseudo tty without any input to the tty device. This allows an application generating output to do so using content recognized by the tty device (e.g. color output). If you want to be able to interactively type in that tty, then you need to pass the input option, from a terminal with tty support (not from a shell script, and not from some windows command prompts), to attach your console to the stdin of that container.
As a simple example with docker run, these two commands will look different:
docker run -t --rm debian ls -al --color=always
docker run --rm debian ls -al --color=always
The first will have color output, the second will not, and neither will allow you to type input to the ls command being run inside the container.

what is docker run -it flag?

I was doing some complex stuff with docker, but as turn out I don't know what -it flag means.
Recently I've come across on some example of docker run command which has confused me a little.
docker run -itd ubuntu:xenial /bin/bash
My question is what is sense to write -it flag here, if container during instantiation run bin/bash
In documentation we have an example
docker run --name test -it debian
with explanation
The -it instructs Docker to allocate a pseudo-TTY connected to the
container’s stdin; creating an interactive bash shell in the
container.
and explanation for -t flag from help page
-t, --tty Allocate a pseudo-TTY
if I delete -it flag during
docker run -d ubuntu:xenial /bin/bash
my newly created container doesn't live so much
in docker ps -a
it is designated as exited
Sorry, if my question quite stupid, I can't find explanation on the Internet (I have significant misunderstanding of that point).
-it is short for --interactive + --tty. When you docker run with this command it takes you straight inside the container.
-d is short for --detach, which means you just run the container and then detach from it. Essentially, you run container in the background.
Edit: So if you run the Docker container with -itd, it runs both the -it options and detaches you from the container. As a result, your container will still be running in the background even without any default app to run.
docker run -it ubuntu:xenial /bin/bash starts the container in the interactive mode (hence -it flag) that allows you to interact with /bin/bash of the container. That means now you will have bash session inside the container, so you can ls, mkdir, or do any bash command inside the container.
The key here is the word "interactive". If you omit the flag, the container still executes /bin/bash but exits immediately. With the flag, the container executes /bin/bash then patiently waits for your input.
Normal execution without any flags:
[ec2-user#ip-172-31-109-14 ~]$ sudo docker exec 69e937450dab ls
bin
boot
dev
docker-entrypoint.d
docker-entrypoint.sh
etc
If your command needs an input like cat, you can try:
[ec2-user#ip-172-31-109-14 ~]$ echo test | sudo docker exec 69e937450dab cat
Nothing will show, because there is no input stream going to the docker container. This can be achieved with the -i flag.
[ec2-user#ip-172-31-109-14 ~]$ echo test | sudo docker exec -i 69e937450dab cat
test
Now, let us suppose, you want the bash to start as process:
sudo docker exec 69e937450dab bash
You will see nothing, because the process started in the container. Adding the flag will do the deal:
[ec2-user#ip-172-31-109-14 ~]$ sudo docker exec -t 69e937450dab bash
root#69e937450dab:/#
But this does not really help, because we need an input stream, which takes our commands and can be received by the bash. Therefore, we need to combine the two:
[ec2-user#ip-172-31-109-14 ~]$ sudo docker exec -i -t 69e937450dab bash
root#69e937450dab:/# ls
bin boot dev docker-entrypoint.d docker-entrypoint.sh etc hi home lib lib64 media mnt opt proc root run sbin srv sys tmp usr var
root#69e937450dab:/#
small recap:
-t for attaching the bash process to our terminal
-i for being able to send inputs via STDIN for example with the keyboard to the bash in the container
Without -i can be used for commands, that don't need inputs. Without -t and bash can be used, when you dont want to attach the docker containers process to your shell.

What does it mean to attach a tty/std-in-out to dockers or lxc?

I read some docker docs and I do not understand what it might mean to
attach a tty
attach std-in and std-out
for these purposes, I see that -i and -t flags are use.
What does this process mean?
stdin, stdout, and ttys are related concepts. stdin and stdout are the input and output streams of a process. A pseudo terminal (also known as a tty or a pts) connects a user's "terminal" with the stdin and stdout stream, commonly (but not necessarily) through a shell such as bash. I use quotes around "terminal" since we really don't use a terminal in the same sense today.
In the case of docker, you'll often use -t and -i together when you run processes in interactive mode, such as when starting a bash shell. In the case of the shell you want to be able to issue commands and read the output.
The code docker uses to attach stdout/stdin has all the dirty details.
We can see what is happening under the hood by using the lsof command. For a demonstration we can create a simple docker container from a Debian image that just runs sleep:
docker run -d --name tty-test debian /bin/bash -c "sleep 1000"
This will start the sleep command in a new container (note that we did not use -i or -t).
Next we "login" into our container though the exec command and start a bash:
docker exec -it tty-test /bin/bash
A plain debian image will not have lsof installed so we need to install it:
apt update && apt install -y lsof
Next we run lsof:
lsof
If run without any options, lsof will print open files for all running processes. You should see three processes in its output (sleep, bash, and lsof itself).
Here are the relevant lines are those that show the file descriptors (FD column) 0 to 2.
Note how the sleep process, which we started without the -t option has three FIFO pipes for stdin,stdout and stderr:
sleep 1 root 0r FIFO 0,10 0t0 8226490 pipe
sleep 1 root 1w FIFO 0,10 0t0 8226491 pipe
sleep 1 root 2w FIFO 0,10 0t0 8226492 pipe
While the bash process has an actual device attached to stdin, stdout and stderr:
bash 7 root 0u CHR 136,15 0t0 18 /dev/pts/15
bash 7 root 1u CHR 136,15 0t0 18 /dev/pts/15
bash 7 root 2u CHR 136,15 0t0 18 /dev/pts/15
Lets create another container with the -t option:
docker run -d -t --name tty-test2 debian /bin/bash -c "sleep 1000"
After installing lsof again (see above) we get a different output from lsof for the sleep process:
sleep 1 root 0u CHR 136,15 0t0 18 /15
sleep 1 root 1u CHR 136,15 0t0 18 /15
sleep 1 root 2u CHR 136,15 0t0 18 /15
Note how the type column has changed to CHR and the name column shows /15.
Finally, when we omit the -t option from the exec command and like this:
docker exec -it tty-test /bin/bash
then we can notice two things. First, we do not get a shell prompt from the bash now, but we can still type commands and see their output. When we run lsof we see that the bash process now also has pipes rather then a tty attached to stdin, stdout, and stderr
bash 379 root 0r FIFO 0,10 0t0 8263037 pipe
bash 379 root 1w FIFO 0,10 0t0 8263038 pipe
bash 379 root 2w FIFO 0,10 0t0 8263039 pipe
It means you can log in to your container using TTY, ie terminal. It's as if you've got a Linux machine in front of you and you're logging into it. If you have a container that's not running SSH server or telnet, this is your only mode of getting into the command line prompt.
As for why -i and -t are different arguments I'm not sure about, I can't imagine a scenario where you want to connect using TTY and don't want the stdin/stdout option or vice versa.
Option -i -t is useful for interacting with certain interactive process's like bash shell running inside the container.
consider the below command
PS C:\Users\nssh> docker run -it ubuntu /bin/bash
root#c7537bbf2941:/# ls
bin boot dev etc home lib lib32 lib64
When we run this command, docker will start the ubuntu container with bash shell running inside.
The -i flag tells docker that anything we type should be sent to bash process's stdin. ls command typed above is sent to bash.
The -t flag tells docker that this will be an interactive session and the stdin will be a tty.
For a typical SSH like experience, -t and -i must be used together.
The only reason to leave out -t is if the stdin is a pipe (and not a tty like above). Specifying -t is forbidden when the client is receiving its standard input from a pipe, as in:
PS C:\Users\nssh> echo test | docker run -it ubuntu cat
the input device is not a TTY. If you are using mintty, try prefixing the command with 'winpty'
Leave out the -t flag if stdin is a pipe.
PS C:\Users\nssh> echo test | docker run -i ubuntu cat
test
Simply put it allows us to attach and detach from the containers terminal. To attach we use the docker attach command and to detach we use the CTRL+P & CTRL+Q command.

Resources