Understanding Kubernetes Multi-Container Pod Patterns

A guide to Sidecar, Ambassador, and Adapter patterns with hands-on examples

Gaurav Agarwal
Nov 17, 2020 · 5 min read
Image for post
Image for post
Photo by Tevin Trinh on Unsplash

A Kubernetes Pod is the basic building block of Kubernetes. Comprising of one or more containers, it is the smallest entity you can break Kubernetes architecture into.

When I was new to Kubernetes, I often wondered why they designed it so. I mean why containers did not become the basic build block instead. Well, a bit of doing things in the real environment and it makes more sense now.

So, Pods can contain multiple containers, for some excellent reasons — primarily, the fact that containers in a pod get scheduled in the same node in a multi-node cluster. This makes communication between them faster and more secure, and they can share volume mounts and filesystems with each other.

That helps us build on patterns we can implement to solve some particular problems. There are three widely recognised, multi-container pod patterns:

  • Sidecar
  • Ambassador
  • Adapter

Let’s look at each of these with some hands-on examples.

Prerequisites

For the purpose of the hands-on exercise, you need to have a running Kubernetes cluster, and you need to clone this repo, and cd into it.

Sidecar

Sidecars derive their name from motorcycle sidecars. While your motorcycle can work fine without the sidecar, having one enhances or extends the functionality of your bike, by giving it an extra seat. Similarly, in Kubernetes, a sidecar pattern is used to enhance or extend the existing functionality of the container.

Your container works perfectly well without the sidecar, but with it, it can perform some extra functions. Some great examples are using a sidecar for monitoring and logging and adding an agent for these purposes.

Image for post
Image for post
Sidecar

For the demo, let’s look at a sidecar pattern with an application generating logs at a particular file path, where the sidecar pushes the records to the nginx HTML directory for users to view.

So, if you look at the manifest you’ll see we have two containers: app-container and log-exporter-sidecar. The app-container continuously streams logs to /var/log/app.log, and the sidecar container mounts the logs to the nginx HTML directory. This allows anyone to visualise the logs using a web browser.

When you apply the manifest and run a port-forward on port 80, you should be able to access the logs using a browser. For this demo, we will use curl for that.

Image for post
Image for post

As you can see, logs can be viewed through curl, and though it’s a very rudimentary way of doing things, it explains the concept.

Ambassador

The ambassador pattern derives its name from an Ambassador, who is an envoy and a person a country chooses to represent their country and connect with the rest of the world. Similarly, in the Kubernetes perspective, an Ambassador pattern implements a proxy to the external world. Let me give you an example — If you build an application that needs to connect with a database server, the server configuration, etc, changes with the environment.

Now, the official recommendation to handle these is to use Config Maps, but what if you have legacy code that is already using another way of connecting to the database. Maybe, a properties file, or even worse, a hardcoded set of values. What if you want to communicate with localhost, and you can leave the rest to the admin? You can use the Ambassador pattern for these kinds of scenarios.

So, what we can do is create another container that can act as a TCP Proxy to the database, and you can connect to the proxy via localhost. The sysadmin can then use config maps and secrets with the proxy container to inject the correct connection and auth information.

Image for post
Image for post
Ambassador

For the demo, we will use a simple NGINX config that acts as a TCP proxy to example.com. That should also work for databases and other back ends.

If you look carefully in the manifest YAML, you will find there are three containers. The app-container-poller continuously calls http://localhost:81 and sends the content to /usr/share/nginx/html/index.html.

The app-container-server runs nginx and listens on port 80 to handle external requests. Both these containers share a common mountPath. That is similar to the sidecar approach.

There is an ambassador-container running within the pod that listens on localhost:81 and proxies the connection to example.com, so when we curl the app-container-server endpoint on port 80, we get a response from example.com.

Let’s have a look:

Image for post
Image for post

Adapter

The Adapter is another pattern that you can implement with multiple containers. The adapter pattern helps you standardise something heterogeneous in nature. For example, you’re running multiple applications within separate containers, but every application has a different way of outputting log files.

Now, you have a centralised logging system that accepts logs in a particular format only. What can you do in such a situation? Well, you can either change the source code of each application to output a standard log format or use an adapter to standardise the logs before sending it to your central server. That’s where the adapter pattern comes in.

Image for post
Image for post
Adapter

For our hands-on exercise, let’s consider that an application that outputs logs in a particular format, that we want to change to something standard.

In the manifest, we have an app-container that outputs a stream of dates to a log file. The log-adapter container adds a Date prefix in front. Yes, it’s a very rudimentary example, but enough to get how the adapter works.

Let’s apply the manifest and see for ourselves.

Image for post
Image for post

So, when we cat the log file /var/log/out.log for the log-adapter, we see a Date prefixed to the output that was not in the original log /var/log/app.log. You can then use a sidecar to export these logs to your monitoring and alerting engine.

Conclusion

Multi-container pods exist for a good reason in Kubernetes. The patterns above are just one of the most widely used — there are other scenarios where you may want to use them. Containers within a Pod use the same IPC Namespace and Networking, so you may need to tightly couple two containers based on their utility.

Thanks for reading! I hope you enjoyed the article.

Better Programming

Advice for programmers.

Sign up for The Best of Better Programming

By Better Programming

A weekly newsletter sent every Friday with the best articles we published that week. Code tutorials, advice, career opportunities, and more! Take a look

By signing up, you will create a Medium account if you don’t already have one. Review our Privacy Policy for more information about our privacy practices.

Check your inbox
Medium sent you an email at to complete your subscription.

Gaurav Agarwal

Written by

Certified Kubernetes Administrator | Cloud Architect | DevOps Enthusiast | Connect @ https://gauravdevops.com | https://freedevtools.net

Better Programming

Advice for programmers.

Gaurav Agarwal

Written by

Certified Kubernetes Administrator | Cloud Architect | DevOps Enthusiast | Connect @ https://gauravdevops.com | https://freedevtools.net

Better Programming

Advice for programmers.

Medium is an open platform where 170 million readers come to find insightful and dynamic thinking. Here, expert and undiscovered voices alike dive into the heart of any topic and bring new ideas to the surface. Learn more

Follow the writers, publications, and topics that matter to you, and you’ll see them on your homepage and in your inbox. Explore

If you have a story to tell, knowledge to share, or a perspective to offer — welcome home. It’s easy and free to post your thinking on any topic. Write on Medium

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store