K8S patterns

Kubernetes is a container orchestration tool very useful for the implementation of microservices and widely used by companies. One of the most important challenges when working with microservices is managing them; how to scale them, how to orchestrate them… how to keep them under control.

The way Kubernetes works with microservices is by packaging each microservice component into different containers and managing them at the cluster level. Broadly speaking, Kubernetes can help us with the implementation of microservices in the following areas:

Automatic scaling: Kubernetes can automatically monitor network traffic and workload for each container and automatically scale the number of instances to meet demand. This allows microservices to scale quickly and efficiently, without having to manually manage scaling.

Load Balancing: Kubernetes can also automatically balance the workload between containers, ensuring that each instance receives an equal amount of traffic. This improves the performance and stability of microservices, as requests do not stack up on a particular instance.

Version Management: Kubernetes makes it easy to manage microservice versions. You can deploy multiple versions of a microservice at the same time and send traffic to the appropriate version according to routing rules. This allows testing of new features in a test version before deploying them to the production version.

Resiliency: Kubernetes can ensure that microservices are always up and running. If an instance fails, Kubernetes can automatically replace it with a new, healthy instance. This allows microservices to recover quickly from failures and maintain availability for users.

Dynamic configuration: Kubernetes makes it easy to dynamically configure microservices. You can update the configuration of a microservice without having to restart it. This allows for greater flexibility and agility in deploying new changes and updates.

During the implementation of solutions using containers in general and Kubernetes in particular, we will encounter common problems that can be solved using common solutions; using patterns. The use of patterns is a highly recommended practice, as they are a practical solution that has already been developed and tested in real situations.
There are multiple patterns for each type of problem, but I would like to focus on what are called structural patterns, which are those that help us in the relationship between different containers. Structural patterns will help us to keep the containers focused on performing a single task. Within this category we would find the Init, Sidecar, Ambassador and Adapter patterns.

Init (or Initializator)

Init containers in Kubernetes allow you to separate the tasks related to initialization from the main application logic. This separation ensures that all initialization steps are completed correctly in the defined sequence before starting the main application containers. Multiple init containers can be concatenated so that initialization is performed sequentially, with each container being assigned a task. The business logic will only be available when all the init containers have been executed.

Init Pattern

The pattern consists of adding one or more additional containers to the same pod as the main container. The additional container acts as a preparation for the execution of the core functionality, performing initialization tasks before the main application is started. They work in a similar way to the constructors of some programming languages, so that until the initialization requirements are satisfied, the functionality is not available.
We can imagine having an application that needs specific configurations before it can be used. Instead of manually configuring the application each time it is started, an Init can be added to prepare the environment before the main application can be used. The Initializator can perform tasks such as setting environment variables, creating configuration files and setting permissions or even downloading code from repositories.

Sidecar (or Sidekick)

The Sidecar pattern is used to extend the functionality of a container without modifying its code. The pattern consists of adding additional containers to the same pod as the main container. The additional container acts as a Sidecar, providing additional functionality to the main container, such as logs, metrics, caching or security. Usually a container represents a natural boundary for a unit of functionality that has its own runtime, release cycle, API and proprietary equipment. A good container, behaves like a single linux process, solves a problem and does it well, and is created with replaceability and reusability in mind. By using the Sidecar pattern, we can preserve the main container focused on business logic and have other containers extending its functionality without interference.

Sidecar Pattern

We can imagine that we have an application that needs access to a database. Instead of adding additional logic to the application to handle the database connection, we can add a Sidecar that manages the connection. The Sidecar acts as an intermediary between the application and the database, providing a secure and reliable connection.

Ambassador

The Ambassador pattern is a particular case of the Sidecar pattern. This pattern itself does not extend the functionality of the main container, but provides a unified view of the world outside its container, hiding complexity and providing a unified interface to services outside the pod.
Sometimes, a container must work with other services that are not easily accessible; these difficulties are due to changes in dynamic addresses, complex configurations, security elements, etc. We are almost certainly not interested in mixing this complexity with the basic functionality of our main container, so we can resort to this pattern to solve it.

Ambassador Pattern

With this model, our application container can focus on performing its processing and delegate the responsibility and the particularities of the consumption of external services to another container that acts as a proxy.

Adapter

If the Ambassador pattern allowed us to hide the complexity from outside our pod, the Adapter allows us to hide the complexity of our pod to the outside.
In this case the container is used to expose services in a Kubernetes cluster to external services. The pattern consists of adding an additional container to the same pod as the main container. The additional container acts as a reverse proxy, so we can leverage it to route the service traffic to the correct service in the cluster or to provide a unified interface to the outside of the different containers; very useful for monitoring and data extraction. The Adapter acts as an intermediary between the external service and the internal services, hiding the complexity of these (and providing greater security).

Adapter Pattern

We can imagine that we have several services in a Kubernetes cluster and we want to expose them to external agents. Instead of manually configuring each service to expose it to the outside world, we can add an Ambassador to manage the routing of external traffic.

We have seen how the use of patterns is an effective way to address common problems in Kubernetes application implementation. While they may seem complex at first, they are powerful tools that can save time and effort in both deployment and application management and maintenance.