Chapter 20 of 47 · Configuration and resources

Self awareness: the Downward API

Your app runs in a Pod, but it does not automatically know which Pod. What is my name? Which node am I on? How much memory am I allowed? The Downward API hands these facts "down" from Kubernetes into the container, as environment variables or as files. No extra code, no calls to the Kubernetes API.

Why an app wants to know about itself

Think of a name badge handed to a new employee at the door: name, department, desk number. They did not have to go and ask. The Downward API is that badge.

Kubernetes hands Pod facts down into the containerKubernetes knows the Pod name, namespace, IP, node, labels and resource limits. Through the Downward API it puts them into the container as environment variables or files. Kubernetes knows name: posts-depl-4kq2pnamespace: defaultpodIP: 10.244.1.15 · node: node-1labels: app=posts, version=v2limits.memory: 512Mi Downward API container posts env: POD_NAME, POD_IP … files: /etc/podinfo/labels

What you can ask for

FieldGivesAs envAs file
metadata.namePod name✓✓
metadata.namespaceNamespace✓✓
metadata.uidUnique Pod ID✓✓
metadata.labels['app']One label✓✓
metadata.labels, metadata.annotationsAll labels / annotations—✓ (and kept up to date)
status.podIP, status.hostIPPod IP, node IP✓—
spec.nodeNameNode name✓—
spec.serviceAccountNameServiceAccount (chapter 43)✓—
limits.cpu, limits.memory, requests.*The container's own resources (chapter 22)✓✓

The first group uses fieldRef; the resource numbers use resourceFieldRef.

As environment variables

Same env list as chapter 16, but instead of value: you say where the value comes from. Click the parts:

containers:
  - name: posts
    image: micro-posts:1.0
    env:
      - name: POD_NAME
        valueFrom:
          fieldRef:
            fieldPath: metadata.name
      - name: NODE_NAME
        valueFrom:
          fieldRef:
            fieldPath: spec.nodeName
      - name: POD_IP
        valueFrom:
          fieldRef:
            fieldPath: status.podIP
      - name: MEMORY_LIMIT_MB
        valueFrom:
          resourceFieldRef:
            resource: limits.memory
            divisor: 1Mi
    resources:
      limits:
        memory: 512Mi

fieldRef

Points at a field of the Pod object. metadata.name becomes something like posts-depl-7d9f8c6b5d-4kq2p.

// posts/index.js
const log = (msg) => console.log(`[${process.env.POD_NAME} on ${process.env.NODE_NAME}] ${msg}`);
log('Posts listening on 4000');
// [posts-depl-7d9f8c6b5d-4kq2p on node-1] Posts listening on 4000

As files

spec:
  containers:
    - name: posts
      image: micro-posts:1.0
      volumeMounts:
        - name: podinfo
          mountPath: /etc/podinfo
  volumes:
    - name: podinfo
      downwardAPI:
        items:
          - path: labels
            fieldRef: { fieldPath: metadata.labels }
          - path: annotations
            fieldRef: { fieldPath: metadata.annotations }
          - path: name
            fieldRef: { fieldPath: metadata.name }

The big difference: files stay up to date, environment variables do not. Step through:

  1. $ kubectl label pod posts-depl-7d9f8c6b5d-4kq2p tier=gold

    You add a label while the Pod is running.

  2. env:   (no change: env vars were copied when the container started)
    file:  /etc/podinfo/labels   → still the old content for a moment

    The kubelet refreshes Downward API files periodically.

  3. $ kubectl exec … -- cat /etc/podinfo/labels
    app="posts"
    tier="gold"            ← appears without a restart
    version="v2"

    Shortly after, the file shows the new label. Use files for anything that can change while the Pod runs: labels and annotations.

Real example: sizing Node's memory

Node.js picks its maximum heap size on its own, and it does not always look at the container's limit. If the heap grows past the limit, the container is OOMKilled (chapter 22). Hand Node the real limit and keep some room for everything outside the heap:

env:
  - name: MEMORY_LIMIT_MB
    valueFrom:
      resourceFieldRef:
        resource: limits.memory
        divisor: 1Mi
command: ["sh", "-c", "exec node --max-old-space-size=$((MEMORY_LIMIT_MB * 3 / 4)) index.js"]

Change the limit in YAML and the heap follows automatically. No second number to keep in sync. (exec keeps Node as the main process, so it still receives SIGTERM: chapter 14.)

What it cannot do

Recap

Quick check

You give MEMORY_LIMIT_MB from limits.memory, but the container has no memory limit. What value arrives?
Show answer

The node's allocatable memory, which is far too big to size a heap from. Always set a memory limit when you read it this way.

Downward API: common questions

How does a Pod know its own name in Kubernetes?

Use the Downward API: an env var with valueFrom.fieldRef.fieldPath set to metadata.name, or a downwardAPI volume.