Busybox as a web CGI Server within Kubernetes

It is always useful to have basic tools for testing when working with Pods and services within Kubernetes. In this post I'll be taking a look at using Busybox the 'swiss army knife' of embedded Linux to run a light weight http server.

The aim of this lab is to build a simple Pod that will display basic information such as IP address, hostname and environmental values that are sent to it. This can be used to confirm the functionality of an attached service load balancing between the Pods within the Deployment.

The information that is displayed will be generated by a simple bash script that displays the hostname and IP address of the running container as well as the current date and an environmental value passed to it.

As well as this there is a simple CSS file that will be used to add a little colour. These files will be mounted into the Busybox container using ConfigMaps to keep things simple.

Creating the ConfigMaps

Before the Deployment can be created the two Configmaps must be created. There are various ways of creating the two necessary objects and the way they were built for this lab was generating some suitable files in a directory and using them to create the configmaps.

The following files were used:

cgi-table.sh

My_date=$(date)
My_host=$(hostname)
My_env=$(env)
My_var=$(echo "$MY_VAR")
My_IP=$(ip add show eth0 | grep inet)

echo "Content-type: text/html" # Tells the browser what type of content
echo "" # Empty Line

echo "<link rel="stylesheet" type="text/css" href="/css/mystyle.css""
echo ""
echo "table style="width:100%">"
echo "<table border=1>"
echo ""
echo "<tr>"
echo "  <th>Information</th>"
echo "  <th>Value</th>"
echo "</tr>"
echo "<tr>"
echo "  <td>Hostname</td>"
echo "  <td>$My_host</td>"
echo "</tr>"
echo "<tr>"
echo "  <td>Date</td>"
echo "  <td>$My_date</td>"
echo "</tr>"
echo "<tr>"
echo "  <td>IP Address</td>"
echo "  <td>$My_IP</td>"
echo "</tr>"
echo "<tr>"
echo "  <td>My Var</td>"
echo "  <td>$My_var</td>"
echo "</tr>"
echo "</table>"

mystyle.css

body {                                                                                                                                        
  background-color: lightblue;
}

h1 {
  color: navy;
  margin-left: 20px;
}

p {
  color: blue;
}

table,th,td {
  border: 1px solid black;
  width: 75%;
  text-align: left;
  padding: 15px;
}

Both of the configmaps were built by running the following commands:

kubectl create configmap cgi-table.sh --from-file=cgi-table.sh
kubectl create configmap mystyle.css --from-file=mystyle.css

Once this was done the existance of the configmaps can be confirmed.

vagrant@k8s-master:~/httpd$ kubectl get configmaps 
NAME           DATA   AGE
cgi-table.sh   1      94m
mystyle.css    1      151m

The configmaps must be made before the Deployment of the Pods or the Deployment will stay in the pending state.

Adding the Deployment

The Deployment being used is based upon a Busybox image and will mount the configMaps to add the necessary script and CSS file. It also has a command argument to run the httpd service to listen on port 8080.

An environmental variable is also being added to the container and will be displayed by the httpd server.

The manifest file is

apiVersion: apps/v1
kind: Deployment
metadata:
  creationTimestamp: null
  labels:
    app: my-busybox-httpd
  name: my-busybox-httpd
spec:
  replicas: 1
  selector:
    matchLabels:
      app: my-busybox-httpd
  strategy: {}
  template:
    metadata:
      creationTimestamp: null
      labels:
        app: my-busybox-httpd
    spec:
      containers:
      - image: busybox:1.28
        name: busybox
        command: ["bin/sh","-c"]
        args:
        - httpd -p 8080 -f -v -h /var/www/;
        volumeMounts:
        - name: cgi-script
          mountPath: /var/www/cgi-bin
        - name: mystyle-css
          mountPath: /var/www/css
        env:
        - name: MY_VAR
          value: "This is an environmental variable"
        resources: {}
      volumes:
        - name: cgi-script
          configMap:
              name: cgi-table.sh
              defaultMode: 0777
        - name: mystyle-css
          configMap:
             name: mystyle.css
status: {}

The Deployment is created using the manifest file.

kubectl create -f http-busybox.yaml

As the cgi-script must be executable the permissions of the volume must be updated, which is the purpose of the defaultMode line within the manifest.

Exposing the Deployment

After the Deployment has been created it can be exposed using a simple Nodeport to allow access from outside the cluster.

kubectl expose deployment my-busybox-httpd --port=8080 --type=NodePort

It is important to ensure that the Nodeport connects to the correct port that has been exposed within the container, which in this case has been set as 8080.

Scaling the Deployment to 3 Replicas

The manifest only creates a single replica of the Pod so the final stage is to scale this up to 3 replicas that will allow the verification of the load balancing.

kubectl scale deployment my-busybox-httpd --replicas=3 --record

Confirming Everything is Running

The next step is to confirm that everything is running

Once this is done the created cgi-script can be viewed in a browser from outside the cluster using the relevant port that has been exposed by the Nodeport service.

It is important that the correct URL be used to display the output of the script. The browser can be refreshed to prove that the traffic is coming from different Pods via the Nodeport service.

It can be seen from the hostname being displayed as well as the IP address that the traffic is being served by different containers. It's also actually being served by containers running on different nodes within the cluster (which is determined by the IP address of the container).

The environmental variable has also been sent to all the relevant Pods and remains the same.

Conclusions

This lab has shown to serve simple dynamic web pages it is possible to make use of a simple Busybox image running httpd. The page being served is actually a cgi script running within the container which returns the relevant output as http to the end browser.

The necessary script and css file have been mounted into the container using configmaps for ease of use. However the script generating the page had to have it's permissions set appropriately within the container to allow it to execute.

The Deployment was created initially with a single replica and was then dynamically scaled up. This allowed the confirmation the traffic was indeed being load balanced by the attached Nodeport service.