Kubernetes: Using Downward API metadata from a GoLang application

In a previous post, I described the Kubernetes Downward API and how it allows us to inject pod/container metadata into our runtime container.

In this article, I’ll show how you can read the environment variables and mounted files from inside a containerized GoLang based application.

Injection of metadata

As explained in the earlier article, there are two types of metadata that can be exposed with the Downward API: pod metadata and container metadata.

This injection is put into environment variables by definitions located at ‘.spec.template.spec.containers[].env’, like below:

env:

  # pulling pod fields
  - name: MY_NODE_NAME
    valueFrom:
      fieldRef:
        fieldPath: spec.nodeName

And the injection of metadata as volume files is defined at ‘.spec.template.spec.volumes’ like below:

volumes:
  - name: podinfo
    downwardAPI:
      items:

        # pulling container fields from containers[].name
        - path: "mem_limit"
          resourceFieldRef:
            containerName: golang-hello-world-web
            resource: limits.memory
            divisor: 1Mi

View my downward-golang-web.yaml for a full example.

Environment variables from GoLang

From the example above, if you have an environment variable named MY_NODE_NAME coming from the Downward API that is the name of the Kubernetes worker node the pod is running on, then you can get that information like this using GoLang.

// pull from OS environment variable, provide default
func getenv(key, fallback string) string {
    value := os.Getenv(key)
    if len(value) == 0 {
        return fallback
    }
    return value
}

The basic call is ‘os.Getenv()’, but this function allows us to provide a default value in case it is not passed in, which is useful during non-Kubernetes testing of the container.

File from GoLang

In our manifest, the Downward API metadata is mounted at the directory “/etc/podinfo”.  Given that we injected “mem_limit”, a simple file read in GoLang for this file looks like:

data, ferr := ioutil.ReadFile("/etc/podinfo/mem_limit")
if ferr == nil {
  log.Printf("FILE %s = %s\n",file,data)
} else {
  log.Printf("error reading file %v",ferr)
}

But even better is to use “filepath.Walk()” to iterate through all the files located in “/etc/podinfo”.  This full logic is provided in main.go.

Full example

If you would like to see a full example at work, see my k8s-downward-golang-web github project.  Here is how you would grab the project, then run ‘kubectl apply’ on the manifest that deploys the Downward API with Python Flask application.

# get requirements
sudo apt-get install git make -y

# grab project from github
git clone https://github.com/fabianlee/k8s-downward-golang-web.git
cd k8s-downward-golang-web

# run kubectl apply
make k8s-apply

View the logs using kubectl

kubectl logs -l app=downward-golang-web --tail=100

And you should see a result similar to below that shows the injected environment variable and file metadata that are printed out when the container starts and the Python Flask application reads them from ‘main’.

2021/05/01 03:52:56 build version/time: 0.1.1/now
2021/05/01 03:52:56 ENV MY_NODE_NAME = microk8s-2
2021/05/01 03:52:56 ENV MY_POD_NAME = downward-golang-web-66cc9b56fb-gx929
2021/05/01 03:52:56 ENV MY_POD_IP = 10.1.100.148
2021/05/01 03:52:56 ENV MY_POD_SERVICE_ACCOUNT = default
2021/05/01 03:52:56 ENV MY_POD_LABEL_APP = downward-golang-web
2021/05/01 03:52:56 ENV MY_POD_ANNOTATION_AUTHOR = fabian lee
2021/05/01 03:52:56 ENV MY_POD_MEM_LIMIT_MB = 16
2021/05/01 03:52:56 ENV MY_POD_MEM_REQUEST_MB = 4
2021/05/01 03:52:56 FILE /etc/podinfo/annotations
2021/05/01 03:52:56 FILE /etc/podinfo/labels
2021/05/01 03:52:56 FILE /etc/podinfo/mem_limit
2021/05/01 03:52:56 FILE /etc/podinfo/mem_request
2021/05/01 03:52:56 FILE /etc/podinfo/name
2021/05/01 03:52:56 FILE /etc/podinfo/namespace
2021/05/01 03:52:56 app context: /downward-golang/
2021/05/01 03:52:56 Starting web server on port 8080

You can also exec directly into the shell and view  the env vars and volume mounted files directly.

# try 'env' and 'ls /etc/podinfo'
# use 'exit' to quit shell
kubectl exec -it $(kubectl -l app=downward-golang-web get pods --output=jsonpath={.items..metadata.name}) -- sh

 

 

REFERENCES

kubernetes.io, expose pod info to containers through env vars

kubernetes.io, expose pod info to containers through files

kubernetes.io, capabilities of Downward API