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.