12 Factor App Principles and Cloud-Native Microservices
12-factor app is a methodology or set of principles for building the scalable and performant, independent, and most resilient enterprise applications. It establishes the general principles and guidelines for creating robust enterprise applications. 12-factor app principles got very popular as it aligns with Microservice principles.
Below are the 12-factor principles
- Codebase (One codebase tracked in revision control, many deploys)
- Dependencies (Explicitly declare and isolate the dependencies)
- Config (Store configurations in an environment)
- Backing Services (treat backing resources as attached resources)
- Build, release, and Run (Strictly separate build and run stages)
- Processes (execute the app as one or more stateless processes)
- Port Binding (Export services via port binding)
- Concurrency (Scale out via the process model)
- Disposability (maximize the robustness with fast startup and graceful shutdown)
- Dev/prod parity (Keep development, staging, and production as similar as possible)
- Logs (Treat logs as event streams)
- Admin processes (Run admin/management tasks as one-off processes)
Codebase (One codebase tracked in revision control, many deploys)
12-factor app advocates that every application should have its own codebase (repos). Multiple codebases for multiple versions must be avoided. Please do note that having branches would be fine. I.e. For all the deployment environments there should be only one repo but not multiple.
Multiple apps sharing the same code are a violation of the twelve-factor. Here you should opt-in for shared libraries.
From the 12-factor app perspective app, deploy meaning the running instance of an app like production, staging, QA, etc. Additionally, every developer has a copy of the app running in their local development environment, each of which also qualifies as a deploy.
Different versions (the version is like a code change that is available in one environment but not in other) may be active in multiple deploys.
Microservices: In Microservices, every service should have its own codebase. Having an independent codebase helps you to easy CI/CD process for your applications.
Twelve-factor app advocates of not sharing the code between the application. If you need to share you need to build a library and make it as a dependency and manage through package repository like maven.
Dependencies (Explicitly declare and isolate the dependencies)
It talks about managing the dependencies externally using dependency management tools instead of adding them to your codebase.
From the perspective of the java, you can think of Gradle as a dependency manager. You will mention all the dependencies in build.gradle file and your application will download all the mentioned dependencies from maven repository or various other repositories.
You also need to consider the dependencies from the operating system/ execution environment perspective as well.
Microservices: All the application packages will be managed through package managers like sbt, maven.
- In non-containerized environments, you can go for configuration management tools like chef, ansible, etc. to install system-level dependencies.
- For a containerized environment, you can go for dockerfile.
Config (Store configurations in an environment)
Anything that varies between the deployment environments is considered as configuration. This includes:
- Database connections and credentials, system integration endpoints
- Credentials to external services such as Amazon S3 or Twitter or any other external apps
- Application-specific information like IP Addresses, ports, and hostnames, etc.
You should not hardcode any configuration values as constants in the codebase. This is a direct violation of 12-factor app principles.
12-factor app principles suggest saving the configuration values in the environment variables.
It advocates the strict separation between the code and configurations. The code must be the same irrespective of where the application being deployed.
As per “config”, what varies for the environment to the environment must be moved to configurations and managed via environment variables.
Microservices: Externalize the configurations from the application. In a microservice service environment, you can manage the configurations for your applications from a source control like git (spring-cloud-config) and use the environment variables to not to maintain the sensitive information in the source control.
Backing Services (treat backing resources as attached resources)
As per 12 factor app principles, a backing service is an application/service the app consumes over the network as part of its normal operation.
Database, Message Brokers, any other external systems that the app communicates is treated as Backing service.
12-factor app can automatically swap the application from one provider to another without making any further modifications to the code base. Let us say, you would like to change the database server from MySQL to Aurora. To do so, you should not make any code changes to your application. Only configuration change should be able to take care of it.
Microservices: In a microservice ecosystem, anything external to service is treated as attached resource. The resource can be swapped at any given point of time without impacting the service.
By following the interfaced based programming allow to swap the provider dynamically without impact on the system. Plug-in based implementation also helps you to support multiple providers.
Build, release, and Run (Strictly separate build and run stages)
The application must have a strict separation between the build, release, and run stages. Let us understand each stage in more detail.
Build stage: transform the code into an executable bundle/ build package.
Release stage: get the build package from the build stage and combines with the configurations of the deployment environment and make your application ready to run.
Run stage: It is like running your app in the execution environment.
Microservices:
You can use CI/CD tools to automate the builds and deployment process. Docker images make it easy to separate the build, release, and run stages more efficiently.
Processes (execute the app as one or more stateless processes)
The app is executed inside the execution environment as a process. An app can have one or more instances/processes to meet the user/customer demands.
As per 12-factor principles, the application should not store the data in in-memory and it must be saved to a store and use from there. As far as the state concern, your application should store the state in the database instead of in memory of the process.
Avoid using sticky sessions, using sticky sessions are a violation of 12-factor app principles. If you would store the session information, you can choose redis or memcached or any other cache provider based on your requirements.
By following these, your app can be highly scalable without any impact on the system
Microservices: By adopting the stateless nature of REST, your services can be horizontally scaled as per the needs with zero impact. If your system still requires to maintain the state use the attached resources (redis, Memcached, or datastore) to store the state instead of in-memory.
Port binding (Export services via port binding)
The twelve-factor app is completely self-contained and doesn’t rely on runtime injection of a webserver into the execution environment to create a web-facing service. The web app exports HTTP as a service by binding to a port, and listening to requests coming in on that port.
In short, this is all about having your application as a standalone instead of deploying them into any of the external web servers.
Microservices: Spring boot is one example of this one. Spring boot by default comes with embedded tomcat, jetty, or undertow.
Concurrency (Scale out via the process model)
This talks about scaling the application. Twelve-factor app principles suggest to consider running your application as multiple processes/instances instead of running in one large system. You can still opt-in for threads to improve the concurrent handling of the requests.
In a nutshell, twelve-factor app principles advocate to opt-in for horizontal scaling instead of vertical scaling.
(vertical scaling- Add additional hardware to the system
Horizontal scaling — Add additional instances of the application)
Microservices: By adopting the containerization, applications can be scaled horizontally as per the demands.
Disposability (maximize the robustness with fast startup and graceful shutdown)
The twelve-factor app’s processes are disposable, meaning they can be started or stopped at a moment’s notice. When the application is shutting down or starting, an instance should not impact the application state.
Graceful shutdowns are very important. The system must ensure the correct state.
The system should not get impacted when new instances are added or takedown the existing instances as per need. This is also known as system disposability.
Systems do crash due to various reasons. the system should ensure that the impact would be minimal and the application should be stored in a valid state.
Microservices: By adopting the containerization into the deployment process of microservices, your application implicitly follows this principle at a maximum extent. Docker containers can be started or stopped instantly. Storing request, state, or session data in queues or other backing services ensures that a request is handled seamlessly in the event of a container crash.
Dev/prod parity (Keep development, staging, and production as similar as possible)
The twelve-factor methodology suggests keeping the gap between development and production environment as minimal as possible. This reduces the risks of showing up bugs in a specific environment.
The twelve-factor developer resists the urge to use different backing services between development and production.
Microservices: This is an inherent feature of the Microservices that is run using the containerization techniques.
Logs (Treat logs as event streams)
Logs become paramount in troubleshooting the production issues or understanding the user behavior. Logs provide visibility into the behavior of a running application.
Twelve-factor app principles advocate separating the log generation and processing the log's information. From the application logs will be written as a standard output and the execution environment takes care of capture, storage, curation, and archival of such stream should be handled by the execution environment.
Microservices: In Microservices, observability is the first-class citizen. Observability can be achieved through using APM tools (ELK, Newrelic, and other tools) or log aggregations tools like Splunk, logs, etc.
By following the above-mentioned guidelines all you need is to debug an issue is to go to the central dashboard of your tool and search for it.
Admin processes (Run admin/management tasks as one-off processes)
There is a number of one-off processes as part of the application deployment like data migration, executing one-off scripts in a specific environment.
Twelve-factor principles advocates for keeping such administrative tasks as part of the application codebase in the repository. By doing so, one-off scripts follow the same process defined for your codebase.
Ensure one-off scripts are automated so that you don’t need to worry about executing them manually before releasing the build.
Twelve-factor principles also suggest using the built-in tool of the execution environment to run those scripts on production servers.
Microservices: Containerization also helps here to run the one-off processes as a task and shutdown automatically one done with the implementation.
That’s all for today. Hope you have enjoyed the article. Please share your thoughts or ideas or improvements in the below comments box.
References:
https://12factor.net/build-release-run https://www.nginx.com/blog/microservices-reference-architecture-nginx-twelve-factor-app/ https://blog.scottlogic.com/2017/07/17/successful-microservices-with-12factor-app.html
Originally published at http://www.techmonks.org on September 10, 2020.