Kubernetes has become the de facto container orchestration platform for running and scaling application workloads. When deploying the application on Kubernetes, some specific commands might need to run in the container to ensure that it is running properly or has the desired configurations. You might even need to run some commands within a sidecar container to generate and store log data.
This blog will examine how commands and arguments can be used when creating Kubernetes pods with some hands-on examples. Adding commands to Docker containers can be done using CMD
and Entrypoint
while creating the Dockerfile. If you are unfamiliar with how this works in Docker, please refer to this blog, as understanding commands in Kubernetes is a prerequisite.
What are commands & Arguments in Kubernetes?
Before we move onto the actual differences between commands and arguments, let’s first try and understand what exactly are they and why do we require them. Both parameters are used to pass certain parameters to the container running within the pod. If you are familiar with commands and arguments within bash, or any shell environment this will be easy to understand.
- Command: A piece of executable code that is run within the container shell
- Argument: Additional inputs for the command to execute a certain behaviour
Let’s try and understand this with an example. Consider the below bash command.
echo “Hello world”
You may be familiar with the above command. It simply prints out the string Hello World
. In the above example, echo
is the actual command, and "Hello World"
is the argument for the command. This is a similar functioning within Kubernetes. If we try to relate the Kubernetes way of defining commands and arguments with how it is defined in Docker, it can be thought of in the following way.
Kubernetes | Docker |
---|---|
Command | Entrypoint |
Args | CMD |
You might be wondering, if commands are already defined while creating the Docker containers, wouldn’t it conflict if you try to define them within Kubernetes? The answer to this is no. When you use the command & arguments fields in the pod’s definition file, it will override the commands that were used when building the container.
Similarly, if you do not pass in any values in the pod’s definition file, then by default, the commands that were defined within the container will be used. However, when you create the Docker container, you can change the values of CMD
and Entrypoint
by passing in appropriate flags in the docker run command. This is not possible within Kubernetes.
Once the pod is created and is running, the commands cannot be edited. If you need to edit the commands, you must also create the pod.
Kubernetes Commands
As we have already discussed earlier, a command is the executed piece of code that we run within the container. Not to confuse it with the application code, commands are separate system binaries that exist within the container. Most of the commands that we will use in the hands-on section later in the blog, will be commands that are built-in to the container.
If you run a minimal custom container, you will first need to ensure that the package exists within the container. This is similar to running commands in your local system. For example, you will not be able to run kubectl if you have not installed the correct package. This same behaviour is true for the container as well.
Let’s create a simple nginx pod, and add a command to it for printing "Hello World"
. You can use the below manifest to do so. Please ensure you have a cluster set up to try out the following commands. You can use kind or minikube or an online Kubernetes playground.
The following YAML will create a pod named nginx-hello
and run the echo “Hello World”
command in the pod.
apiVersion: v1
kind: Pod
metadata:
name: nginx-hello
spec:
containers:
- image: nginx
name: nginx
command: ["echo","Hello World"]
If you want to check if this command has worked or not, you can check the logs of this pod.
In the above YAML file, the command was defined by using the exec mode. You also have the option to define the commands as an array within YAML. In the below YAML, the same pod is being created, with the same command. The only difference is, that this is defining the commands within an array.
apiVersion: v1
kind: Pod
metadata:
name: nginx-hello
spec:
containers:
- image: nginx
name: pod
command:
- echo
- Hello
- World
If you want to quickly create a pod using imperative commands, you can pass a command within the kubectl command itself. You can do so using the below command
kubectl run pod –image=nginx –command – echo “hello world”
In the above example, echo hello world
will be interpreted as a command and will appear as an array in the created YAML.
It’s important to note that if you do not specify --command
, it will be read as arguments. If you are unsure about how the command is getting interpreted, you can generate a sample YAML file without creating the actual K8s resource. To do so, you can use the --dry-run=client
flag to ensure that the resource does not get created, and the -o yaml
flag to get the YAML output.
Kubernetes Arguments
Similar to how it works in any shell, arguments in Kubernetes are the additional inputs that come after the command. The arguments are useful for providing instructions on the exact behavior that should occur when the container is created and the commands are run.
Similar to the commands, arguments cannot be changed once the pod is running, and if you wish to change it, you would need to recreate the pod. Before discussing how the arguments are interpreted, let’s create a busybox pod and pass in an argument. We will not be passing in any commands here.
You have two ways to create this pod, either imperatively, or declaratively by writing the manifest file. Let’s look at both methods. First, we will create a pod declarively using the below manifest file
apiVersion: v1
kind: Pod
metadata:
name: busybox-sleeper
spec:
containers:
- args:
- sleep
- "5000"
image: busybox
name: busybox-sleeper
The above YAML file creates a pod with the busybox container, and put’s it to sleep. The default behavior of the busybox container is to exit out as soon as it’s process is completed. We can infer that the sleep command is running, as the pod is in the running state.
Note: In the below images, I have used an aliased kubectl to k. If you wish to set the name, you can run the below command
echo “alias k=kubectl” >> ~/.bashrc
source ~/.bashrc
To create the same pod imperatively, you can use the below command.
kubectl run busybox-sleeper --image=busybox -- sleep 5000
If you want to see the YAML that is getting generated, you can dry-run the command and get the YAML output, similar to what we did to see the command.
In the above two examples, only the arguments field has been used without the command. Yet the behavior is the same as if we use the command field.
This is because of how the shell behaves. The shell ignores any whitespaces before the actual command itself. This same behavior is being followed by the container as well. Hence, even if the command field is omitted and only args are passed, the proper command will still execute.
Combining Kubernetes Commands and Arguments
While we have seen above that commands and arguments can be used interchangeably, the best practice is to use both commands and arguments. If you want to just run a single command, it is recommended to use the command field, and it’s arguments should be passed in the args field.
Here is an example where we use both the command and the args field.
apiVersion: v1
kind: Pod
metadata:
name: busybox-combine
spec:
containers:
- name: busybox
image: busybox
command: ["echo"]
args: ["hello", "world"]
Let’s say that you want to run multiple different commands. You can do so by using operators such as && or separating the two commands by using a semicolon (;). You can pass in the different commands in the args field. However, when passing multiple commands, the best practice is to use the shell’s executable file within the command field.
In the below yaml file, a while loop is being run that print’s hello every 10 seconds. We write the script within the args field, and in the command field we execute the shell within which the commands should run. You can see the YAML of the entire file below
apiVersion: v1
kind: Pod
metadata:
name: busybox-multi-cmd
spec:
containers:
- name: busybox
image: busybox
command: ["/bin/sh"]
args: ["-c", "while true; do echo hello; sleep 10;done"]
We can see that this script is being executed and the command is being run every 10 seconds.
Use Cases for commands & args
There are multiple different reasons why you would want to use commands & arguments within your pods. When you build the Docker container, it will have the default commands that should be run, but there might be some cases where you might want to update the commands that have to run. In such cases, it becomes important to be able to override the default container commands.
Some common use cases for commands & args include:
- Running container startup scripts
- Running a script within an Init container
- Run a sidecar container that generates logs
- Scripts that need to run for jobs/cronjobs
- Populating databases
Additionally, you might want to test the network, whether a pod is working properly, or if the network policies are behaving as expected. In such cases, you need to run network-related commands from within a pod.
You can create a ephemeral pod imperative, and have it deleted as soon as the command has finished executing. As an example, let’s say that you want to run a nslookup command from within the busybox container to check if the pod has the DNS address of GitHub. You can use the following command to run the curl command from the busybox pod, and delete the pod once execution is complete
kubectl run network-test –image=busybox –rm -it –restart=Never – nslookup github.com
Using Commands & Arguments in Devtron
Devtron is an open-source Kubernetes dashboard aimed at simplifying the application lifecycle management. It aims to abstract the complexities of managing Kubernetes and avoid many of its different struggles and challenges. Whenever deploying applications, Devtron provides a base deployment template which makes it a lot easier to configure applications, since you need not write the manifests from scratch.
Within the Deployment template, you can very easily write the required commands and arguments. You can search for the commands and arguments, enable them and enter the commands that you wish to run within the container.
Devtron also provides a lot of different functionalities that simplify the deployment and lifecycle of applications deployed on Kubernetes. It can help you easily roll back deployments, perform security scans, add policies based on them, group various helm charts together, deploy them easily while gaining visibility into all the workloads, and a lot more. If you’re looking for a tool to ease the pains of application management, do check out Devtron.
Conclusion
Defining commands to execute within the container is crucial for running the containerized workloads without any hassle. While we do define these commands within the Dockerfile while building the container, the commands may need to be updated for certain use cases. The commands can be overridden using the appropriate flags when running the containers using docker run.
When it comes time to deploy those containers within the Kubernetes pods, we no longer have access to docker run in case the container commands have to be updated. To account for the need to change the container commands, Kubernetes has the commands
and arguments
field which allows overriding the default container commands.
If you have any questions regarding how to use these, please join our Discord Community and we would be happy to clear any doubts you might have.