Skip to content

自定义资源

概述

自定义资源(Custom Resource,CR)是Kubernetes API的扩展机制,允许用户定义自己的资源类型。通过自定义资源定义(Custom Resource Definition,CRD),用户可以扩展Kubernetes API,创建新的资源类型,并像使用内置资源一样使用kubectl操作这些资源。CRD是Kubernetes扩展生态的基石,为Operator、控制器和自定义工作负载提供了强大的扩展能力。

核心概念

1. Custom Resource Definition (CRD)

CRD是定义自定义资源类型的Kubernetes资源:

  • 定义资源的名称、作用域、版本
  • 定义资源的结构(Schema)
  • 定义资源的验证规则
  • 定义资源的打印列和子资源

2. Custom Resource (CR)

CR是CRD定义的资源类型的实例:

  • 遵循CRD定义的结构
  • 可以通过kubectl创建、更新、删除
  • 存储在etcd中
  • 可以被控制器监控和协调

3. API Group和Version

CRD属于API Group和Version:

  • API Group:资源的分组,如webapp.my.domain
  • Version:资源的版本,如v1v1beta1
  • 支持多版本共存和转换

4. Schema

Schema定义资源的结构:

  • 使用OpenAPI v3规范
  • 定义字段的类型、必填、默认值
  • 支持复杂的嵌套结构
  • 支持验证规则

CRD结构

基本CRD定义

yaml
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
  name: crontabs.stable.example.com
spec:
  group: stable.example.com
  versions:
    - name: v1
      served: true
      storage: true
      schema:
        openAPIV3Schema:
          type: object
          properties:
            spec:
              type: object
              properties:
                cronSpec:
                  type: string
                image:
                  type: string
                replicas:
                  type: integer
  scope: Namespaced
  names:
    plural: crontabs
    singular: crontab
    kind: CronTab
    shortNames:
    - ct

完整CRD定义

yaml
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
  name: applications.app.example.com
  labels:
    app.kubernetes.io/name: application-crd
    app.kubernetes.io/version: v1.0.0
  annotations:
    controller-gen.kubebuilder.io/version: v0.11.0
spec:
  group: app.example.com
  names:
    kind: Application
    listKind: ApplicationList
    plural: applications
    singular: application
    shortNames:
    - app
    - apps
    categories:
    - all
    - apps
  scope: Namespaced
  versions:
  - name: v1
    served: true
    storage: true
    deprecated: false
    deprecationWarning: ""
    schema:
      openAPIV3Schema:
        description: Application is the Schema for the applications API
        type: object
        properties:
          apiVersion:
            description: 'APIVersion defines the versioned schema of this representation
              of an object. Servers should convert recognized schemas to the latest
              internal value, and may reject unrecognized values.'
            type: string
          kind:
            description: 'Kind is a string value representing the REST resource this
              object represents. Servers may infer this from the endpoint the client
              submits requests to.'
            type: string
          metadata:
            type: object
          spec:
            description: ApplicationSpec defines the desired state of Application
            type: object
            required:
            - image
            properties:
              affinity:
                description: Affinity rules for pod scheduling
                properties:
                  nodeAffinity:
                    description: Node affinity rules
                    properties:
                      preferredDuringSchedulingIgnoredDuringExecution:
                        items:
                          properties:
                            preference:
                              properties:
                                matchExpressions:
                                  items:
                                    properties:
                                      key:
                                        type: string
                                      operator:
                                        type: string
                                      values:
                                        items:
                                          type: string
                                        type: array
                                    required:
                                    - key
                                    - operator
                                    type: object
                                  type: array
                                matchFields:
                                  items:
                                    properties:
                                      key:
                                        type: string
                                      operator:
                                        type: string
                                      values:
                                        items:
                                          type: string
                                        type: array
                                    required:
                                    - key
                                    - operator
                                    type: object
                                  type: array
                              type: object
                            weight:
                              format: int32
                              type: integer
                          required:
                          - preference
                          - weight
                          type: object
                        type: array
                      requiredDuringSchedulingIgnoredDuringExecution:
                        properties:
                          nodeSelectorTerms:
                            items:
                              properties:
                                matchExpressions:
                                  items:
                                    properties:
                                      key:
                                        type: string
                                      operator:
                                        type: string
                                      values:
                                        items:
                                          type: string
                                        type: array
                                    required:
                                    - key
                                    - operator
                                    type: object
                                  type: array
                                matchFields:
                                  items:
                                    properties:
                                      key:
                                        type: string
                                      operator:
                                        type: string
                                      values:
                                        items:
                                          type: string
                                        type: array
                                    required:
                                    - key
                                    - operator
                                    type: object
                                  type: array
                              type: object
                            type: array
                        required:
                        - nodeSelectorTerms
                        type: object
                    type: object
                  podAffinity:
                    type: object
                  podAntiAffinity:
                    type: object
                type: object
              autoscaling:
                description: Autoscaling configuration
                properties:
                  enabled:
                    type: boolean
                  maxReplicas:
                    format: int32
                    maximum: 100
                    minimum: 1
                    type: integer
                  minReplicas:
                    format: int32
                    maximum: 100
                    minimum: 1
                    type: integer
                  targetCPUUtilizationPercentage:
                    format: int32
                    maximum: 100
                    minimum: 1
                    type: integer
                  targetMemoryUtilizationPercentage:
                    format: int32
                    maximum: 100
                    minimum: 1
                    type: integer
                required:
                - enabled
                type: object
              config:
                additionalProperties:
                  type: string
                description: Configuration key-value pairs
                type: object
              env:
                description: Environment variables
                items:
                  properties:
                    name:
                      type: string
                    value:
                      type: string
                    valueFrom:
                      properties:
                        configMapKeyRef:
                          properties:
                            key:
                              type: string
                            name:
                              type: string
                            optional:
                              type: boolean
                          required:
                          - key
                          type: object
                        fieldRef:
                          properties:
                            apiVersion:
                              type: string
                            fieldPath:
                              type: string
                          required:
                          - fieldPath
                          type: object
                        resourceFieldRef:
                          properties:
                            containerName:
                              type: string
                            divisor:
                              anyOf:
                              - type: integer
                              - type: string
                              pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
                              x-kubernetes-int-or-string: true
                            resource:
                              type: string
                          required:
                          - resource
                          type: object
                        secretKeyRef:
                          properties:
                            key:
                              type: string
                            name:
                              type: string
                            optional:
                              type: boolean
                          required:
                          - key
                          type: object
                      type: object
                  required:
                  - name
                  type: object
                type: array
              image:
                description: Container image
                pattern: '^[a-zA-Z0-9._-]+(/[a-zA-Z0-9._-]+)*:[a-zA-Z0-9._-]+$'
                type: string
              imagePullPolicy:
                description: Image pull policy
                enum:
                - Always
                - Never
                - IfNotPresent
                type: string
              imagePullSecrets:
                description: Image pull secrets
                items:
                  properties:
                    name:
                      type: string
                  required:
                  - name
                  type: object
                type: array
              ingress:
                description: Ingress configuration
                properties:
                  annotations:
                    additionalProperties:
                      type: string
                    type: object
                  enabled:
                    type: boolean
                  hosts:
                    items:
                      properties:
                        host:
                          type: string
                        paths:
                          items:
                            properties:
                              path:
                                type: string
                              pathType:
                                enum:
                                - Exact
                                - Prefix
                                - ImplementationSpecific
                                type: string
                            required:
                            - path
                            - pathType
                            type: object
                          type: array
                      required:
                      - host
                      type: object
                    type: array
                  tls:
                    items:
                      properties:
                        hosts:
                          items:
                            type: string
                          type: array
                        secretName:
                          type: string
                      required:
                      - secretName
                      type: object
                    type: array
                required:
                - enabled
                type: object
              livenessProbe:
                description: Liveness probe configuration
                properties:
                  exec:
                    properties:
                      command:
                        items:
                          type: string
                        type: array
                    required:
                    - command
                    type: object
                  failureThreshold:
                    format: int32
                    minimum: 1
                    type: integer
                  httpGet:
                    properties:
                      host:
                        type: string
                      httpHeaders:
                        items:
                          properties:
                            name:
                              type: string
                            value:
                              type: string
                          required:
                          - name
                          - value
                          type: object
                        type: array
                      path:
                        type: string
                      port:
                        anyOf:
                        - type: integer
                        - type: string
                        x-kubernetes-int-or-string: true
                      scheme:
                        type: string
                    required:
                    - port
                    type: object
                  initialDelaySeconds:
                    format: int32
                    minimum: 0
                    type: integer
                  periodSeconds:
                    format: int32
                    minimum: 1
                    type: integer
                  successThreshold:
                    format: int32
                    minimum: 1
                    type: integer
                  tcpSocket:
                    properties:
                      host:
                        type: string
                      port:
                        anyOf:
                        - type: integer
                        - type: string
                        x-kubernetes-int-or-string: true
                    required:
                    - port
                    type: object
                  terminationGracePeriodSeconds:
                    format: int64
                    type: integer
                  timeoutSeconds:
                    format: int32
                    minimum: 1
                    type: integer
                type: object
              nodeSelector:
                additionalProperties:
                  type: string
                description: Node selector for pod scheduling
                type: object
              port:
                description: Container port
                format: int32
                maximum: 65535
                minimum: 1
                type: integer
              readinessProbe:
                description: Readiness probe configuration
                properties:
                  exec:
                    properties:
                      command:
                        items:
                          type: string
                        type: array
                    required:
                    - command
                    type: object
                  failureThreshold:
                    format: int32
                    minimum: 1
                    type: integer
                  httpGet:
                    properties:
                      host:
                        type: string
                      httpHeaders:
                        items:
                          properties:
                            name:
                              type: string
                            value:
                              type: string
                          required:
                          - name
                          - value
                          type: object
                        type: array
                      path:
                        type: string
                      port:
                        anyOf:
                        - type: integer
                        - type: string
                        x-kubernetes-int-or-string: true
                      scheme:
                        type: string
                    required:
                    - port
                    type: object
                  initialDelaySeconds:
                    format: int32
                    minimum: 0
                    type: integer
                  periodSeconds:
                    format: int32
                    minimum: 1
                    type: integer
                  successThreshold:
                    format: int32
                    minimum: 1
                    type: integer
                  tcpSocket:
                    properties:
                      host:
                        type: string
                      port:
                        anyOf:
                        - type: integer
                        - type: string
                        x-kubernetes-int-or-string: true
                    required:
                    - port
                    type: object
                  terminationGracePeriodSeconds:
                    format: int64
                    type: integer
                  timeoutSeconds:
                    format: int32
                    minimum: 1
                    type: integer
                type: object
              replicas:
                default: 1
                description: Number of replicas
                format: int32
                maximum: 100
                minimum: 1
                type: integer
              resources:
                description: Resource requirements
                properties:
                  limits:
                    additionalProperties:
                      anyOf:
                      - type: integer
                      - type: string
                      pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
                      x-kubernetes-int-or-string: true
                    type: object
                  requests:
                    additionalProperties:
                      anyOf:
                      - type: integer
                      - type: string
                      pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
                      x-kubernetes-int-or-string: true
                    type: object
                type: object
              service:
                description: Service configuration
                properties:
                  annotations:
                    additionalProperties:
                      type: string
                    type: object
                  port:
                    format: int32
                    maximum: 65535
                    minimum: 1
                    type: integer
                  targetPort:
                    anyOf:
                    - type: integer
                    - type: string
                    x-kubernetes-int-or-string: true
                  type:
                    enum:
                    - ClusterIP
                    - NodePort
                    - LoadBalancer
                    - ExternalName
                    type: string
                required:
                - type
                type: object
              tolerations:
                description: Tolerations for pod scheduling
                items:
                  properties:
                    effect:
                      type: string
                    key:
                      type: string
                    operator:
                      type: string
                    tolerationSeconds:
                      format: int64
                      type: integer
                    value:
                      type: string
                  type: object
                type: array
              volumeMounts:
                description: Volume mounts
                items:
                  properties:
                    mountPath:
                      type: string
                    mountPropagation:
                      type: string
                    name:
                      type: string
                    readOnly:
                      type: boolean
                    subPath:
                      type: string
                    subPathExpr:
                      type: string
                  required:
                  - mountPath
                  - name
                  type: object
                type: array
              volumes:
                description: Volumes
                items:
                  properties:
                    awsElasticBlockStore:
                      properties:
                        fsType:
                          type: string
                        partition:
                          format: int32
                          type: integer
                        readOnly:
                          type: boolean
                        volumeID:
                          type: string
                      required:
                      - volumeID
                      type: object
                    azureDisk:
                      properties:
                        cachingMode:
                          type: string
                        diskName:
                          type: string
                        diskURI:
                          type: string
                        fsType:
                          type: string
                        kind:
                          type: string
                        readOnly:
                          type: boolean
                      required:
                      - diskName
                      - diskURI
                      type: object
                    azureFile:
                      properties:
                        readOnly:
                          type: boolean
                        secretName:
                          type: string
                        shareName:
                          type: string
                      required:
                      - secretName
                      - shareName
                      type: object
                    cephfs:
                      properties:
                        monitors:
                          items:
                            type: string
                          type: array
                        path:
                          type: string
                        readOnly:
                          type: boolean
                        secretFile:
                          type: string
                        secretRef:
                          properties:
                            name:
                              type: string
                          type: object
                        user:
                          type: string
                      required:
                      - monitors
                      type: object
                    cinder:
                      properties:
                        fsType:
                          type: string
                        readOnly:
                          type: boolean
                        secretRef:
                          properties:
                            name:
                              type: string
                          type: object
                        volumeID:
                          type: string
                      required:
                      - volumeID
                      type: object
                    configMap:
                      properties:
                        defaultMode:
                          format: int32
                          type: integer
                        items:
                          items:
                            properties:
                              key:
                                type: string
                              mode:
                                format: int32
                                type: integer
                              path:
                                type: string
                            required:
                            - key
                            - path
                            type: object
                          type: array
                        name:
                          type: string
                        optional:
                          type: boolean
                      type: object
                    csi:
                      properties:
                        driver:
                          type: string
                        fsType:
                          type: string
                        nodePublishSecretRef:
                          properties:
                            name:
                              type: string
                          type: object
                        readOnly:
                          type: boolean
                        volumeAttributes:
                          additionalProperties:
                            type: string
                          type: object
                      required:
                      - driver
                      type: object
                    downwardAPI:
                      properties:
                        defaultMode:
                          format: int32
                          type: integer
                        items:
                          items:
                            properties:
                              fieldRef:
                                properties:
                                  apiVersion:
                                    type: string
                                  fieldPath:
                                    type: string
                                required:
                                - fieldPath
                                type: object
                              mode:
                                format: int32
                                type: integer
                              path:
                                type: string
                              resourceFieldRef:
                                properties:
                                  containerName:
                                    type: string
                                  divisor:
                                    anyOf:
                                    - type: integer
                                    - type: string
                                    pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
                                    x-kubernetes-int-or-string: true
                                  resource:
                                    type: string
                                required:
                                - resource
                                type: object
                            required:
                            - path
                            type: object
                          type: array
                      type: object
                    emptyDir:
                      properties:
                        medium:
                          type: string
                        sizeLimit:
                          anyOf:
                          - type: integer
                          - type: string
                          pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
                          x-kubernetes-int-or-string: true
                      type: object
                    ephemeral:
                      properties:
                        volumeClaimTemplate:
                          properties:
                            metadata:
                              type: object
                            spec:
                              properties:
                                accessModes:
                                  items:
                                    type: string
                                  type: array
                                dataSource:
                                  properties:
                                    apiGroup:
                                      type: string
                                    kind:
                                      type: string
                                    name:
                                      type: string
                                  required:
                                  - kind
                                  - name
                                  type: object
                                dataSourceRef:
                                  properties:
                                    apiGroup:
                                      type: string
                                    kind:
                                      type: string
                                    name:
                                      type: string
                                  required:
                                  - kind
                                  - name
                                  type: object
                                resources:
                                  properties:
                                    limits:
                                      additionalProperties:
                                        anyOf:
                                        - type: integer
                                        - type: string
                                        pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
                                        x-kubernetes-int-or-string: true
                                      type: object
                                    requests:
                                      additionalProperties:
                                        anyOf:
                                        - type: integer
                                        - type: string
                                        pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
                                        x-kubernetes-int-or-string: true
                                      type: object
                                  type: object
                                selector:
                                  properties:
                                    matchExpressions:
                                      items:
                                        properties:
                                          key:
                                            type: string
                                          operator:
                                            type: string
                                          values:
                                            items:
                                              type: string
                                            type: array
                                        required:
                                        - key
                                        - operator
                                        type: object
                                      type: array
                                    matchLabels:
                                      additionalProperties:
                                        type: string
                                      type: object
                                  type: object
                                storageClassName:
                                  type: string
                                volumeMode:
                                  type: string
                                volumeName:
                                  type: string
                              type: object
                          required:
                          - spec
                          type: object
                      required:
                      - volumeClaimTemplate
                      type: object
                    fc:
                      properties:
                        fsType:
                          type: string
                        lun:
                          format: int32
                          type: integer
                        readOnly:
                          type: boolean
                        targetWWNs:
                          items:
                            type: string
                          type: array
                        wwids:
                          items:
                            type: string
                          type: array
                      type: object
                    flexVolume:
                      properties:
                        driver:
                          type: string
                        fsType:
                          type: string
                        options:
                          additionalProperties:
                            type: string
                          type: object
                        readOnly:
                          type: boolean
                        secretRef:
                          properties:
                            name:
                              type: string
                          type: object
                      required:
                      - driver
                      type: object
                    flocker:
                      properties:
                        datasetName:
                          type: string
                        datasetUUID:
                          type: string
                      type: object
                    gcePersistentDisk:
                      properties:
                        fsType:
                          type: string
                        partition:
                          format: int32
                          type: integer
                        pdName:
                          type: string
                        readOnly:
                          type: boolean
                      required:
                      - pdName
                      type: object
                    gitRepo:
                      properties:
                        directory:
                          type: string
                        repository:
                          type: string
                        revision:
                          type: string
                      required:
                      - repository
                      type: object
                    glusterfs:
                      properties:
                        endpoints:
                          type: string
                        path:
                          type: string
                        readOnly:
                          type: boolean
                      required:
                      - endpoints
                      - path
                      type: object
                    hostPath:
                      properties:
                        path:
                          type: string
                        type:
                          type: string
                      required:
                      - path
                      type: object
                    iscsi:
                      properties:
                        chapAuthDiscovery:
                          type: boolean
                        chapAuthSession:
                          type: boolean
                        fsType:
                          type: string
                        initiatorName:
                          type: string
                        iqn:
                          type: string
                        iscsiInterface:
                          type: string
                        lun:
                          format: int32
                          type: integer
                        portals:
                          items:
                            type: string
                          type: array
                        readOnly:
                          type: boolean
                        secretRef:
                          properties:
                            name:
                              type: string
                          type: object
                        targetPortal:
                          type: string
                      required:
                      - iqn
                      - lun
                      - targetPortal
                      type: object
                    name:
                      type: string
                    nfs:
                      properties:
                        path:
                          type: string
                        readOnly:
                          type: boolean
                        server:
                          type: string
                      required:
                      - path
                      - server
                      type: object
                    persistentVolumeClaim:
                      properties:
                        claimName:
                          type: string
                        readOnly:
                          type: boolean
                      required:
                      - claimName
                      type: object
                    photonPersistentDisk:
                      properties:
                        fsType:
                          type: string
                        pdID:
                          type: string
                      required:
                      - pdID
                      type: object
                    portworxVolume:
                      properties:
                        fsType:
                          type: string
                        readOnly:
                          type: boolean
                        volumeID:
                          type: string
                      required:
                      - volumeID
                      type: object
                    projected:
                      properties:
                        defaultMode:
                          format: int32
                          type: integer
                        sources:
                          items:
                            properties:
                              configMap:
                                properties:
                                  items:
                                    items:
                                      properties:
                                        key:
                                          type: string
                                        mode:
                                          format: int32
                                          type: integer
                                        path:
                                          type: string
                                      required:
                                      - key
                                      - path
                                      type: object
                                    type: array
                                  name:
                                    type: string
                                  optional:
                                    type: boolean
                                type: object
                              downwardAPI:
                                properties:
                                  items:
                                    items:
                                      properties:
                                        fieldRef:
                                          properties:
                                            apiVersion:
                                              type: string
                                            fieldPath:
                                              type: string
                                          required:
                                          - fieldPath
                                          type: object
                                        mode:
                                          format: int32
                                          type: integer
                                        path:
                                          type: string
                                        resourceFieldRef:
                                          properties:
                                            containerName:
                                              type: string
                                            divisor:
                                              anyOf:
                                              - type: integer
                                              - type: string
                                              pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
                                              x-kubernetes-int-or-string: true
                                            resource:
                                              type: string
                                          required:
                                          - resource
                                          type: object
                                      required:
                                      - path
                                      type: object
                                    type: array
                                type: object
                              secret:
                                properties:
                                  items:
                                    items:
                                      properties:
                                        key:
                                          type: string
                                        mode:
                                          format: int32
                                          type: integer
                                        path:
                                          type: string
                                      required:
                                      - key
                                      - path
                                      type: object
                                    type: array
                                  name:
                                    type: string
                                  optional:
                                    type: boolean
                                type: object
                              serviceAccountToken:
                                properties:
                                  audience:
                                    type: string
                                  expirationSeconds:
                                    format: int64
                                    type: integer
                                  path:
                                    type: string
                                required:
                                - path
                                type: object
                            type: object
                          type: array
                      required:
                      - sources
                      type: object
                    quobyte:
                      properties:
                        group:
                          type: string
                        readOnly:
                          type: boolean
                        registry:
                          type: string
                        tenant:
                          type: string
                        user:
                          type: string
                        volume:
                          type: string
                      required:
                      - registry
                      - volume
                      type: object
                    rbd:
                      properties:
                        fsType:
                          type: string
                        image:
                          type: string
                        keyring:
                          type: string
                        monitors:
                          items:
                            type: string
                          type: array
                        pool:
                          type: string
                        readOnly:
                          type: boolean
                        secretRef:
                          properties:
                            name:
                              type: string
                          type: object
                        user:
                          type: string
                      required:
                      - image
                      - monitors
                      type: object
                    scaleIO:
                      properties:
                        fsType:
                          type: string
                        gateway:
                          type: string
                        protectionDomain:
                          type: string
                        readOnly:
                          type: boolean
                        secretRef:
                          properties:
                            name:
                              type: string
                          type: object
                        sslEnabled:
                          type: boolean
                        storageMode:
                          type: string
                        storagePool:
                          type: string
                        system:
                          type: string
                        volumeName:
                          type: string
                      required:
                      - gateway
                      - secretRef
                      - system
                      type: object
                    secret:
                      properties:
                        defaultMode:
                          format: int32
                          type: integer
                        items:
                          items:
                            properties:
                              key:
                                type: string
                              mode:
                                format: int32
                                type: integer
                              path:
                                type: string
                            required:
                            - key
                            - path
                            type: object
                          type: array
                        optional:
                          type: boolean
                        secretName:
                          type: string
                      type: object
                    storageos:
                      properties:
                        fsType:
                          type: string
                        readOnly:
                          type: boolean
                        secretRef:
                          properties:
                            apiVersion:
                              type: string
                            fieldPath:
                              type: string
                            kind:
                              type: string
                            name:
                              type: string
                            namespace:
                              type: string
                            resourceVersion:
                              type: string
                            uid:
                              type: string
                          type: object
                        volumeName:
                          type: string
                        volumeNamespace:
                          type: string
                      type: object
                    vsphereVolume:
                      properties:
                        fsType:
                          type: string
                        storagePolicyID:
                          type: string
                        storagePolicyName:
                          type: string
                        volumePath:
                          type: string
                      required:
                      - volumePath
                      type: object
                  required:
                  - name
                  type: object
                type: array
            required:
            - image
            - port
            type: object
          status:
            description: ApplicationStatus defines the observed state of Application
            properties:
              availableReplicas:
                format: int32
                type: integer
              conditions:
                items:
                  properties:
                    lastTransitionTime:
                      format: date-time
                      type: string
                    message:
                      type: string
                    observedGeneration:
                      format: int64
                      type: integer
                    reason:
                      type: string
                    status:
                      type: string
                    type:
                      type: string
                  required:
                  - lastTransitionTime
                  - status
                  - type
                  type: object
                type: array
                x-kubernetes-list-map-keys:
                - type
                x-kubernetes-list-type: map
              currentReplicas:
                format: int32
                type: integer
              observedGeneration:
                format: int64
                type: integer
              phase:
                type: string
              readyReplicas:
                format: int32
                type: integer
              replicas:
                format: int32
                type: integer
              unavailableReplicas:
                format: int32
                type: integer
              updatedReplicas:
                format: int32
                type: integer
            required:
            - availableReplicas
            - currentReplicas
            - readyReplicas
            - replicas
            type: object
        required:
        - spec
        type: object
    subresources:
      status: {}
      scale:
        specReplicasPath: .spec.replicas
        statusReplicasPath: .status.replicas
        labelSelectorPath: .status.labelSelector
    additionalPrinterColumns:
    - name: Replicas
      type: integer
      description: The number of replicas
      jsonPath: .spec.replicas
    - name: Ready
      type: integer
      description: The number of ready replicas
      jsonPath: .status.readyReplicas
    - name: Available
      type: integer
      description: The number of available replicas
      jsonPath: .status.availableReplicas
    - name: Phase
      type: string
      description: The phase of the application
      jsonPath: .status.phase
    - name: Age
      type: date
      jsonPath: .metadata.creationTimestamp
    selectableFields:
    - jsonPath: .spec.image
    - jsonPath: .status.phase

实践示例

示例1:简单的CRD和CR

定义CRD

yaml
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
  name: websites.web.example.com
spec:
  group: web.example.com
  versions:
  - name: v1
    served: true
    storage: true
    schema:
      openAPIV3Schema:
        type: object
        properties:
          spec:
            type: object
            required:
            - domain
            - image
            properties:
              domain:
                type: string
                pattern: '^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$'
              image:
                type: string
              replicas:
                type: integer
                minimum: 1
                maximum: 10
                default: 1
              tls:
                type: boolean
                default: false
          status:
            type: object
            properties:
              url:
                type: string
              phase:
                type: string
    subresources:
      status: {}
    additionalPrinterColumns:
    - name: Domain
      type: string
      jsonPath: .spec.domain
    - name: Replicas
      type: integer
      jsonPath: .spec.replicas
    - name: TLS
      type: boolean
      jsonPath: .spec.tls
    - name: Phase
      type: string
      jsonPath: .status.phase
    - name: Age
      type: date
      jsonPath: .metadata.creationTimestamp
  scope: Namespaced
  names:
    plural: websites
    singular: website
    kind: Website
    shortNames:
    - web

创建CR

yaml
apiVersion: web.example.com/v1
kind: Website
metadata:
  name: my-website
  namespace: default
spec:
  domain: my-website.example.com
  image: nginx:1.25
  replicas: 3
  tls: true

kubectl操作

bash
kubectl apply -f website-crd.yaml

kubectl get crd websites.web.example.com

kubectl apply -f my-website.yaml

kubectl get websites

kubectl describe website my-website

kubectl get website my-website -o yaml

kubectl patch website my-website --type merge -p '{"spec":{"replicas":5}}'

kubectl delete website my-website

示例2:带验证的CRD

定义CRD

yaml
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
  name: databases.db.example.com
spec:
  group: db.example.com
  versions:
  - name: v1
    served: true
    storage: true
    schema:
      openAPIV3Schema:
        type: object
        required:
        - spec
        properties:
          spec:
            type: object
            required:
            - version
            - storageSize
            - database
            properties:
              version:
                type: string
                enum:
                - "13"
                - "14"
                - "15"
                - "16"
              storageSize:
                type: string
                pattern: '^[1-9][0-9]*Gi$'
              storageClass:
                type: string
              database:
                type: string
                minLength: 1
                maxLength: 63
                pattern: '^[a-z_][a-z0-9_]*$'
              replicas:
                type: integer
                minimum: 1
                maximum: 10
                default: 1
              resources:
                type: object
                properties:
                  requests:
                    type: object
                    properties:
                      cpu:
                        type: string
                        pattern: '^[0-9]+m?$'
                      memory:
                        type: string
                        pattern: '^[0-9]+Mi?$'
                  limits:
                    type: object
                    properties:
                      cpu:
                        type: string
                        pattern: '^[0-9]+m?$'
                      memory:
                        type: string
                        pattern: '^[0-9]+Mi?$'
              backup:
                type: object
                properties:
                  enabled:
                    type: boolean
                  schedule:
                    type: string
                    pattern: '^(@(annually|yearly|monthly|weekly|daily|hourly|reboot))|(@every (\d+(ns|us|ms|s|m|h))+)|((((\d+,)+\d+|(\d+(\/|-)\d+)|\d+|\*) ?){5,7})$'
                  retention:
                    type: integer
                    minimum: 1
                    maximum: 30
          status:
            type: object
            properties:
              phase:
                type: string
                enum:
                - Creating
                - Running
                - Updating
                - Failed
              currentVersion:
                type: string
              currentReplicas:
                type: integer
              readyReplicas:
                type: integer
              lastBackup:
                type: string
                format: date-time
              conditions:
                type: array
                items:
                  type: object
                  required:
                  - type
                  - status
                  properties:
                    type:
                      type: string
                    status:
                      type: string
                      enum:
                      - "True"
                      - "False"
                      - "Unknown"
                    reason:
                      type: string
                    message:
                      type: string
                    lastTransitionTime:
                      type: string
                      format: date-time
    subresources:
      status: {}
      scale:
        specReplicasPath: .spec.replicas
        statusReplicasPath: .status.readyReplicas
    additionalPrinterColumns:
    - name: Version
      type: string
      jsonPath: .spec.version
    - name: Storage
      type: string
      jsonPath: .spec.storageSize
    - name: Replicas
      type: integer
      jsonPath: .spec.replicas
    - name: Ready
      type: integer
      jsonPath: .status.readyReplicas
    - name: Phase
      type: string
      jsonPath: .status.phase
    - name: Age
      type: date
      jsonPath: .metadata.creationTimestamp
  scope: Namespaced
  names:
    plural: databases
    singular: database
    kind: Database
    shortNames:
    - db

创建CR

yaml
apiVersion: db.example.com/v1
kind: Database
metadata:
  name: my-database
  namespace: default
spec:
  version: "15"
  storageSize: "50Gi"
  storageClass: "fast-ssd"
  database: "myapp"
  replicas: 3
  resources:
    requests:
      cpu: "500m"
      memory: "1Gi"
    limits:
      cpu: "2000m"
      memory: "4Gi"
  backup:
    enabled: true
    schedule: "0 2 * * *"
    retention: 7

示例3:多版本CRD

定义CRD

yaml
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
  name: queues.messaging.example.com
spec:
  group: messaging.example.com
  versions:
  - name: v1alpha1
    served: true
    storage: false
    deprecated: true
    deprecationWarning: "messaging.example.com/v1alpha1 Queue is deprecated; use messaging.example.com/v1 Queue instead"
    schema:
      openAPIV3Schema:
        type: object
        properties:
          spec:
            type: object
            required:
            - name
            properties:
              name:
                type: string
              maxSize:
                type: integer
              ttl:
                type: integer
          status:
            type: object
            properties:
              messageCount:
                type: integer
    subresources:
      status: {}
  - name: v1beta1
    served: true
    storage: false
    deprecated: false
    schema:
      openAPIV3Schema:
        type: object
        properties:
          spec:
            type: object
            required:
            - queueName
            properties:
              queueName:
                type: string
              maxSize:
                type: integer
              ttl:
                type: integer
              deadLetterQueue:
                type: string
          status:
            type: object
            properties:
              messageCount:
                type: integer
              consumerCount:
                type: integer
    subresources:
      status: {}
    additionalPrinterColumns:
    - name: Name
      type: string
      jsonPath: .spec.queueName
    - name: Messages
      type: integer
      jsonPath: .status.messageCount
    - name: Age
      type: date
      jsonPath: .metadata.creationTimestamp
  - name: v1
    served: true
    storage: true
    schema:
      openAPIV3Schema:
        type: object
        properties:
          spec:
            type: object
            required:
            - name
            properties:
              name:
                type: string
                minLength: 1
                maxLength: 255
              maxSize:
                type: integer
                minimum: 1
                maximum: 1000000
              ttl:
                type: integer
                minimum: 1
              deadLetterQueue:
                type: string
              retryPolicy:
                type: object
                properties:
                  maxRetries:
                    type: integer
                    minimum: 0
                    maximum: 100
                  backoffMultiplier:
                    type: number
                    minimum: 1
                    maximum: 10
              encryption:
                type: object
                properties:
                  enabled:
                    type: boolean
                  kmsKeyId:
                    type: string
          status:
            type: object
            properties:
              messageCount:
                type: integer
              consumerCount:
                type: integer
              phase:
                type: string
              conditions:
                type: array
                items:
                  type: object
                  properties:
                    type:
                      type: string
                    status:
                      type: string
                    message:
                      type: string
    subresources:
      status: {}
      scale:
        specReplicasPath: .spec.maxSize
        statusReplicasPath: .status.messageCount
    additionalPrinterColumns:
    - name: Name
      type: string
      jsonPath: .spec.name
    - name: Max Size
      type: integer
      jsonPath: .spec.maxSize
    - name: Messages
      type: integer
      jsonPath: .status.messageCount
    - name: Consumers
      type: integer
      jsonPath: .status.consumerCount
    - name: Phase
      type: string
      jsonPath: .status.phase
    - name: Age
      type: date
      jsonPath: .metadata.creationTimestamp
  scope: Namespaced
  names:
    plural: queues
    singular: queue
    kind: Queue
    shortNames:
    - q
  conversion:
    strategy: Webhook
    webhook:
      conversionReviewVersions: ["v1", "v1beta1"]
      clientConfig:
        service:
          name: queue-conversion-webhook
          namespace: messaging-system
          path: /convert

版本转换Webhook

go
package main

import (
    "encoding/json"
    "fmt"
    "net/http"
    
    apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
    metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

type ConversionReview struct {
    apiextensionsv1.ConversionReview
}

func convertHandler(w http.ResponseWriter, r *http.Request) {
    var review ConversionReview
    if err := json.NewDecoder(r.Body).Decode(&review); err != nil {
        http.Error(w, err.Error(), http.StatusBadRequest)
        return
    }
    
    response := apiextensionsv1.ConversionReview{
        TypeMeta: metav1.TypeMeta{
            Kind:       "ConversionReview",
            APIVersion: "apiextensions.k8s.io/v1",
        },
        Response: &apiextensionsv1.ConversionResponse{
            UID:              review.Request.UID,
            ConvertedObjects: []runtime.Object{},
            Result: metav1.Status{
                Status: metav1.StatusSuccess,
            },
        },
    }
    
    for _, obj := range review.Request.Objects {
        converted, err := convertObject(obj, review.Request.DesiredAPIVersion)
        if err != nil {
            response.Response.Result.Status = metav1.StatusFailure
            response.Response.Result.Message = err.Error()
            break
        }
        response.Response.ConvertedObjects = append(response.Response.ConvertedObjects, converted)
    }
    
    w.Header().Set("Content-Type", "application/json")
    json.NewEncoder(w).Encode(response)
}

func convertObject(obj runtime.RawExtension, targetVersion string) (runtime.Object, error) {
    switch targetVersion {
    case "messaging.example.com/v1":
        return convertToV1(obj)
    case "messaging.example.com/v1beta1":
        return convertToV1beta1(obj)
    case "messaging.example.com/v1alpha1":
        return convertToV1alpha1(obj)
    default:
        return nil, fmt.Errorf("unsupported target version: %s", targetVersion)
    }
}

func main() {
    http.HandleFunc("/convert", convertHandler)
    http.ListenAndServeTLS(":8443", "/certs/tls.crt", "/certs/tls.key", nil)
}

自定义控制器

控制器框架

使用controller-runtime

go
package main

import (
    "context"
    "fmt"
    "os"
    
    "k8s.io/apimachinery/pkg/runtime"
    "k8s.io/apimachinery/pkg/runtime/schema"
    "k8s.io/apimachinery/pkg/runtime/serializer"
    "k8s.io/client-go/kubernetes/scheme"
    "k8s.io/client-go/rest"
    ctrl "sigs.k8s.io/controller-runtime"
    "sigs.k8s.io/controller-runtime/pkg/client"
    "sigs.k8s.io/controller-runtime/pkg/log/zap"
    
    webv1 "example.com/webhook/api/v1"
)

func init() {
    _ = webv1.AddToScheme(scheme.Scheme)
}

type WebsiteReconciler struct {
    client.Client
    Scheme *runtime.Scheme
}

func (r *WebsiteReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
    logger := ctrl.LoggerFrom(ctx)
    
    website := &webv1.Website{}
    if err := r.Get(ctx, req.NamespacedName, website); err != nil {
        return ctrl.Result{}, client.IgnoreNotFound(err)
    }
    
    logger.Info("Reconciling Website", "name", website.Name)
    
    return ctrl.Result{}, nil
}

func (r *WebsiteReconciler) SetupWithManager(mgr ctrl.Manager) error {
    return ctrl.NewControllerManagedBy(mgr).
        For(&webv1.Website{}).
        Complete(r)
}

func main() {
    ctrl.SetLogger(zap.New(zap.UseDevMode(true)))
    
    mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{
        Scheme:             scheme.Scheme,
        MetricsBindAddress: ":8080",
        Port:               9443,
    })
    if err != nil {
        setupLog.Error(err, "unable to start manager")
        os.Exit(1)
    }
    
    if err = (&WebsiteReconciler{
        Client: mgr.GetClient(),
        Scheme: mgr.GetScheme(),
    }).SetupWithManager(mgr); err != nil {
        setupLog.Error(err, "unable to create controller", "controller", "Website")
        os.Exit(1)
    }
    
    if err := mgr.Start(ctrl.SetupSignalHandler()); err != nil {
        setupLog.Error(err, "problem running manager")
        os.Exit(1)
    }
}

资源验证

使用ValidatingAdmissionWebhook

Webhook定义

yaml
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingWebhookConfiguration
metadata:
  name: website-validator
webhooks:
- name: validate.website.web.example.com
  rules:
  - apiGroups:   ["web.example.com"]
    apiVersions: ["v1"]
    operations:  ["CREATE", "UPDATE"]
    resources:   ["websites"]
    scope:       "Namespaced"
  clientConfig:
    service:
      name: website-validator
      namespace: webhook-system
      path: "/validate"
    caBundle: LS0tLS...
  admissionReviewVersions: ["v1"]
  sideEffects: None
  timeoutSeconds: 5

Webhook实现

go
package main

import (
    "encoding/json"
    "fmt"
    "net/http"
    "strings"
    
    admissionv1 "k8s.io/api/admission/v1"
    metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    
    webv1 "example.com/webhook/api/v1"
)

type AdmissionReview struct {
    admissionv1.AdmissionReview
}

func validateHandler(w http.ResponseWriter, r *http.Request) {
    var review AdmissionReview
    if err := json.NewDecoder(r.Body).Decode(&review); err != nil {
        http.Error(w, err.Error(), http.StatusBadRequest)
        return
    }
    
    response := admissionv1.AdmissionReview{
        TypeMeta: metav1.TypeMeta{
            Kind:       "AdmissionReview",
            APIVersion: "admission.k8s.io/v1",
        },
        Response: &admissionv1.AdmissionResponse{
            UID:     review.Request.UID,
            Allowed: true,
        },
    }
    
    website := &webv1.Website{}
    if err := json.Unmarshal(review.Request.Object.Raw, website); err != nil {
        response.Response.Allowed = false
        response.Response.Result = &metav1.Status{
            Status:  metav1.StatusFailure,
            Message: err.Error(),
        }
    } else if err := validateWebsite(website); err != nil {
        response.Response.Allowed = false
        response.Response.Result = &metav1.Status{
            Status:  metav1.StatusFailure,
            Message: err.Error(),
        }
    }
    
    w.Header().Set("Content-Type", "application/json")
    json.NewEncoder(w).Encode(response)
}

func validateWebsite(website *webv1.Website) error {
    if website.Spec.Domain == "" {
        return fmt.Errorf("domain is required")
    }
    
    if strings.Contains(website.Spec.Domain, " ") {
        return fmt.Errorf("domain cannot contain spaces")
    }
    
    if website.Spec.Replicas < 1 || website.Spec.Replicas > 10 {
        return fmt.Errorf("replicas must be between 1 and 10")
    }
    
    return nil
}

func main() {
    http.HandleFunc("/validate", validateHandler)
    http.ListenAndServeTLS(":8443", "/certs/tls.crt", "/certs/tls.key", nil)
}

使用MutatingAdmissionWebhook

Webhook定义

yaml
apiVersion: admissionregistration.k8s.io/v1
kind: MutatingWebhookConfiguration
metadata:
  name: website-mutator
webhooks:
- name: mutate.website.web.example.com
  rules:
  - apiGroups:   ["web.example.com"]
    apiVersions: ["v1"]
    operations:  ["CREATE", "UPDATE"]
    resources:   ["websites"]
    scope:       "Namespaced"
  clientConfig:
    service:
      name: website-mutator
      namespace: webhook-system
      path: "/mutate"
    caBundle: LS0tLS...
  admissionReviewVersions: ["v1"]
  sideEffects: None
  timeoutSeconds: 5

Webhook实现

go
package main

import (
    "encoding/json"
    "net/http"
    
    admissionv1 "k8s.io/api/admission/v1"
    metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    
    webv1 "example.com/webhook/api/v1"
)

func mutateHandler(w http.ResponseWriter, r *http.Request) {
    var review AdmissionReview
    if err := json.NewDecoder(r.Body).Decode(&review); err != nil {
        http.Error(w, err.Error(), http.StatusBadRequest)
        return
    }
    
    website := &webv1.Website{}
    if err := json.Unmarshal(review.Request.Object.Raw, website); err != nil {
        http.Error(w, err.Error(), http.StatusBadRequest)
        return
    }
    
    patches := []PatchOperation{}
    
    if website.Spec.Replicas == 0 {
        patches = append(patches, PatchOperation{
            Op:    "add",
            Path:  "/spec/replicas",
            Value: 1,
        })
    }
    
    if website.Spec.TLS == nil {
        patches = append(patches, PatchOperation{
            Op:    "add",
            Path:  "/spec/tls",
            Value: false,
        })
    }
    
    patchBytes, _ := json.Marshal(patches)
    
    response := admissionv1.AdmissionReview{
        TypeMeta: metav1.TypeMeta{
            Kind:       "AdmissionReview",
            APIVersion: "admission.k8s.io/v1",
        },
        Response: &admissionv1.AdmissionResponse{
            UID:     review.Request.UID,
            Allowed: true,
            Patch:   patchBytes,
            PatchType: func() *admissionv1.PatchType {
                pt := admissionv1.PatchTypeJSONPatch
                return &pt
            }(),
        },
    }
    
    w.Header().Set("Content-Type", "application/json")
    json.NewEncoder(w).Encode(response)
}

type PatchOperation struct {
    Op    string      `json:"op"`
    Path  string      `json:"path"`
    Value interface{} `json:"value,omitempty"`
}

func main() {
    http.HandleFunc("/mutate", mutateHandler)
    http.ListenAndServeTLS(":8443", "/certs/tls.crt", "/certs/tls.key", nil)
}

kubectl操作命令

CRD管理

bash
kubectl get crd

kubectl get crd -o wide

kubectl describe crd websites.web.example.com

kubectl get crd websites.web.example.com -o yaml

kubectl apply -f crd.yaml

kubectl edit crd websites.web.example.com

kubectl delete crd websites.web.example.com

kubectl api-resources | grep website

kubectl explain website.spec

kubectl explain website.status

CR管理

bash
kubectl get websites

kubectl get websites -o wide

kubectl get websites -o yaml

kubectl get websites -o json

kubectl describe website my-website

kubectl get website my-website -o jsonpath='{.spec.domain}'

kubectl get website my-website -o jsonpath='{.status.phase}'

kubectl apply -f my-website.yaml

kubectl create -f my-website.yaml

kubectl edit website my-website

kubectl patch website my-website --type merge -p '{"spec":{"replicas":5}}'

kubectl patch website my-website --type strategic -p '{"spec":{"tls":true}}'

kubectl delete website my-website

kubectl delete websites --all

kubectl get websites --selector=app=myapp

kubectl get websites --field-selector=status.phase=Running

kubectl scale website my-website --replicas=5

kubectl get websites --sort-by=.metadata.creationTimestamp

kubectl get websites -o custom-columns=NAME:.metadata.name,DOMAIN:.spec.domain,REPLICAS:.spec.replicas

kubectl get websites -o go-template='{{range .items}}{{.metadata.name}}: {{.spec.domain}}{{"\n"}}{{end}}'

Webhook管理

bash
kubectl get validatingwebhookconfigurations

kubectl describe validatingwebhookconfiguration website-validator

kubectl get mutatingwebhookconfigurations

kubectl describe mutatingwebhookconfiguration website-mutator

kubectl logs -n webhook-system -l app=website-validator

kubectl get events -n webhook-system --sort-by='.lastTimestamp'

故障排查指南

问题1:CRD创建失败

症状

bash
Error from server (BadRequest): error when creating "crd.yaml": CustomResourceDefinition.apiextensions.k8s.io "websites.web.example.com" is invalid: spec.names.singular: Invalid value: "website": must be unique

排查步骤

bash
kubectl get crd | grep website

kubectl describe crd websites.web.example.com

kubectl logs -n kube-system -l component=kube-apiserver

kubectl api-resources | grep website

解决方案

  • 检查CRD名称是否已存在
  • 验证API Group和Version格式
  • 确认Schema定义正确
  • 检查字段命名规范

问题2:CR创建失败

症状

bash
Error from server (BadRequest): error when creating "website.yaml": Website.web.example.com "my-website" is invalid: spec.domain: Invalid value: "my website": spec.domain in body should match '^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$'

排查步骤

bash
kubectl explain website.spec

kubectl get crd websites.web.example.com -o jsonpath='{.spec.versions[0].schema.openAPIV3Schema}'

kubectl apply -f website.yaml --dry-run=client

kubectl apply -f website.yaml --dry-run=server

解决方案

  • 检查字段值是否符合Schema定义
  • 验证必填字段是否提供
  • 确认枚举值是否正确
  • 检查正则表达式匹配

问题3:Webhook验证失败

症状

bash
Error from server (InternalError): error when creating "website.yaml": Internal error occurred: failed calling webhook "validate.website.web.example.com": Post "https://website-validator.webhook-system.svc:443/validate?timeout=5s": dial tcp 10.96.100.200:443: connect: connection refused

排查步骤

bash
kubectl get svc -n webhook-system

kubectl get pods -n webhook-system -l app=website-validator

kubectl logs -n webhook-system -l app=website-validator

kubectl describe validatingwebhookconfiguration website-validator

kubectl exec -n webhook-system -it <pod-name> -- curl -k https://localhost:8443/validate

解决方案

  • 检查Webhook服务是否正常运行
  • 验证证书配置是否正确
  • 确认Webhook路径是否正确
  • 检查网络策略限制

问题4:版本转换失败

症状

bash
Error from server: error when creating "queue.yaml": conversion webhook for messaging.example.com/v1, Kind=Queue failed: Post "https://queue-conversion-webhook.messaging-system.svc:443/convert?timeout=30s": no such host

排查步骤

bash
kubectl get crd queues.messaging.example.com -o yaml | grep -A 10 conversion

kubectl get svc -n messaging-system

kubectl get pods -n messaging-system -l app=queue-conversion-webhook

kubectl logs -n messaging-system -l app=queue-conversion-webhook

解决方案

  • 检查转换Webhook服务配置
  • 验证Webhook服务是否运行
  • 确认版本转换逻辑正确
  • 检查Webhook证书配置

问题5:状态更新失败

症状

bash
kubectl get website my-website -o yaml
status:
  phase: ""

排查步骤

bash
kubectl describe website my-website

kubectl get website my-website -o jsonpath='{.metadata.resourceVersion}'

kubectl auth can-i update websites/status --as=system:serviceaccount:default:default

kubectl logs -n controller-system -l app=website-controller

解决方案

  • 检查控制器是否正常运行
  • 验证RBAC权限配置
  • 确认状态子资源定义
  • 检查控制器协调逻辑

最佳实践

1. CRD设计最佳实践

命名规范

yaml
metadata:
  name: <plural>.<group>
  
spec:
  group: <domain>.<organization>
  names:
    kind: <Kind>
    plural: <plural>
    singular: <singular>
    shortNames:
    - <short>

Schema设计

yaml
schema:
  openAPIV3Schema:
    type: object
    description: "Clear description of the resource"
    properties:
      spec:
        type: object
        description: "Specification of the resource"
        required:
        - requiredField
        properties:
          requiredField:
            type: string
            description: "Description of the field"
            minLength: 1
            maxLength: 255
          optionalField:
            type: string
            description: "Optional field with default"
            default: "default-value"

2. 版本管理最佳实践

版本策略

yaml
versions:
- name: v1alpha1
  served: true
  storage: false
  deprecated: true
  deprecationWarning: "Use v1 instead"
  
- name: v1beta1
  served: true
  storage: false
  
- name: v1
  served: true
  storage: true

版本转换

yaml
conversion:
  strategy: Webhook
  webhook:
    conversionReviewVersions: ["v1", "v1beta1"]
    clientConfig:
      service:
        name: conversion-webhook
        namespace: system

3. 验证最佳实践

Schema验证

yaml
properties:
  email:
    type: string
    format: email
    pattern: '^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'
  
  port:
    type: integer
    minimum: 1
    maximum: 65535
  
  replicas:
    type: integer
    minimum: 1
    maximum: 100
    default: 1

Webhook验证

go
func validateResource(obj runtime.Object) error {
    switch o := obj.(type) {
    case *MyResource:
        if o.Spec.RequiredField == "" {
            return fmt.Errorf("requiredField is required")
        }
        if err := validateEmail(o.Spec.Email); err != nil {
            return err
        }
    }
    return nil
}

4. 状态管理最佳实践

状态定义

yaml
status:
  properties:
    phase:
      type: string
      enum:
      - Pending
      - Running
      - Failed
      - Unknown
    conditions:
      type: array
      items:
        type: object
        required:
        - type
        - status
        properties:
          type:
            type: string
          status:
            type: string
            enum:
            - "True"
            - "False"
            - "Unknown"
          reason:
            type: string
          message:
            type: string
          lastTransitionTime:
            type: string
            format: date-time

状态更新

go
func (r *MyReconciler) updateStatus(ctx context.Context, obj *MyResource) error {
    obj.Status.ObservedGeneration = obj.Generation
    obj.Status.LastUpdateTime = metav1.Now()
    
    if obj.Status.Phase == "" {
        obj.Status.Phase = "Pending"
    }
    
    return r.Status().Update(ctx, obj)
}

5. 性能优化最佳实践

打印列优化

yaml
additionalPrinterColumns:
- name: Replicas
  type: integer
  description: "Number of replicas"
  jsonPath: .spec.replicas
  priority: 0
- name: Phase
  type: string
  description: "Current phase"
  jsonPath: .status.phase
  priority: 0
- name: Internal
  type: string
  jsonPath: .status.internalInfo
  priority: 1

选择器字段

yaml
selectableFields:
- jsonPath: .spec.image
- jsonPath: .status.phase

6. 文档最佳实践

CRD文档

yaml
schema:
  openAPIV3Schema:
    type: object
    description: |
      MyResource is a custom resource that manages application deployments.
      
      ## Overview
      MyResource provides a simplified interface for deploying applications
      with built-in best practices for production workloads.
      
      ## Features
      - Automatic scaling
      - Rolling updates
      - Health monitoring
      - Resource management
      
      ## Example
      ```yaml
      apiVersion: app.example.com/v1
      kind: MyResource
      metadata:
        name: my-app
      spec:
        replicas: 3
        image: nginx:1.25
      ```
    properties:
      spec:
        type: object
        description: "Specification of the desired behavior"

总结

本章详细介绍了自定义资源的定义和管理方法:

  1. 基础概念: 掌握了CRD、CR、API Group等核心概念
  2. CRD定义: 学会了完整的CRD结构定义
  3. 验证机制: 理解了Schema验证和Webhook验证
  4. 版本管理: 掌握了多版本CRD和版本转换
  5. 控制器开发: 学会了自定义控制器的开发
  6. 故障排查: 掌握了常见问题的诊断和解决方法

自定义资源是Kubernetes扩展的核心机制,为Operator模式和高级应用管理提供了基础。

下一步学习