Table of Contents
Introduction
In my previous blog, we explored Docker's RUN
, CMD
, and ENTRYPOINT
commands, focusing on their purpose and how they interact to define container behavior. This article dives deeper into the distinction between shell
and exec
forms, emphasizing their impact on signal handling and how improper usage can lead to unexpected issues. Let's explore and learn best practices to avoid pitfalls.
Docker supports two syntaxes for specifying CMD
and ENTRYPOINT
: shell
form and exec
form.
- Written as a simple string.
- Example:
CMD echo "Hello, World!"
- Executes commands via
/bin/sh
by default (or /bin/bash
on some systems).
- Written as a JSON array of strings.
- Example:
CMD ["echo", "Hello, World!"]
- Executes commands directly, bypassing the shell.
Feature | Shell Form | Exec Form |
---|
Syntax | String | JSON Array |
Command Execution | Through Shell (e.g., /bin/sh ) | Direct Execution |
Signal Handling | Managed by Shell | Direct to Process |
How Shell Form CMD Affects ENTRYPOINT
When combining CMD
and ENTRYPOINT
, their interplay depends on the form used:
Example: Using Shell Form CMD with ENTRYPOINT
ENTRYPOINT ["echo"]
CMD "Hello, World!"
In this example:
ENTRYPOINT
specifies echo
as the primary process.
CMD
should provide additional arguments to echo
.
Behavior:
docker run my-image
should run echo "Hello, World!"
.
- However, because
CMD
is in shell form, it is interpreted as /bin/sh -c "Hello, World!"
. This string cannot be passed directly to ENTRYPOINT
as intended, leading to unexpected behavior.
If you want to understand the behavior in more detail, you can visit the official Docker documentation.
Signal handling is critical for cleanly stopping or restarting Docker containers. For example, running docker kill --signal INT
should terminate the main process.
The Problem
When using shell form:
- Docker wraps the command in a shell process (e.g.,
/bin/sh -c
).
- Signals (like
INT
, TERM
) are sent to the shell process, not directly to the application.
Python Example
Consider the following Dockerfile
:
FROM python:3.9-slim
ENTRYPOINT ["python"]
CMD "-m http.server"
Running docker kill --signal INT <container>
results in:
- The
INT
signal is sent to /bin/sh
.
/bin/sh
does not forward the signal to the Python process.
- The Python process continues running, even though the container should stop.
Best Practices
Using the exec form ensures:
- Direct execution of the intended process.
- Proper signal handling, as signals are sent directly to the main process.
Combine ENTRYPOINT and CMD Thoughtfully
- Use
ENTRYPOINT
for the main command or application.
- Use
CMD
for default arguments or configurations.
Examples
Correct Usage: Exec Form CMD and ENTRYPOINT
FROM python:3.9-slim
ENTRYPOINT ["python"]
CMD ["-m", "http.server"]
Behavior:
docker run my-image
runs python -m http.server
.
- Signals are sent directly to the Python process.
Conclusion
Understanding the difference between shell and exec forms in Docker can save you from subtle bugs, especially around signal handling. By adhering to best practices and using the exec form, you ensure that your containers behave predictably, even under unexpected conditions.