Published on Feb 19, 2022 in #kubernetes
Perhaps one of the most perplexing parts of starting a Docker container in Kubernetes is the variety of options for specifying the command to be run.
A Dockerfile
can include one or both of CMD
and ENTRYPOINT
instructions:
FROM alpine
CMD ["echo", "hello, world!"]
FROM alpine
ENTRYPOINT ["echo"]
CMD ["hello, world!"]
FROM alpine
ENTRYPOINT ["echo", "hello, world!"]
Similarly, Kubernetes has command
and args
fields:
apiVersion: v1
kind: Pod
metadata:
name: hello-world
spec:
containers:
- name: hello-world
image: alpine
command: ["echo"]
args: ["hello, world!"]
restartPolicy: OnFailure
Frustratingly, the terminology doesn’t quite line up between the two:
command | arguments | |
---|---|---|
Docker | ENTRYPOINT |
CMD |
Kubernetes | command |
args |
NOTE: In the cases where command
or args
isn’t set, Kubernetes will use
the equivalent instruction from the Dockerfile
.
Both ‘command’ and ‘arguments’ are lists. As such, the ‘command’ portion can include both the executable and some arguments.
command | arguments | command run |
---|---|---|
["echo"] |
["hello", "world"] |
echo hello world |
["echo", "hello"] |
["world"] |
echo hello world |
We can leverage these two fields to build Docker images that are easy to run locally, yet are flexible when deployed.
In our Dockerfile
, we’ll use ENTRYPOINT
to define the “base command” and
CMD
to provide a set of default options. When deploying this container,
we can override the default options by specifying args
in our Kubernetes
manifests.
For example, here’s a Dockerfile
file using Python’s
bundled web server:
FROM python:3-alpine
WORKDIR /site
COPY index.html ./
ENTRYPOINT ["python3", "-m", "http.server"]
CMD ["8000"]
We can build and run this locally:
docker build -t my-python-web-server:v1 .
# uses port 8000 from the Dockerfile
docker run -it -p8000:8000 my-python-web-server:v1
# and, we can override CMD to use a different port
docker run -it -p8123:8123 my-python-web-server:v1 8123
When we deploy this to Kubernetes, we can use args
to easily set a port
to listen on:
NOTE: using a static Pod for brevity, usually a Deployment or similar would be used in a production environment.
# pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: my-python-web-server
spec:
containers:
- name: server
image: my-python-web-server:v1
args: ["8888"]
We can apply this and use kubectl
’s port forwarding functionality to access
our web server:
kubectl apply -f pod.yaml
kubectl port-forward pods/my-python-web-server 8888:8888
By using this pattern, we’ve:
It should also be noted, this pattern works good for the above use case. There are many other uses cases for these parameters.