Multi container pod

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

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

There are three widely recognized, multi-container pod patterns

  • Sidecar
  • Ambassador
  • Adapter

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

our 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.

# cat side-car-pod.yaml

apiVersion: v1

kind: Pod

metadata:

  name: sidecar-pod

spec:

  volumes:

  - name: logs

    emptyDir: {}

  containers:

  - name: app-container

    image: alpine

    command: ["/bin/sh"]

    args: ["-c", "while true; do date >> /var/log/app.log; sleep 2;done"]

    volumeMounts:

    - name: logs

      mountPath: /var/log

  - name: log-exporter-sidecar

    image: nginx

    ports:

      - containerPort: 80

    volumeMounts:

    - name: logs

      mountPath: /usr/share/nginx/html

save and exit the yaml file

So, if we 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 visualize the logs using a web browser.

Lets run the below command 

# kubectl apply -f side-car-pod.yaml

pod/sidecar-pod created

once the pod is running

# kubectl get pod sidecar-pod

NAME          READY   STATUS    RESTARTS   AGE

sidecar-pod   2/2     Running   0          19m

# kubectl port-forward sidecar-pod 80:80

Forwarding from 127.0.0.1:80 -> 80

Forwarding from [::1]:80 -> 80

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.

#cat ambassador-pod.yaml

apiVersion: v1

kind: Pod

metadata:

  name: ambassador-pod

  labels:

    app: ambassador-app

spec:

  volumes:

  - name: shared

    emptyDir: {}

  containers:

  - name: app-container-poller

    image: yauritux/busybox-curl

    command: ["/bin/sh"]

    args: ["-c", "while true; do curl 127.0.0.1:81 > /usr/share/nginx/html/index.html; sleep 10; done"]

    volumeMounts:

    - name: shared

      mountPath: /usr/share/nginx/html

  - name: app-container-server

    image: nginx

    ports:

      - containerPort: 80

    volumeMounts:

    - name: shared

      mountPath: /usr/share/nginx/html

  - name: ambassador-container

    image: bharamicrosystems/nginx-forward-proxy

    ports:

      - containerPort: 81

save and exit the file

# kubectl apply -f ambassdor-pod.yaml

pod/ambassdor-pod configured

# kubectl get pod ambassdor-pod

NAME          READY   STATUS    RESTARTS   AGE

ambassdor-pod   2/3     Running   0          19m

# kubectl port-forward ambassdor-pod 80:80

Forwarding from 127.0.0.1:80 -> 80

Forwarding from [::1]:80 -> 80

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 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 the endpoint on port 80, we get a response from example.com.

Adapter

The Adapter is another pattern that you can implement with multiple containers. The adapter pattern helps you standardize 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.

#cat adapter-pod.yaml

apiVersion: v1

kind: Pod

metadata:

  name: adapter-pod

  labels:

    app: adapter-app

spec:

  volumes:

  - name: logs

    emptyDir: {}

  containers:

  - name: app-container

    image: alpine

    command: ["/bin/sh"]

    args: ["-c", "while true; do date >> /var/log/app.log; sleep 2;done"]

    volumeMounts:

    - name: logs

      mountPath: /var/log

  - name: log-adapter

    image: alpine

    command: ["/bin/sh"]

    args: ["-c", "tail -f /var/log/app.log|sed -e 's/^/Date /' > /var/log/out.log"]

    volumeMounts:

    - name: logs

      mountPath: /var/log

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.

# kubectl apply -f adapter-pod.yaml

pod/adapter-pod configured

# kubectl get pod adapter-pod

NAME          READY   STATUS    RESTARTS   AGE

adapter-pod   2/2     Running   0          19m

# kubectl port-forward adapter-pod 80:80

Forwarding from 127.0.0.1:80 -> 80

Forwarding from [::1]:80 -> 80

And that's how we can deploy a multi-container pod into your Kubernetes cluster




Relevant Blogs:

Kubernetes cluster provisioning in lxc 

Service Now 

Amazon Web Services 

ZA Proxy run scan

Recent Comments

No comments

Leave a Comment