Add binaries to Kubernetes Pod without modifying the container image

Advertisements

Given an scenario where you need to add some binaries in a Kubernetes pod running a docker image, how could you achieve it? The first approach could be, let’s modify the docker image and build a new customize image on top of the “existing” one, adding a new layer including the new binaries, downloading, installing the packages and the building that image. Finally, let’s use the new customized image.

That approach is valid and useful in most of the cases, but what about a situation where it’s needed to do it without building a new image. Is that possible? Yes, there is a little dirty workaround to achieve it, by using an Init-Container, where the binaries are downloaded, and installed and finally shared them as a volume to the main container of the pod.

In other words, it will “inject” the binaries in the main container of the pod by delegating another container to download it and make it available in a shared location of the main container. The main container docker image kept without changes at all.

Let’s start with a the pod definition for the main container.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: myapp-deployment
spec:
  template:
    spec:
      containers:
      - name: my-app-container
        image: "my-app-image:latest"

For some use case, it’s required to have some binaries available on the pod but the image should not be modified. For example, adding a tool in the environment for monitoring, logs, creating some statistics, the cases will vary depending on the needs. – And remember modifying the existing docker image is not an option -.

The following code is an example of an Init Container in a pod downloading some binaries

Advertisements
spec:
  template:
    spec:
      initContainers:
      - name: download-additional-tools
        image: alpine:3.15  
        command:
        - sh
        - -c
        args:
        - echo Downloading additional tools;
          wget -O /tools/another-tool https://another-tool.com/releases/download/v42.0.1/another-tool-amd64;
          chmod 755 /tools/another-tool;
          echo Finished downloading;

Good! but the main container still does not have access to those binaries. That can be done by adding a volume to the pod, and volume mounts on the containers sharing the location where the binary was downloaded to the main container.

spec:
  template:
    spec:
      volumes:
      - name: tools
        emptyDir: {}
      initContainers:
      - name: download-additional-tools
        ....
        volumeMounts:
        - mountPath: /tools
          name: tools
      containers:
      - name: my-app-container
        ...
        volumeMounts:
        - mountPath: /usr/local/bin/another-tool
          name: tools
          subPath: another-tool

It creates a new volume named tools of type emptyDir (a temporary directory is created with the pod and its content is deleted when the pod is removed). Both the init and the main container shared the volume tools , so the “main container” can access on the location /usr/local/bin/another-tool whatever the “init container” contains in the location /tools/.

Let’s see the complete yaml file for the kubernetes pod.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: myapp-deployment
spec:
  template:
    spec:
      volumes:
      - name: tools
        emptyDir: {}
        
      initContainers:
      - name: download-additional-tools
        image: alpine:3.15  
        command:
        - sh
        - -c
        args:
        - echo Downloading additional tools;
          wget -O /tools/another-tool https://another-tool.com/releases/download/v42.0.1/another-tool-amd64;
          chmod 755 /tools/another-tool;
          echo Finished downloading;
        volumeMounts:
        - mountPath: /tools
          name: tools  
          
      containers:
      - name: my-app-container
        image: "my-app-image:latest"
        volumeMounts:
        - mountPath: /usr/local/bin/another-tool
          name: tools
          subPath: another-tool

Voila, applying the changes to the deployment will create a new pod that will include the binaries without modifying the docker image.

kubectl patch deployments/myapp-deployment --patch-file=deployment-patch.yaml

Checking at the pod, the binaries should be available for the main container.

References

Advertisements

Leave a Reply

Your email address will not be published. Required fields are marked *