From Docker to Kubernetes: Starting apps in containers
CMD, ENTRYPOINT, and ARGS
Containers have become the de facto compute units of modern cloud-native applications, and at the end of the day,…containers are just a fancy way to run a process.
The first step is to containerize your application. Let’s use a very simple app more exactly cowsay, which is a program that generates ASCII art pictures of a cow with a message. 🐮
Docker
First Dockerfile, using plain CMD
instruction, we’re going to build the image docker build -t first <build context with the location of the Dockerfile
:
FROM ubuntu:14.04
# install cowsay: "cowsay" default installs to /usr/games
RUN apt-get update && apt-get install -y cowsay --no-install-recommends \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*
ENV PATH $PATH:/usr/games
CMD ["cowsay", "Hello World!"]
Using the exec form of CMD
instruction we can provide a default execution for our app when the container is being started.
When starting the container using doker run
without any arguments, the default behavior (from Dockerfile) is the desired one, but when passing arguments e.g. Test cowsay
the container runtime (runc in this case) tries to start Test
as an executable in the image, but as one can expect this is not possible.
This behavior can be corrected in the Dockerfile using ENTRYPOINT
:
FROM ubuntu:14.04
# install cowsay: "cowsay" default installs to /usr/games
RUN apt-get update && apt-get install -y cowsay --no-install-recommends \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*
ENV PATH $PATH:/usr/games
ENTRYPOINT ["cowsay"]
CMD ["Hello World!"]
Building the image, and starting the container once more, we can see that the behaviour of the container is the desired one and when args are being used, they are actually passed to the cowsay
bin.
Basically using ENTRYPOINT
we set cowsay
bin to run as an executable and using CMD
we set default arguments that can be overridden when starting the container.
Kubernetes
Next, we are going to start the app in Kubernetes using kubectl in an imperative fashion.
We started a pod (as a job) and once more overridden the default arguments of cowsay
app using kubect run
:
kubectl run cowsay --image=dejanualex/dockersay:1.0 --restart=Never -- Test me
# check the args of the po
kubectl get po cowsay -ojsonpath="{.spec.containers[0].args}"
What happens if we don’t pass any arguments when running the pod? And the answer is that we’re going to have the default behavior (embedded in our Dockerfile).
Last but not least if we would like to simply start the pod in which our bin cowsay
will run without any arguments, we’re going to leverage the command
flag:
kubectl run cowsay --image=dejanualex/dockersay:1.0 --restart=Never --command -- cowsay
# check args and command
kubectl get po cowsay -ojsonpath="{.spec.containers[0].args}"
kubectl get po cowsay -ojsonpath="{.spec.containers[0].command}"
It is rather straightforward to control how the app starts in the container using CMD, ENTRYPOINT, and ARGS.
# start container
docker run [OPTIONS] IMAGE [COMMAND] [ARG...]
# start pod
kubectl run NAME --image=image ... [--command] -- [COMMAND] [args...]
In terms of usage, one can say that:
ENTRYPOINT
Dockerfile instruction corresponds toCOMMAND
passed tokubectl run
CMD
Dockerfile instruction corresponds toARG
passed tokubectl run
Last but not least, some resources:
- Dockerhub repo dejanualex/dockersay, more about containers: The “dirty” secrets of containers. and hands-on lab 👉 here