Disclaimer: here you might not find something new if you know 12 factors app.
The 12-factor app methodology 12factor.net, is a set of principles designed to enable applications to be built with portability and resilience when deployed to the web. These principles focus on declarative formats for automation, clean contracts with the operating system, and suitability for deployment on modern cloud platforms, thus minimizing divergence between development and production, enabling continuous deployment for maximum agility.
Here’s how each factor is applied and the benefits they bring, drawn from firsthand experiences in the field:
1. Codebase
Use Git repositories to store your application code. Different branches can align with different deployments through CI/CD pipelines.
2. Dependencies
Explicitly declare and isolate dependencies: Managing dependencies via a declaration document (package.json) and ensuring no implicit reliance on system-wide packages ensures that the app runs consistently across all environments.
In k8s, containerization usage can encapsulate environments perfectly to maintain this isolation.
3. Config
Store config in the environment: Configuration variables (like database URLs and external service credentials) should be stored in the environment to keep configuration dynamic and secure. This separation of config from code helps in avoiding security risks and making the app environment agnostic, simplifying deployment across different environments.
In k8s, utilize ConfigMaps, Secrets, mutation webhook to store and manage environmental configurations separately from the application code. This keeps sensitive information out of your codebase. Your sensitive data should be preferrably injected into memory or mounted against added as env variable.
4. Backing services
Treat backing services as attached resources: Services like databases, queueing systems, and caching systems should be treated as attachable resources, which can be replaced or attached without code changes. This makes scaling and migrating to different services easier without major codebase changes.
5. Build, release, run
CI/CD pipelines should handle the build and deployment stages separately, ensuring that the build stage produces an immutable artifact that moves to the release and run stages.
6. Processes
Execute the app as one or more stateless processes: State should be externalized (e.g., using a database or caching layer), allowing the application to scale horizontally without side-effects. Stateless applications are easier to manage and scale, as each process can be started or stopped independently.
This also aligns with Unix philosophy and CNCF principle where systems must have clear separation between their processes.
CNCF principal:
Cloud native systems have a clear separation between their processes [2]. They utilize the Unix philosophy of doing one thing and doing it well. These microservices usually use technologies such as containers and aim for one process per container [3]. As such, cloud native applications should have all of their dependencies packaged in the container during the build phase and leveraged during deployment [4].
In k8s world we don’t store pod state internally. Use external data stores like database or cache to manage state.
7. Port binding
Containers within pods expose ports which are then mapped to services that abstract these details away, allowing external traffic to reach the containers.
8. Concurrency
Use k8s HPA to manage the scaling of applications based on CPU usage or other metrics, effectively handling concurrency.
9. Disposability
If your application doesn’t gracefully shut down when receiving a SIGTERM you can use kubernetes lifecycle preStop hook to trigger a graceful shutdown. You can set terminationGracePeriodSeconds
to change graceful shutdown time to ensure SIGKILL isn’t sent forcibly.
10. Dev/prod parity
Maintain similar configurations across environments using Kubernetes manifests or Helm charts values, minimizing drift between environments.
11. Logs
Implement logging at the application level, exporting logs to stdout and preferrably in JSON format. Kubernetes then aggregates these logs, which can be collected and analyzed by logs collectors like Logstash, Fluentd, etc.
12. Admin processes
Use k8s Job for one-off tasks like database migrations or batch jobs. This ensures that these tasks run in an environment identical to the regular application environment.
Which factors do you think are missing in 12 factors but present in k8s? ;)
In addition to the traditional 12 factors, there are more principles considered crucial for modern production environments, especially in cloud platforms like Kubernetes.
13.Observability
Ensures applications provide insights into their internal states and metrics, crucial for managing distributed systems effectively. Traces, metrics, profiling are very useful and loved by developers.
14. Schedulability
Focuses on predictable resource demands, enabling better scheduling and stability across distributed services. K8s requests, limits are your good friends, specially if you are on lower versions of k8s.
15. Upgradability
Pertains to easy and seamless updates and data format evolution without downtime, maintaining forward compatibility. Here we should mention such upgrades like rolling, blue-green, canary, progressive delivery and more.
16. Least privilege
Involves running processes with the minimal set of permissions necessary, enhancing security
17. Auditability
Allows tracking of who did what and when, which is essential for security and compliance
18. Security
Concerns the hardening of applications against unauthorized access and attacks. Usage of tools like OPA, Kyverno, Falso is a good start.
19. Measurability
Involves quantifying resource usage and application performance, important for cost management and scaling decisions. Tools like Anodot or kubecost are very cool for understanding your bills.
Other useful resources:
- SRE Principles
- SRE lessons learned
- CAP theorem
- AWS Well-architechted framework
- CNCF principles