如何在Docker容器停止/关闭前执行自定义清理命令?
I’ve run into this exact union filesystem residue issue before, so let’s break down why your previous attempts didn’t work and fix this properly.
Why Your Existing Approaches Failed
- Appending commands to entrypoint: When you chain commands like
entrypoint; cleanup, the cleanup only runs if the entrypoint exits normally. But Docker sendsSIGTERM(default) to the PID 1 process to stop the container—if PID 1 is your main app (not the shell), the shell gets killed immediately before it can execute the cleanup. - Trap with
STOPSIGNAL SIGINT: Chances are your main process wasn’t being waited on by the shell, or you usedexecto launch the app (which replaces the shell process, losing all trap handlers). Docker defaults to sendingSIGTERMfirst, so settingSTOPSIGNAL SIGINTmeant you missed the initial termination signal.
Working Solutions
1. Proper Shell Entrypoint with Trap & Wait
This is the most reliable method. Create a shell script as your entrypoint that handles signal trapping, runs your main process, and triggers cleanup on any termination signal.
Step 1: Create the Entrypoint Script (/entrypoint.sh)
#!/bin/bash # Define your cleanup logic cleanup() { echo "Cleaning up union filesystem mount..." if fusermount -uz /mount/point; then echo "Successfully unmounted /mount/point" else echo "Warning: Failed to unmount /mount/point" >&2 fi exit 0 } # Trap all common termination signals trap cleanup SIGINT SIGTERM SIGQUIT SIGABRT # Launch your main application (run in foreground so the shell stays active) echo "Starting main application..." your_main_app_command_here
Step 2: Update Your Dockerfile
# Copy the entrypoint script into the container COPY entrypoint.sh /entrypoint.sh # Make it executable RUN chmod +x /entrypoint.sh # Set the entrypoint and default command (adjust CMD if your app needs arguments) ENTRYPOINT ["/entrypoint.sh"] CMD ["your_main_app_command_here"]
Key Notes:
- Don’t use
execfor your main app:exec your_main_appreplaces the shell process with the app, which discards all trap handlers. Let the shell run the app as a child process. - Stick with default
STOPSIGNAL: Docker sendsSIGTERMfirst (with a 10-second grace period), thenSIGKILLif the process doesn’t exit. Our trap handlesSIGTERM, so cleanup will trigger before the container shuts down. - Extend grace period if needed: If cleanup takes longer than 10 seconds, add
stop_grace_period: 30sto yourdocker-compose.ymlor usedocker stop --time 30 <container-name>.
2. Use dumb-init for Robust Signal Forwarding
If your main app ignores signals or you want a more reliable PID 1 process, use dumb-init—a lightweight init system designed for containers that properly forwards signals to child processes.
Step 1: Install dumb-init in Your Dockerfile
# For Debian/Ubuntu-based images RUN apt-get update && apt-get install -y --no-install-recommends dumb-init # For Alpine-based images RUN apk add --no-cache dumb-init
Step 2: Update Entrypoint to Use dumb-init
Modify your Dockerfile to set dumb-init as the entrypoint, which will launch your shell script:
ENTRYPOINT ["dumb-init", "--", "/entrypoint.sh"] CMD ["your_main_app_command_here"]
This ensures signals are correctly passed down to the shell, which will trigger your cleanup trap even if your main app doesn’t handle signals well.
3. Test the Setup
To verify cleanup runs as expected:
- Start the container
- Stop it with
docker stop <container-name> - Check logs with
docker logs <container-name>—you should see your cleanup messages.
Final Checks
- Ensure
/mount/pointexists in the container and is mounted before cleanup runs. - If your cleanup command requires elevated privileges, run the container as root or a user with mount permissions (avoid
sudounless absolutely necessary).
内容的提问来源于stack exchange,提问作者OJFord

