Discover and Use Custom Resources

KubernetesBeginner
Practice Now

Introduction

Kubernetes can be extended with new resource types. A CustomResourceDefinition, usually called a CRD, registers a new API resource with the Kubernetes API server. After that, you can use familiar kubectl commands to create, get, describe, label, and patch objects of that new type.

In this lab, you will create a small namespaced CRD named AppProfile, discover it with Kubernetes API discovery commands, create custom resources from YAML, and update them like regular Kubernetes objects. No controller is needed for this lab; the focus is understanding how custom resource types appear and how to work with their objects.

Prepare a Custom Resource Namespace

In this step, you will prepare a clean namespace for custom resource practice.

Start in /home/labex/project. You will write the CRD and custom resource manifests in this directory.

mkdir -p /home/labex/project
cd /home/labex/project

Check that the Kubernetes API server is reachable. If the VM has just started and the node is not ready yet, wait a few seconds and run the command again.

kubectl get nodes --request-timeout=5s

Expected output includes:

NAME         STATUS   ROLES           AGE   VERSION
labex-v135   Ready    control-plane   ...   v1.35...

Wait until the cluster networking Pod is Ready before creating application objects.

kubectl wait --namespace kube-system --for=condition=Ready pod -l k8s-app=calico-node --timeout=120s

Expected output:

pod/calico-node-... condition met

Create a namespace named ckad-prep-customresources:

kubectl create namespace ckad-prep-customresources

Set this namespace as the current context namespace:

kubectl config set-context --current --namespace=ckad-prep-customresources

Confirm the current namespace:

kubectl config view --minify --output 'jsonpath={..namespace}'; echo

Expected output:

ckad-prep-customresources

Register a New Resource Type with a CRD

In this step, you will create a CustomResourceDefinition named AppProfile.

A CRD has two important naming layers. The metadata.name is the plural resource name plus the API group, such as appprofiles.training.labex.io. The spec.names section defines how people will refer to the resource: kind: AppProfile, plural: appprofiles, singular: appprofile, and a short name appprof.

Create app-profile-crd.yaml:

cat > app-profile-crd.yaml <<'EOF'
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
  name: appprofiles.training.labex.io
spec:
  group: training.labex.io
  scope: Namespaced
  names:
    plural: appprofiles
    singular: appprofile
    kind: AppProfile
    shortNames:
    - appprof
  versions:
  - name: v1
    served: true
    storage: true
    schema:
      openAPIV3Schema:
        type: object
        properties:
          spec:
            type: object
            required:
            - image
            - replicas
            properties:
              image:
                type: string
              replicas:
                type: integer
                minimum: 1
              tier:
                type: string
EOF

Apply the CRD:

kubectl apply -f app-profile-crd.yaml

Expected output:

customresourcedefinition.apiextensions.k8s.io/appprofiles.training.labex.io created

Wait until the API server reports that the CRD is established:

kubectl wait --for=condition=Established crd/appprofiles.training.labex.io --timeout=120s

Expected output:

customresourcedefinition.apiextensions.k8s.io/appprofiles.training.labex.io condition met

Inspect the CRD's names and scope:

kubectl get crd appprofiles.training.labex.io -o 'jsonpath={.spec.names.kind}{" "}{.spec.names.plural}{" "}{.spec.scope}{"\n"}'

Expected output:

AppProfile appprofiles Namespaced

Discover and Create Custom Resources

In this step, you will discover the new resource type and create your first AppProfile.

kubectl api-resources lists resource types known to the API server. After a CRD is established, it appears in this discovery output beside built-in resource types.

kubectl api-resources --api-group=training.labex.io

Expected output includes:

NAME          SHORTNAMES   APIVERSION              NAMESPACED   KIND
appprofiles   appprof      training.labex.io/v1    true         AppProfile

Use kubectl explain to read the schema Kubernetes learned from the CRD. The spec field is where the application-specific settings live.

kubectl explain appprofile.spec

Expected output includes:

GROUP:      training.labex.io
KIND:       AppProfile
VERSION:    v1

FIELD: spec <Object>

Create frontend-profile.yaml:

cat > frontend-profile.yaml <<'EOF'
apiVersion: training.labex.io/v1
kind: AppProfile
metadata:
  name: frontend
  labels:
    app.kubernetes.io/part-of: storefront
    tier: web
spec:
  image: nginx:1.27
  replicas: 2
  tier: web
EOF

Apply the custom resource:

kubectl apply -f frontend-profile.yaml

Expected output:

appprofile.training.labex.io/frontend created

List the custom resources with the plural name:

kubectl get appprofiles

Expected output includes:

NAME       AGE
frontend   ...

Read fields from the custom resource with jsonpath:

kubectl get appprofile frontend -o 'jsonpath={.spec.image}{" "}{.spec.replicas}{" "}{.spec.tier}{"\n"}'

Expected output:

nginx:1.27 2 web

Update and Select Custom Resources

In this step, you will manage custom resources with the same kubectl patterns you already use for built-in objects.

Patch the frontend custom resource to change its desired replica count. The --type=merge option tells Kubernetes to merge your JSON object into the existing object instead of replacing the whole resource.

kubectl patch appprofile frontend --type=merge -p '{"spec":{"replicas":3}}'

Expected output:

appprofile.training.labex.io/frontend patched

Confirm the updated value:

kubectl get appprofile frontend -o 'jsonpath={.spec.replicas}{"\n"}'

Expected output:

3

Create a second custom resource named worker:

cat > worker-profile.yaml <<'EOF'
apiVersion: training.labex.io/v1
kind: AppProfile
metadata:
  name: worker
  labels:
    app.kubernetes.io/part-of: storefront
    tier: worker
spec:
  image: busybox:1.36
  replicas: 1
  tier: worker
EOF
kubectl apply -f worker-profile.yaml

Expected output:

appprofile.training.labex.io/worker created

List only the custom resources that belong to the storefront application. This works because custom resources support normal Kubernetes labels.

kubectl get appprofiles -l app.kubernetes.io/part-of=storefront

Expected output includes:

NAME       AGE
frontend   ...
worker     ...

Use the short name appprof to print each resource name and replica count:

kubectl get appprof -o 'jsonpath={range .items[*]}{.metadata.name}{"="}{.spec.replicas}{"\n"}{end}'

Expected output includes:

frontend=3
worker=1

Summary

You created a CRD, waited for it to become established, discovered the new API resource with kubectl api-resources and kubectl explain, created custom resources, patched a custom resource, selected them by label, and used a short name. These are the same operational skills you will use when CKAD tasks involve resources installed by an operator, controller, or platform extension.