General Purpose Containers in Kubernetes

Sometimes you just need to have a quick solution for a simple task.

Up until recently I had the habit of creating dedicated containers for each specific Pod or Deployment I needed in Kubernetes.

But I found this approach to be a complete overkill for smaller tasks that usually just consist of a simple Python or shell script. The base image, tooling etc. are always the same.

Through some experimentation, and because I love using Python, I created a general purpose Python container that I can use for many different smaller tasks without needing to create new container images for each "little project".

Towards a solution...

Typically, containers can be deployed in Kubernetes (in a Pod) and without any special or fancy configuration, it will just run the default CMD as defined in the Dockerfile.

With this in mind, it is possible to quickly serve static content through nginx, as can be seen from the following example (credit to zjor on GitHub):

# Adapted from https://gist.github.com/zjor/25c79bc5792ca32805cd6f50d180952e
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: config-map
  namespace: testing
data:
  index.html: |
    <!DOCTYPE html>
    <html>
      <body>
        <h1>Hello from the Cloud</h1>
        <script>
          const queryDict = {};
          location.search.substr(1).split("&").forEach(function(item) {queryDict[item.split("=")[0]] = item.split("=")[1]})
          document.querySelector("h1").innerText=`Hello ${queryDict["name"]} from the Cloud`;
        </script>
      </body>
    </html>
---
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: nginx
  name: nginx
  namespace: testing
spec:
  selector:
    matchLabels:
      app: nginx
  replicas: 1
  template:
    metadata:
      labels:
        app: nginx
    spec:    
      restartPolicy: Always
      containers:
      - image: nginx:stable-alpine
        imagePullPolicy: Always
        name: nginx
        ports:
        - containerPort: 80
        volumeMounts:
        - name: index-volume
          mountPath: /usr/share/nginx/html
      volumes:
      - name: index-volume
        configMap:
          name: config-map
---
apiVersion: v1
kind: Service
metadata:
  labels:
    app: nginx
  name: nginx-svc
  namespace: testing
spec:
  ports:
  - name: "80"
    port: 80
    targetPort: 80
  selector:
    app: nginx

For a quick deployment, follow these steps (you will need two terminals):

Terminal 1:

# If not already created earlier, create a testing namespace
kubectl  create namespace testing

# Deploy
kubectl apply -f - <<EOF
# Content from above manifest...
EOF

# Port Forward
kubectl port-forward $(kubectl get pods -lapp=nginx --output jsonpath='{.items[0].metadata.name}' -n testing) -n testing 9090:80

In terminal two the static content can be retrieved:

curl -s http://localhost:9090/

Or. open this link in a web browser.

To cleanup, stop the port forwarding and run the following:

kubectl delete namespace testing

Therefore, the nginx container is configured to automatically start the HTTP server and serve what ever is in the directory /usr/share/nginx/html. If you use ConfigMaps and Volumes, you can easily serve static pages from Kubernetes.

What is even better, is that you can have multiple of these static web page serving Pods, all serving different content while they are all using the same base container image.

Pretty cool!

A more advanced use case

Serving static content is one thing, but what if I need a REST API with some logic, or even some interaction with the Kubernetes API?

And that is why I created my general purpose Python container.

In this repository there is an example using a Python script with integration to the Kubernetes API, which will return a list of namespaces (just as a test).

It uses the same basic principle of the nginx example, but there is no default CMD option, and therefore in the deployment, the container has a specific command and arguments to start the REST API server. The source code is defined in a ConfigMap.

It should be really easy to see how more complex solutions can be devised through the use of volumes. If you have a packaged Python module in a ZIP or other compressed format, the started ConfigMap can easily just contain a script to unpack and install the package and then run the main application.

Advantages and Disadvantages

There are a lot of advantages, and I think the key ones for me include the following:

The main disadvantages are:

Conclusion

Kubernetes and containers gives us a lot of flexibility and using a shared container image to run various workloads is definitely a very handy tool to have. However, always be aware of the risks and make sure you make informed decisions before blindly just using a shared container for everything.

Tags

kubernetes, python