Advanced Pod Configuration: Projected Volumes, Health Checks, and Automatic Field Population

Projected Volumes in Kubernetes

Kubernetes introduces a category of volumes that don't serve traditional data storage purposes. Instead, these Projected Volumes inject predefined data directly into containers at runtime. The Kubernetes documentation defines four types of Projected Volumes: Secret, ConfigMap, Downward API, and ServiceAccountToken.

Secret Volume

The Secret volume type enables sensitive credential data to be stored in etcd and mounted into containers as files. This approach proves particularly useful for database access credentials and API keys.

Consider a scenario where database credentials need to be injected into a container:

credentials-pod.yaml

apiVersion: v1
kind: Pod
metadata:
  name: db-client-pod
spec:
  containers:
  - name: db-client
    image: busybox
    args:
    - sleep
    - "86400"
    volumeMounts:
    - name: db-credentials
      mountPath: "/secrets"
      readOnly: true
  volumes:
  - name: db-credentials
    projected:
      sources:
      - secret:
          name: db-username
      - secret:
          name: db-password

The projected volume references two Secret objects containing the database username and password. To create these secrets from files:

echo -n 'dbadmin' > ./dbuser.txt
echo -n 's3cr3tP@ss' > ./dbpass.txt

kubectl create secret generic db-username --from-file=./dbuser.txt
kubectl create secret generic db-password --from-file=./dbpass.txt

Alternatively, define secrets directly in YAML format using Base64 encoding:

db-secrets.yaml

apiVersion: v1
kind: Secret
metadata:
  name: db-secrets
type: Opaque
data:
  username: ZGJhZG1pbg==
  password: czNjcnM0VEBzc3M=

Encoding values to Base64 requires the following transformation:

echo -n 'dbadmin' | base64
# Output: ZGJhZG1pbg==

echo -n 's3cr3tP@ss' | base64
# Output: czNjcnM0VEBzc3M=

Important: Secret data encoded in Base64 is not encrypted by default. Production environments should enable Kubernetes Secret encryption providers for adequate security.

When the Pod executes, the secret files appear in the container's mounted directory:

kubectl apply -f db-secrets.yaml
kubectl apply -f credentials-pod.yaml
kubectl exec -it db-client-pod -- /bin/sh

# Within the container:
ls /secrets/
# Output: db-password  db-username
cat /secrets/username
# Output: dbadmin

Kubernetes automatically syncs volume contents when etcd data changes. Applications should implement retry logic and connection timeouts to handle potential synchronization delays.

ConfigMap Volume

ConfigMap volumes serve the same purpose as Secrets but target non-sensitive configuration data. A Java application might store its properties file in a ConfigMap:

# application.properties content
server.port=8080
app.name=my-application
log.level=INFO

kubectl create configmap app-config --from-file=./application.properties

kubectl get configmaps app-config -o yaml

The resulting ConfigMap preserves the original file structure and content.

Downward API Volume

The Downward API exposes Pod metadata directly into containers through the filesystem. This mechanism allows applications to access Pod-level information without coupling to the Kubernetes API.

metadata-pod.yaml

apiVersion: v1
kind: Pod
metadata:
  name: metadata-consumer
  labels:
    environment: production
    tier: backend
spec:
  containers:
    - name: app-container
      image: busybox
      command: ["sh", "-c"]
      args:
      - while true; do
          echo "=== Pod Labels ===";
          cat /etc/podmeta/labels;
          sleep 10;
        done;
      volumeMounts:
        - name: pod-metadata
          mountPath: /etc/podmeta
  volumes:
    - name: pod-metadata
      projected:
        sources:
        - downwardAPI:
            items:
              - path: "labels"
                fieldRef:
                  fieldPath: metadata.labels

The Downward API supports numerous fields through fieldRef:

fieldRef available fields:

  • spec.nodeName - Host node name
  • status.hostIP - Host IP address
  • metadata.name - Pod name
  • metadata.namespace - Pod namespace
  • status.podIP - Pod IP address
  • metadata.uid - Pod UID
  • metadata.labels['<KEY>'] - Specific label value
  • metadata.annotations['<KEY>'] - Specific annotation value
  • metadata.labels - All labels
  • metadata.annotations - All annotations

resourceFieldRef available fields:

  • Container CPU and memory limits
  • Container CPU and memory requests

Downward API can only expose information available before container startup. Runtime data like process IDs require sidecar containers instead.

ServiceAccountToken Volume

ServiceAccount objects provide identity for workloads running within the cluster. Each ServiceAccount links to a dedicated Secret containing authentication tokens and certificates.

Kubernetes automatically provisions a default ServiceAccount for every namespace. All Pods automatically receive this ServiceAccount's token via automatic volume mounting:

kubectl describe pod nginx-789f7d5c5-abc12

# Relevant output:
Volumes:
  default-token-xyz89:
    Type: Secret (a volume populated by a Secret)
    SecretName: default-token-xyz89
    
Mounts:
  /var/run/secrets/kubernetes.io/serviceaccount from default-token-xyz89 (ro)

The mounted directory contains essential authentication files:

ls /var/run/secrets/kubernetes.io/serviceaccount/
# Output: ca.crt  namespace  token

Applications using the official Kubernetes client libraries can leverage this automatic configuration through "InClusterConfig"—the standard approach for cluster-internal API access.

To prevent automatic token mounting, disable it at the Pod level:

spec:
  automountServiceAccountToken: false

Container Health Probes

Kubernetes delegates container health determination to kubelet through configurable probes rather than relying solely on container runtime status.

Liveness Probe

The liveness probe determines whether a container requires restarting. This example demonstrates file-based health checking:

liveness-demo.yaml

apiVersion: v1
kind: Pod
metadata:
  labels:
    check: liveness
  name: process-monitor
spec:
  containers:
  - name: monitored-process
    image: busybox
    args:
    - /bin/sh
    - -c
    - touch /tmp/running; sleep 30; rm -tmp/running; sleep 600
    livenessProbe:
      exec:
        command:
        - cat
        - /tmp/running
      initialDelaySeconds: 5
      periodSeconds: 5

This container creates /tmp/running at startup, simulating a health indicator. After 30 seconds, the file disappears, triggering the liveness probe to fail.

kubectl create -f liveness-demo.yaml

# Initial status - healthy
kubectl get pod process-monitor
NAME            READY   STATUS    RESTARTS   AGE
process-monitor  1/1    Running     0         10s

# After 30 seconds - probe fails, container restarts
kubectl describe pod process-monitor
# Events show: Liveness probe failed

kubectl get pod process-monitor
NAME            READY   STATUS    RESTARTS   AGE
process-monitor  1/1    Running     1         1m

The container restarted (RESTARTS incremented from 0 to 1), but the Pod maintains Running status because kubelet recreated the container locally rather than migrating it.

Alternative Probe Configurations

HTTP Health Check:

livenessProbe:
  httpGet:
    path: /healthz
    port: 8080
    httpHeaders:
    - name: X-Health-Check
      value: enabled
  initialDelaySeconds: 3
  periodSeconds: 3

TCP Socket Check:

livenessProbe:
  tcpSocket:
    port: 5432
  initialDelaySeconds: 15
  periodSeconds: 20

ReadinesProbe

Unlike liveness probes, readinessProbe affects Service routing rather than container lifecycle. A container failing its readiness probe gets excluded from Service load balancing until it recovers.

Pod Recovery Mechanism

The restartPolicy field controls container recovery behavior after failures:

spec:
  restartPolicy: Always | OnFailure | Never

Policy behaviors:

  • Always: Container restarts regardless of exit code. Suitable for long-running services like web servers.
  • OnFailure: Container restarts only on non-zero exit codes. Appropriate for batch jobs that complete successfully.
  • Never: Container does not restart. Use when preserving exit state and logs matters.

Critical constraint: Pod recovery always occurs on the original node. Pods never migrate automatically to different nodes unless explicitly rescheduled through controllers like Deployments.

Multi-container Pod behavior:

When a Pod contains multiple containers and one fails:

  • With restartPolicy=Always: Only the failed container restarts, Pod stays Running
  • With restartPolicy=Never: Pod stays Running, failed container stays terminated
  • All containers must fail before the entire Pod enters Failed state
kubectl get pod multi-container-pod
NAME               READY   STATUS    RESTARTS   AGE
multi-container-pod  0/2    Running      1         5m

READY 0/2 indicates one of two containers is not ready.

PodPreset for Automated Field Injection

PodPreset objects automatically augment Pod specifications at creation time, enabling separation between developer-provided configurations and operations-managed requirements.

Basic Pod submitted by developer:

frontend-pod.yaml

apiVersion: v1
kind: Pod
metadata:
  name: web-frontend
  labels:
    component: frontend
spec:
  containers:
    - name: web-server
      image: nginx
      ports:
        - containerPort: 80

Operations-defined PodPreset:

cache-preset.yaml

apiVersion: settings.k8s.io/v1alpha1
kind: PodPreset
metadata:
  name: cache-configuration
spec:
  selector:
    matchLabels:
      component: frontend
  env:
    - name: REDIS_PORT
      value: "6380"
  volumeMounts:
    - mountPath: /tmp/cache
      name: shared-cache
  volumes:
    - name: shared-cache
      emptyDir: {}

When the PodPreset selector matches the Pod's labels, Kubernetes merges the configurations:

kubectl create -f cache-preset.yaml
kubectl create -f frontend-pod.yaml

kubectl get pod web-frontend -o yaml

The resulting Pod specification includes injected values:

apiVersion: v1
kind: Pod
metadata:
  name: web-frontend
  labels:
    component: frontend
  annotations:
    podpreset.admission.kubernetes.io/cache-configuration: "resource version"
spec:
  containers:
    - name: web-server
      image: nginx
      ports:
        - containerPort: 80
      env:
        - name: REDIS_PORT
          value: "6380"
      volumeMounts:
        - mountPath: /tmp/cache
          name: shared-cache
  volumes:
    - name: shared-cache
      emptyDir: {}

The annotation indicates PodPreset modification. When multiple PodPresets target the same Pod, Kubernetes merges their changes. Conflicting field values remain unmodified from the original specification.

Tags: kubernetes pods Projected Volumes Secrets ConfigMap

Posted on Sat, 31 Oct 2026 16:18:17 +0000 by gaogier