Skip to content

Understanding Kubernetes Objects and YAML Manifests

Master the foundation of Kubernetes declarative configuration. Learn object anatomy, YAML syntax, labels, selectors, and annotations for CKA exam success and production deployments.

Overview

Kubernetes objects are persistent entities in the Kubernetes system that represent the desired state of your cluster. Understanding object structure and YAML manifests is fundamental to the CKA exam and real-world Kubernetes administration.

CKA Exam Domain: All domains (objects are used everywhere)

Key Insight: Every Kubernetes resource you create, modify, or delete is an object with a consistent structure. Mastering this structure enables you to work with any Kubernetes resource confidently.

What You'll Learn: - Kubernetes API object model and resource types - YAML syntax fundamentals and best practices - Object anatomy: metadata, spec, status structure - Labels and selectors for resource organization - Annotations for non-identifying metadata - Field validation and troubleshooting strategies


Kubernetes Object Model

Kubernetes uses a declarative model where you describe the desired state, and the control plane works continuously to maintain that state.

What is a Kubernetes Object?

Definition: A Kubernetes object is a persistent entity that represents: - What applications are running (and on which nodes) - How many replicas should exist - Which resources are available to applications - Policies around behavior (restart, upgrades, fault-tolerance)

Object Categories

graph TB
    subgraph "Workload Resources"
        POD[Pod]
        DEPLOY[Deployment]
        RS[ReplicaSet]
        SS[StatefulSet]
        DS[DaemonSet]
        JOB[Job]
        CRON[CronJob]
    end

    subgraph "Service & Networking"
        SVC[Service]
        ING[Ingress]
        NP[NetworkPolicy]
        EP[Endpoints]
    end

    subgraph "Configuration & Storage"
        CM[ConfigMap]
        SECRET[Secret]
        PV[PersistentVolume]
        PVC[PersistentVolumeClaim]
        SC[StorageClass]
    end

    subgraph "Cluster Resources"
        NS[Namespace]
        NODE[Node]
        SA[ServiceAccount]
        ROLE[Role/ClusterRole]
    end

    DEPLOY --> RS
    RS --> POD
    SS --> POD
    DS --> POD
    SVC --> POD
    ING --> SVC

    style POD fill:#e1f5ff
    style DEPLOY fill:#e8f5e8
    style SVC fill:#fff4e1
    style CM fill:#f5e1ff

Object Lifecycle

stateDiagram-v2
    [*] --> Desired: User creates manifest

    Desired --> Submitted: kubectl apply
    Submitted --> Validated: API server validates

    Validated --> Rejected: Validation fails
    Rejected --> [*]

    Validated --> Persisted: Write to etcd
    Persisted --> Scheduled: Controller processes
    Scheduled --> Running: Kubelet executes

    Running --> Updated: User modifies
    Updated --> Validated

    Running --> Deleted: User deletes
    Deleted --> [*]

    Running --> Running: System reconciles

Object Anatomy: The Four Essential Fields

Every Kubernetes object manifest contains four top-level fields:

Complete Object Structure

apiVersion: apps/v1              # 1. API version
kind: Deployment                 # 2. Object type
metadata:                        # 3. Identifying metadata
  name: nginx-deployment
  namespace: production
  labels:
    app: nginx
    tier: frontend
  annotations:
    description: "Production web server"
spec:                            # 4. Desired state
  replicas: 3
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.21
        ports:
        - containerPort: 80
status:                          # System-managed current state
  availableReplicas: 3
  readyReplicas: 3
  observedGeneration: 1

1. apiVersion

Purpose: Specifies the API group and version for the object type.

Format: <group>/<version> or just <version> for core API

Common API Versions:

# Core API (no group)
v1                              # Pod, Service, ConfigMap, Secret, Namespace

# Apps API
apps/v1                         # Deployment, StatefulSet, DaemonSet, ReplicaSet

# Batch API
batch/v1                        # Job, CronJob

# Networking API
networking.k8s.io/v1           # Ingress, NetworkPolicy

# RBAC API
rbac.authorization.k8s.io/v1   # Role, ClusterRole, RoleBinding

# Storage API
storage.k8s.io/v1              # StorageClass, VolumeAttachment

Finding API Versions:

# List all API resources and versions
kubectl api-resources

# Check specific resource
kubectl explain deployment | head -5
# KIND:       Deployment
# VERSION:    apps/v1

# List all API versions
kubectl api-versions

2. kind

Purpose: Identifies the type of object being created.

Common Object Kinds: - Workloads: Pod, Deployment, StatefulSet, DaemonSet, Job, CronJob - Services: Service, Ingress, Endpoints - Configuration: ConfigMap, Secret - Storage: PersistentVolume, PersistentVolumeClaim, StorageClass - Cluster: Namespace, Node, ServiceAccount - Access: Role, ClusterRole, RoleBinding, ClusterRoleBinding

Case Sensitivity: Kind names are case-sensitive (must be exact capitalization).

3. metadata

Purpose: Data that uniquely identifies the object.

Required Fields: - name - Unique within namespace and object type - namespace - (optional for cluster-scoped resources)

Common Optional Fields: - labels - Key-value pairs for organization and selection - annotations - Non-identifying metadata - uid - System-generated unique identifier - resourceVersion - Internal version for optimistic concurrency - creationTimestamp - When object was created

Metadata Example:

metadata:
  name: webapp                           # Required: object name
  namespace: production                  # Namespace (required for namespaced resources)
  labels:                                # Labels for selection
    app: webapp
    tier: frontend
    version: "2.0"
    environment: production
  annotations:                           # Annotations for metadata
    description: "Main production web application"
    owner: "platform-team@company.com"
    version: "2.0.1"
    deployment-date: "2024-01-15"
  uid: 12345678-1234-1234-1234-123456789012    # System-generated
  resourceVersion: "1234567"             # System-managed version
  creationTimestamp: "2024-01-15T10:30:00Z"  # System-generated

4. spec

Purpose: Describes the desired state of the object.

Characteristics: - Varies by object kind - User-defined and user-managed - Controller reads spec to understand desired state - Declarative: describes "what", not "how"

Pod Spec Example:

spec:
  containers:
  - name: nginx
    image: nginx:1.21
    ports:
    - containerPort: 80
    resources:
      requests:
        cpu: 100m
        memory: 128Mi
      limits:
        cpu: 200m
        memory: 256Mi
    livenessProbe:
      httpGet:
        path: /healthz
        port: 80
      initialDelaySeconds: 30
      periodSeconds: 10
  restartPolicy: Always
  nodeSelector:
    disk: ssd

Deployment Spec Example:

spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx
  template:                    # Pod template
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.21
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 1
      maxUnavailable: 0

5. status (System-Managed)

Purpose: Describes the current observed state of the object.

Characteristics: - Read-only for users - Managed by Kubernetes controllers - Updated continuously by the control plane - Represents actual state vs desired state (spec)

Pod Status Example:

status:
  phase: Running               # Pod lifecycle phase
  conditions:                  # Detailed status conditions
  - type: Initialized
    status: "True"
    lastTransitionTime: "2024-01-15T10:30:05Z"
  - type: Ready
    status: "True"
    lastTransitionTime: "2024-01-15T10:30:10Z"
  - type: ContainersReady
    status: "True"
    lastTransitionTime: "2024-01-15T10:30:10Z"
  - type: PodScheduled
    status: "True"
    lastTransitionTime: "2024-01-15T10:30:00Z"
  containerStatuses:
  - name: nginx
    ready: true
    restartCount: 0
    state:
      running:
        startedAt: "2024-01-15T10:30:08Z"
  hostIP: 192.168.1.100
  podIP: 10.244.1.50
  startTime: "2024-01-15T10:30:00Z"

Deployment Status Example:

status:
  availableReplicas: 3
  readyReplicas: 3
  replicas: 3
  updatedReplicas: 3
  observedGeneration: 5
  conditions:
  - type: Available
    status: "True"
    reason: MinimumReplicasAvailable
  - type: Progressing
    status: "True"
    reason: NewReplicaSetAvailable


YAML Syntax Fundamentals

YAML (YAML Ain't Markup Language) is the standard format for Kubernetes manifests.

YAML Basics

Data Types:

# Strings
name: nginx-deployment
description: "Multi-word string in quotes"
multiline: |
  This is a multi-line string
  that preserves newlines

# Numbers
replicas: 3
port: 80
cpu: 0.5

# Booleans
enabled: true
debug: false

# Lists (arrays)
args:
- "arg1"
- "arg2"
- "arg3"

# Or inline
args: ["arg1", "arg2", "arg3"]

# Maps (key-value pairs)
labels:
  app: nginx
  tier: frontend

# Nested structures
metadata:
  labels:
    app: nginx
  annotations:
    description: "Production app"

Indentation Rules

Critical: YAML uses spaces only (no tabs) for indentation.

# CORRECT - 2 spaces per level
metadata:
  name: nginx
  labels:
    app: nginx
    tier: frontend

# CORRECT - 4 spaces also works (be consistent)
metadata:
    name: nginx
    labels:
        app: nginx

# WRONG - mixing tabs and spaces
metadata:
    name: nginx        # Tab used (will fail)
  labels:
    app: nginx       # Spaces used

Best Practice: Use 2-space indentation consistently.

Common YAML Patterns

Lists of Objects:

containers:
- name: nginx                   # First container
  image: nginx:1.21
  ports:
  - containerPort: 80
- name: sidecar                 # Second container
  image: busybox:latest
  command: ["sh", "-c", "sleep 3600"]

Multi-line Strings:

# Literal block (preserves newlines)
script: |
  #!/bin/bash
  echo "Line 1"
  echo "Line 2"

# Folded block (newlines become spaces)
description: >
  This is a very long description
  that spans multiple lines but
  will be folded into a single line.

# Result: "This is a very long description that spans multiple lines..."

Environment Variables:

env:
- name: DATABASE_HOST
  value: "mysql.default.svc.cluster.local"
- name: DATABASE_PORT
  value: "3306"
- name: DATABASE_PASSWORD
  valueFrom:
    secretKeyRef:
      name: db-secret
      key: password

YAML Validation

Common Errors:

# ERROR: Missing colon
metadata
  name: nginx         # Should be "metadata:"

# ERROR: Wrong indentation
metadata:
  name: nginx
 labels:              # Misaligned (should be 2 spaces)
   app: nginx

# ERROR: Missing quotes for special characters
annotation: "true"   # Boolean - needs quotes to be string
annotation: true     # Boolean value

# ERROR: List item indentation
containers:
  - name: nginx      # Correct
- name: sidecar      # Wrong (should align with first item)

Validation Tools:

# kubectl validates before applying
kubectl apply --dry-run=client -f manifest.yaml

# Check syntax only
kubectl apply --dry-run=server -f manifest.yaml

# Use yamllint for detailed validation
yamllint manifest.yaml

# Python YAML validation
python -c 'import yaml, sys; yaml.safe_load(sys.stdin)' < manifest.yaml


Labels: Organizing and Selecting Resources

Labels are key-value pairs attached to objects for identification and grouping.

Label Syntax

Format: key: value

Key Syntax: - Optional prefix: <prefix>/<name> (prefix ≤ 253 chars, name ≤ 63 chars) - Name: alphanumeric, -, _, . (must start/end with alphanumeric)

Value Syntax: - ≤ 63 characters - Alphanumeric, -, _, . (can be empty)

Examples:

labels:
  # Simple labels
  app: nginx
  tier: frontend
  environment: production

  # Prefixed labels (for third-party tools)
  example.com/team: platform
  app.kubernetes.io/name: nginx
  app.kubernetes.io/version: "1.21"

  # Valid special characters
  app.version: "2.0.1"
  team_name: platform-team

Kubernetes recommends a standard set of labels for consistency:

metadata:
  labels:
    # Application identity
    app.kubernetes.io/name: nginx                    # Application name
    app.kubernetes.io/instance: nginx-prod           # Unique instance
    app.kubernetes.io/version: "1.21.0"             # Application version
    app.kubernetes.io/component: webserver           # Component role
    app.kubernetes.io/part-of: ecommerce-platform   # Parent application

    # Management metadata
    app.kubernetes.io/managed-by: helm              # Management tool

    # Custom organizational labels
    team: platform
    cost-center: engineering
    environment: production

Label Use Cases

1. Resource Organization:

# Development pods
metadata:
  labels:
    environment: dev
    team: backend

# Production pods
metadata:
  labels:
    environment: production
    team: backend

2. Service Selection:

# Service selects pods with matching labels
apiVersion: v1
kind: Service
metadata:
  name: nginx-service
spec:
  selector:
    app: nginx           # Selects all pods with app=nginx
    tier: frontend
  ports:
  - port: 80

3. Deployment Management:

apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: nginx
spec:
  selector:
    matchLabels:
      app: nginx        # Deployment manages pods with this label
  template:
    metadata:
      labels:
        app: nginx      # Pods get this label

Label Operations

View Labels:

# Show labels column
kubectl get pods --show-labels

# Show specific labels as columns
kubectl get pods -L app,tier,environment

# Filter output
kubectl get pods -l app=nginx
kubectl get pods -l 'environment in (dev,staging)'
kubectl get pods -l app=nginx,tier!=backend

Add/Modify Labels:

# Add label
kubectl label pod nginx-pod version=1.0

# Modify label (requires --overwrite)
kubectl label pod nginx-pod version=2.0 --overwrite

# Add label to all pods
kubectl label pods --all environment=production

# Remove label
kubectl label pod nginx-pod version-


Selectors: Matching Resources

Selectors use labels to identify sets of objects.

Selector Types

graph TB
    SELECTOR[Label Selectors] --> EQUALITY[Equality-Based]
    SELECTOR --> SET[Set-Based]

    EQUALITY --> EQ[Equal: =, ==]
    EQUALITY --> NEQ[Not Equal: !=]

    SET --> IN[In: in (...)]
    SET --> NOTIN[Not In: notin (...)]
    SET --> EXISTS[Exists: key]
    SET --> NOTEXISTS[Not Exists: !key]

    style EQUALITY fill:#e1f5ff
    style SET fill:#e8f5e8

Equality-Based Selectors

Syntax: key=value or key!=value

Command-Line Examples:

# Single equality
kubectl get pods -l app=nginx

# Multiple conditions (AND logic)
kubectl get pods -l app=nginx,tier=frontend

# Not equal
kubectl get pods -l app=nginx,environment!=production

Manifest Examples:

# Service selector (equality-based only)
selector:
  app: nginx
  tier: frontend

# Equivalent to:
# app=nginx AND tier=frontend

Set-Based Selectors

Syntax: More expressive matching with in, notin, exists

Command-Line Examples:

# In set
kubectl get pods -l 'environment in (dev,staging)'

# Not in set
kubectl get pods -l 'tier notin (cache,db)'

# Label exists
kubectl get pods -l app

# Label does not exist
kubectl get pods -l '!app'

# Complex combination
kubectl get pods -l 'environment in (prod),tier notin (cache),app'

Manifest Examples:

# Deployment selector (supports both)
selector:
  matchLabels:                    # Equality-based
    app: nginx
  matchExpressions:               # Set-based
  - key: tier
    operator: In
    values:
    - frontend
    - api
  - key: environment
    operator: NotIn
    values:
    - testing
  - key: critical
    operator: Exists
  - key: deprecated
    operator: DoesNotExist

Selector Operators

Operator Description Example
In Value in set tier in (frontend, api)
NotIn Value not in set env notin (test, dev)
Exists Label exists critical (key exists)
DoesNotExist Label doesn't exist !deprecated (key absent)

Selector Matching Logic

flowchart TD
    START([Resource with Labels]) --> CHECK1{matchLabels<br/>satisfied?}

    CHECK1 -->|No| NOMATCH[No Match]
    CHECK1 -->|Yes| CHECK2{matchExpressions<br/>all true?}

    CHECK2 -->|No| NOMATCH
    CHECK2 -->|Yes| MATCH[Match Found]

    MATCH --> SELECTED[Resource Selected]
    NOMATCH --> IGNORED[Resource Ignored]

    style MATCH fill:#e8f5e8
    style NOMATCH fill:#ffe5e5

Example:

# Pod labels
metadata:
  labels:
    app: nginx
    tier: frontend
    environment: production
    version: "2.0"

# Selector
selector:
  matchLabels:
    app: nginx                    # ✅ Match
  matchExpressions:
  - key: tier
    operator: In
    values: [frontend, api]       # ✅ Match (tier=frontend)
  - key: environment
    operator: NotIn
    values: [dev, test]           # ✅ Match (production not in list)
  - key: version
    operator: Exists              # ✅ Match (version label exists)

# Result: Pod matches selector


Annotations: Non-Identifying Metadata

Annotations store arbitrary metadata that doesn't identify or select objects.

Annotations vs Labels

Aspect Labels Annotations
Purpose Identify and select Store metadata
Selectable Yes (with selectors) No
Size Limit 63 chars (value) 256 KB (total)
Structure Simple key-value Can store JSON, YAML
Use Case Grouping, selection Documentation, config

Annotation Syntax

Format: Same as labels but values can be larger and more complex

metadata:
  annotations:
    # Documentation
    description: "Production Nginx deployment with 3 replicas"
    owner: "platform-team@company.com"
    documentation: "https://wiki.company.com/nginx-deployment"

    # Build information
    build-version: "2.0.1"
    git-commit: "a3f2b1c"
    ci-pipeline: "https://jenkins.company.com/job/nginx/123"

    # Operational metadata
    deployment-date: "2024-01-15T10:30:00Z"
    last-updated-by: "john.doe@company.com"

    # Tool-specific configuration
    prometheus.io/scrape: "true"
    prometheus.io/port: "9090"
    prometheus.io/path: "/metrics"

    # JSON configuration
    custom-config: '{"timeout": 30, "retries": 3}'

Common Annotation Use Cases

1. Tool Integration:

annotations:
  # Prometheus monitoring
  prometheus.io/scrape: "true"
  prometheus.io/port: "8080"

  # Nginx Ingress
  nginx.ingress.kubernetes.io/rewrite-target: "/"
  nginx.ingress.kubernetes.io/ssl-redirect: "true"

  # Cert-manager
  cert-manager.io/cluster-issuer: "letsencrypt-prod"

  # Istio service mesh
  sidecar.istio.io/inject: "true"

2. Change Tracking:

annotations:
  kubernetes.io/change-cause: "Update nginx to version 1.21"
  deployment.kubernetes.io/revision: "5"

3. Documentation:

annotations:
  description: |
    Main production web server deployment.

    Handles customer-facing traffic with:
    - 3 replicas for high availability
    - Rolling update strategy
    - Health checks configured

    Contact: platform-team@company.com

  runbook: "https://wiki.company.com/runbooks/nginx-troubleshooting"
  oncall: "https://pagerduty.com/schedules/nginx-team"

4. Configuration Storage:

annotations:
  # Complex JSON configuration
  fluentd-config: |
    {
      "outputs": [
        {"type": "elasticsearch", "host": "es.logging.svc"},
        {"type": "s3", "bucket": "logs-backup"}
      ]
    }

Annotation Operations

# View annotations
kubectl describe pod nginx-pod | grep -A 10 "Annotations:"

# Add annotation
kubectl annotate pod nginx-pod description="Production web server"

# Update annotation
kubectl annotate pod nginx-pod description="Updated description" --overwrite

# Remove annotation
kubectl annotate pod nginx-pod description-

# Annotate all resources of type
kubectl annotate deployments --all team=platform

Field Validation and Troubleshooting

Required vs Optional Fields

Required Fields (most common):

# All objects
apiVersion: apps/v1              # Required
kind: Deployment                 # Required
metadata:
  name: nginx                    # Required
spec:                            # Required

# Pod spec
spec:
  containers:                    # Required
  - name: nginx                  # Required
    image: nginx:1.21            # Required

# Service spec
spec:
  selector:                      # Required
    app: nginx
  ports:                         # Required
  - port: 80

Determining Required Fields:

# Use kubectl explain
kubectl explain pod.spec
# FIELDS:
#   containers    <[]Container> -required-
#   volumes       <[]Volume>

kubectl explain pod.spec.containers
# FIELDS:
#   name          <string> -required-
#   image         <string> -required-
#   command       <[]string>        # Optional (no -required-)

Common Validation Errors

1. Missing Required Field:

# ERROR: Missing image
spec:
  containers:
  - name: nginx
    # image: nginx:1.21    # Missing required field

# Error message:
# error: error validating "pod.yaml": error validating data:
# ValidationError(Pod.spec.containers[0]): missing required field "image"

2. Invalid Field Name:

# ERROR: Typo in field name
metadata:
  name: nginx
  lables:              # Should be "labels"
    app: nginx

# Error message:
# error: error validating "pod.yaml": error validating data:
# ValidationError(Pod.metadata): unknown field "lables"

3. Wrong Data Type:

# ERROR: String instead of integer
spec:
  replicas: "3"        # Should be: replicas: 3

# Error message:
# error: error validating data: ValidationError(Deployment.spec.replicas):
# invalid type for io.k8s.api.apps.v1.DeploymentSpec.replicas: got "string", expected "integer"

4. Invalid Value:

# ERROR: Invalid restart policy
spec:
  restartPolicy: OnError    # Valid: Always, OnFailure, Never

# Error message:
# error: error validating data: ValidationError(Pod.spec.restartPolicy):
# unsupported value: "OnError": supported values: "Always", "OnFailure", "Never"

Validation Workflow

sequenceDiagram
    participant User
    participant kubectl
    participant API
    participant Validation
    participant etcd

    User->>kubectl: kubectl apply -f manifest.yaml
    kubectl->>kubectl: Parse YAML syntax

    alt YAML syntax error
        kubectl-->>User: Syntax error (invalid YAML)
    end

    kubectl->>API: Send parsed object
    API->>Validation: Validate object

    Validation->>Validation: Check required fields
    Validation->>Validation: Validate field types
    Validation->>Validation: Validate field values
    Validation->>Validation: Run admission controllers

    alt Validation fails
        Validation-->>API: Validation error
        API-->>User: Error message with details
    else Validation succeeds
        Validation->>etcd: Persist object
        etcd-->>User: Created/Updated confirmation
    end

Troubleshooting Commands

# Validate YAML without creating
kubectl apply --dry-run=client -f manifest.yaml

# Server-side validation (includes admission controllers)
kubectl apply --dry-run=server -f manifest.yaml

# Explain field structure
kubectl explain deployment.spec.template.spec.containers

# Get field path for specific property
kubectl explain deployment --recursive | grep -A 5 "replicas"

# Validate all manifests in directory
kubectl apply --dry-run=client -f ./manifests/

# Check API resource availability
kubectl api-resources | grep -i deployment

# View object in YAML (for comparison)
kubectl get deployment nginx -o yaml

Complete Object Examples

Example 1: Multi-Container Pod with Full Metadata

apiVersion: v1
kind: Pod
metadata:
  name: multi-container-pod
  namespace: production
  labels:
    app: webapp
    tier: frontend
    environment: production
    version: "2.0"
  annotations:
    description: "Production web application with logging sidecar"
    owner: "platform-team@company.com"
    prometheus.io/scrape: "true"
    prometheus.io/port: "9090"
spec:
  containers:
  - name: webapp
    image: nginx:1.21
    ports:
    - name: http
      containerPort: 80
    resources:
      requests:
        cpu: 100m
        memory: 128Mi
      limits:
        cpu: 200m
        memory: 256Mi
    livenessProbe:
      httpGet:
        path: /healthz
        port: 80
      initialDelaySeconds: 30
      periodSeconds: 10
    env:
    - name: ENVIRONMENT
      value: "production"
  - name: log-forwarder
    image: fluent/fluent-bit:1.9
    volumeMounts:
    - name: logs
      mountPath: /var/log/nginx
  volumes:
  - name: logs
    emptyDir: {}
  restartPolicy: Always

Example 2: Deployment with Complete Selectors

apiVersion: apps/v1
kind: Deployment
metadata:
  name: webapp-deployment
  namespace: production
  labels:
    app.kubernetes.io/name: webapp
    app.kubernetes.io/version: "2.0.1"
    app.kubernetes.io/component: frontend
    app.kubernetes.io/part-of: ecommerce-platform
    app.kubernetes.io/managed-by: kubectl
  annotations:
    deployment.kubernetes.io/revision: "3"
    kubernetes.io/change-cause: "Update to version 2.0.1"
spec:
  replicas: 3
  selector:
    matchLabels:
      app: webapp
      tier: frontend
    matchExpressions:
    - key: environment
      operator: In
      values:
      - production
      - staging
    - key: deprecated
      operator: DoesNotExist
  template:
    metadata:
      labels:
        app: webapp
        tier: frontend
        environment: production
        version: "2.0.1"
      annotations:
        prometheus.io/scrape: "true"
        prometheus.io/port: "8080"
    spec:
      containers:
      - name: webapp
        image: webapp:2.0.1
        ports:
        - containerPort: 8080
        resources:
          requests:
            cpu: 200m
            memory: 256Mi
          limits:
            cpu: 500m
            memory: 512Mi
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 1
      maxUnavailable: 0

Example 3: Service with Label Selector

apiVersion: v1
kind: Service
metadata:
  name: webapp-service
  namespace: production
  labels:
    app: webapp
    tier: frontend
  annotations:
    service.kubernetes.io/topology-aware-hints: "auto"
spec:
  type: ClusterIP
  selector:
    app: webapp           # Selects pods with app=webapp
    tier: frontend        # AND tier=frontend
  ports:
  - name: http
    port: 80
    targetPort: 8080
    protocol: TCP
  sessionAffinity: ClientIP

Common Exam Tasks

Scenario 1: Create Object with Labels

Task: Create a pod named web with nginx image, labels app=web and tier=frontend

# Imperative with labels
kubectl run web --image=nginx --labels="app=web,tier=frontend"

# Or generate and modify
kubectl run web --image=nginx --dry-run=client -o yaml > pod.yaml
# Edit pod.yaml to add labels
kubectl apply -f pod.yaml

# Verify
kubectl get pod web --show-labels

Scenario 2: Select Resources by Labels

Task: Get all pods with label environment=production and tier!=backend

# Single label
kubectl get pods -l environment=production

# Multiple labels (AND)
kubectl get pods -l environment=production,tier!=backend

# Set-based
kubectl get pods -l 'environment in (production,staging),tier!=backend'

# Show labels
kubectl get pods -l environment=production --show-labels

Scenario 3: Add/Modify Annotations

Task: Add annotation description="Production web server" to deployment webapp

# Add annotation
kubectl annotate deployment webapp description="Production web server"

# Modify existing (requires --overwrite)
kubectl annotate deployment webapp description="Updated description" --overwrite

# Verify
kubectl describe deployment webapp | grep -A 5 "Annotations:"

Scenario 4: Validate Manifest Before Apply

Task: Check if deployment.yaml is valid without creating it

# Client-side validation (YAML syntax + basic structure)
kubectl apply --dry-run=client -f deployment.yaml

# Server-side validation (includes admission controllers)
kubectl apply --dry-run=server -f deployment.yaml

# Explain specific fields
kubectl explain deployment.spec.template.spec.containers.resources

Scenario 5: Extract Object YAML

Task: Get running pod's YAML to create template

# Get full YAML (includes status)
kubectl get pod nginx -o yaml

# Get YAML without system fields (for template)
kubectl get pod nginx -o yaml | kubectl neat

# Or manually clean
kubectl get pod nginx -o yaml > template.yaml
# Remove status, uid, resourceVersion, creationTimestamp, etc.

Practice Exercises

Exercise 1: Object Creation (10 minutes)

Objective: Create objects with proper metadata structure

Tasks: 1. Create pod frontend-pod with nginx:1.21 image 2. Add labels: app=frontend, tier=web, env=dev 3. Add annotation: description="Development frontend pod" 4. Verify labels and annotations

Solution:

# Generate template
kubectl run frontend-pod --image=nginx:1.21 --dry-run=client -o yaml > pod.yaml

# Edit pod.yaml
vim pod.yaml
# Add to metadata:
#   labels:
#     app: frontend
#     tier: web
#     env: dev
#   annotations:
#     description: "Development frontend pod"

# Apply
kubectl apply -f pod.yaml

# Verify
kubectl get pod frontend-pod --show-labels
kubectl describe pod frontend-pod | grep -A 5 "Annotations:"

Exercise 2: Label Selection (15 minutes)

Objective: Practice label selectors

Tasks: 1. Create 5 pods with various labels 2. Select pods with environment=production 3. Select pods with tier in (frontend, api) 4. Select pods with environment=production AND tier!=backend 5. Count pods matching each selector

Solution:

# Create pods with different labels
kubectl run pod1 --image=nginx --labels="app=web,environment=production,tier=frontend"
kubectl run pod2 --image=nginx --labels="app=api,environment=production,tier=api"
kubectl run pod3 --image=nginx --labels="app=db,environment=production,tier=backend"
kubectl run pod4 --image=nginx --labels="app=web,environment=staging,tier=frontend"
kubectl run pod5 --image=nginx --labels="app=cache,environment=dev,tier=cache"

# Select by environment
kubectl get pods -l environment=production
# Expected: pod1, pod2, pod3

# Select by tier (set-based)
kubectl get pods -l 'tier in (frontend,api)'
# Expected: pod1, pod2, pod4

# Complex selector
kubectl get pods -l environment=production,tier!=backend
# Expected: pod1, pod2

# Count matches
kubectl get pods -l environment=production --no-headers | wc -l
# Expected: 3

Exercise 3: Deployment with Selectors (20 minutes)

Objective: Create deployment with proper label selectors

Tasks: 1. Create deployment webapp with 3 replicas 2. Use nginx:1.21 image 3. Add deployment labels: app=webapp, version=v1 4. Pod template labels: app=webapp, tier=frontend, version=v1 5. Configure selector to match pod labels 6. Verify pods are created with correct labels

Solution:

# Generate template
kubectl create deployment webapp --image=nginx:1.21 --replicas=3 --dry-run=client -o yaml > deployment.yaml

# Edit deployment.yaml
vim deployment.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: webapp
  labels:
    app: webapp
    version: v1
spec:
  replicas: 3
  selector:
    matchLabels:
      app: webapp
      tier: frontend
      version: v1
  template:
    metadata:
      labels:
        app: webapp
        tier: frontend
        version: v1
    spec:
      containers:
      - name: nginx
        image: nginx:1.21
# Apply
kubectl apply -f deployment.yaml

# Verify deployment
kubectl get deployment webapp

# Verify pods have correct labels
kubectl get pods --show-labels -l app=webapp

# Verify selector works
kubectl get pods -l app=webapp,tier=frontend,version=v1

Exercise 4: Annotations and Documentation (15 minutes)

Objective: Use annotations for metadata

Tasks: 1. Create deployment with comprehensive annotations 2. Add build info, owner, documentation links 3. Add tool-specific annotations (e.g., Prometheus) 4. Extract and view annotations

Solution:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: documented-app
  annotations:
    description: "Production application with comprehensive documentation"
    owner: "platform-team@company.com"
    documentation: "https://wiki.company.com/apps/documented-app"
    runbook: "https://wiki.company.com/runbooks/documented-app"
    build-version: "2.0.1"
    git-commit: "abc123def456"
    ci-pipeline: "https://jenkins.company.com/job/app/123"
    deployment-date: "2024-01-15T10:30:00Z"
    prometheus.io/scrape: "true"
    prometheus.io/port: "9090"
spec:
  replicas: 2
  selector:
    matchLabels:
      app: documented-app
  template:
    metadata:
      labels:
        app: documented-app
      annotations:
        prometheus.io/scrape: "true"
        prometheus.io/port: "9090"
    spec:
      containers:
      - name: app
        image: nginx:1.21

# Apply
kubectl apply -f documented-app.yaml

# View annotations
kubectl describe deployment documented-app | grep -A 15 "Annotations:"

# Get specific annotation
kubectl get deployment documented-app -o jsonpath='{.metadata.annotations.owner}'

Exercise 5: Validation and Troubleshooting (20 minutes)

Objective: Practice validation and error fixing

Tasks: 1. Create manifest with intentional errors 2. Use validation to identify errors 3. Fix each error 4. Successfully create object

Broken Manifest:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: broken-deploy
  lables:                          # ERROR: Typo
    app: broken
spec:
  replicas: "3"                    # ERROR: String instead of int
  selector:
    matchLabels:
      app: broken
  template:
    metadata:
      labels:
        app: broken
    spec:
      containers:
      - name: nginx
        # image: nginx:1.21         # ERROR: Missing required field
        ports:
        - containerPort: 80
        restartPolicy: OnError      # ERROR: Invalid value (wrong location too)

Solution:

# Try to validate
kubectl apply --dry-run=client -f broken.yaml
# See errors for each issue

# Fixed version:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: fixed-deploy
  labels:                          # Fixed: labels (not lables)
    app: fixed
spec:
  replicas: 3                      # Fixed: integer (not string)
  selector:
    matchLabels:
      app: fixed
  template:
    metadata:
      labels:
        app: fixed
    spec:
      containers:
      - name: nginx
        image: nginx:1.21          # Fixed: added required field
        ports:
        - containerPort: 80
      restartPolicy: Always        # Fixed: moved to pod spec, valid value
# Validate fixed version
kubectl apply --dry-run=client -f fixed.yaml
# Should succeed

# Apply
kubectl apply -f fixed.yaml

Quick Reference

Object Structure Template

apiVersion: <group>/<version>
kind: <ObjectKind>
metadata:
  name: <object-name>
  namespace: <namespace>
  labels:
    <key>: <value>
  annotations:
    <key>: <value>
spec:
  # Object-specific desired state
status:
  # System-managed current state (read-only)

Common apiVersion Values

v1                              # Core: Pod, Service, ConfigMap
apps/v1                         # Deployment, StatefulSet, DaemonSet
batch/v1                        # Job, CronJob
networking.k8s.io/v1           # Ingress, NetworkPolicy
rbac.authorization.k8s.io/v1   # Role, ClusterRole, RoleBinding
storage.k8s.io/v1              # StorageClass

Label Selector Syntax

# Equality-based (command-line)
kubectl get pods -l app=nginx                    # Single label
kubectl get pods -l app=nginx,tier=frontend      # Multiple (AND)
kubectl get pods -l app=nginx,tier!=backend      # Not equal

# Set-based (command-line)
kubectl get pods -l 'environment in (prod,staging)'
kubectl get pods -l 'tier notin (cache,db)'
kubectl get pods -l app                          # Label exists
kubectl get pods -l '!app'                       # Label doesn't exist

# Manifest (YAML)
selector:
  matchLabels:                   # Equality-based
    app: nginx
  matchExpressions:              # Set-based
  - key: tier
    operator: In                 # In, NotIn, Exists, DoesNotExist
    values: [frontend, api]

Essential Commands

# Object creation
kubectl apply -f manifest.yaml
kubectl create -f manifest.yaml

# Label operations
kubectl label pod nginx app=web                  # Add
kubectl label pod nginx app=web --overwrite      # Modify
kubectl label pod nginx app-                     # Remove
kubectl get pods --show-labels                   # View
kubectl get pods -L app,tier                     # Show specific labels
kubectl get pods -l app=nginx                    # Filter by labels

# Annotation operations
kubectl annotate pod nginx description="Web server"    # Add
kubectl annotate pod nginx description- # Remove
kubectl describe pod nginx                       # View

# Validation
kubectl apply --dry-run=client -f manifest.yaml  # Client-side
kubectl apply --dry-run=server -f manifest.yaml  # Server-side
kubectl explain deployment.spec                  # Field documentation

# Extraction
kubectl get pod nginx -o yaml                    # Full YAML
kubectl get pod nginx -o json                    # Full JSON

Key Takeaways

Every Kubernetes resource is an object with consistent structure: apiVersion, kind, metadata, spec

YAML is the standard format - master 2-space indentation and common patterns

Four essential fields define objects - apiVersion, kind, metadata (name/namespace/labels), spec

Labels enable selection - use for grouping, Services, Deployments, and queries

Selectors match labels - equality-based (simple) and set-based (complex matching)

Annotations store metadata - documentation, configuration, tool integration (no selection)

Use kubectl explain extensively - fastest way to discover field structure during exam

Validation catches errors early - always use --dry-run before applying

Recommended labels maintain consistency - app.kubernetes.io/* prefix for standard labels

Master label/annotation operations - critical for exam speed and production work


Next Steps

Continue your CKA journey with:

Post 5: Namespaces and Resource Quotas - Learn cluster resource organization and multi-tenancy


Related Posts: - Kubernetes Architecture Fundamentals - Understanding cluster components - kubectl Essentials - Command-line mastery - Kubernetes CKA Mastery - Complete Learning Path - Full exam preparation series

External Resources: - Kubernetes Objects (Official Docs) - Labels and Selectors - Annotations - Recommended Labels - Understanding Kubernetes Objects