Reputation: 1316
I'm trying to exec command in livenessProbe, I have connected to the container interactively and tested the command:
exit $(test $(date +%H) -eq 3 && echo 1 || echo 0)
which produced the desired result, however when I specify livenessProbe:
livenessProbe:
exec:
command:
- exit $(test $(date +%H) -eq 3 && echo 1 || echo 0)
it produces an error:
(combined from similar events): Liveness probe errored: rpc error: code = Unknown desc = failed to exec in container: failed to start exec "53fd8ffa05772ae7cebf99fadbf3767d902e40a58a8910f3fd501cfc8a65757d": OCI runtime exec failed: exec failed: unable to start container process: exec: "exit $(test $(date +%H) -eq 3 && echo 1 || echo 0)": executable file not found in $PATH: unknown
it works fine with:
livenessProbe:
exec:
command:
- sh
- -c
- exit $(test $(date +%H) -eq 3 && echo 1 || echo 0)
I want to understand why is that the case? Are there cases where I don't need to specify (ba)sh -c ?
Upvotes: 1
Views: 162
Reputation: 141165
A script written in a language is not the program that is used to run it.
Let's instead of shell, imagine python. Imagine:
livenessProbe:
exec:
command:
- import datetime; exit(datetime.datetime.now().hour == 3)
You would expect a lot of errors. The command
contains the command to execute, not a script written in some langauge.
what kubernetes does differently compared to how that command is executed when pasted in the console
Inside console (the terminal application) there is a shell running. You type your command inside the shell which the shell (the program connected to your terminal program) displays what you type for you and then the shell interprets what you type. You can type in your shell to try to execute a command named exit $(test $(date +%H) -eq 3 && echo 1 || echo 0)
by applying appropriate quoting. The quoting depends on the shell that you are using, many people use different shell. In Bash:
$ 'exit $(test $(date +%H) -eq 3 && echo 1 || echo 0)'
-bash: exit $(test $(date +%H) -eq 3 && echo 1 || echo 0): command not found
Which is the same error as executable file not found
you see from docker, as in bash is not able to find the command named exit $(test $(date +%H) -eq 3 && echo 1 || echo 0)
in PATH.
This is the command that kubernetes tries to execute. Instead, you want to execute a program that interprets that script. As the code you posted is a shell script - contains such constructs as $(
or &&
or ||
, you have to run the shell to interpret it, just like you would run python to interpret a python script or perl to interpret perl script.
In many contexts (some) shell is the interpreter run implicitly, always, for example system()
from C programming language or when doing ssh
. In many context you do not what shell this is, for example gitlab-ci.yml
executes bash or sh depending on what is available. But in many contexts, like when doing exec
in C programming language, you specify the actual program to execute.
You might be interested in: https://en.wikipedia.org/wiki/Shell_(computing) https://en.wikipedia.org/wiki/Interpreter_(computing) https://en.wikipedia.org/wiki/Exec_(system_call) https://man7.org/linux/man-pages/man3/system.3.html .
Upvotes: 1
Reputation: 265271
command:
- exit $(test $(date +%H) -eq 3 && echo 1 || echo 0)
Tries to exec
the full line as single command name (1 argument to exec). It's akin to running 'exit $(test $(date +%H) -eq 3 && echo 1 || echo 0)'
in your shell. That won't work (unless you have a binary in your PATH with that exact name, including the blanks and special chars).
command:
- sh
- -c
- exit $(test $(date +%H) -eq 3 && echo 1 || echo 0)
Calls exec with 3 arguments: sh
, -c
and exit $(test $(date +%H) -eq 3 && echo 1 || echo 0)
. The third argument will then be parsed and executed by the shell as a shell command. $(…)
is command substitution and will only work if interpreted by a shell.
The difference maybe becomes more clear if using the inline array syntax:
command: ["exit $(test $(date +%H) -eq 3 && echo 1 || echo 0)"]
vs
command: ["sh", "-c", "exit $(test $(date +%H) -eq 3 && echo 1 || echo 0)"]
Try it in your local shell:
$ 'exit $(test $(date +%H) -eq 3 && echo 1 || echo 0)'
exit $(test $(date +%H) -eq 3 && echo 1 || echo 0): command not found
$ 'sh' '-c' 'exit $(test $(date +%H) -eq 3 && echo 1 || echo 0)'
$ # no output
Note that your way of setting the exit status is quite convoluted and test "$(date +%H)" -ne 3
does the same with a lot less typing and more correct (your original commands exits with 0 if the first echo
fails).
You don't need to run with a shell, if you are only executing a single command and don't need to run a shell script. One example would be a simple curl test:
command:
- curl
- -f
- localhost:8080
Upvotes: 1
Reputation: 46
For me it is because when you specify the command directly in the exec probe without sh -c
, Kubernetes tries to execute it as if it were a script. But when you use sh -c
, you're telling Kubernetes to start a new shell and pass the rest of the command to that shell.
That's why in the error message you have the mention "executable file not found in $PATH: unknown".
In fact I think you can specify directly some shell commands without telling to Kubernetes to enter in a shell before but it is a valid option only for basic commmands like ls
or cat
for me because they are standalone or native executable if i can call them like that.
Upvotes: 1