How to enter bash of an ubuntu docker container? - linux

I want to run an ubuntu container and enter bash:
[root#localhost backup]# docker run ubuntu bash
[root#localhost backup]#
The ubuntu container exits directly. How can I enter the bash?

Use -i and -t options.
Example:
$ docker run -i -t ubuntu /bin/bash
root#9055316d5ae4:/# echo "Hello Ubuntu"
Hello Ubuntu
root#9055316d5ae4:/# exit
See: Docker run Reference
$ docker run --help | egrep "(-i,|-t,)"
-i, --interactive=false Keep STDIN open even if not attached
-t, --tty=false Allocate a pseudo-TTY
Update: The reason this works and keeps the container running (running /bin/bash) is because the -i and -t options (specifically -i) keep STDIN open and so /bin/bash does not immediately terminate thus terminate the container. -- The reason you also need/want -t is because you presumably want to have an interactive terminal-like session so t creates a new pseudo-tty for you. -- Furthermore if you looked at the output of docker ps -a without using the -i/-t options you'd see that your container terminated normally with an exit code of 0.

Related

How to redirtect terminal STDIN when running a script in Docker?

I have a script that consumes users input. It is run like this:
./script <<EOF
> input
> onther input
> more input
> EOF
I need to distribute the script as a Docker image.
I am able to run the script in two steps.
First, I get into the containers shell.
docker run -it my-docker-tag sh
And then, inside the shell, I execute the script itself.
Question: Is it possible to run the script in on shut (without having to navigate to the containers shall)?
I tried this:
docker run -it my-docker-tag ./script <<EOF
> input
> onther input
> more input
> EOF
But it fails with:
the input device is not a TTY
The Docker run reference notes:
Specifying -t is forbidden when the client is receiving its standard input from a pipe, as in:
$ echo test | docker run -i busybox cat
If you remove the -t option, shell pipes and redirections around a (foreground) docker run command will work as you expect.
# A heredoc as in the question should work too
sudo docker run --rm my-docker-tag ./script <script-input.txt

Connecting to a specific shell instance in a docker container?

Let's say I have a running docker container my_container. I start a new shell session with:
docker exec -it my_container bash
And then I start a process (a Python script for example), and exit the container with cntrl-p then cntrl-q to keep the script running in the background. If I do this a few different times with a few different scripts, how do I reconnect to a specific shell instance so I can see the std out of my scripts? If I use docker attach my_container, I'm always placed into the first shell instance I initiated when I did my docker run command.
What I usually do is to start tmux inside the first shell. And then start any other processes inside a new window.
Although it is theoretically possible to do so, docker exec still has many issues and it is always better to avoid it for now.
This is a trivial mode, but may be it helps. Instead of "echo "..." substitude with your script names.
Run the container, then run your scripts directly with docker exec and redirect their output to different files.
docker exec -ti containerId /bin/bash -c 'echo "Hello script1" > /var/log/1.log'
docker exec -ti containerId /bin/bash -c 'echo "Hello script2" > /var/log/2.log'
Then you can look at the files by docker exec(uting) some other commands like cat, grep, tail or whatever you want:
docker exec -ti containerId /bin/tail -f /var/log/1.log
docker exec -ti containerId /bin/tail -f /var/log/2.log
Remind you could also use
docker logs containerId
to see the output redirect to /dev/stdout from commands running in the container, but, if I understood your need, in this case you would get the output from many scritps mixed in stdout.

'su' command in Docker returns 'must be run from terminal'

I'm developing a docker environment for teaching purposes and need to be able to switch users inside docker.
I set up the 'user' user with a password but when I try to switch to it with su, I get "su must be run from terminal".
I get this if I try to ssh into the docker and also by issuing commands through a php shell (an apache service is running on the Docker instance).
Any help is much appreciated.
When you are ssh-ing in or going in via php your session is not being allocated a pty. I have used each of the following solutions:
ANSWER 1: use ssh -t or ssh -tt to get pty allocated when logging in using ssh:
I had great fun getting commands to run right due to ptys when running sessions like this: jenkins shell -> ssh driver -> ssh test -> docker exec.
Good answer here:
https://unix.stackexchange.com/questions/105422/command-must-be-run-from-a-terminal
"Try the -t option to ssh. If that does not work try -tt."
"-t Force pseudo-tty allocation. This can be used to execute arbitrary screen-based programs on a remote machine, which can be very useful, e.g. when implementing menu services. Multiple -t options force tty allocation, even if ssh has no local tty."
ANSWER 2: use docker run -t ... and docker exec -it
Use the -t and -it options to allocate pty in your docker exec session.
Also with docker exec you can simply use the -u option to login to container as different users and avoid using su. e.g.
$ docker exec -u root -it small_hypatia bash
There is a good question and answer on this here:
https://github.com/docker/docker/issues/8631
ANSWER 3: use python to spawn a pty in your shell
Quite a cute hack :)
jenkins#e9fbe94d4c89:~$ su -
su: must be run from a terminal
$ echo "import pty; pty.spawn('/bin/bash')" > /tmp/asdf.py
$ python /tmp/asdf.py
$ su -
Password:
root#e9fbe94d4c89:~#
This solution work by using 'script' command from the 'bsdutiles' package that setup a pty (a terminal). The 'sleep' command is there to prevent sending the password before the 'su' command is ready to read it. The 'tail' command remove the "Password:" input line issued by 'su'.
sh -c "sleep 1; echo rootpassword" | script -qc 'su -c whoami - root' | tail -n +2
Beware that the rootpassword could be see in many ways (history, ps, /proc/, etc...). Start the command with a space to at least avoid history recording.
If you use su-exec instead of su the issue with tty completely vanishes since it calls execvp directly instead of forking like su does.
Gosu is another similar alternative.

Why does "docker attach" hang?

I can run an ubuntu container successfully:
# docker run -it -d ubuntu
3aef6e642327ce7d19c7381eb145f3ad10291f1f2393af16a6327ee78d7c60bb
# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
3aef6e642327 ubuntu "/bin/bash" 3 seconds ago Up 2 seconds condescending_sammet
But executing docker attach hangs:
# docker attach 3aef6e642327
Until I press any key, such as Enter:
# docker attach 3aef6e642327
root#3aef6e642327:/#
root#3aef6e642327:/# ls
bin boot dev etc home lib lib64 media mnt opt proc root run sbin srv sys tmp usr var
Why does docker attach hang?
Update:
After reading the comments, I think I get the answers:
prerequisite:
"docker attach" reuse the same tty, not open new tty.
(1) Executing the docker run without daemon mode:
# docker run -it ubuntu
root#eb3c9d86d7a2:/#
Everything is OK, then run ls command:
root#eb3c9d86d7a2:/# ls
bin boot dev etc home lib lib64 media mnt opt proc root run sbin srv sys tmp usr var
root#eb3c9d86d7a2:/#
(2) Run docker run in daemon mode:
# docker run -it -d ubuntu
91262536f7c9a3060641448120bda7af5ca812b0beb8f3c9fe72811a61db07fc
Actually, the following should have been outputted to stdout from the running container:
root#91262536f7c9:/#
So executing docker attach seems to hang, but actually it is waiting for your input:
# docker attach 91262536f7c9
ls
bin boot dev etc home lib lib64 media mnt opt proc root run sbin srv sys tmp usr var
root#91262536f7c9:/#
It does not really hang. As you can see in the comment below (You are running "/bin/bash" as command) it seems to be expected behaviour when attaching.
As far as I understand you attach to the running shell and just the stdin/stdout/stderr - depending on the options you pass along with the run command - will just show you whatever goes in/out from that moment. (Someone with a bit more in-depth knowledge hopefuly can explain this on a higher level).
As I wrote in my comment on your question, there are several people who have opened an issue on the docker github repo describing similar behaviour:
docker attach [container] hangs, requires input #8521
docker attach hangs setting terminal state when attaching to container
Since you mention shell, I assume you have a shell already running. attach doesn't start a new process, so what is the expected behavior of connecting to the in/out/err streams of a running process?
I didn't think about this. Of course this is the expected behavior of attaching to a running shell, but is it desirable?
Would it be at all possible to flush stdout/stderr on docker attach thereby forcing the shell prompt to be printed or is it a bit more complex than that? That's what I personally would "expect" when attaching to an already running shell.
Feel free to close this issue if necessary, I just felt the need to document this and get some feedback.
Taken from a comment on this github issue. You can find more insight in the comments of this issue.
If instead of enter you would start typing a command, you would not see the extra empty prompt line. If you were to run
$ docker exec -it ubuntu <container-ID-or-name> bash
where <container-ID-or-name> is the ID or name of the container after you run docker run -it -d ubuntu (so 3aef6e642327 or condescending_sammet in your question) it would run a new command, thus not having this "stdout problem" of attaching to an existing one.
Example
If you would have a Dockerfile in a directory containing:
FROM ubuntu:latest
ADD ./script.sh /timescript.sh
RUN chmod +x /timescript.sh
CMD ["/timescript.sh"]
And have a simple bash script script.sh in the same directory containing:
#!/bin/bash
#trap ctrl-c and exit, couldn't get out
#of the docker container once attached
trap ctrl_c INT
function ctrl_c() {
exit
}
while true; do
time=$(date +%N)
echo $time;
sleep 1;
done
Then build (in this example in the same directory as the Dockerfile and script.sh) and run it with
$ docker build -t nan-xiao/time-test .
..stuff happening...
$ docker run -itd --name time-test nan-xiao/time-test
Finally attach
$ docker attach time-test
You will end up attached to a container printing out the time every second. (CTRL-C to get out)
Example 2
Or if you would have a Dockerfile containing for example the following:
FROM ubuntu:latest
RUN apt-get -y install irssi
ENTRYPOINT ["irssi"]
Then run in the same directory:
$ docker build -t nan-xiao/irssi-test .
Then run it:
$ docker run -itd --name irssi-test nan-xiao/irssi-test
And finally
$ docker attach irssi-test
You would end up in a running irssi window without this particular behaviour. Of course you can substitute irrsi for another program.
I ran into this issue as well when attempting to attach to a container that was developed by someone else and already running a daemon. (In this case, it was LinuxServer's transmission docker image).
Problem:
What happened was the terminal appeared to 'hang', where typing anything didn't help and wouldn't show up. Only Ctrl-C would kick me back out.
docker run, docker start, docker attach all was not successful, turns out the command I needed (after the container has been started with run or start) was to execute bash, as chances are the container you pulled from doesn't have bash already running.
Solution:
docker exec -it <container-id> bash
(you can find the container-id from running docker ps -a).
This will pull you into the instance with a functional bash as root (assuming there was no other explicit set up done by the image you pulled).
I know the accepted answer has captured this as well, but decided to post another one that is a little more terse and obvious, as the solution didn't pop out for me when I was reading it.
When I run docker attach container-name, then nothing output, even Ctrl-c is invalid. So, first try
docker attach container-name --sig-proxy=false
and then ctrl-c can stop it. Why it didn't output anything?
just because the container doesn't output. Actually I need to enter my container and run some shell command. So the correct command is
docker exec -ti container-name bash
This happened to me once for the following reason:
It could be that the bash command inside the container is executing a "cat" command.
So when you attach to the container (the bash command) you are actualy inside the cat command which is expecting input. (text and/or ctrl-d to write the file)
If you cannot access command line, just make sure you run your container with -i flag at start.
I just had a similar problem today and was able to fix it:
Here is what was happening for me:
docker-compose logs -f nginx
Attaching to laradock_nginx_1
Then it would hang there until I quit via CTRL-C: ^CERROR: Aborting.
docker ps -a showed that what SHOULD have been called laradock_nginx did not exist with that Image Name, so I figured I'd just remove and re "up" that container:
docker stop cce0c32f7556
docker rm cce0c32f7556
docker-compose up -d laradock_nginx
Unfortunately: ERROR: No such service: laradock_nginx
So I did a sudo reboot and then docker ps -a, but laradock_nginx still wasn't there.
Luckily, docker-compose up -d nginx then worked and docker-compose logs -f nginx now works.
Using: docker exec -it CONTAINER_ID/NAME bash
Instead: docker attach...

Piping data to Linux program which expects a TTY (terminal)

I have a program in Linux which refuses to run if its stdin/stdout is not a TTY (terminal device). Is there an easy-to-use tool which will create a PTY, start the program with the newly created TTY, and copy all data over stdin/stdout?
The use case is not interactive, but scripting. I'm looking for the most lightweight solution, preferably not creating TCP connections, and not requiring too many other tools and libraries to be installed.
unbuffer, part of expect (sudo apt-get install expect-dev on Ubuntu Lucid), can fool a program into thinking it's connected to a TTY.
$ tty
/dev/pts/3
$ echo | tty
not a tty
$ echo | unbuffer tty
/dev/pts/11
You can use socat for this: echo your stdin strings | socat EXEC:"your_program",pty STDIO >/stdout_file
For example with bash: echo ls | socat EXEC:'bash',pty STDIO >/tmp/ls_out
Or as described here, for a program run with docker:
# Run the docker task, here bash, in background
docker run -it --rm --name test ubuntu &
# Send "ls -la" to the bash running inside docker
echo 'ls -la' | socat EXEC:'docker attach test',pty STDIN
# Show the result
docker logs test

Resources