There are many way to set command in pod.
1.
command: ["/bin/sh","-c","sleep 1000"]
2.
command: ["/bin/sh"]
args: ["-c","sleep 1000"]
3.
args: ["/bin/sh","-c","sleep 1000"]
Are they the same or different?
These are all almost the same; except that if the underlying image has a Docker ENTRYPOINT then the args: are passed to it as arguments in the third case, without command:.
In core Docker, there are two ways to specify the main container command, the Dockerfile ENTRYPOINT and CMD directives. Either or both of these can be overridden when the container starts, but resetting the ENTRYPOINT value always clears CMD. If both are present then the CMD is passed as arguments to the ENTRYPOINT.
Kubernetes has the same concept and uses the same underlying image structure, but its names are different:
Kubernetes command: overrides Docker ENTRYPOINT (and resets CMD)
Kubernetes args: overrides Docker CMD
So in the first case setting command: overrides the container entrypoint and resets the container command to an empty list; in the second you directly set both parts. In both cases the two lists are combined together and you get an identical 3-word command.
In the last case args: only overrides the container command part. If the image has a Dockerfile ENTRYPOINT then this list of arguments is passed as arguments to the entrypoint process.
Which one is "right" depends on how the image is constructed:
Some images use CMD to specify the main command to run and don't have ENTRYPOINT; in this case any of the forms you show will work.
CMD the-main-program
# override with either Kubernetes command: or args:
Some images use ENTRYPOINT to specify the main command to run (and depending on how they're constructed may completely ignore CMD). In this case you must use one of the first two forms with command:.
ENTRYPOINT ["the-main-command"]
CMD ["--argument", "value"]
# override with Kubernetes command: and optionally args:
Some images use CMD to specify the main container command, and ENTRYPOINT as a wrapper script to do first-time setup. In this case you need the third form that sets only args: or else the entrypoint script will get skipped.
ENTRYPOINT ["docker-entrypoint.sh"]
CMD ["the-main-command"]
# override with Kubernetes args: only
Related
Question
Is there a way to define a variable within a docker-compose's config yaml file?
My usecase FYI
Using "extends" (docker.com) I'm thinking to use a base yaml and configure it from many child yaml files by passing a variable.
One limitation is that I cannot make the operational commands complicated at all (intended users are not developers) so I want the command to be a single-line, and cannot be too long. So defining variable in the operation command (e.g. docker-compose -f ... up is fine, but adding VAR=foo at the beginning of it is too long and complicated for me, particularly I might want to define multiple variables).
What I've tried but didn't work
So far I thought of using docker-compose's environment variable features (note that I don't care if the variable I want to set is environment variable or something else. I just gave a try as the features are already there).
NG: Using "environment"
version: '3.3'
services:
service_a:
image: ubuntu:focal
environment:
ENV_A: "env_a"
command: >
/bin/bash -c "echo In docker container: $ENV_A"
Then have env_a, the content of ENV_A, printed. But it didn't get printed:
$ docker-compose -f a.yaml up
WARNING: The ENV_A variable is not set. Defaulting to a blank string.
:
service_a_1 | In docker container:
tmp_service_a_1 exited with code 0
NG: Using "env_file"
$ cat a.env
ENV_A=env_a
$ cat b.yaml
version: '3.3'
services:
service_b:
image: ubuntu:focal
env_file: ./a.env
command: >
/bin/bash -c "echo In docker container: $ENV_A"
Result: NG.
$ docker-compose -f b.yaml up
WARNING: The ENV_A variable is not set. Defaulting to a blank string.
:
service_b_1 | In docker container:
Operation environment
$ docker-compose --version
docker-compose version 1.29.2, build 5becea4c
$ lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description: Ubuntu 18.04.6 LTS
:
First of all, extends is not supported on compose 3.x. This is at the top of the link you sent. So you may not want to rely on that.
As for environment variables, only variables set in the .env file are available to compose. Variables set in env_file or environment settings within the compose file will be passed into the container, but will NOT be available for use in compose itself.
You're asking compose to interpolate $ENV_A, but you haven't set it on the command line (like you described at the top, VAR=foo docker...) or in the .env, so it can't find a value.
More in this thread on GitHub.
Do you think there is a way to create custom health check in kubernetes?
For example, using http get but if the content contains some string, then it's counted as failure.
You may use exec probe to create whichever logic you want. If your image contains curl, and your applications listens on 8080 port, you may insert something like
livenessProbe:
exec:
command:
- bash
- -c
- exit "$(curl localhost:8080 | grep -c 'BAD_STRING')"
grep will return 0 if no "bad" strings are found, thus check will pass. Anything non-zero will result in probe failure.
You can use whichever script you find necessary, maybe you can put a healthcheck script inside your container and call it in exec section.
Relevant doc:
kubectl explain deployment.spec.template.spec.containers.readinessProbe.exec
KIND: Deployment
VERSION: apps/v1
RESOURCE: exec <Object>
DESCRIPTION:
One and only one of the following should be specified. Exec specifies the
action to take.
ExecAction describes a "run in container" action.
FIELDS:
command <[]string>
Command is the command line to execute inside the container, the working
directory for the command is root ('/') in the container's filesystem. The
command is simply exec'd, it is not run inside a shell, so traditional
shell instructions ('|', etc) won't work. To use a shell, you need to
explicitly call out to that shell. Exit status of 0 is treated as
live/healthy and non-zero is unhealthy.
I am bit confused with commands in kubectl. I am not sure when I can use the commands directly like
command: ["command"] or -- some_command
vs
command: [/bin/sh, -c, "command"] or -- /bin/sh -c some_command
I am bit confused with commands in kubectl. I am not sure when I can use the commands directly
Thankfully the distinction is easy(?): every command: is fed into the exec system call (or its golang equivalent); so if your container contains a binary that the kernel can successfully execute, you are welcome to use it in command:; if it is a shell built-in, shell alias, or otherwise requires sh (or python or whatever) to execute, then you must be explicit to the container runtime about that distinction
If it helps any, the command: syntax of kubernetes container:s are the equivalent of ENTRYPOINT ["",""] line of Dockerfile, not CMD ["", ""] and for sure not ENTRYPOINT echo this is fed to /bin/sh for you.
At a low level, every (Unix/Linux) command is invoked as a series of "words". If you type a command into your shell, the shell does some preprocessing and then creates the "words" and runs the command. In Kubernetes command: (and args:) there isn't a shell involved, unless you explicitly supply one.
I would default to using the list form unless you specifically need shell features.
command: # overrides Docker ENTRYPOINT
- the_command
- --an-argument
- --another
- value
If you use list form, you must explicitly list out each word. You may use either YAML block list syntax as above or flow list syntax [command, arg1, arg2]. If there are embedded spaces in a single item [command, --option value] then those spaces are included in a single command-line option as if you quoted it, which frequently confuses programs.
You can explicitly invoke a shell if you need to:
command:
- sh
- -c
- the_command --an-argument --another value
This command is in exactly three words, sh, the option -c, and the shell command. The shell will process this command in the usual way and execute it.
You need the shell form only if you're doing something more complicated than running a simple command with fixed arguments. Running multiple sequential commands c1 && c2 or environment variable expansion c1 "$OPTION" are probably the most common ones, but any standard Bourne shell syntax would be acceptable here (redirects, pipelines, ...).
I need to run a [Windows] Docker container as an executable, running a rather involved PowerShell script (invoking Java and .NET applications) and exiting. Docker documentation suggests using ENTRYPOINT for this purpose. So I went ahead and created a Dockerfile with the following contents:
FROM microsoft/dotnet-framework
COPY run.ps1 /
ENTRYPOINT [ "powershell.exe", "C:\\run.ps1" ]
The contents of run.ps1 (uber-simplified for this question):
gci
write-host "looks like everything is good!"
Then, I ran the following commands:
# Build the Docker image
docker build --rm -t testdockerps .
# Create/run container using above image
docker run -it testdockerps
The container ran successfully, displaying the contents of C:\ followed by the message - looks like everything is good!.
I have a couple of questions based on what my observations:
What is the default shell for a Windows based Docker container? Is there any way to set it to PowerShell, so I don't have to specify "powershell" as the first element of the ENTRYPOINT JSON array? Should I be using the SHELL command in the Dockerfile?
Creating and running the container takes about 3-4 seconds which is somewhat understandable, but after the PS1 script completes, it takes nearly a questionable 10 seconds for the container to exit and return to the command prompt. What may be the cause of this delay?
Yes you can specify powershell as default shell like below on top of DOCKERFILE
SHELL ["powershell", "-Command", "$ErrorActionPreference = 'Stop'; $ProgressPreference = 'Continue'; $verbosePreference='Continue';"]
I'm not sure you can do anything about the time it takes to spin down your VM
I am having troubles in passing environment variables to my custom image via the compose command option:
My compose file:
---
version: '2'
services:
myservice:
image: mycustomimage_lms
environment:
CONF_HOME: /opt/apps-java/
APP_ENV: dev
UUID: me1
command: -Dconfig.home=${CONF_HOME} -Dcomponent.name=LMS -Denv=${APP_ENV} -Duser.dir=/tmp/ -DLMS_UUID=${UUID} -jar /opt/apps-java/my.jar
ports:
- "9060"
volumes:
- ./:/opt/apps-java/
- ./:/var/logs/apps-logs/
- ./:/tmp/data
My image is just a custom jre image which has an entrypoint set to a shell script that accepts jvm arguments. My run.sh that is called from enrtypoint
#!/bin/sh
export JAVA_HOME="/usr/java/latest/"
exec $JAVA_HOME/bin/java $#
I need to pass values to command at runtime since I can then use my image for a lot of other jars and just changing parameters to my image.
This is what i get:
$> docker-compose up
WARNING: The CONF_HOME variable is not set. Defaulting to a blank string.
WARNING: The APP_ENV variable is not set. Defaulting to a blank string.
WARNING: The UUID variable is not set. Defaulting to a blank string.
I have also
gone through couple of answers such as :
Docker Compose - Command using Container Environment Variable
and
Docker-compose environment variables
but could not get it working. Any directions please?
The variables are being read by Compose when the file is parsed. But setting environment only provides values to the container, not to the file parsing.
If you're trying to pass those variables into the container, you need to escape them in the command using an extra $
-Dconfig.home=$${CONF_HOME} -Dcomponent.name=LMS -Denv=$${APP_ENV} -Duser.dir=/tmp/ -DLMS_UUID=$${UUID
If you're just trying to use variables in the Compose file, you need to put those variables into an .env file.
See https://docs.docker.com/compose/compose-file/compose-file-v3/#variable-substitution for the full documentation
Escaping the $ so the variable is not substituted immediately but later in the container environment is the trick, as the accepted answer does in the the docker-compose.yml.
I just wanted to add that in case you pass a command to the docker-compose call, you need to escape the character in the shell, i.e. with \
docker-compose run --rm myservice "-Dconfig.home=\${CONF_HOME} -Dcomponent.name=LMS -Denv=\${APP_ENV} -Duser.dir=/tmp/ -DLMS_UUID=\${UUID} -jar /opt/apps-java/my.jar"