Production engineering workshop · 45 MIN
DevOps / Cloud · Drain a service during deployment
Readiness, liveness and graceful shutdown solve different problems.
Readiness decides whether a service should receive new traffic. Liveness indicates whether restarting may help. On termination, fail readiness, stop accepting requests and allow a bounded drain before exit. Configure the platform’s grace period to exceed your drain deadline. Keep dependencies and secrets outside the image; log request IDs and latency without logging passwords or tokens.
First reproduce the normal path. Then force a failure at each boundary and inspect what remains true.
Read the example
import http from "node:http";
let draining=false;
const server=http.createServer((request,response) => {
if (request.url==="/live") { response.end("alive"); return; }
if (request.url==="/ready") { response.statusCode=draining?503:200; response.end(draining?"draining":"ready"); return; }
if (draining) { response.statusCode=503; response.end("retry later"); return; }
response.end("CodingNeed workshop");
});
server.listen(Number(process.env.PORT??3000));
function shutdown() {
if (draining) return;
draining=true;
const deadline=setTimeout(()=>process.exit(1),10000);
deadline.unref();
server.close(error=>{ clearTimeout(deadline); process.exitCode=error?1:0; });
}
process.on("SIGTERM",shutdown);
process.on("SIGINT",shutdown);
// Production: close DB/queue connections after requests drain.
// Never include database passwords in an image or build argument.Check the expected output
Review the behavior in the stated project environment.
Your challenge
Run this Node service locally and in a non-root container. Send a request and terminate the process while it is active. Define readiness and liveness probes in your orchestrator and test deployment rollback with an intentionally failing readiness probe.
Solution cost: O(1) probe handling; drain bounded by a configured deadline. time · Active connections and per-request state; set admission and resource limits. space
Common trap
Using dependency health as liveness can restart every instance during a database outage, worsening recovery.
Study the project implementation
import http from "node:http";
let draining=false;
const server=http.createServer((request,response) => {
if (request.url==="/live") { response.end("alive"); return; }
if (request.url==="/ready") { response.statusCode=draining?503:200; response.end(draining?"draining":"ready"); return; }
if (draining) { response.statusCode=503; response.end("retry later"); return; }
response.end("CodingNeed workshop");
});
server.listen(Number(process.env.PORT??3000));
function shutdown() {
if (draining) return;
draining=true;
const deadline=setTimeout(()=>process.exit(1),10000);
deadline.unref();
server.close(error=>{ clearTimeout(deadline); process.exitCode=error?1:0; });
}
process.on("SIGTERM",shutdown);
process.on("SIGINT",shutdown);
// Production: close DB/queue connections after requests drain.
// Never include database passwords in an image or build argument.Further reading: Official documentation