In this blog post, we will discuss sidecar containers. Sidecar containers are supportive containers that run alongside your application. They can serve various purposes, such as log shipping, where they collect logs from your primary container, or acting as proxy servers that handle inbound and outbound traffic.
However, up until now, Kubernetes has not provided any official support for sidecar containers. Consequently, there is no assurance that a sidecar container will start prior to your application or cease after your application has shut down.
This situation could give rise to the various issues and some of the scenarios are listed below:
- In the context of a service mesh, all inbound and outbound traffic from the pods goes through the proxy sidecar. If your application container launches before the proxy sidecar container, all outbound connections will encounter failures, causing the application initialization to falter.
- If the proxy sidecar container shuts down prior to the app container, the app container could lose connectivity before gracefully shutting down, ultimately resulting in transaction failures.
- Alternatively, when operating a Kubernetes job, if the job container closes upon completion while the sidecar continues to run, the job might be hindered from entering the completion state.
Beginning with Kubernetes version 1.28, a new capability has been introduced. This enhancement enables you to specify a distinct restartPolicy for init containers, separate from the overall Pod and other init containers. When an init container is configured with its own restartPolicy, it is referred to as a “sidecar init container.” This can be enabled using the feature gate SidecarContainers in the kubelet. It’s important to note that this particular feature is currently in its alpha stage and is not yet classified as production-ready.
Note: Please note for init containers, the only valid value for restartPolicy is Always.
Container lifecycle
In this section, we will delve into the container lifecycle, exploring how init containers, sidecar init containers, and other containers are initiated and ultimately brought to a halt. We will delve into pod termination phases.
Starting phase
- Kubernetes will start all sidecar init containers in the order they are defined.
- Once all the sidecar init containers are launched, the init containers will be initiated that runs to completion before application startup. The Kubelet will update the status of the sidecar init container as “started” under following conditions.
- If no startup probe defined, and the process is running inside the container and the postStart handler is completed. Or,
- Once the startup probe is defined and produces a successful result, and the postStart handler execution is finished,
- If an init container is created with its restartPolicy set to Always, it will start and remain running during the entire life of the Pod, which is useful for running supporting services separated from the main application containers (like log shipper or application proxy).
Running phase
- Application containers will be started.
- Sidecar init containers will keep on running. Even if the Pod’s overall restartPolicy is set to Never or OnFailure, the sidecar container will be restarted if it initially starts successfully (with a running process and a successful postStart hook), but encounters a subsequent failure. Additionally, regardless of the restartPolicy, these sidecar containers will undergo restarts in the event of either a failure or a regular exit, even as the Pod is in the process of termination.
Termination phase
- All application containers are stopped first.
- Sidecar init containers are terminated.
This lifecycle and ordering mechanism address certain issues outlined in this blog post.
Demonstration
We will begin by installing a Kubernetes cluster on our system. To achieve this, we will utilize kind to set up a local cluster. Subsequently, we will explore practical implementations of sidecar container use cases within Kubernetes deployments and jobs.
- Create a file named “kind-sidecar-k8s1.28.yaml” and populate it with the provided contents. This file contains the configuration required to create a kind Kubernetes cluster. We will enable the “SidecarContainers” feature gate and set the minimum version as 1.28.0.
kind: Cluster apiVersion: kind.x-k8s.io/v1alpha4 name: kind-sidecar-k8s1.28 featureGates: "SidecarContainers": true nodes: - role: control-plane image: kindest/node:v1.28.0
- Create the cluster using the following command.
$ kind create cluster --config kind-sidecar-k8s1.28.yaml
- Create a file “deployment-sidecar.yaml” and add the following contents. In this deployment, we will create a static web server as a sidecar init container (sws-init). This container will start before the main container (sidecar-deploy) and will be terminated last.
apiVersion: apps/v1 kind: Deployment metadata: name: sidecar-deploy labels: app: sidecar-deploy spec: replicas: 1 selector: matchLabels: app: sidecar-deploy template: metadata: labels: app: sidecar-deploy spec: containers: - name: sidecar-deploy image: alpine:latest imagePullPolicy: IfNotPresent command: ['sh', '-c', 'while true; do echo "test" >> /tmp/info.log; sleep 1; done'] volumeMounts: - name: data mountPath: /tmp resources: limits: memory: "10Mi" cpu: "10m" requests: memory: "10Mi" cpu: "10m" initContainers: - name: sws-init image: joseluisq/static-web-server:latest imagePullPolicy: IfNotPresent restartPolicy: Always ports: - containerPort: 80 startupProbe: httpGet: path: / port: 80 resources: limits: memory: "10Mi" cpu: "10m" requests: memory: "10Mi" cpu: "10m" volumes: - name: data emptyDir: {}
- Likewise, create a file named “job-sidecar.yaml” and add the following contents to it. This will demonstrate that the sidecar init container no longer blocks the Kubernetes job from transitioning into the completion state.
apiVersion: batch/v1 kind: Job metadata: name: sidecar-job spec: template: spec: containers: - name: sidecar-job image: alpine:latest command: ['sh', '-c', 'echo "test" > /tmp/info.log'] imagePullPolicy: IfNotPresent volumeMounts: - name: data mountPath: /tmp resources: limits: memory: "10Mi" cpu: "10m" requests: memory: "10Mi" cpu: "10m" initContainers: - name: sws-init image: joseluisq/static-web-server:latest imagePullPolicy: IfNotPresent restartPolicy: Always ports: - containerPort: 80 startupProbe: httpGet: path: / port: 80 resources: limits: memory: "10Mi" cpu: "10m" requests: memory: "10Mi" cpu: "10m" restartPolicy: Never volumes: - name: data emptyDir: {}
The final code is available at the following link.