Qingular

CRD and Operator

·CKAk8s练习

CustomResourceDefinition (CRD) and the Operator pattern are the core of Kubernetes extensibility, allowing users to define custom resources and implement automated operational logic.

← Back to CKA Practice Index

Overview

CustomResourceDefinition (CRD) allows users to extend the Kubernetes API and define their own resource types. The Operator pattern combines CRDs with controller logic to achieve automated application management. The CKA exam requires a basic understanding of CRDs and the Operator pattern.


Part 1: CRD (CustomResourceDefinition)

1. CRD Concepts

CRD is Kubernetes' extension mechanism that allows users to define new resource types (such as Database, Backup, Application). The Kubernetes API Server provides full RESTful API support for these custom resources.

After creating a custom resource:
- API Server automatically generates RESTful endpoints: /apis/<group>/<version>/namespaces/<ns>/<resource-plural>/
- Supports kubectl operations (get, create, delete, etc.)
- Supports RBAC access control
- Stored in etcd

CRD vs. Native Resources

FeatureNative Resources (Pod, Service...)CRD
Definition locationKubernetes source codeCRD YAML file
Validation logicBuilt-inOpenAPI v3 schema
ControllerBuilt-in controllerCustom controller (Operator)
Storageetcdetcd

2. CRD YAML Structure

2.1 Basic CRD Example

# crd-database.yaml
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
  name: databases.example.com    # Required format: <plural>.<group>
spec:
  group: example.com             # API group
  names:
    plural: databases            # Plural name (used by kubectl)
    singular: database           # Singular name
    shortNames:                  # Short names
    - db
    kind: Database               # Resource type (used for the kind field in YAML)
    listKind: DatabaseList       # List type
  scope: Namespaced              # Scope: Namespaced or Cluster
  versions:
  - name: v1                     # API version
    served: true                 # Whether served by API Server
    storage: true                # Whether used as the etcd storage version
    schema:                      # OpenAPI v3 validation Schema
      openAPIV3Schema:
        type: object
        required:
        - spec
        properties:
          spec:
            type: object
            required:
            - engine
            - version
            properties:
              engine:
                type: string
                enum:
                - mysql
                - postgres
                - mongodb
              version:
                type: string
                pattern: '^\d+\.\d+\.\d+$'
              replicas:
                type: integer
                minimum: 1
                maximum: 10
                default: 1
              storage:
                type: string
                pattern: '^\d+(Gi|Ti)$'
                default: "10Gi"
              adminUser:
                type: string
                default: "admin"
              backup:
                type: object
                properties:
                  enabled:
                    type: boolean
                    default: false
                  schedule:
                    type: string
                    pattern: '^(\d+|\*)(/\d+)?(\s+(\d+|\*)(/\d+)?){4}$'
                    description: "Cron schedule for backups"

2.2 Creating a Custom Resource Using a CRD

# database-sample.yaml
apiVersion: example.com/v1
kind: Database
metadata:
  name: my-production-db
  labels:
    environment: production
    team: backend
spec:
  engine: postgres
  version: "14.5"
  replicas: 3
  storage: 100Gi
  adminUser: dbadmin
  backup:
    enabled: true
    schedule: "0 2 * * *"
# Apply the CRD
kubectl apply -f crd-database.yaml

# View the CRD
kubectl get crd
kubectl get crd databases.example.com -o yaml

# Use the custom resource
kubectl apply -f database-sample.yaml

# Work with custom resources (exactly like native resources)
kubectl get databases
kubectl get db                                 # Using the shortName
kubectl get databases --all-namespaces
kubectl describe database my-production-db
kubectl delete database my-production-db
kubectl get databases -o wide
kubectl get databases -o yaml

2.3 Additional CRD Feature Configuration

# CRD advanced configuration example
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
  name: databases.example.com
spec:
  # ... basic configuration ...
  versions:
  - name: v1
    # ... schema ...
    additionalPrinterColumns:    # Extra columns for kubectl get
    - name: Engine
      type: string
      jsonPath: .spec.engine
      description: Database engine type
    - name: Version
      type: string
      jsonPath: .spec.version
    - name: Replicas
      type: integer
      jsonPath: .spec.replicas
    - name: Status
      type: string
      jsonPath: .status.phase
    subresources:                # Subresources
      status: {}                 # Enable /status subresource
      scale:                     # Enable /scale subresource
        specReplicasPath: .spec.replicas
        statusReplicasPath: .status.replicas
  conversion:                    # Version conversion
    strategy: None               # or Webhook
# With additionalPrinterColumns:
kubectl get databases
# NAME                ENGINE    VERSION   REPLICAS   STATUS
# my-production-db    postgres  14.5      3          Running

# View status subresource
kubectl get database my-production-db -o json | jq '.status'

2.4 CRD Version Management

# CRD supports multiple coexisting versions, enabling smooth upgrades through version conversion
# View CRD versions
kubectl get crd databases.example.com -o json | jq '.spec.versions[].name'

# Access a specific version
kubectl get databases.v1.example.com

3. Operator Pattern

3.1 Operator Concept

The Operator is a pattern for packaging, deploying, and managing Kubernetes applications. It extends the API through CRDs and uses custom controllers to maintain the desired state of custom resources.

Operator = CRD + Controller + Domain Knowledge

How it works:
1. User creates a custom resource (e.g., a Database instance)
2. The Operator controller watches for changes to that resource
3. The controller creates/manages underlying resources (StatefulSet, Service, PVC...) based on business logic
4. The controller continuously adjusts the actual state to match the user's desired state

3.2 Operator vs. Traditional Deployment

FeatureTraditional ApproachOperator Approach
DeploymentManually create multiple YAMLsCreate a single CR instance
ScalingManually modify DeploymentModify the CR's replicas field
UpgradesManually update image versionsModify the CR's version field
Backup/RestoreManually execute scriptsOperator handles automatically
Failure RecoveryManually troubleshoot and fixOperator automatically detects and recovers
RollbackManual executionOperator automates

3.3 Operator Workflow

          ┌──────────────────────┐
          │    etcd (CR storage)  │
          └──────────┬───────────┘
                     │ watch
          ┌──────────▼───────────┐
          │   Reconciler Loop     │
          │  ─────────────────    │
          │  1. Read CR (desired) │
          │  2. Query actual state│
          │  3. Compare diff      │
          │  4. Execute adjustment│
          │  5. Update CR Status  │
          └──────────────────────┘

4. Using Existing Operators

4.1 etcd-operator

# Install etcd-operator
kubectl apply -f https://raw.githubusercontent.com/coreos/etcd-operator/master/example/service_account.yaml
kubectl apply -f https://raw.githubusercontent.com/coreos/etcd-operator/master/example/rbac/cluster-role.yaml
kubectl apply -f https://raw.githubusercontent.com/coreos/etcd-operator/master/example/rbac/cluster-role-binding.yaml
kubectl apply -f https://raw.githubusercontent.com/coreos/etcd-operator/master/example/deployment.yaml

# View the Operator
kubectl get pods -l name=etcd-operator

# Create an etcd cluster
cat <<EOF | kubectl apply -f -
apiVersion: "etcd.database.coreos.com/v1beta2"
kind: "EtcdCluster"
metadata:
  name: "example-etcd-cluster"
spec:
  size: 3
  version: "3.5.15"
EOF

# View the etcd cluster
kubectl get etcdclusters
kubectl get pods -l etcd_cluster=example-etcd-cluster

# Scale the etcd cluster
kubectl patch etcdcluster example-etcd-cluster --type='json' -p='[{"op": "replace", "path": "/spec/size", "value": 5}]'

# Change etcd version (Operator auto rolling upgrade)
kubectl patch etcdcluster example-etcd-cluster --type='json' -p='[{"op": "replace", "path": "/spec/version", "value": "3.5.15"}]'

4.2 prometheus-operator

# Use Helm to install kube-prometheus-stack (includes prometheus-operator)
helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
helm install prometheus prometheus-community/kube-prometheus-stack

# View the operator
kubectl get pods -l app.kubernetes.io/name=prometheus-operator

# View custom resources (prometheus-operator registers many CRDs)
kubectl get crd | grep monitoring.coreos.com
# prometheuses.monitoring.coreos.com
# alertmanagers.monitoring.coreos.com
# servicemonitors.monitoring.coreos.com
# podmonitors.monitoring.coreos.com
# prometheusrules.monitoring.coreos.com
# thanosrulers.monitoring.coreos.com

# Use ServiceMonitor to configure scraping
cat <<EOF | kubectl apply -f -
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
  name: my-app-monitor
spec:
  selector:
    matchLabels:
      app: my-app
  endpoints:
  - port: metrics
    interval: 15s
EOF

# View ServiceMonitor
kubectl get servicemonitors

4.3 cert-manager Operator

# Install cert-manager
kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.14.0/cert-manager.yaml

# View cert-manager Pods
kubectl get pods -n cert-manager

# Registered custom resources
kubectl get crd | grep cert-manager
# certificates.cert-manager.io
# issuers.cert-manager.io
# clusterissuers.cert-manager.io

# Create a certificate
cat <<EOF | kubectl apply -f -
apiVersion: cert-manager.io/v1
kind: Issuer
metadata:
  name: selfsigned-issuer
spec:
  selfSigned: {}
---
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  name: my-tls-cert
spec:
  dnsNames:
  - example.com
  secretName: my-tls-secret
  issuerRef:
    name: selfsigned-issuer
    kind: Issuer
EOF

# View certificates
kubectl get certificates
kubectl get certificate my-tls-cert -o yaml
kubectl get secret my-tls-secret

5. OLM (Operator Lifecycle Manager)

OLM is a package manager for Operators, responsible for installation, upgrades, and lifecycle management of Operators.

# Install OLM
curl -sL https://github.com/Operator-framework/operator-lifecycle-manager/releases/download/v0.25.0/install.sh | bash -s v0.25.0

# View OLM components
kubectl get pods -n olm

# View available Operators (from OperatorHub)
kubectl get packagemanifests -n olm

# Install an Operator (via Subscription)
cat <<EOF | kubectl apply -f -
apiVersion: operators.coreos.com/v1alpha1
kind: Subscription
metadata:
  name: prometheus
  namespace: operators
spec:
  channel: stable
  name: prometheus
  source: operatorhubio-catalog
  sourceNamespace: olm
EOF

# View installed Operators
kubectl get operators
kubectl get subscriptions

# View Operator versions
kubectl get clusterserviceversion
kubectl get csv -n operators

6. kubebuilder / operator-sdk (Tools for Building Operators)

# Install operator-sdk
export ARCH=$(case $(uname -m) in x86_64) echo -n amd64 ;; aarch64) echo -n arm64 ;; esac)
export OS=$(uname | awk '{print tolower($0)}')
curl -sL https://github.com/operator-framework/operator-sdk/releases/download/v1.33.0/operator-sdk_${OS}_${ARCH} -o operator-sdk
chmod +x operator-sdk && sudo mv operator-sdk /usr/local/bin/

# Create an Operator project
operator-sdk init --domain example.com --repo github.com/example/database-operator

# Create API (CRD)
operator-sdk create api --group example --version v1 --kind Database --resource --controller

# Generate CRD manifests
make generate
make manifests

CKA Exam Key Points

  1. CRD basic structure -- Know the group, names, scope, versions, and schema fields
  2. kubectl operations on CRD resources -- CRD resources are used with kubectl just like native resources
  3. Operator = CRD + Controller -- Understand the core idea of the Operator pattern
  4. additionalPrinterColumns -- Customize kubectl get output columns
  5. CRD scope -- The difference between Namespaced and Cluster

🧪 Complete Hands-on Example: Create a CRD and Use an Operator

Scenario Description

Create a CRD named databases.example.com, then install prometheus-operator (kube-prometheus-stack) using Helm, and verify that the custom resource and Operator are working properly.

Prerequisites

  • A running Kubernetes cluster
  • kubectl configured
  • Helm v3 installed

Steps

Step 1: Create the CRD YAML

cat <<'EOF' > ~/crd-database.yaml
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
  name: databases.example.com
spec:
  group: example.com
  names:
    plural: databases
    singular: database
    shortNames:
    - db
    kind: Database
    listKind: DatabaseList
  scope: Namespaced
  versions:
  - name: v1
    served: true
    storage: true
    schema:
      openAPIV3Schema:
        type: object
        required:
        - spec
        properties:
          spec:
            type: object
            required:
            - engine
            - version
            properties:
              engine:
                type: string
                enum:
                - mysql
                - postgres
              version:
                type: string
              replicas:
                type: integer
                minimum: 1
                maximum: 10
                default: 1
EOF

Step 2: Apply the CRD

kubectl apply -f ~/crd-database.yaml
# customresourcedefinition.apiextensions.k8s.io/databases.example.com created

# Verify the CRD
kubectl get crd
# NAME                     CREATED AT
# databases.example.com    2026-05-27T10:00:00Z

kubectl get crd databases.example.com -o yaml

Step 3: Create a custom resource

cat <<'EOF' | kubectl apply -f -
apiVersion: example.com/v1
kind: Database
metadata:
  name: my-production-db
spec:
  engine: postgres
  version: "14.5"
  replicas: 3
EOF
# database.example.com/my-production-db created

# Use kubectl to work with the custom resource (same as native resources)
kubectl get databases
# NAME               ENGINE    VERSION   AGE
# my-production-db   postgres  14.5      10s

kubectl get db   # Using the shortName
# NAME               ENGINE    VERSION   AGE
# my-production-db   postgres  14.5      10s

kubectl describe database my-production-db

Step 4: Install prometheus-operator (kube-prometheus-stack)

# Add the prometheus repository
helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
helm repo update

# Install kube-prometheus-stack (includes prometheus-operator)
helm install prometheus prometheus-community/kube-prometheus-stack -n monitoring --create-namespace
# NAME: prometheus
# LAST DEPLOYED: Tue May 27 10:00:00 2026
# NAMESPACE: monitoring
# STATUS: deployed
# REVISION: 1

Step 5: View CRDs registered by the Operator

# View CRDs registered by prometheus-operator
kubectl get crd | grep monitoring.coreos.com
# alertmanagerconfigs.monitoring.coreos.com
# alertmanagers.monitoring.coreos.com
# podmonitors.monitoring.coreos.com
# prometheuses.monitoring.coreos.com
# prometheusrules.monitoring.coreos.com
# servicemonitors.monitoring.coreos.com

# View Operator Pods
kubectl get pods -n monitoring
# NAME                                                     READY   STATUS    RESTARTS   AGE
# prometheus-kube-prometheus-operator-xxxxxxxxx-xxxxx      1/1     Running   0          2m
# prometheus-kube-state-metrics-xxxxxxxxx-xxxxx            1/1     Running   0          2m
# prometheus-prometheus-node-exporter-xxxxx                1/1     Running   0          2m

Step 6: Use the Operator's CRD to create a ServiceMonitor

cat <<'EOF' | kubectl apply -f -
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
  name: example-app-monitor
  namespace: monitoring
spec:
  selector:
    matchLabels:
      app: example-app
  endpoints:
  - port: metrics
    interval: 30s
EOF
# servicemonitor.monitoring.coreos.com/example-app-monitor created

# View the ServiceMonitor
kubectl get servicemonitors -n monitoring
# NAME                    AGE
# example-app-monitor     10s

Verification Results

# Verify the CRD custom resource
kubectl get databases
# NAME               ENGINE    VERSION   AGE
# my-production-db   postgres  14.5      5m

# Verify the Operator is working
kubectl get pods -n monitoring | grep operator
# prometheus-kube-prometheus-operator-xxxxxxxxx-xxxxx   1/1     Running   0   5m

# Verify Prometheus managed by the Operator
kubectl get prometheus -n monitoring
# NAME                                    VERSION   REPLICAS   AGE
# prometheus-kube-prometheus-prometheus   v2.xx.x   1          5m

Exam Tips

  • CRD operations are exactly the same as native resources: kubectl get/create/delete/describe <crd-resource>
  • The CRD's metadata.name must follow the <plural>.<group> format (e.g., databases.example.com)
  • scope determines whether the resource is Namespaced or Cluster-level
  • Operator = CRD + Controller; related CRDs are automatically registered when an Operator is installed
  • The CKA exam only requires a basic understanding of CRDs/Operators, not writing Operator code

Official Documentation