Dockerfile中CMD两种写法的差异及后台运行start.sh的方法咨询
Nice question! Let's tackle both parts clearly and practically.
CMD ["./start.sh"] and CMD ./start.sh in Dockerfile These two syntaxes might look similar, but they behave very differently under the hood:
Execution Mode
CMD ["./start.sh"]uses the exec form: Docker runs the script directly via theexecsystem call, no shell is spawned. This makesstart.shthe container's PID 1 process.CMD ./start.shuses the shell form: Docker wraps the command in/bin/sh -c "./start.sh", so the shell process becomes PID 1, andstart.shruns as its child.
Environment Variable Access
- Exec form doesn't load shell-specific environment variables (like those in
~/.bashrcor/etc/profile) unless yourstart.shexplicitly sources them. - Shell form automatically loads the default shell environment, so any global shell variables will be available to your script.
- Exec form doesn't load shell-specific environment variables (like those in
Signal Handling
- In exec form, since
start.shis PID 1, it directly receives signals likeSIGTERM(sent when you rundocker stop). This lets you implement graceful shutdown logic in your script easily. - In shell form, the shell (PID 1) doesn't forward signals to child processes by default. If you stop the container,
start.shmight get killed abruptly without cleaning up. You'd need to add custom signal forwarding logic to fix this.
- In exec form, since
start.sh (and Its API) in the Background in a Docker Container A quick heads-up: Docker containers stop when their PID 1 process exits. So if you just run start.sh in the background without a foreground process keeping the container alive, it'll shut down immediately. Here are reliable ways to handle this:
1. Wrap the Background API with wait in start.sh
Modify your script to launch the API in the background, then wait for it to finish. This keeps the script (and container) running:
#!/bin/bash # Start your API server in the background ./your-api-binary & # Capture the API's process ID API_PID=$! # Wait for the API to exit (keeps the script in foreground) wait $!
Now when you use CMD ["./start.sh"], the container stays alive as long as the API runs. If the API crashes, the script exits too—this is ideal for monitoring, as you'll know immediately if the service fails.
2. Use a Process Supervisor like supervisord
If you need to manage multiple background processes, supervisord acts as a foreground process that oversees your start.sh and other services.
First, install supervisord in your image:
RUN apt-get update && apt-get install -y supervisor
Create a supervisord.conf file to define your process:
[supervisord] nodaemon=true # Run in foreground to keep the container alive [program:start-script] command=/path/to/start.sh autostart=true autorestart=true stdout_logfile=/var/log/start.log stderr_logfile=/var/log/start.err.log
Copy the config into the image and set CMD to run supervisord:
COPY supervisord.conf /etc/supervisor/conf.d/supervisord.conf CMD ["supervisord", "-n"]
3. Skip the "Quick Fix" of tail -f /dev/null
You might see suggestions like CMD ./start.sh & tail -f /dev/null, but this is an anti-pattern. If your API crashes, the container will keep running because tail is still active—you won't get any alert that the service is down. Stick with the wait or supervisor methods for proper process monitoring.
内容的提问来源于stack exchange,提问作者Rituraj kumar

