In this post, I’m going to discuss how the Separation of Concerns (SoC) and Do not Repeat Yourself (DRY) design principles are being applied in the context of containerised applications.
Oftentimes, an application may need to perform multiple tasks at different stages of its lifecycle or perform some tasks that are not part of the business logic such as monitoring and log collection.
Instead of having a single monolithic container perform these miscellaneous tasks in addition to handling business logic, the idea is to make the application as ‘slim’ as possible by breaking down that monolithic container into discrete, reusable containers.
By delegating different tasks to individual containers, each fulfilling a separate and unique responsibility, we achieve separation of concerns.
The idea of Separation of Concerns is to decompose program functionality into distinct sections, each addressing a different concern.
By identifying which functionalities can be reused by other pods, and logically group them into separate containers, we achieve DRY.
The core idea of DRY is to reduce repetition by dividing logic into smaller, reusable units.
How are Init and Sidecar containers used?
To enforce both these principles at a pod-level, init containers and sidecar containers can be used. These containers reside in the same pod as the application container, providing the additional required functionality.
Init containers run before the main container runs and exit after its operation is complete. They perform tasks that need to be performed prior to starting up the main container. They allow developers to explicitly sequence what steps should occur prior to running the application container.
An example use case of init containers would be to separate application dependencies from application source code. An init container can be used to copy application dependencies into a volume which would subsequently be mounted into the main container.
Here, it is the responsibility of the init container to ensure that dependencies are present upon startup. The application container does not need to handle dependency management — it only handles business-related logic. Asides from the benefit of limiting the scope of responsibilities for each container, this approach also allows reuse since this init container can be included in other pods that share the same set of dependencies.
Sidecar containers perform auxiliary work for the main container. By placing non-business logic in these containers, we decouple auxiliary work from the main business logic, allowing changes to the main business logic and to auxiliary logic to occur independently.
A common use case for sidecar containers would be log collection. The auxiliary work here is log collection and shipping. As both the main container and the sidecar container reside in the same pod, they can share an emptydir volume. Logs are generated by the main application container and written into the shared volume. The sidecar container then tails these logs from the shared volume and forwards them to a log sink. The main application does not need to know how these logs are being handled. Similar to init containers, this would also allow reuse since the sidecar container can be included in other pods that require this logging functionality.
A concrete example in which both init and sidecar containers are used would be in Istio-configured clusters. Istio helps developers to manage a network of microservices by providing functionality such as secure pod-to-pod or service-to-service communication. To enable this functionality, Istio installs two containers in addition to the application container — an init container and a sidecar.
The init container is used to set up the iptables rules such that all incoming and outgoing traffic will be intercepted by the Istio sidecar container. After this task is done, the init container exits. The Istio sidecar container runs as a proxy alongside the application container inside each pod to intercept all incoming and outgoing communication from/to the pod. When incoming encrypted messages are intercepted by the Istio sidecar proxy, they will be decrypted for the application container. Similarly, any outbound messages generated by the application container will be intercepted and encrypted by the Istio sidecar proxy before leaving the pod.
By delegating the responsibility of handling security to Istio, this allows developers to separate security concerns and business-related logic, thereby increasing application maintainability.