11. System Design & Deployment
Ship small, observable services; deploy safely and make failures visible and recoverable.
Question: What are some key principles of the "12-Factor App" methodology?
Answer: The 12-Factor App is a set of best practices for building modern, scalable web apps. Key principles include keeping configuration in the environment, treating logs as event streams, and running the app as one or more stateless processes.
Explanation:
Config via env vars: Storing configuration (like database URLs) in environment variables, not code, makes the app portable.
Logs to stdout: Apps should write logs to standard output, letting the execution environment (e.g., Docker) collect and route them.
Stateless processes: The application should not store any state locally. State should be offloaded to a backing service like a database or object store. This allows you to easily scale out by adding more copies of the application.
Question: How do you ensure your application shuts down gracefully?
Answer: A graceful shutdown is implemented by catching termination signals (like SIGTERM
) and allowing the application to finish its current work before exiting.
Explanation: In a containerized environment like Kubernetes, when a pod is terminated, it is sent SIGTERM
. The application should catch this signal, stop accepting new requests, finish processing in-flight requests, and clean up resources (like database connections) before exiting. If it doesn't exit within a grace period, it will be sent SIGKILL
.
import signal, asyncio
async def main():
stop = asyncio.Event()
loop = asyncio.get_running_loop()
loop.add_signal_handler(signal.SIGTERM, stop.set)
# Run server until stop event is set
# server = await start_server()
# await stop.wait()
# server.close()
Question: What are readiness and liveness probes, and how do they differ?
Answer: Liveness indicates if the app should be restarted; readiness indicates if it can receive traffic. Fail readiness during startup, migrations, or backpressure.
Explanation: In Kubernetes, expose /healthz
(liveness) and /readyz
(readiness) with fast, dependency-aware checks.
Question: How do you achieve observability beyond logs?
Answer: Emit metrics (counters, histograms) and traces (OpenTelemetry) with consistent labels and sampling.
Explanation: Metrics power SLOs/alerts; traces reveal cross-service latency and bottlenecks.
Question: What are some best practices for writing a Dockerfile for a Python application?
Answer: Best practices focus on creating small, secure, and efficient images. This includes using a minimal base image, leveraging multi-stage builds, running as a non-root user, and optimizing layer caching.
Explanation:
Use a slim base image (e.g.,
python:3.12-slim
) to reduce the image size.Use multi-stage builds to separate build-time dependencies from the final runtime image, making it smaller and more secure.
Copy
requirements.txt
and install dependencies first. This takes advantage of Docker's layer caching, so dependencies are only re-installed when the requirements file changes.Run as a non-root user. This is a critical security practice to reduce the container's privileges.
Set environment variables like
PYTHONDONTWRITEBYTECODE=1
andPYTHONUNBUFFERED=1
for cleaner output and no.pyc
files.
Question: What deployment strategies reduce risk?
Answer: Blue/green and canary releases shift traffic gradually and enable fast rollback.