Skip to content

Cloud-Native App Dev

Introduction

Cloud-Native is all about the concepts that are used for building and deploying your applications on any cloud platform. It involves many things like adopting microservices architecture, containerization, orchestration etc.

Cloud-native applications will be managed by the infrastructure which in turn is managed by applications. In this installment, let us see what you need to include in your cloud-native applications and how to run them on the cloud infrastructure.

Application Design

An application is called Cloud-Native if it is designed in a way such that it takes advantage of most of the benefits of the cloud. So, these applications are all managed by software like mentioned before.

Let's say we have an application packaged in a container and running on Kubernetes. This application does not accept any runtime configuration. There is no logging defined. Some of the configurations like database IP, credentials etc are hardcoded.

What happens if the application stops working? How are you going to debug it?

Will you call such an application cloud-native? Definitely not.

Containers and Kubernetes help you to run the applications smoothly on cloud infrastructure. Along with this, you also need to know how to effectively build and manage these applications.

Cloud-native Capabilities

While developing cloud-native applications, some of the capabilities that we include in the applications are as follows.

  • Configuration
  • Health Checks
  • Logging
  • Metrics
  • Resiliency
  • Service discovery

Usually, you can implement them in your applications in two ways.

  • Using the language libraries
  • Using sidecar

To implement these capabilities, you can import the language libraries into your application which will automatically get you most of these capabilities with out defining any extra code. This is easy to do and is well suitable if you have few languages in your applications.

But if your application is a polyglot with many different languages, it is difficult to manage all the libraries. In such cases, you can use sidecar which is implemented as separate service including things like Health end point monitoring health of the application, configuration watching changes in the configs and reloading the application when required, registered for service discovery, envoy proxy to handle resiliency and metrics etc.

Application Lifecycle

Deploy

Deploying your cloud-native application is not just taking the existing code and running it on cloud. Cloud-native applications are defined in a way such the software manage them. For this, make sure you have the below embedded in your application.

  • Continuous integration
  • Continuous deployment
  • Health checks

Deployments for your application should be automated, tested and verified. If you are introducing new features to your applications, you should be able to deploy them dynamically without restarting your applications. Also, when you are planning on a new feature or a new version to be deployed, make sure you have traffic control mechanisms in place which allows you to route the traffic towards or away from the application as per your requirements to reduce the outage impact.

Run

Running your application is one of the most important phases in the application lifecycle. While running the application, two most important aspects to keep in mind are

  • Observability
  • Operability

While running your application, you need to understand the what the application is doing which is observability and also you you should be able to change the application as needed which is operability.

When your application is not meeting the service-level objectives (SLOs) or is broken, what do you do? In a cloud-native application, we follow the below steps to see where the problem resides.

  1. Verify infrastructure tests
  2. Application debugging - This can be done by using application performance monitoring (APM), distributed tracing etc.
  3. More verbose Logging

In today's world, as the business keeps increasing, the application grows and you need to make sure that you defined a proper debugging strategy for your application which makes it easy to dynamically debug the applications similar to how we dynamically deploy them.

One more important things to remember is that it is always easy to push new applications but the converse is not true. Though that is the case, it is still very important to retire the old applications that are not in use.

Retire

In cloud-native applications, all the new services are deployed automatically. Also, the services are monitored automatically using the monitoring mechanisms in place.

Don't you think the services should be retired in the same way too?

If you keep deploying new services without cleaning up the old ones which are in no longer use accrues a lot of technical debt. So, make sure your application includes a telemetry mechanism which helps you to identify if a service is being used. If not, the decision should be made by the business to keep it or retire it.

Twelve factor design methodology

  • Code base - One code base tracked in revision control, many deploys.
  • Dependencies - Explicitly declare and isolate dependencies.
  • Config - Store config in the environment.
  • Backing services - Treat backing services as attached resources.
  • Build, release, run - Strictly separate build and run stages.
  • Processes - Execute the app as one (or more) stateless process(es).
  • Port binding - Export services through port binding.
  • Concurrency - Scale-out through the process model.
  • Disposability - Maximize 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.

Application Requirements

Runtime and Isolation

Your applications must be isolated from the operating system. You should be able to run them any where. This allows you to run multiple applications on same server and also allows to control their dependencies and resources.

One way to achieve this is containerization. Among the different container options, Docker is popular. Container is nothing but a way to package your application and run it in an isolated environment. While developing the applications, also make sure all the dependencies are declared in your application before packaging it.

Resource Allocation and Scheduling

Your applications must include dynamic scheduling. This helps you to figure out where the application must run and this decisions are automatically taken for you by the scheduler. This scheduler collects all the information of resources for different system and chooses the right place to run the application. Operator can override the decisions of the scheduler if he wants to.

Environment isolation

You need a proper environment isolation to differentiate dev, test, stage, production etc. based on your requirements. With out the complete duplication of your cluster, the infrastructure should be able to separate the dependencies through different application environments.

These environments should include all of the resources like databases, network resources etc. needed by the application. Cloud-native infrastructure can create environments with very low overhead.

Service discovery

In your application, there may be multiple services. These services may depend on one another. How will they find each other if one service needs to communicate with other? For this, the infrastructure should provide a way for services to find each other.

This may be in different ways. It can be using API calls or using DNS or with network proxies. There should be a service discovery mechanism in place and how you do this does not matter.

Usually cloud-native applications make use their infrastructure for service discovery to identify the dependent services. Some of them are cloud metadata services, DNS, etcd and consul etc.

State Management

While defining your cloud-native application, you should provide a mechanism to check the status of the application. This can be done by an API or hook that checks the current state of the application like if it is submitted, Scheduled, ready, healthy, unhealthy, terminating etc.

We usually have such capabilities in any of the orchestration platform we use. For example, if you consider Kubernetes, you can do this using events, probes and hooks. When the application is submitted, scheduled, or scaled, the event is triggered. Readiness probe checks if the application is ready and liveness probes checks if the application is healthy. Hooks are used for events that need to happen before or after processes start.

Monitoring and logging

Monitoring and logging should be a part of the cloud-native application. Dynamically monitoring all the services of the application is important. It keeps checking the entire application and is used for debugging purposes when required. Also, make sure your logging system should be able to collect all the logs and consolidate them together based on application, environments, tags etc.

Metrics

Cloud-native applications must include metrics as a part of their code. All the telemetry data needed will be provided by the metrics. This helps you to know whether your application is meeting the service-level objectives.

Metrics are collected at instance level and later aggregated together to provide the complete view of the application. Once the application provides metrics, underlying infrastructure will scrape them out and use them for analysis.

Debugging and tracing

When an application is deployed and problem occurs, we refer to logging system. But if that does not resolve the issue, we need distributed tracing. Distributed tracing helps us to understand what is happening in the application. They will us to debug problems by providing us an interface to visualize which is different from the details we get from logging. Also, it provides shorter feedback loops which helps you to debug distributed systems easily.

Application tracing is always important and make sure it is a part of your cloud-native application. If in case you cannot include it in the application, you can also enable it at infrastructure level using proxies or traffic analysis.

Activities

Conclusion

We discussed the cloud-native application design, implementations of cloud-native patterns, and application life cycle. We also saw how we can design our cloud-native applications using the twelve factor methodology. Along with this, we also explored what we need to include in our cloud naive application while building it.

References