by Kiran Chitturi
Preparing for a trip is almost as much fun as taking the journey itself; planning, making reservations, packing the right clothes – it’s all part of the experience. As you look at taking your applications on the Cloud/DevOps adoption journey (whether they be line of business or internal enterprise), preparation is just as important. Here are a few guidelines to consider.
In my previous article, I discussed the DevOps culture and how “planning to fail” in your iterations makes you succeed in your overall DevOps strategy. Today we will focus on the application/development side of DevOps. Going forward, thinking of this as part of a DevOps series, I will delve into Infrastructure and CICD perspectives, with a complete end-to-end DevOps sample.
New delivery methods in infrastructure (virtualization, declarative/immutable, containers, serverless computing, etc.) and application areas, mean that application developers can no longer simply “throw the code over the fence” for Operations to support.
While there are many areas in the applications space, here are a few that you will want to ensure traction on.
Enterprises and their application architectures have undergone multiple transformations over the last decade or two – from huge monolithic apps to more silo-based. For example, one massive application for an enterprise became multiple apps based on the department – sales, shipping, finance & accounting, etc. Then came the service bus, when SOA-type application architectures (where services based on functions/processes) started emerging and being consumed by front-end applications.
The next evolution has been getting to REST (Representational State Transfer)-based services/API (where we are dealing with resources when the state gets transferred, similar to the way the world-wide web works). These internet-based services provide a headless service layer and a completely independent backend. Having a decentralized database gives you:
- the ability to choose the right data store for the job (based on different read/write access patterns for SQL, NoSQL, Cache, Search, Object, Streaming, Search etc.)
- minimal disruption on schema changes and;
- freedom to scale independently – saving costs by having a small backend for smaller services that have few requests while being flexible to scale the heavily used services
Focusing on building these ‘Legos’ gives you the foundational blocks for the business, thereby positioning you for the next generation of innovations – possibly involving various types of UX interfaces, IOT devices, functions, etc. (because you build a clear separation between the way you access the resources/functionality and the processes themselves).
You can build various types/levels of based services, such as:
- utility services for Identity management, logging etc.
- capability services delivering reusable core business services
- application services for orchestrating /integrating your APIs (look at leveraging frameworks like Node.js in this space)
- UX services for mainly dealing with layout aspects across various channels.
It helps to design from the API backwards. Strive not to build your app first and then slap on a bunch of public APIs. Also, don’t create a second-class citizen for Public APIs and low documentation. Whenever possible, use the same public APIs internally (albeit, you might want to limit what the public APIs have access to). If you do this, when you are ready to expose your APIs (at least a portion of them) to the public, you would already have clean, modularly-built APIs to leverage. By forcing developers to think modularly, the API will in fact be re-usable, stateless, and degrade gracefully.
These ‘micro-services’ help us with abilities to rapidly develop, test and deploy in small chunks, and many times – fully embracing the key tenet in DevOps of modular/rolling updates.
As you mature in your micro-services journey, you will need to address the following aspects:
- aggregation/analytics needs (using either push, pull or pub-sub models)
- API discovery – simple DNS based or dynamic – using services – like Consul, etcd, Zookeeper – that provide health checking, failover)
- API management aspects (standardized metrics, patterns for consistency, author versioning, caching and throttling)
- standardized logging.
Design for failure
Werner Vogels, CTO and VP of Amazon.com, is popular for saying “Everything fails all the time”. When you design any component in your application, always ask yourself the fundamental question: What if this fails? Maybe it’s the server (EC2) the app is running on, the network, the availability zone, or managed services like DynamoDB that are causing problems. Netflix is famous for building their app services with this principle in mind. Because of this, they rarely fail even when things in AWS are down. They also use this philosophy to test their resilience often using tools like the “Chaos Monkey.”
Loosely Coupled and Elastic
Build your app with elasticity in mind.
Your multi-tier app should be stateless and the tiers completely independent of each other. This enables the app to scale easier and the system also becomes loosely coupled, while helping you better leverage the pay-as-you-go billing model in the cloud and its elastic nature. It also allows you to have no fear – not be too conservative or aggressive – in your resource provisioning.
The above measures will make your app architecture flexible and helps with removal of bottlenecks and single points of failure and makes it easier to perform system maintenance/upgrades.
When I say concurrency, I mean more parallel workloads or worker processes; not multi-threading/thread based, but worker based workloads. This ability gives you much faster execution, time-to-market, and customer satisfaction while saving you money as well.
Your application might be bound by one of the resources (like compute, memory, etc.) more than others. You should be able to spin up extra compute, memory, disk or network resources – whatever the app is bound by – and truly leverage the cloud’s scale. The question to ask yourself is, if you have a job that can get done in four hours with one type of machine, can the same job be done in one hour with multiple smaller machines?
Development & Testing Practices
Applying software development best practices to create versioned, reusable, maintainable, extensible, and testable applications is a must. It is critical to get the test environment, framework and project structure right at the beginning.
Use the best tools to get the job done. A few areas to focus on include:
- editors with built-in support for linting, debugging etc.
- language-specific package managers such as npm, NuGet, and pip
- build and test tools such as Apache Ant, JUnit, and PyUnit, Chai & Moca.
Explore and encourage a test-driven culture for development. There are various types of testing that you would want your application to leverage at various stages in its release lifecycle – static analysis, unit-testing, integration/end-to-end functional tests, performance and security testing. The goal should be for all the above to be fully automated.
Feel free to experiment with Vagrant and/or Docker to help you speed up development. More on this below.
Explore with newer technologies
Cloud services and DevOps tooling are evolving at a rapid pace. Docker and Serverless frameworks have taken the IT world by storm. If you haven’t yet, you must look at exploring these in your enterprise.
Docker provides huge benefits in the application space, three of which are listed below:
- Immutable – you will have a clean way of ensuring that a versioned artifact is being deployed
- Minimal overhead – you only need to deploy the minimal app and not the whole VM + the app
- Portability – the way an application is set up is the same on laptop & prod, which improves developer productivity and minimizes the ‘works on my machine’ environment issues.
Serverless (an event-driven paradigm from the ground up) takes this one step further and makes it very easy for developers to focus on the application functionality, while everything else on the infrastructure is handled by the provider. Lambda by AWS is the most popular (Google and Azure also have their flavors). There is still some maturity in this space around tooling and platform management, but overall the adoption has been tremendous and the use cases are ever-growing.
Cloud providers like AWS release major updates often, and by keeping abreast of these (for example, AWS’ API Gateway, Lambda StepFunctions, etc.), you can design your app to leverage the maturity and cost efficiencies along the way.
The picture below helps summarize the above points. You could use it in your DevOps teams when considering application architecture/design options.
In the remaining articles of this series, we will delve into Infrastructure and CICD perspectives, plus a complete end-end DevOps sample.
I hope you find this helpful and as always, keep your comments coming. Safe travels!