Learning Docker, and encountered an interesting scenario. Consider the following sequence of Docker commands. For reference, the docker version in the machine I'm connected at is 20.10.18, build b40c2f6 and I have the latest centos image downloaded.
[cloud_user#eb993010811c ~]$ docker container run -dit --name interactive_centos centos
42afd26322dddeb7878113f9a9a753c09b7c0a43f30d08c04b5a8ac7e9540fcf
[cloud_user#eb993010811c ~]$ docker container run -d --name non_interactive_centos centos
1387f5f8654361d2370e776842560c2e1255a8eb9f5eb9db4fd7bbfcc48aae99
[cloud_user#eb993010811c ~]$ docker container start -ia interactive_centos
[root#42afd26322dd /]# exit
exit
[cloud_user#eb993010811c ~]$ docker container start -ia non_interactive_centos
[cloud_user#eb993010811c ~]$ docker container start -ia non_interactive_centos
[cloud_user#eb993010811c ~]$ echo $?
0
[cloud_user#eb993010811c ~]$ docker container logs non_interactive_centos | tail -n 5
[cloud_user#eb993010811c ~]$
We can see that the centos container interactive_centos, which was run with the -dit flags, provides us with a root shell when we start it with the -ia flag. On the other hand, the container non_interactive_centos, which was run from the very same centos image but without the -i and -t flags, does NOT provide us with an interactive TTY when we try to start it with -i and -a flags, and does not even output any logs either at the bash command or the docker logs level.
This is not too surprising, of course, and can be partly explained by a diff on the inspections of these containers:
[cloud_user#eb993010811c ~]$ docker container inspect non_interactive_centos > nic.conf
[cloud_user#eb993010811c ~]$ docker container inspect interactive_centos > ic.conf
[cloud_user#eb993010811c ~]$ diff nic.conf ic.conf
.
.
.
142,143c142,143
< "Tty": false,
< "OpenStdin": false,
---
> "Tty": true,
> "OpenStdin": true,
.
.
.
But my question is: why? If the container is run without the -ia flag, does that mean that as long as that container is alive, no matter how many times it is started, stopped, resumed, an interactive TTY will never be an option? That sounds a bit inflexible as a design choice, but there's probably a reason, and I was wondering what the reason is.
Related
Any commands hang terminal inside docker container.
I login in container with docker exec -t php-zts /bin/bash
And then print any elementary command (date, ls, cd /, etc.)
Command hang
When I press ctrl+c I going back to host machine.
But, if I run any command without container - it's work normally
docker exec -t php-zts date
Wed Jan 26 00:04:38 UTC 2022
tty is enabled in docker-compose.yml
docker system prune and all cleanups can not help me.
I can't identify the problem and smashed my brain. Please help :(
The solution is to use the flag -i/--interactive with docker run. Here is a relevant section of the documentation:
--interactive , -i Keep STDIN open even if not attached
You can try to run your container using -i for interactive and -t for tty which will allow you to navigate and execute commands inside the container
docker run -it --rm alpine
In the other hand you can run the container with docker run then execute commands inside that container like so:
tail -f /dev/null will keep your container running.
-d will run the command in the background.
docker run --rm -d --name container1 alpine tail -f /dev/null
or
docker run --rm -itd --name container1 alpine sh # You can use -id or -td or -itd
This will allow you to run commands from inside the container.
you can choose sh, bash, or any other shell you prefer.
docker exec -it container1 alpine sh
As I understood c120809b91b5 == ubuntu
[dev#fedora ~]$ sudo docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
c120809b91b5 ubuntu "bash" 11 days ago Up 39 minutes ubuntuContainer
[dev#fedora ~]$ docker run -it --add-host=host.docker.internal:host-gateway ubuntu bash
root#c760c5696300:/# cd testfolder
bash: cd: testfolder: No such file or directory
but why they not similar?
[dev#fedora ~]$ docker exec -it c120809b91b5 bash
(base) root#c120809b91b5:/# cd testfolder
(base) root#c120809b91b5:/testfolder#
What I do wrong?
I need to run c120809b91b5 container with --add-host=host.docker.internal:host-gateway but can't because ubuntu container acts like another with another internal environment.
What is happening here is that with docker run you are creating a new container from the ubuntu image. So when you run docker run -it --add-host=host.docker.internal:host-gateway ubuntu bash you are just creating a new container from the ubuntu image which is given a new containerId: c760c5696300
If you run docker ps -a you will see that you now have two containers with containerIds c120809b91b5 and c760c5696300
Please read docs https://docs.docker.com/engine/reference/run/
try adding "detached"
docker run -d -it --add-host=host.docker.internal:host-gateway ubuntu bash
and then look at the result.
I hope it helps.
You probably have some exited docker containers by now look at them with:
docker ps -a
Why when i run the command
docker run ubuntu
without option '-it' is not possible to interact with the created container even when running command start with the -a -i options
docker start -a -i CONTAINER_ID
or when i run
docker start CONTAINER_ID
simply the container has the status "Exit (0) 4 seconds ago"
But when i run
docker run -it ubuntu
i can use bash shell of ubuntu using 'docker start -a -i'
When you run docker run without -it it's still running the container but you've not given it a command, so it finishes and exits.
If you try:
docker run ubuntu /bin/bash -c "echo 'hello'";
It'll run ubunu, then the command, and then finish because there is no reason for it to be kept alive afterwards.
-i is saying keep it alive and work within in the terminal (allow it to be interactive), but if you type exit, you're done and the container stops.
-t is showing the terminal of within the docker container (see: What are pseudo terminals (pty/tty)?)
-it allows you to see the terminal in the docker instance and interact with it.
Additionally you can use -d to run it in the background and then get to it afterwards.
Ex:
docker run -it -d --name mydocker ubuntu;
docker exec -it mydocker /bin/bash;
TLDR is -it allows you connect a terminal to interactively connect to the container.
If you run docker run --help, you can find the details about docker run options.
$ docker run --help
Usage: docker run [OPTIONS] IMAGE [COMMAND] [ARG...]
Run a command in a new container
Options:
...
-i, --interactive Keep STDIN open even if not attached
...
-t, --tty Allocate a pseudo-TTY
I'm studying the Docker documentation, but I'm having a hard time understanding the concept of creating a container, ssh, and ssh back.
I created a container with
docker run -ti ubuntu /bin/bash
Then, it starts the container and I can run commands. docker ps gives me
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
0e37da213a37 ubuntu "/bin/bash" About a minute ago Up About a minute keen_sammet
The issue is after I exit the container I can't ssh back.
I tried docker attach that gives me Error: No such container and I tried docker exec -ti <container>/bin/bash that gives me the same message Error: No such container
How do I run and ssh back to the container?
When you exit the bash process, the container exits (in general, a container will exit when the foreground process exits). The error message you are seeing is accurately describing the situation (the container is no longer running).
If you want to be able to docker exec into a container, you will want to run some sort of persistent command. For example, if you were to run:
docker run -ti -d --name mycontainer ubuntu bash
This would start a "detached" container. That means you've started bash, but it's just hanging around doing nothing. You could use docker exec to start a new process in this container:
$ docker exec -it mycontainer ps -fe
UID PID PPID C STIME TTY TIME CMD
root 1 0 0 16:28 pts/0 00:00:00 bash
root 17 0 0 16:28 pts/1 00:00:00 ps -fe
Or:
$ docker exec -it mycontainer bash
There's really no reason to start bash as the main process in this case, since you're not interacting with it. You can just as easily run...
docker run -ti -d --name mycontainer ubuntu sleep inf
...and the behavior would be the same.
The most common use case for all of this is when your docker run command starts up some sort of persistent service (like a web server, or a database server, etc), and then you use docker exec to perform diagnostic or maintenance tasks.
The docker attach command will re-connect you with the primary console of a detached container. In other words, if we return to the initial example:
docker run -ti -d --name mycontainer ubuntu bash
You could connect to that bash process (instead of starting a new one) by running:
docker attach mycontainer
At this point, exit would cause the container to exit.
First, you don't ssh to a docker container (unless you have a sshd process in that container). But you can execute a command with docker exec -ti mycontainer bash -l
But you can exec a command only on running container. If the container exited already you must use another approach : create an image from the container and run a new one.
Here is an example. First I create a container and create a file within then I exit it.
$ docker run -ti debian:9-slim bash -l
root#09f889e80153:/# echo aaaaaaaaaa > /zzz
root#09f889e80153:/# cat /zzz
aaaaaaaaaa
root#09f889e80153:/# exit
logout
As you can see the container is exited (Exited (0) 24 seconds ago)
$ docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
09f889e80153 debian:9-slim "bash -l" 45 seconds ago Exited (0) 24 seconds ago thirsty_hodgkin
So I create a new image with docker commit
$ docker commit 09f889e80153 bla
sha256:6ceb88470326d2da4741099c144a11a00e7eb1f86310cfa745e8d3441ac9639e
So I can run a new container that contains previous container content.
$ docker run -ti bla bash -l
root#479a0af3d197:/# cat zzz
aaaaaaaaaa
How can I see the full command of a running container/process in Docker?
$ docker ps --all
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
5b6291859b61 nginx:1.7.8 "nginx -g 'daemon of 4 minutes ago Exited (0) 4 minutes ago thirsty_brattain
I can only see "nginx -g 'daemon of".. here, not the full command.
docker ps --no-trunc will display the full command along with the other details of the running containers.
Use:
docker inspect -f "{{.Name}} {{.Config.Cmd}}" $(docker ps -a -q)
... it does a "docker inspect" for all containers.
Use:
docker inspect -f "{{.Path}} {{.Args}} ({{.Id}})" $(docker ps -a -q)
That will display the command path and arguments, similar to docker ps.
Moving Dylan's comment into a full-blown answer because TOO USEFUL:
docker run --rm -v /var/run/docker.sock:/var/run/docker.sock assaflavie/runlike YOUR-CONTAINER
What does it do? Runs https://github.com/lavie/runlike inside a container, gets you the complete docker run command, then removes the container for you.
TL-DR
docker ps --no-trunc and docker inspect CONTAINER provide the entrypoint executed to start the container, along the command passed to, but that may miss some parts such as ${ANY_VAR} because container environment variables are not printed as resolved.
To overcome that, docker inspect CONTAINER has an advantage because it also allow to retrieve separately env variables and their values defined in the container from the Config.Env property.
docker ps and docker inspect provide information about the executed entrypoint and its command. Often, that is a wrapper entrypoint script (.sh) and not the "real" program started by the container. To get information on that, requesting process information with ps or /proc/1/cmdline help.
1) docker ps --no-trunc
It prints the entrypoint and the command executed for all running containers.
While it prints the command passed to the entrypoint (if we pass that), it doesn't show value of docker env variables (such as $FOO or ${FOO}).
If our containers use env variables, it may be not enough.
For example, run an alpine container :
docker run --name alpine-example -e MY_VAR=/var alpine:latest sh -c 'ls $MY_VAR'
When use docker -ps such as :
docker ps -a --filter name=alpine-example --no-trunc
It prints :
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
5b064a6de6d8417... alpine:latest "sh -c 'ls $MY_VAR'" 2 minutes ago Exited (0) 2 minutes ago alpine-example
We see the command passed to the entrypoint : sh -c 'ls $MY_VAR' but $MY_VAR is indeed not resolved.
2) docker inspect CONTAINER
When we inspect the alpine-example container :
docker inspect alpine-example | grep -4 Cmd
The command is also there but we don't still see the env variable value :
"Cmd": [
"sh",
"-c",
"ls $MY_VAR"
],
In fact, we could not see interpolated variables with these docker commands.
While as a trade-off, we could display separately both command and env variables for a container with docker inspect :
docker inspect alpine-example | grep -4 -E "Cmd|Env"
That prints :
"Env": [
"MY_VAR=/var",
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
],
"Cmd": [
"sh",
"-c",
"ls $MY_VAR"
]
A more docker way would be to use the --format flag of docker inspect that allows to specify JSON attributes to render :
docker inspect --format '{{.Name}} {{.Config.Cmd}} {{ (.Config.Env) }}' alpine-example
That outputs :
/alpine-example [sh -c ls $MY_VAR] [MY_VAR=/var PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin]
3) Retrieve the started process from the container itself for running containers
The entrypoint and command executed by docker may be helpful but in some cases, it is not enough because that is "only" a wrapper entrypoint script (.sh) that is responsible to start the real/core process.
For example when I run a Nexus container, the command executed and shown to run the container is "sh -c ${SONATYPE_DIR}/start-nexus-repository-manager.sh".
For PostgreSQL that is "docker-entrypoint.sh postgres".
To get more information, we could execute on a running container
docker exec CONTAINER ps aux.
It may print other processes that may not interest us.
To narrow to the initial process launched by the entrypoint, we could do :
docker exec CONTAINER ps -1
I specify 1 because the process executed by the entrypoint is generally the one with the 1 id.
Without ps, we could still find the information in /proc/1/cmdline (in most of Linux distros but not all). For example :
docker exec CONTAINER cat /proc/1/cmdline | sed -e "s/\x00/ /g"; echo
If we have access to the docker host that started the container, another alternative to get the full command of the process executed by the entrypoint is :
: execute ps -PID where PID is the local process created by the Docker daemon to run the container such as :
ps -$(docker container inspect --format '{{.State.Pid}}' CONTAINER)
User-friendly formatting with docker ps
docker ps --no-trunc is not always easy to read.
Specifying columns to print and in a tabular format may make it better :
docker ps --no-trunc --format "table{{.Names}}\t{{.CreatedAt}}\t{{.Command}}"
Create an alias may help :
alias dps='docker ps --no-trunc --format "table{{.Names}}\t{{.CreatedAt}}\t{{.Command}}"'
Use runlike from git repository https://github.com/lavie/runlike
To install runlike
pip install runlike
As it accept container id as an argument so to extract container id use following command
docker ps -a -q
You are good to use runlike to extract complete docker run command with following command
runlike <docker container ID>