Java is all about concept of classes, objects, packages, inheritance, encapsulation and polymorphism. Then there’s Java runtime which provides features and guarantees on how to manage the lifecycle of the objects and the application itself. For a long time we relied on the Java OOP primitives which gave birth to objects that are managed by the JVM.
That was then. These days one can extend the behavior of applications by applying distributes primitives. They operate at a different abstraction level and have different preconditions and guarantees.
Containers are the fundamental building blocks of Cloud Native applications. If you compare that to Java, the containers are classes and container images are class objects.
Kubernetes distributed primitives
Kubernetes as a container orchestration provides many distributed primitives and abstractions to address the different concerns for distributed applications. The same way that principles and patterns apply to OOP, they play a part in the distributed Kubernetes primitives.
Kubernetes could be seen as the JVM with the responsibility of managing and running of container instances. If you look at a Kubernetes Pod it would be no different from an Inversion of Control context where multiple objects share a managed lifecycle and can access each other directly.
The containers enable you to build modularised, reusable and single purpose containers. When you write OOP applications you typically follow the principles of software design (the famous SOLID principles). The principles for creating containerised applications must apply. The principles will ensure that the containers will behave as cloud native citizens – allowing them to be scaled, scheduled and monitored in an automated fashion.
Principles for Container Applications
- Single Concern Principle
In OOP the Single Responsibility Principle solid highlights its responsibility as main reason for change – a class should have one responsibility and therefore one reason for change. In distributed Kubernetes primitives, the single concern speaks to a Pod which is a single deployment Unit. Even when you combine init containers and sidecar patterns, the individual containers must be handled as a single concern. Advantages here are that you can swap out containers that deal with the same concern, and scale them independently as you wish.
- High Observability Principle
A container that aims to become a Cloud Native citizen must provide Application programming interfaces for the runtime environment to observe the container health and act accordingly. The APIs could be metrics, readiness, tracing, logs and liveness. Kubernetes tools like Prometheus and Open Tracing can read the logs/metrics/traces to help manage your applications as best as possible.
- Life-Cycle Conformance Principle
A container is not responsible for its life cycle. It should therefore conform and respond to events from the platform and conform to their life cycles accordingly. There are other common events such as PostStart and PreStop that can be significant in the lifecycle management of your application.
- Image Immutability Principle
Containers are supposed to be immutable – once built they are not expected to change across different environments. The Principle prevents the creation of similar container images for different environments. Rather stick to one image that is configured for different environments.
- Process Disposability Principle
It should be easier to replace a container during run time. The Principle focuses on very short start and stop times. The containers must also be very small in size.
- Self Containment Principle
The container should contain everything it needs at build time, except environment specific elements.
- Runtime Confinement Principle
Every container should declare its resource requirements and pass those to the Kubernetes platform. Resources may be CPU, memory, networking, disk, auto scaling requirements so Kubernetes can schedule container and autoscale accordingly.
Kubernetes cloud native applications development requires one to apply a different way of thinking that enables one to create good containerised applications that ultimately become good citizens to Kubernetes and other cloud native platforms. That said though, one must understand the Kubernetes patterns in order to better understand how one can use the distributed primitives to solve cross cutting concerns for containerised applications.
Watch this space for more information on Kubernetes patterns.
by Simiso Zwane