12 Factors in Cloud Native Applications

Introduction.

Deploying an application onto a cloud is trivial and is done many times in every organization, as all that is being done is an application is being deployed to another server.  These are referred to as as cloud based applications. As seen in this funny Dilbert cartoon, most companies expect good results (and assume it solves all the problems they had before), by following a deployment to the cloud.

However, developing a cloud native application which utilizes the benefits of the cloud is different from the monolith applications.

A Cloud Native Application is an application that was developed with the cloud principles of: robustness, scalability, easy integration, portability, and administration into its design.

In order to build a cloud native application the twelve factors of cloud native applications (micro services) has been proposed 12factor.net which guide developers.

The twelve factors for Cloud Native Application are:

Source: https://www.codemotion.com/magazine/microservices/using-the-twelve-factor-methodology-in-cloud-native-microservices/

Over time, since they were first proposed, more have been added that some schools of thought there are 15 and others as high as 23. I feel that the most important ones to be added to the original 12 factors are:

    • API First
    • Circuit breaker
    • Security & Authorization

In this post, we will discuss each of the 12 factors, the definition, a short explanation and how its’ implementation done via tools being used or by coding methodology. Finally, we will describe the three extended factors and conclusions on the twelve factors.

Twelve Factors of Cloud Native Application

1. Single Code base: One code base tracked in revision control, many deploys.
The application should have one code base to be built and can be deployed to any environment without any loss of generality. The code base can be shared so that other applications can use it too.

Implementation.

Using a version control system like Github, gitlab, SVN will enable us to keep one code base.

2. Dependencies: Explicitly declare and isolate dependencies.
Every dependency needs to be managed and each version needs to specified for the libraries being used in the application. This helps in getting from the library the correct version of the dependency.

Implementation.

Using maven, ANT, or gradle as the build tool and state all dependencies and version in the build file.

3. Config: Store config in the environment.

        For each environment we deploy our application, some parameter values like the database connection URL,  the LDAP server will be different. These need to be stored in each environment rather than in the code.

Implementation.

Store these values in externally to the code base of the application. Use canonical hostname and use external sources like Google, Twitter to implement this factor.

4. Backing Services:  Treat backing services as attached resources.

       A backing service is any service the application consumes over the network; yes, this includes connections to databases, LDAP server, file system like S3, email server etc.

Implementation

The code should be developed that it should not make any difference if the services are local or third party service. So if you are running a local database or in teh cloud externally then the code should be able to handle both situations.

5. Build, Release, Run: Strictly separate build & run stages
Build: Converts the code repo into executable artifact (e.g. jar)

         Release: Build with deploy’s current config. Release includes config e.g CF droplet.

         Run: Launches a set of app’s process against a selected release.

Implementation

6. Processes: Excecute the app as one or more stateless processes.
The cloud native application is executed in an environment as one or more stateless processes. If we use a stateful session in the application then when deployed on the cloud if the container dies then it becomes an issue as we cannot replicate the session the user was on. Hence we should write our applications utilizing stateless process.

Implementation

The applications should be microservices and each micro service should be stateless process.

7. Port Binding: Export services via port binding.
The Platform As A Service (PaaS) handles port assignment when application is deployed. This is important that we not bind our URL calls to other services using a specific port number.  We use the application.properties (or application.yaml) in our local settings in case we want to bind to a different port.

Implementation

PaaS on the cloud takes care of port numbers and developer should not be hard code these in the code base.

8. Concurrency:  Scale out via the process model.
The share-nothing, horizontally, partitionable process means that adding concurrency is reliable and a simple operation. This is in total opposition of vertical scaling as was seen in monolith applications by increasing the CPU, memory, or disk space.

Implementation.

Follow the principles of developing micro services which are small and can scale horizontally.

9. Disposability: Maximize robustness with fast startup and graceful shutdown.
A process shoud take a few seconds from launch command to being up and ready. This is very important as in the cloud the application should have high availability (24×7 up and running) and robust- handle sudden crashes and come back live in another container.

Implementation.

Follow the principles of developing micro services which are small and possibly (depending on the design) handle cohesive, homongenous requirements.

10. Dev/Prod Parity: Keep development, staging, and production as similar as possible.
A cloud native app should aim to have maximum parity between environments. This means that more deployments into production so that the deltas (changes) between dev environment and production is minimal.

Implementation.

This can be accomplished by setting up Continuous Integration/Continuous Deployment(CI/CD) in the cloud infrastructure. I have written a separate post on this here. One can use tools like Jenkins, Gitlab, Harness etc to implement CI/CD .

11. Logs:  Treat logs as event streams.
An application should not consider itself with storage of output or manage log files. The cloud has logs where the standard out and standard error are written as streams of all running processes.

Implementation.

As was done in the past, do not create a log file for your application. Remember that your application runs on a cloud container and any local log files are lost when the container is destroyed.

12. Admin Processes: Run admin/management tasks as one-off processes.
An application should have its admin process run in identical environments (dev-test, QA, UAT, and production should all be equivalent) with a commensurate level of discipline. It is important to remember that your application is running on multiple containers as it was needed to be scaled or possibly the initial setting was to be run on multiple containers.

      So when we run an admin process say a time script run daily and notify users say of an upcoming issue then we want it to run on one and only one container and not in all containers.

Implementation.

Use a locking mechanism to lock when one of the containers starts the admin process e.g.  shedLock or Quartz.

Beyond the Twelve Factors.

13. Security & Authorization:  Single sign on encompasses this factor.

        Security is the login credentials for the application to be accessed which is generally done by single sign on. While authorization is the role in that application. The roles for application are stored in LDAP or in the applicaiton itself.

Implementation.

Use single sign on as in LDAP server or Google identity. For authorization, define it in the application.

14. API First: An Application Process Interface (API) first means that the microservice is treated as ‘first class citizen’. It is designed with goal that it is consumed by all clients.

                             The API is the first user interface to application and after that the product is built on it. An API first approach helps developers to work in parallel. API first also includes generating documentation of the API.

Implementation.

Using Swagger or any other tools to design the API and then build the consumers.

15. Circuit Breaker: helps to manage the remote service’s availability.

                               As we need high availability of our application and it depends on external services; it is possible that the external service container is down and about to re start. When this happens then we want to try calling the service automatically again in a few seconds. This is opposed to the user getting an error and then trying again. In order to implement this we use the circuit breaker pattern, whose states are defined in the diagram below:

Implementation

There are now libraries available to implement circuit breaker with minimal code as in Resilience4J. One can also write their own code in the application.

Summary

In this post we have introduced the original twelve factors and more for cloud native applications. I have also given some suggestions on how each can be implemented by developers when implementing these factors. These factors at times can overlap while implementing as was saw in implementing dev/prod parity factor via CI/CD. At times misuse of one or more can lead to an anti-pattern.

A cloud native application consists of many micro services. Some of the factors/patterns used in application return when discuss patterns in micro-services.

In my opinion, some of the factors are now redundant in the list e.g. one codebase – it is over two decades since developers have been using some kind of version control system like github or SVN or the second factor on dependencies to use a build tool. It is difficult for me to imagine that there are companies who still do not use a build tool for their applications.

References

  • http://12Factor.net  – definitions and a little description
  • https://rajeevkuruganti.com/2023/ci-cd-continuous-integration-continuous-deployment/ – My post on CI/CD – an implementation of dev/prod parity.
  • https://vedcraft.com/architecture/3-tricks-twelve-factor-app/ – a good article on how to implement in organizations the 12 factor app patterns including tools to be used.
  • https://blog.heroku.com/twelve-factor-apps – Heroku does a summary and then elaborates in detail each of them.
  • https://docs.microsoft.com/en-us/azure/architecture/patterns/category/resiliency – shows some of the patterns and describes them elaborately
  • https://medium.com/@gabimelo/microservices-and-the-12-factor-app-d7d41c8b859c – website on 12 factors (15) and microservices.
  • https://www.slideshare.net/eduardtomas/microservices-yes-or-not  – What are they and best practices .

Leave a comment

Your email address will not be published. Required fields are marked *