Skip to content

Kubernetes Extension Mechanisms Quiz

Related Document: Kubernetes Extension Mechanisms

Multiple Choice Questions

1. What is the primary purpose of CRD (Custom Resource Definition)?

  • A) To modify existing Kubernetes resources
  • B) To extend the Kubernetes API with custom resource types
  • C) To configure pod networking
  • D) To provision storage volumes
Show Answer

Answer: B) To extend the Kubernetes API with custom resource types

Explanation: CRDs allow you to extend the Kubernetes API by defining custom resource types that behave like native Kubernetes resources.

2. What is the main task performed by a custom controller's reconciliation loop?

  • A) Immediately delete resources
  • B) Reconcile differences between current state and desired state
  • C) Register new APIs with the API server
  • D) Apply network policies
Show Answer

Answer: B) Reconcile differences between current state and desired state

Explanation: The custom controller's reconciliation loop observes the current state of resources, compares it with the desired state (spec), and takes actions to achieve the desired state when differences exist.

3. What are the core components of the Operator pattern?

  • A) Deployment and Service
  • B) CRD and Custom Controller
  • C) ConfigMap and Secret
  • D) Ingress and NetworkPolicy
Show Answer

Answer: B) CRD and Custom Controller

Explanation: The Operator pattern uses CRDs to define application configuration and custom controllers to automate operational knowledge such as deployment, upgrades, and recovery.

4. What is the primary use of MutatingAdmissionWebhook?

  • A) Reject API requests
  • B) Modify API requests
  • C) Log API responses
  • D) Upgrade API versions
Show Answer

Answer: B) Modify API requests

Explanation: MutatingAdmissionWebhook can modify API requests before they are persisted. Common uses include: sidecar container injection, setting defaults, etc.

5. What is the role of the Filter plugin in the scheduler framework?

  • A) Assign scores to nodes
  • B) Exclude nodes that cannot run the pod
  • C) Bind the pod to a node
  • D) Reserve node resources
Show Answer

Answer: B) Exclude nodes that cannot run the pod

Explanation: Filter plugins filter out nodes that don't meet the pod's requirements, excluding them from consideration.

6. What is the difference between Aggregated API Server and CRD?

  • A) No difference, they are the same
  • B) Aggregated API provides more control but requires running a separate server
  • C) CRD provides more features than Aggregated API
  • D) Aggregated API is deprecated
Show Answer

Answer: B) Aggregated API provides more control but requires running a separate server

Explanation: Aggregated API Server provides full control over API behavior, custom storage backends, and advanced features but requires deploying and maintaining a separate API server. CRDs are simpler but have limitations.

7. What is the purpose of a Finalizer in Kubernetes?

  • A) To speed up resource deletion
  • B) To prevent resource deletion until cleanup is complete
  • C) To automatically restart failed pods
  • D) To validate resource creation
Show Answer

Answer: B) To prevent resource deletion until cleanup is complete

Explanation: Finalizers block resource deletion until the controller performs necessary cleanup operations (like deleting external resources) and removes the finalizer.

8. Which scheduler extension point runs after a pod has been bound to a node?

  • A) PreFilter
  • B) PostBind
  • C) Reserve
  • D) Score
Show Answer

Answer: B) PostBind

Explanation: PostBind plugins are called after the pod has been successfully bound to a node. They are informational and used for cleanup or notifications.

9. What annotation is used to inject sidecars via admission webhook in Istio?

  • A) istio.io/inject
  • B) sidecar.istio.io/inject
  • C) istio-injection
  • D) auto-inject.istio.io
Show Answer

Answer: B) sidecar.istio.io/inject

Explanation: The sidecar.istio.io/inject annotation controls whether Istio's mutating webhook injects the Envoy sidecar into a pod. Namespace-level control uses the istio-injection label.

10. What is the purpose of the Score plugin in the scheduler framework?

  • A) To filter out unsuitable nodes
  • B) To rank nodes and select the best one
  • C) To bind the pod to the selected node
  • D) To validate pod specifications
Show Answer

Answer: B) To rank nodes and select the best one

Explanation: Score plugins assign scores to nodes that passed filtering. The scheduler selects the node with the highest combined score from all Score plugins.

Short Answer Questions

1. What standard is used for schema validation in CRDs?

Show Answer

Answer: OpenAPI v3 Schema (openAPIV3Schema)

Explanation: CRDs use OpenAPI v3 schema in spec.versions[].schema.openAPIV3Schema to define the structure and validation rules for custom resources.

2. What is the role of Owner Reference in Kubernetes controllers?

Show Answer

Answer: To define ownership relationships between resources and manage garbage collection and event propagation

Explanation: Owner Reference defines parent-child relationships and automatically deletes children when the parent is deleted through Kubernetes garbage collection.

3. What is the difference between ValidatingAdmissionPolicy and ValidatingAdmissionWebhook?

Show Answer

Answer: ValidatingAdmissionPolicy uses CEL expressions and runs in-process, while ValidatingAdmissionWebhook calls external HTTP endpoints.

Explanation: ValidatingAdmissionPolicy (introduced in 1.26) provides better performance and doesn't require external webhook infrastructure, but has less flexibility than webhooks.

4. What is the controller-runtime library and why is it commonly used?

Show Answer

Answer: controller-runtime is a library that provides common patterns for building Kubernetes controllers, including client caching, leader election, and reconciliation loop management.

Explanation: Part of the Kubebuilder project, controller-runtime abstracts away boilerplate code and best practices, making it easier to build reliable operators.

5. What is the purpose of conversion webhooks in CRDs?

Show Answer

Answer: Conversion webhooks convert resources between different API versions of the same CRD.

Explanation: When a CRD has multiple versions (e.g., v1alpha1, v1beta1, v1), conversion webhooks handle the transformation between versions to support API evolution.

Hands-on Questions

1. Write a CRD that meets the following requirements:

  • Name: WebApp
  • Group: apps.example.com
  • Fields: replicas (integer, minimum 1), image (string, required)
Show Answer
yaml
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
  name: webapps.apps.example.com
spec:
  group: apps.example.com
  names:
    kind: WebApp
    plural: webapps
    singular: webapp
    shortNames:
      - wa
  scope: Namespaced
  versions:
    - name: v1
      served: true
      storage: true
      schema:
        openAPIV3Schema:
          type: object
          properties:
            spec:
              type: object
              required: ["image"]
              properties:
                replicas:
                  type: integer
                  minimum: 1
                  default: 1
                image:
                  type: string
            status:
              type: object
              properties:
                availableReplicas:
                  type: integer
                conditions:
                  type: array
                  items:
                    type: object
                    properties:
                      type:
                        type: string
                      status:
                        type: string
                      lastTransitionTime:
                        type: string
                        format: date-time
      subresources:
        status: {}
      additionalPrinterColumns:
        - name: Replicas
          type: integer
          jsonPath: .spec.replicas
        - name: Available
          type: integer
          jsonPath: .status.availableReplicas
        - name: Age
          type: date
          jsonPath: .metadata.creationTimestamp

2. Write a ValidatingAdmissionWebhook configuration that validates all Deployments in the "production" namespace.

Show Answer
yaml
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingWebhookConfiguration
metadata:
  name: deployment-validator
webhooks:
  - name: validate-deployment.example.com
    clientConfig:
      service:
        name: webhook-service
        namespace: webhook-system
        path: /validate-deployment
      caBundle: <base64-encoded-ca-cert>
    rules:
      - apiGroups: ["apps"]
        apiVersions: ["v1"]
        operations: ["CREATE", "UPDATE"]
        resources: ["deployments"]
        scope: Namespaced
    namespaceSelector:
      matchLabels:
        environment: production
    failurePolicy: Fail
    sideEffects: None
    admissionReviewVersions: ["v1"]
    timeoutSeconds: 10

Explanation:

  • namespaceSelector limits the webhook to namespaces with environment: production label
  • failurePolicy: Fail rejects requests if the webhook is unavailable
  • sideEffects: None indicates the webhook has no side effects

3. Write a simple reconciliation loop pseudocode for a custom controller.

Show Answer
go
func (r *WebAppReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
    log := log.FromContext(ctx)

    // 1. Fetch the WebApp resource
    var webapp appsv1.WebApp
    if err := r.Get(ctx, req.NamespacedName, &webapp); err != nil {
        if errors.IsNotFound(err) {
            // Resource deleted, nothing to do
            return ctrl.Result{}, nil
        }
        return ctrl.Result{}, err
    }

    // 2. Check if being deleted (handle finalizers)
    if !webapp.DeletionTimestamp.IsZero() {
        if containsFinalizer(webapp, finalizerName) {
            // Perform cleanup
            if err := r.cleanupExternalResources(&webapp); err != nil {
                return ctrl.Result{}, err
            }
            // Remove finalizer
            removeFinalizer(&webapp, finalizerName)
            if err := r.Update(ctx, &webapp); err != nil {
                return ctrl.Result{}, err
            }
        }
        return ctrl.Result{}, nil
    }

    // 3. Add finalizer if not present
    if !containsFinalizer(webapp, finalizerName) {
        addFinalizer(&webapp, finalizerName)
        if err := r.Update(ctx, &webapp); err != nil {
            return ctrl.Result{}, err
        }
    }

    // 4. Create or update Deployment
    deployment := r.constructDeployment(&webapp)
    if err := controllerutil.SetControllerReference(&webapp, deployment, r.Scheme); err != nil {
        return ctrl.Result{}, err
    }
    
    if err := r.CreateOrUpdate(ctx, deployment); err != nil {
        return ctrl.Result{}, err
    }

    // 5. Create or update Service
    service := r.constructService(&webapp)
    if err := controllerutil.SetControllerReference(&webapp, service, r.Scheme); err != nil {
        return ctrl.Result{}, err
    }
    
    if err := r.CreateOrUpdate(ctx, service); err != nil {
        return ctrl.Result{}, err
    }

    // 6. Update status
    webapp.Status.AvailableReplicas = deployment.Status.AvailableReplicas
    if err := r.Status().Update(ctx, &webapp); err != nil {
        return ctrl.Result{}, err
    }

    // 7. Requeue after interval for periodic reconciliation
    return ctrl.Result{RequeueAfter: time.Minute * 5}, nil
}

Key Points:

  • Always handle resource not found (may have been deleted)
  • Use finalizers for cleanup of external resources
  • Set owner references for garbage collection
  • Update status subresource separately
  • Consider requeue intervals for periodic checks

Advanced Questions

1. Design a Kubernetes Operator for a complex distributed system.

Show Answer

CRD Design:

yaml
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
  name: postgresclusters.database.example.com
spec:
  group: database.example.com
  names:
    kind: PostgresCluster
    plural: postgresclusters
    shortNames:
      - pg
  scope: Namespaced
  versions:
    - name: v1
      served: true
      storage: true
      schema:
        openAPIV3Schema:
          type: object
          properties:
            spec:
              type: object
              required: ["replicas", "version"]
              properties:
                replicas:
                  type: integer
                  minimum: 1
                  maximum: 10
                version:
                  type: string
                  enum: ["14", "15", "16"]
                storage:
                  type: object
                  properties:
                    size:
                      type: string
                      default: "10Gi"
                    storageClass:
                      type: string
                backup:
                  type: object
                  properties:
                    enabled:
                      type: boolean
                      default: true
                    schedule:
                      type: string
                      default: "0 2 * * *"
                    retention:
                      type: integer
                      default: 7
            status:
              type: object
              properties:
                phase:
                  type: string
                  enum: ["Creating", "Running", "Upgrading", "Failed", "Deleting"]
                primaryEndpoint:
                  type: string
                replicaEndpoints:
                  type: array
                  items:
                    type: string
                currentVersion:
                  type: string
                conditions:
                  type: array
                  items:
                    type: object
                    properties:
                      type:
                        type: string
                      status:
                        type: string
                      reason:
                        type: string
                      message:
                        type: string
                      lastTransitionTime:
                        type: string
                        format: date-time
      subresources:
        status: {}
        scale:
          specReplicasPath: .spec.replicas
          statusReplicasPath: .status.readyReplicas

Controller Core Logic:

  • Phase-based state management (Creating, Running, Upgrading, Failed)
  • Automatic failure recovery (Failover when Primary fails)
  • Rolling upgrade strategy (Upgrade replicas first, then primary)
  • Backup management (CronJob for scheduled backups)

Architecture:

PostgresCluster CR
       |
       v
   Controller
       |
       +---> StatefulSet (PostgreSQL pods)
       +---> Service (Primary endpoint)
       +---> Service (Replica endpoint)
       +---> Secret (Credentials)
       +---> ConfigMap (PostgreSQL config)
       +---> CronJob (Backups)
       +---> PodDisruptionBudget

2. Explain how to implement a custom scheduler using the scheduler framework.

Show Answer

Scheduler Plugin Implementation:

go
// Plugin implementing multiple extension points
type CustomSchedulerPlugin struct {
    handle framework.Handle
}

// Implement PreFilter - check pod requirements
func (p *CustomSchedulerPlugin) PreFilter(ctx context.Context, state *framework.CycleState, pod *v1.Pod) (*framework.PreFilterResult, *framework.Status) {
    // Validate pod has required annotations
    if _, ok := pod.Annotations["custom-scheduler/zone"]; !ok {
        return nil, framework.NewStatus(framework.Unschedulable, "missing zone annotation")
    }
    return nil, framework.NewStatus(framework.Success, "")
}

// Implement Filter - exclude unsuitable nodes
func (p *CustomSchedulerPlugin) Filter(ctx context.Context, state *framework.CycleState, pod *v1.Pod, nodeInfo *framework.NodeInfo) *framework.Status {
    requiredZone := pod.Annotations["custom-scheduler/zone"]
    nodeZone := nodeInfo.Node().Labels["topology.kubernetes.io/zone"]
    
    if requiredZone != nodeZone {
        return framework.NewStatus(framework.Unschedulable, "zone mismatch")
    }
    return framework.NewStatus(framework.Success, "")
}

// Implement Score - rank suitable nodes
func (p *CustomSchedulerPlugin) Score(ctx context.Context, state *framework.CycleState, pod *v1.Pod, nodeName string) (int64, *framework.Status) {
    nodeInfo, err := p.handle.SnapshotSharedLister().NodeInfos().Get(nodeName)
    if err != nil {
        return 0, framework.NewStatus(framework.Error, err.Error())
    }
    
    // Score based on available resources
    allocatable := nodeInfo.Node().Status.Allocatable
    requested := nodeInfo.Requested
    
    cpuScore := calculateResourceScore(allocatable.Cpu(), requested.Cpu)
    memScore := calculateResourceScore(allocatable.Memory(), requested.Memory)
    
    return (cpuScore + memScore) / 2, framework.NewStatus(framework.Success, "")
}

// Register the plugin
func New(_ runtime.Object, h framework.Handle) (framework.Plugin, error) {
    return &CustomSchedulerPlugin{handle: h}, nil
}

Scheduler Configuration:

yaml
apiVersion: kubescheduler.config.k8s.io/v1
kind: KubeSchedulerConfiguration
profiles:
  - schedulerName: custom-scheduler
    plugins:
      preFilter:
        enabled:
          - name: CustomSchedulerPlugin
      filter:
        enabled:
          - name: CustomSchedulerPlugin
      score:
        enabled:
          - name: CustomSchedulerPlugin
        disabled:
          - name: NodeResourcesBalancedAllocation

Extension Points Summary:

Extension PointPurposeRuns When
PreFilterPod-level checksBefore filtering
FilterNode eliminationFor each node
PostFilterHandle unschedulableWhen no node fits
PreScorePrepare for scoringBefore scoring
ScoreNode rankingFor filtered nodes
NormalizeScoreScore normalizationAfter all scores
ReserveResource reservationAfter node selection
PermitFinal approvalBefore binding
PreBindPre-binding actionsBefore API binding
BindActual bindingAPI server update
PostBindPost-binding cleanupAfter binding