A Machine Learning Engineer's Homepage

It is not just about machine learning engineering ...

View on GitHub

Introducing Pods in Kubernetes - II: Pod YAML Manifest

💬 “天地不仁,以万物为刍狗。(Heaven and Earth are impartial, they regard all things as straw dogs.)”
— Laozi

In my previous post, we introduced the concept of Pods in Kubernetes—what they are, how they fit into the Kubernetes architecture, and why they are the smallest deployable unit in a cluster. Now, we’ll transition from theory to practice by exploring the YAML manifest used to define a Pod and focus on several important container-level configurations.

To start a pod in Kubernetes, you may use kubectl run (imperative way) with an exposed port:

kubectl run nginx-pod --image=nginx:1.25 --port=80 

This creates a Pod named nginx-pod running the nginx:1.25 image and opens port 80. It is useful for quick testing, but for production or repeatable deployments, YAML manifests are preferred.

For example, imagine you need to recreate the same Pod after a crash, or you want to deploy the same Pod across multiple environments like dev, staging, and production. If you only used kubectl run, you’d need to remember or script out all the command-line options, which can be error-prone and hard to maintain. Therefore, instead of manually creating Pods using kubectl run or other imperative commands, Kubernetes encourages the use of declarative configuration via YAML files. This approach provides better reproducibility, version control, and consistency across environments.

In this post, we’ll walk through a complete Pod manifest and break down key sections commonly used in production deployments. These configurations ensure your containers are healthy, efficient, and secure. We’ll cover:


Sample Pod YAML Manifest

Here’s a complete sample YAML manifest demonstrating best practices for configuring a container inside a Pod:

apiVersion: v1
kind: Pod
metadata:
  name: sample-app
  namespace: default
  labels:
    app: sample
    environment: production
spec:
  containers:
    - name: sample-container
      image: nginx:1.25
      ports:
        - containerPort: 80
      resources:
        requests:
          memory: "64Mi"
          cpu: "250m"
        limits:
          memory: "128Mi"
          cpu: "500m"
      readinessProbe:
        httpGet:
          path: /
          port: 80
        initialDelaySeconds: 5
        periodSeconds: 10
        failureThreshold: 3
      livenessProbe:
        httpGet:
          path: /
          port: 80
        initialDelaySeconds: 15
        periodSeconds: 20
        failureThreshold: 3
      volumeMounts:
        - name: app-config
          mountPath: /etc/config
          readOnly: true
      envFrom:
        - secretRef:
            name: app-secret
      env:
        - name: API_KEY
          valueFrom:
            secretKeyRef:
              name: app-secret
              key: api-key
  volumes:
    - name: app-config
      configMap:
        name: app-config

Pod Metadata and Spec

The top-level fields of a Pod YAML define its identity and structure.

apiVersion: v1
kind: Pod
metadata:
  name: sample-app
  namespace: default
  labels:
    app: sample
    environment: production
spec:
  ...

Why it matters: Proper metadata ensures the Pod is uniquely identifiable and can be targeted by other Kubernetes resources like Services or HorizontalPodAutoscalers. Incorrect or missing metadata can lead to naming conflicts or Pods being unselectable by controllers.


Readiness Probe

The readiness probe determines when a container is ready to accept traffic. Until the probe passes, the Pod is excluded from Service endpoints, preventing traffic from reaching an unready container.

readinessProbe:
  httpGet:
    path: /
    port: 80
  initialDelaySeconds: 5
  periodSeconds: 10
  failureThreshold: 3

What happens if it fails?: If the probe fails (e.g., the endpoint returns a non-2xx HTTP status), Kubernetes marks the container as “Not Ready,” and the Pod is removed from Service endpoints. This prevents traffic from being routed to an unhealthy container, ensuring reliability. Once the probe succeeds, the Pod is added back to the Service.

Use case: Useful for applications that need time to load configurations or establish database connections before handling requests.


Liveness Probe

The liveness probe checks if a container is still healthy. If it fails, Kubernetes restarts the container to recover it.

livenessProbe:
  httpGet:
    path: /
    port: 80
  initialDelaySeconds: 15
  periodSeconds: 20
  failureThreshold: 3

What happens if it fails?: If the probe fails (e.g., due to a crashed application or unresponsive endpoint), Kubernetes restarts the container. This is useful for recovering from deadlocks, memory leaks, or other issues that cause the application to become unresponsive.

Use case: Essential for long-running applications where temporary failures (e.g., a stuck process) can be resolved by restarting the container.


CPU and Memory: Requests and Limits

Resource requests and limits help Kubernetes schedule Pods and manage resource contention.

resources:
  requests:
    memory: "64Mi"
    cpu: "250m"
  limits:
    memory: "128Mi"
    cpu: "500m"

What happens if limits are exceeded?:

Why it matters: Requests help Kubernetes schedule Pods on nodes with sufficient resources, while limits prevent resource hogging, ensuring cluster stability.


Volumes and VolumeMounts

Volumes provide persistent or shared data to containers, such as configuration files or persistent storage.

volumes:
  - name: app-config
    configMap:
      name: app-config
volumeMounts:
  - name: app-config
    mountPath: /etc/config
    readOnly: true

What is it used for?: Volumes like ConfigMaps are ideal for injecting configuration files, such as Nginx server configs or application settings, without modifying the container image. They decouple configuration from code, making applications more portable and easier to update.

What happens if misconfigured?: If the ConfigMap doesn’t exist or the mount path conflicts with existing files, the container may fail to start or behave unexpectedly. Always ensure the referenced ConfigMap exists in the same namespace.


Environment Variables from Secrets

Secrets securely store sensitive data like API keys or passwords and can be injected as environment variables.

envFrom:
  - secretRef:
      name: app-secret
env:
  - name: API_KEY
    valueFrom:
      secretKeyRef:
        name: app-secret
        key: api-key

What if I want a specific secret?: Use the env field with secretKeyRef (as shown) to retrieve only specific keys from a Secret, rather than exposing all key-value pairs. This is more secure and avoids cluttering the environment with unnecessary variables. By the way, the env field is not necessary if all the envs are already populated by envFrom. It is just for demonstration here.

What happens if misconfigured?: If the Secret app-secret doesn’t exist or the api-key key is missing, the container will fail to start. Ensure the Secret exists in the same namespace and contains the expected keys.

Why it matters: Secrets provide a secure way to manage sensitive data, reducing the risk of exposing credentials in logs or container images.


Deploying and Exploring the Pod

Apply the YAML

kubectl apply -f sample-pod.yaml

Check Pod Status

kubectl get pods
kubectl describe pod sample-app

View Logs

kubectl logs sample-app

Port Forward (to test readiness/liveness endpoints)

kubectl port-forward pod/sample-app 8080:80
curl http://localhost:8080/

Check Probes

kubectl get endpoints
# -A5: include 5 lines After match. -i: ignore cases
kubectl describe pod sample-app | grep -A5 -i "Readiness"

Summary

This post walked through a detailed Pod manifest and highlighted key configuration options at the container level that are critical for observability, reliability, and resource management. Mastering these configurations helps ensure your application runs efficiently and is easier to manage in production.

For further reading, check out the official Kubernetes documentation on Pods.