TT Lab
Get started
Learn Learning paths Courses

CKAD — Kubernetes Application Developer

Multi-Container Pods — init, Sidecar, Ambassador, Adapter

Continue in TT Lab

Goal

You distinguish, pattern by pattern, why several containers belong in one Pod, and write init containers, native sidecars, ambassadors, and adapters as manifests yourself.

Why it matters

Putting two containers in a Pod is not a free decision. The two containers are scheduled together, restarted together, and deleted together. So you put them in one Pod only when both "they must be on the same node" and "they must share a lifecycle" hold. A log collector has to read files the app wrote, so it must be on the same node, and when the app goes away there is nothing to read, so the lifecycle is the same too. A cache server, on the other hand, is shared by many apps, so it gets its own Pod.

The criterion that separates an init container from a sidecar is whether it ends. Work that must end (migration, preparing configuration, waiting for a prerequisite service) belongs in an init container, and work that must not end (shipping logs, a proxy) belongs in a sidecar. In the past you had no choice but to put a sidecar in containers, so startup order was not guaranteed and Jobs would not finish. Now, if you put it in initContainers and give it restartPolicy: Always, it becomes a container that "starts first, keeps running, and ends together with the main container."

An ambassador and an adapter differ in direction. An ambassador handles the outgoing traffic on the app's behalf, and an adapter handles the outgoing data format. Both aim to absorb environment differences without touching the app code.

Steps

  1. Create the namespace ckad-multi.
  2. Create a Pod app-init. An init container prep (image busybox:1.36, command: ["/bin/sh","-c","echo ready > /work/state"]) and a main container app (image nginx:1.27) both mount the emptyDir volume workdir at /work.
  3. Create a Pod ordered-init. Add two init containers in order, wait-config and migrate (both busybox:1.36, command is ["/bin/sh","-c","true"]), and a main container app with nginx:1.27.
  4. Create a Pod log-shipper. Put a native sidecar logger in initContainers and give it restartPolicy: Always (image busybox:1.36, command: ["/bin/sh","-c","tail -F /var/log/nginx/access.log"]). The main container web uses nginx:1.27. Mount the emptyDir volume varlog at /var/log/nginx in both containers.
  5. Create a Pod ambassador-pod. Give the main container app (busybox:1.36, command: ["/bin/sh","-c","sleep 3600"]) the environment variable REDIS_ADDR=localhost:6379, and for the second container proxy (nginx:1.27) specify containerPort: 6379 and name: redis-proxy.
  6. Create a Pod adapter-pod. The main container app (busybox:1.36, command: ["/bin/sh","-c","sleep 3600"]) and the adapter container metrics-adapter (nginx:1.27) mount the emptyDir volume applog both at /var/log/app. For the adapter, specify containerPort: 9113 and name: metrics.
  7. Create a Pod graceful. Include a native sidecar drainer (inside initContainers, restartPolicy: Always, busybox:1.36, command: ["/bin/sh","-c","sleep 3600"]) and a main container web (nginx:1.27), and set the Pod's terminationGracePeriodSeconds to 45. On the main container web, attach a preStop hook that runs the exec command ["/bin/sh","-c","sleep 10"].

Notes

Create the lab namespace

Create the namespace ckad-multi.

A single kubectl create namespace line is enough. Create every later resource inside this namespace.

Init container and shared emptyDir

Create a Pod app-init. An init container prep (image busybox:1.36, command: ["/bin/sh","-c","echo ready > /work/state"]) and a main container app (image nginx:1.27) both mount the emptyDir volume workdir at /work.

spec.initContainers is an array at the same level as spec.containers. For two containers to see the same volume, declare it once in spec.volumes and reference the same volume name from each container's volumeMounts.

Execution order of two init containers

Create a Pod ordered-init. Add two init containers in order, wait-config and migrate (both busybox:1.36, command is ["/bin/sh","-c","true"]), and a main container app with nginx:1.27.

Init containers run one at a time in the order written in the array, and the next one starts only after the previous one succeeds. The order is graded, so check the array indexes.

Native sidecar (restartPolicy: Always)

Create a Pod log-shipper. Put a native sidecar logger in initContainers and give it restartPolicy: Always (image busybox:1.36, command: ["/bin/sh","-c","tail -F /var/log/nginx/access.log"]). The main container web uses nginx:1.27. Mount the emptyDir volume varlog at /var/log/nginx in both containers.

Put a native sidecar in the initContainers list and give only that container restartPolicy: Always. A container-level restartPolicy is used only for this purpose.

Ambassador pattern — opening the way outward on the app's behalf

Create a Pod ambassador-pod. Give the main container app (busybox:1.36, command: ["/bin/sh","-c","sleep 3600"]) the environment variable REDIS_ADDR=localhost:6379, and for the second container proxy (nginx:1.27) specify containerPort: 6379 and name: redis-proxy.

The main container always connects only to a fixed port on localhost, and the container next to it knows the real destination. Containers in a Pod share a network namespace, so localhost reaches each other.

Adapter pattern — matching the output format to the outside

Create a Pod adapter-pod. The main container app (busybox:1.36, command: ["/bin/sh","-c","sleep 3600"]) and the adapter container metrics-adapter (nginx:1.27) mount the emptyDir volume applog both at /var/log/app. For the adapter, specify containerPort: 9113 and name: metrics.

The adapter reads the file the main container writes and exposes it in a different format. Both containers must mount the same volume at the same path, and for the adapter you specify the exposed port together with a name.

Termination time budget and preStop (comprehensive)

Create a Pod graceful. Include a native sidecar drainer (inside initContainers, restartPolicy: Always, busybox:1.36, command: ["/bin/sh","-c","sleep 3600"]) and a main container web (nginx:1.27), and set the Pod's terminationGracePeriodSeconds to 45. On the main container web, attach a preStop hook that runs the exec command ["/bin/sh","-c","sleep 10"].

terminationGracePeriodSeconds is a Pod-level field: the number of seconds from SIGTERM to SIGKILL. The preStop hook lives under the container's lifecycle, and the time this hook takes is also consumed from that budget.