Enviroment variables in docker containers - how does it work? - linux

I can't understand some thing, namely as we know we can pass to docker run argument -e SOME_VAR=13.
Then each process launched (for example using docker exec ping localhost -c $SOME_VAR=13) can see this variable.
How does it work ? After all, enviroment are about bash, we don't launched bash. I can't understand it. Can you explain me how -e does work without shell ?
For example, let's look at following example:
[user#user~]$ sudo docker run -d -e XYZ=123 ubuntu sleep 10000
2543e7235fa9
[user#user~]$ sudo docker exec -it 2543e7235fa9 echo test
test
[user#user~]$ sudo docker exec -it 2543e7235fa9 echo $XYZ
<empty row>
Why did I get <empty row> instead of 123 ?

The problem is that your $XYZ is getting interpolated in the host shell environment, not your container.
$ export XYZ=456
$ docker run -d -e XYZ=123 ubuntu sleep 10000
$ docker exec -it $(docker ps -ql) echo $XYZ
$ 456
$ docker exec -it $(docker ps -ql) sh -c 'echo $XYZ'
$ 123
You have to quote it so it's passed through as a string literal to the container. Then it works fine.

The environment is not specific to shells. Even ordinary processes have environments. They work the same for both shells and ordinary processes. This is because shells are ordinary processes.
When you do SOMEVAR=13 someBinary you define an environment variable called SOMEVAR for the new process, someBinary. You do this with -e in docker because you ask another process to start your process, the docker daemon.

Related

Bash script SSH command variable interpolation

First: I have searched the forum and also went through documentation, but still cannot get it right.
So, I have a docker command I want to run on a remote server, from my bash script. I want to pass an environment variable – on the local machine running the script – to the remote server. Furthermore, I need a response from the remote command.
Here is what I actually am trying to do and what I need: the script is a tiny wrapper around our Traefik/Docker/Elixir/Phoenix app setup to be able to connect easily to the running Elixir application, inside the Erlang observer. With the script, the steps would be:
ssh into the remote machine
docker ps to see all running containers, since in our blue/green deploy the active one changes name
docker exec into the correct container
execute a command inside the docker container to connect to the running Elixir application
The command I am using now is:
CONTAINER=$(ssh -q $USER#$IP 'sudo docker ps --format "{{.Names}}" | grep ""$APP_NAME"" | head -n 1')
The main problem is the part with the grep and the ENV var... It is empty, and does not get replaced. It makes sence, since that var does not exist on the remote machine, it does on my local machine. I tried single quotes, $(), ... Either it just does not work, or the solutions I find online execute the command but then I have no way of getting the container name, which I need for the subsequent command:
ssh -o 'RequestTTY force' $USER#$IP "sudo docker exec -i -t $CONTAINER /bin/bash -c './bin/app remote'"
Thanks for your input!
First, are you sure you need to call sudo docker stop? as stopping the containers did not seem to be part of the workflow you mentioned. [edit: not applicable anymore]
Basically, you use a double-double-quote, grep ""$APP_NAME"", but it seems this variable is not substituted (as the whole command 'sudo docker ps …' is singled-quoted); according to your question, this variable is available locally, but not on the remote machine, so you may try writing:
CONTAINER=$(ssh -q $USER#$IP 'f() { sudo docker ps --format "{{.Names}}" | grep "$1" | head -n 1; }; f "'"$APP_NAME"'"')
You can try this single command :
ssh -t $USER#$IP "docker exec -it \$(docker ps -a -q --filter Name=/$APP_NAME) bash -c './bin/app remote'"
You will need to redirect the command with the local environmental variable (APP_NAME) into the ssh command using <<< and so:
CONTAINER=$(ssh -q $USER#$IP <<< 'sudo docker ps --format "{{.Names}}" | grep "$APP_NAME" | head -n 1 | xargs -I{} sudo docker stop {}')

I can't get env var in the Docker container

I've ran my Docker container using this command:
docker run --name test1 -d -e FLAG='***' rastasheep/ubuntu-sshd
Now, when I connect to it via SSH, I can't get my env there via printenv FLAG.
How can I fix this? When running with -it and sh, I can my get env via printenv FLAG.
Now, when I connect to it via SSH, I can't get my env there via
printenv FLAG. How can I fix this? When running with -it and sh, I can
my get env via printenv FLAG
You are doing two different things:
docker run -it -e FLAG='***' rastasheep/ubuntu-sshd sh will run a container in interactive mode with a shell, and this shell session will have the environment variable you passed on the command line. With docker run -d -e FLAG='***' rastasheep/ubuntu-sshd, a SSH daemon process will start with defined env vars.
when you connect in the container with SSH you will create a new shell session which does not have these environment variable set.
This can be observed when running a container, connecting to it using ssh and showing all processes and their environment variable:
docker run -d -p 2222:22 -e FLAG='test' rastasheep/ubuntu-sshd
ssh root#localhost -p 2222
...
We are now connected into the container, we can see the SSH daemon process (PID 1) and our SSH session process (PID 7):
root#788fa982c2d0:~# ps -xf
PID TTY STAT TIME COMMAND
1 ? Ss 0:00 /usr/sbin/sshd -D # <== does have the FLAG env var
7 ? Ss 0:00 sshd: root#pts/0 # <== no FLAG env var
Lets check it out, print our current process env var, and the env var of the SSH daemon process:
root#788fa982c2d0:~# printenv FLAG # Nothing
root#788fa982c2d0:~# cat /proc/1/environ # We see the FLAG env var!
[..]FLAG=test[...]
As pointed out by #Dmitrii, you can read Dockerize an SSH service for more details.
Try Using below Command:
docker exec <container-id> bash -c 'echo "$<variable-name>"'
As suggested by docs
you might need to create your own Dockerfile with following changes
Project
|--Dockerfile
|--entrypoint.sh
Dockerfile
FROM rastasheep/ubuntu-sshd
COPY entrypoint.sh /usr/local/bin/entrypoint.sh
ENTRYPOINT ["/usr/local/bin/entrypoint.sh"]
CMD ["/usr/sbin/sshd", "-D"]
File: entrypoint.sh
#!/bin/bash
echo "export FLAG=$FLAG" >> /etc/profile
exec "$#"
Command:
docker build -t your-ubuntu-sshd .
docker run --name test1 -d -e FLAG='abc' -p 2222:22 your-ubuntu-sshd

Docker: Unable to run shell script stored in a mounted volume

I am running Docker (1.10.2) on Windows. I created a script to echo 'Hello World' on my machine and stored it in C:/Users/username/MountTest. I created a new container and mounted this directory (MountTest) as a data volume. The command I ran to do so is shown below:
docker run -t -i --name mounttest -v /c/Users/sarin/MountTest:/home ubuntu /bin/bash
Next, I run the command to execute the script within the container mounttest.
docker exec -it mounttest sh /home/helloworld.sh
The result is as follows:
: not foundworld.sh: 2: /home/helloworld.sh:
Hello World
I get the desired output (echo Hello World) but I want to understand the reason behind the not found errors.
Note: This question might look similar to Run shell script on docker from shared volume, but it addresses permission related issues.
References:
The helloworld.sh file:
#!/bin/sh
echo 'Hello World'
The mounted volumes information is captured below.
Considering the default ENTRYPOINT for the 'ubuntu' image is sh -c, the final command executed on docker exec is:
sh -c 'sh /home/helloworld.sh'
It looks a bit strange and might be the cause of the error message.
Try simply:
docker exec -it mounttest /home/helloworld.sh
# or
docker exec -it mounttest sh -c '/home/helloworld.sh'
Of course, the docker exec should be done in a boot2docker ssh session, simalar to the shell session in which you did a docker run.
Since the docker run opens a bash, you should make a new boot2docker session (docker-machine ssh), and in that new boot2docker shell session, try the docker exec.
Trying docker exec from within the bash made by docker run means trying to do DiD (Docker in Docker). It is not relevant for your test.

What is the purpose of the "-i" and "-t" options for the "docker exec" command?

To be honest, I have always been confused about docker exec -it …, docker exec -i … and docker exec -t …, so I decide to do a test:
docker exec -it …:
# docker exec -it 115c89122e72 bash
root#115c89122e72:/# ls
bin boot dev etc home lib lib64 media mnt opt proc root run sbin srv sys tmp usr var
It works normally.
docker exec -i …:
# docker exec -i 115c89122e72 bash
^C
The command hangs and I have to use Ctl + c to interrupt it.
docker exec -t …:
# docker exec -t 115c89122e72 bash
root#115c89122e72:/# ls
^C
It enters the container successfully but hangs on executing the first command.
So it seems there is no point in having the docker exec -i … and docker exec -t … commands. Could anyone elaborate on why there exist -i and -t options for the docker exec command?
-i, --interactive keeps STDIN open even if not attached, which you need if you want to type any command at all.
-t, --tty Allocates a pseudo-TTY, a pseudo terminal which connects a user's "terminal" with stdin and stdout. (See container/container.go)
If you do an echo, only -t is needed.
But for an interactive session where you enter inputs, you need -i.
Since -i keeps stdin open, it is also used in order to pipe input to a detached docker container. That would work even with -d (detach).
See "When would I use --interactive without --tty in a Docker container?":
$ echo hello | docker run -i busybox cat
hello
-i keeps STDIN open even if not attached, what is the status of STDOUT in this case?
It is, for docker exec, the one set by docker run.
But, regarding docker exec, there is a current issue (issue 8755: Docker tty is not a tty with docker exec
unfortunately your discovery only amounts to a difference between the behaviour of tty in centos6 vs ubuntu:14.04. There is still not a functional tty inside the exec - just do ls -la /proc/self/fd/0 and see that it's a broken link pointing to a pts which doesn't exist.
the actual bug we're dealing with is that certain standard libraries assume that the symlinks in /proc/self/fds/ must be valid symlinks
The problem is that the tty is created outside on the host and there is no reference to it in the container like how /dev/console is setup in the primary container.
One options to fix this would be allocate and bind mount the devpts from the host in to the containers.
Note (Q4 2017): this should been fixed by now (docker 17.06-ce).
See PR 33007.
That PR now allows (since 17.06):
zacharys-pro:dev razic$ docker run --rm -t -d ubuntu bash
83c292c8e2d13d1b1a8b34680f3fb95c2b2b3fef71d4ce2b6e12c954ae50965a
zacharys-pro:dev razic$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
83c292c8e2d1 ubuntu "bash" 2 seconds ago Up 1 second xenodochial_bardeen
zacharys-pro:dev razic$ docker exec -ti xenodochial_bardeen tty
/dev/pts/1
(before 17.06, tty was returning "not a tty")

Environment variables in docker when exec docker run

I've got a problem with environment variables in docker.
When I run command:
$ docker run ubuntu /bin/bash -c "echo $HOME"
I've got response:
/Users/bylek
But when I run:
$ docker run -it ubuntu /bin/bash
and then:
root#5e079c47affa:/# echo $HOME
I've got:
/root
Second response is correct. Why first command return $HOME value from my host?
echo $HOME is being evaluated on your host because you haven't got the syntax of the switch to bash correct. It's Linux so you need single quotes.
Try replacing your double quotes with single quotes.
eg. This is what I get:
bash-3.2$ docker run ubuntu /bin/bash -c 'echo $HOME'
/root

Resources