DevOps & Kubernetes (50 Qs)•Part 1: Candid Experience, Container Internals & Docker (1 - 10)
Why do containers fail to handle SIGTERM/SIGINT when running as PID 1, and what is the Zombie Process Reaping problem?
The PID 1 Problem in Linux Containers
In a standard Linux system, PID 1 (typically systemd or init) has two non-delegable kernel responsibilities:
- Handling Signals: The Linux kernel grants PID 1 special status. If PID 1 does not explicitly install a signal handler for a signal (such as
SIGTERMorSIGINT), the kernel silently ignores the signal. - Reaping Zombie Processes: When a child process terminates, it enters a "Zombie" state until its parent calls
wait()orwaitpid(). If the parent dies first, the child is orphaned and reparented to PID 1. PID 1 must continuously callwait()to clean them up from the process table.
rust
Problem: Node/Python as PID 1
[ Host sends SIGTERM ]
|
v
[ Container PID 1 (node) ] --> Has no SIGTERM handler registered!
Kernel ignores signal -> 30s timeout expires -> SIGKILL!
Result: Abrupt termination, dropped DB connections, data corruption.
Solution: Using an Init Process (tini)
[ Host sends SIGTERM ]
|
v
[ Container PID 1 (tini) ] --> Catches SIGTERM, cleanly forwards it to child (node)
| Calls waitpid() to reap orphaned zombie processes.
v
[ Child PID 2 (node) ] --> Completes in-flight requests and exits gracefully (code 0).
Production Solutions
- Use
tinias Entrypoint in Dockerfile:
dockerfile
# Alpine includes tini package
RUN apk add --no-cache tini
ENTRYPOINT ["/sbin/tini", "--"]
CMD ["node", "dist/server.js"]
- Docker CLI
--initflag:
bash
docker run --init -d my-node-app:latest
- Handle signals explicitly in application runtime:
javascript
process.on('SIGTERM', async () => {
console.log('SIGTERM received. Closing HTTP server and DB pools...');
await server.close();
await dbPool.end();
process.exit(0);
});