In this post we will briefly examine what we need to consider when we are looking to prepare our application(s) for Cloud Citizenship, i.e. ensuring they are native to the Cloud.
What necessitates 12 Factor?
The image above somewhere brings us to Pets vs. Cattle story…
We treated App Servers as our pets, kept them along with us (in premise), cared for them, when they were not healthy, we got them treated, added power to them etc.
But actually servers are cattle, today they are cheap, if you need more, go and buy them, if they are not healthy, we kill them, if we have extra, we return them to the market etc.
In Java, App Servers are pet, they are not disposable, on the other hand, microservices are cattle, they are cheap, we can start them quickly, we can bring them down faster etc. Easy to replace, they are decoupled.
What is 12 Factor?
- A methodology
- Set of Principles
- Best Practices based on experience and observations at Heroku
that leads to…….
The mechanism through which these are achieved:
- Immutability – Infrastructure is immutable.
- Ephemerality – Application is ephemeral, not persistent, are disposable.
- Declarativity – Declarative setups, configurations.
- Automation – as much as we can automate
What are those 12 factors?
|Build/Deploy Focused||Architecture/Design Focused|
|Build, Release, Run||Dev/Prod Parity|
Let’s take a look at each in detail;
Build/Deploy Factors Detailed…
- Should use VCS
- Most important one repository per application
- Shared code should be migrated to an applications itself and be treated as library
- Explicitly declared and managed
- Don’t expect your dependencies will be provided by OS/Container etc.
- Don’t check in jar files into code repo
- Should be separated from code
- items which are specific to an environment and not to an application
- Should be made available through environment variables or any other similar mechanism, like our AMC.
- Litmus test – can we open source our code base without exposing any internal URLs or credentials.
- Backing Services
- any service that is communicated with over a network
- database connections, cache providers, file sharing services like SFTP or Amazon S3, email services
- are bound by a URL to the remote or local resource identically, are treated the same as the local services and URL is provided by the configuration
- Consider these as attachable resources
- Allows swapping out the service in each environment or data center
- Build, Release, Run
- Should be executed in 3 discrete steps
- Build, compiles code and produces executable binary, e.g. a jar file
- Combine configuration with build output to create a release image per deployment need
- Release image has everything that an application needs to run
- Run the application from release image
Architectural/Design Factors Detailed…
- Should be stateless, when goes down, shouldn’t take anything important down along
- Memory usage should be single threaded, and short lived.
- Anything that needs to be stored from operation to operation, needs to leverage a database or a cache.
- Sticky sessions are not good
- Cache managers like EHCACHE, keeping state in memory, but still distributing is OK.
- Port Binding
- Should be fully self contained, shouldn’t rely on external infrastructure for anything
- Should expose itself over a port, instead relying on application server to do this for it
- Each process should have its communication protocols bound to a usually non-standard port, allowing it run in a container in an isolated fashion.
- JVM has some great concurrency libraries (Java.util.concurrent, RxJava, Java.util.stream etc.), but they are for scaling up
- To scale out, diversify work load, break tasks into applications to do single job, e.g. web request handler, backend job, schedule job etc.
- Microservices helps here
- Quick to startup, well within 60 secs. Refactor application to get there
- Graceful shutdown, within 10 secs of receiving TERM signal, should release resources, clean itself up and goes down gracefully
- Resilient to failure, if it shutdown gracefully and come up quickly, it can be called resilient to failure
- App servers are pets, microservices are cattle, they are disposable
- Dev/Prod Parity
- Dev environment should be identical to PROD environment and every environment in between (staging, QA, UAT etc.)
- Parity leads to reproducibility and reproducibility paves way towards disposability
- Log messages are critical for operations in helping troubleshoot issues
- Treat logs as an event data stream
- Application writes its logs to standard out in the form of a stream
- Each application shares the same stream
- The logs can then be aggregated to another system like ELK for archival and reporting
- Standardizing logging output (as a JSON message) across all of applications, makes this aggregation easier
- Admin Processes
- Admin tasks should be run as an isolated processes
- Task shouldn’t be built in the application
- Should be migrated and managed as an application