本文首發於微信公衆號「 個人小碗湯」,掃碼文末二維碼便可關注,歡迎一塊兒交流!
kubernetes自定義資源對象再極大程度提升了API Server的可擴展性,讓企業可以根據業務需求經過CRD編寫controller或者operator來實現生產中各類特殊場景。隨着k8s的版本升級,CRD的功能也愈來愈完善,下面對其中幾點進行說明。html
如下驗證kubernetes版本爲1.13.2,docker版本:18.09.5前端
在項目中用自定義資源對象時,若是建立自定義資源時某些字段不符合要求,會致使監聽該資源對象的controller或者operator出現異常,解析結構體報錯,因此Validation這個功能很是實用,在建立時就進行校驗,減小後面的排錯和異常處理的麻煩。java
能夠經過 OpenAPI v3 schema驗證自定義對象是否符合標準 。此外,如下限制適用於 schema:node
default
、nullable
、discriminator
、readOnly
、writeOnly
、xml
、 deprecated
和 $ref
不能設置。uniqueItems
不能設置爲 true。additionalProperties
不能設置爲 false。可使用 kube-apiserverCustomResourceValidation
上的功能門(feature gate)禁用此功能:python
--feature-gates=CustomResourceValidation=false
從如下特性門參數說明地址,能夠看到Validation功能在k8s 1.8版本就已經有了,可是CustomResourceValidation特性門是默認false,1.9Beta以後版本默認爲truegit
https://kubernetes.io/docs/re... github
如下示例將大概對該功能進行應用和說明,在如下示例中,CustomResourceDefinition 對自定義對象應用如下驗證:golang
spec.replicas
爲必填項,類型爲integer,值爲大於等於0小於50的偶數(2的倍數);spec.repository
爲必填項;spec.version
爲必填項;spec.pause
爲boolean類型;spec.updateStrategy
爲object類型,該object中有type、pipeline、assignStrategies屬性;spec.updateStrategy.type
爲string類型,並且只能爲"AssignReceive", "AutoReceive"兩個枚舉值;spec.updateStrategy.pipeline
爲string類型,並且爲正整數的字符串,符合正則表達式^([1-9][0-9]*){1,3}$
;spec.updateStrategy.assignStrategies
爲array類型,其元素爲object類型(包含slots和fromReplicas屬性);spec.updateStrategy.assignStrategies.slots
爲1-16384的正整數;spec.updateStrategy.assignStrategies.fromReplicas
爲字符串,符合正則表達式^[a-z0-9,]{3,}$
,即至少匹配3位a-z或者0-9或者逗號的字符串;spec.pod
爲array類型,其元素爲object類型(包含configmap、monitorImage、initImage、middlewareImage字段);spec.pod.configmap
、spec.pod.monitorImage
、spec.pod.initImage
、spec.pod.middlewareImage
爲string類型;且用required指定configmap、initImage、middlewareImage字段爲必填項。將如下內容保存到 redis-cluster-crd.yaml
:面試
apiVersion: apiextensions.k8s.io/v1beta1 kind: CustomResourceDefinition metadata: name: redisclusters.redis.middleware.hc.cn spec: group: redis.middleware.hc.cn versions: - name: v1alpha1 # Each version can be enabled/disabled by Served flag. served: true # One and only one version must be marked as the storage version. storage: true scope: Namespaced names: kind: RedisCluster singular: rediscluster listKind: RedisClusterList plural: redisclusters shortNames: - rec # 執行kubectl get all時會查到pod、service、該crd等屬於all categories的資源對象 categories: - all validation: # openAPIV3Schema 適用於驗證自定義對象的 schema。 openAPIV3Schema: properties: spec: required: ["replicas", "repository", "version"] properties: pause: type: boolean replicas: type: integer minimum: 0 maximum: 50 # 偶數 multipleOf: 2 updateStrategy: type: object properties: type: type: string # 枚舉 enum: ["AssignReceive", "AutoReceive"] pipeline: type: string pattern: '^([1-9][0-9]*){1,3}$' assignStrategies: type: array items: type: object properties: slots: type: integer minimum: 1 maximum: 16384 fromReplicas: type: string # 至少匹配3位,a-z或者0-9或者, pattern: '^[a-z0-9,]{3,}$' pod: type: array items: type: object required: ["configmap", "middlewareImage", "initImage"] properties: configmap: type: string monitorImage: type: string initImage: type: string middlewareImage: type: string
建立它:正則表達式
kubectl create -f redis-cluster-crd.yaml
默認不加validation時,在建立自定義資源對象時,不會校驗,有些字段沒有了(如spec.replicas
)均可以正常被建立,爲了減小排錯的難度和operator、controller的麻煩的檢驗,因此在建立自定義資源定義時,就把validation加上。以上的檢驗應該覆蓋到了常見的檢驗場景,其餘場景能夠本身摸索。具體還能夠參考kubernetes源碼,1.13.2版本kubernetes源碼位於types.go第327行CustomResourceValidation結構體:
$GOPATH/src/k8s.io/kubernetes/staging/src/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/types.go
將如下YAML保存到redis-cluster-cr.yaml
:
apiVersion: redis.middleware.hc.cn/v1alpha1 kind: RedisCluster metadata: name: example000-redis-cluster namespace: kube-system spec: # 表明redis集羣的個數 replicas: 3 # 表明是否進入維修狀態 pause: true repository: library/redis # 鏡像版本,便於後續多版本特化支持 version: 3.2.6 #redis集羣升級策略 updateStrategy: # 升級類型爲AutoReceive(自動分配,不用AssignStrategies), AssignReceive(指定值分配,須要用AssignStrategies) type: AssignReceive1 pipeline: "100a" assignStrategies: - slots: 0 fromReplicas: nodeId1 - # 從nodeId3,nodeId4一共分配1000個卡槽 slots: 1000 # 多個nodeId用逗號分隔 fromReplicas: nodeId3,nodeId4 # redis 實例配置詳情 pod: # 配置文件模板名 - configmap: example000-redis-cluster-config # 監控鏡像 monitorImage: redis-exporter:v1 # 初始化鏡像 #initImage: redis-init:v1 # 中間件容器鏡像 middlewareImage: redis-trib:3.2.6
並建立它:
kubectl create -f redis-cluster-cr.yaml
會發現報如下錯誤:
# kubectl apply -f redis-cluster-cr.yaml The RedisCluster "example000-redis-cluster" is invalid: []: Invalid value: map[string]interface {}{"apiVersion":"redis.middleware.hc.cn/v1alpha1", "kind":"RedisCluster", "metadata":map[string]interface {}{"namespace":"kube-system", "uid":"b0946031-766b-11e9-b457-000c295db389", "resourceVersion":"44231", "generation":19, "creationTimestamp":"2019-05-14T17:14:10Z", "annotations":map[string]interface {}{"kubectl.kubernetes.io/last-applied-configuration":"{\"apiVersion\":\"redis.middleware.hc.cn/v1alpha1\",\"kind\":\"RedisCluster\",\"metadata\":{\"annotations\":{},\"name\":\"example000-redis-cluster\",\"namespace\":\"kube-system\"},\"spec\":{\"pause\":true,\"pod\":[{\"configmap\":\"example000-redis-cluster-config\",\"middlewareImage\":\"redis-trib:3.2.6\",\"monitorImage\":\"redis-exporter:v1\"}],\"replicas\":3,\"repository\":\"library/redis\",\"updateStrategy\":{\"assignStrategies\":[{\"fromReplicas\":\"nodeId1\",\"slots\":0},{\"fromReplicas\":\"nodeId3,nodeId4\",\"slots\":1000}],\"pipeline\":\"100a\",\"type\":\"AssignReceive1\"},\"version\":\"3.2.6\"}}\n"}, "name":"example000-redis-cluster"}, "spec":map[string]interface {}{"version":"3.2.6", "pause":true, "pod":[]interface {}{map[string]interface {}{"middlewareImage":"redis-trib:3.2.6", "monitorImage":"redis-exporter:v1", "configmap":"example000-redis-cluster-config"}}, "replicas":3, "repository":"library/redis", "updateStrategy":map[string]interface {}{"assignStrategies":[]interface {}{map[string]interface {}{"fromReplicas":"nodeId1", "slots":0}, map[string]interface {}{"fromReplicas":"nodeId3,nodeId4", "slots":1000}}, "pipeline":"100a", "type":"AssignReceive1"}}}: validation failure list: spec.updateStrategy.assignStrategies.fromReplicas in body should match '^[a-z0-9,]{3,}$' spec.updateStrategy.assignStrategies.slots in body should be greater than or equal to 1 spec.updateStrategy.pipeline in body should match '^([1-9][0-9]*){1,3}$' spec.updateStrategy.type in body should be one of [AssignReceive AutoReceive] spec.pod.initImage in body is required spec.replicas in body should be a multiple of 2
若是全部字段都符合校驗邏輯,才能夠建立對象。
將如下 YAML 保存到 redis-cluster-cr.yaml
:
apiVersion: redis.middleware.hc.cn/v1alpha1 kind: RedisCluster metadata: name: example000-redis-cluster namespace: kube-system spec: # 表明redis集羣的個數 replicas: 6 # 表明是否進入維修狀態 pause: true repository: library/redis # 鏡像版本,便於後續多版本特化支持 version: 3.2.6 #redis集羣升級策略 updateStrategy: # 升級類型爲AutoReceive(自動分配,不用AssignStrategies), AssignReceive(指定值分配,須要用AssignStrategies) type: AssignReceive pipeline: "100" assignStrategies: - slots: 1 fromReplicas: all - # 從nodeId3,nodeId4一共分配1000個卡槽 slots: 1000 # 多個nodeId用逗號分隔 fromReplicas: node1,node2 # redis 實例配置詳情 pod: # 配置文件模板名 - configmap: example000-redis-cluster-config # 監控鏡像 monitorImage: redis-exporter:v1 # 初始化鏡像 initImage: redis-init:v1 # 中間件容器鏡像 middlewareImage: redis-trib:3.2.6
並建立它,發現才能夠建立:
# kubectl apply -f redis-cluster-cr.yaml rediscluster.redis.middleware.hc.cn/example000-redis-cluster configured
類別是自定義資源所屬的分組資源的列表(例如 all
)。您可使用 kubectl get <category-name>
列出屬於該類別的資源。此功能可用於 v1.10 及以上k8s版本自定義資源。
如下示例添加 all
CustomResourceDefinition 中的類別列表,並說明如何使用 kubectl get all
輸出自定義資源 。
將如下 內容保存到 redis-cluster-crd.yaml
中執行kubectl apply -f redis-cluster-crd.yaml
:
apiVersion: apiextensions.k8s.io/v1beta1 kind: CustomResourceDefinition metadata: name: redisclusters.redis.middleware.hc.cn spec: group: redis.middleware.hc.cn versions: - name: v1alpha1 # Each version can be enabled/disabled by Served flag. served: true # One and only one version must be marked as the storage version. storage: true scope: Namespaced names: kind: RedisCluster singular: rediscluster listKind: RedisClusterList plural: redisclusters shortNames: - rec # 執行kubectl get all時會查到pod、service、該crd等屬於all categories的資源對象 categories: - all
將如下內容保存到redis-cluster-cr.yaml
中執行kubectl apply -f redis-cluster-cr.yaml
:
apiVersion: redis.middleware.hc.cn/v1alpha1 kind: RedisCluster metadata: name: example000-redis-cluster namespace: kube-system spec: # 表明redis集羣的個數 replicas: 6 # 表明是否進入維修狀態 pause: true repository: library/redis # 鏡像版本,便於後續多版本特化支持 version: 3.2.6 #redis集羣升級策略 updateStrategy: # 升級類型爲AutoReceive(自動分配,不用AssignStrategies), AssignReceive(指定值分配,須要用AssignStrategies) type: AssignReceive pipeline: "100" assignStrategies: - slots: 2000 fromReplicas: nodeId1 - # 從nodeId3,nodeId4一共分配1000個卡槽 slots: 1000 # 多個nodeId用逗號分隔 fromReplicas: nodeId3,nodeId4 # redis 實例配置詳情 pod: # 配置文件模板名 - configmap: example000-redis-cluster-config # 監控鏡像 monitorImage: redis-exporter:v1 # 初始化鏡像 initImage: redis-init:v1 # 中間件容器鏡像 middlewareImage: redis-trib:3.2.6
執行kubectl get all -nkube-system時會查到pod、service、該crd等屬於all categories的資源對象。(這個可能得等幾分鐘才能生效)
啓用狀態子資源後,將公開自定義資源的子資源 /status
。
.status
和 .spec
JSONPath 表示。PUT /status
對子資源的請求採用自定義資源對象,並忽略除狀態節以外的任何更改。PUT /status
對子資源的請求僅驗證自定義資源的狀態節。PUT/ POST/ PATCH
請求自定義資源忽略更改狀態節。.metadata.generation
的值。在code-generator生成代碼時會生成,以下方法:
// RedisClusterInterface has methods to work with RedisCluster resources. type RedisClusterInterface interface { Create(*v1alpha1.RedisCluster) (*v1alpha1.RedisCluster, error) Update(*v1alpha1.RedisCluster) (*v1alpha1.RedisCluster, error) UpdateStatus(*v1alpha1.RedisCluster) (*v1alpha1.RedisCluster, error) ...... }
啓用 scale 子資源後,將公開自定義資源的子資源 /scale
。該 autoscaling/v1.Scale 對象做爲有效負載發送 /scale。
要啓用 scale 子資源,CustomResourceDefinition 中須要定義如下值。
在如下示例中,啓用了status 和 scale 子資源。
將如下內容保存到redis-cluster-crd.yaml
並建立 kubectl apply -f redis-cluster-crd.yaml
:
apiVersion: apiextensions.k8s.io/v1beta1 kind: CustomResourceDefinition metadata: name: redisclusters.redis.middleware.hc.cn spec: group: redis.middleware.hc.cn versions: - name: v1alpha1 # Each version can be enabled/disabled by Served flag. served: true # One and only one version must be marked as the storage version. storage: true scope: Namespaced names: kind: RedisCluster singular: rediscluster listKind: RedisClusterList plural: redisclusters shortNames: - rec # 執行kubectl get all時會查到pod、service、該crd等屬於all categories的資源對象 categories: - all subresources: # status enables the status subresource. status: {} scale: # specReplicasPath defines the JSONPath inside of a custom resource that corresponds to Scale.Spec.Replicas. specReplicasPath: .spec.replicas # statusReplicasPath defines the JSONPath inside of a custom resource that corresponds to Scale.Status.Replicas. statusReplicasPath: .status.replicas # labelSelectorPath defines the JSONPath inside of a custom resource that corresponds to Scale.Status.Selector. labelSelectorPath: .status.labelSelector
建立 CustomResourceDefinition 對象後,您能夠建立自定義對象。
若是您將如下 YAML 保存到 redis-cluster-cr.yaml
:
apiVersion: redis.middleware.hc.cn/v1alpha1 kind: RedisCluster metadata: name: example000-redis-cluster namespace: kube-system spec: # 表明redis集羣的個數 replicas: 6 # 表明是否進入維修狀態 pause: true repository: library/redis # 鏡像版本,便於後續多版本特化支持 version: 3.2.6 #redis集羣升級策略 updateStrategy: # 升級類型爲AutoReceive(自動分配,不用AssignStrategies), AssignReceive(指定值分配,須要用AssignStrategies) type: AssignReceive pipeline: "100" assignStrategies: - slots: 2000 fromReplicas: nodeId1 - # 從nodeId3,nodeId4一共分配1000個卡槽 slots: 1000 # 多個nodeId用逗號分隔 fromReplicas: nodeId3,nodeId4 # redis 實例配置詳情 pod: # 配置文件模板名 - configmap: example000-redis-cluster-config # 監控鏡像 monitorImage: redis-exporter:v1 # 初始化鏡像 initImage: redis-init:v1 # 中間件容器鏡像 middlewareImage: redis-trib:3.2.6
並建立它:
kubectl create -f redis-cluster-cr.yaml
而後在如下位置建立新的命名空間 RESTful API 端點:
/apis/redis.middleware.hc.cn/v1alpha1/namespaces/kube-system/redisclusters/example000-redis-cluster/status
和
/apis/redis.middleware.hc.cn/v1alpha1/namespaces/kube-system/redisclusters/example000-redis-cluster/scale
可使用該 kubectl scale
命令縮放自定義資源。例如,以上建立的自定義資源的的 .spec.replicas
設置爲 10:
# kubectl get rec --all-namespaces NAMESPACE NAME DESIRED PAUSE AGE kube-system example000-redis-cluster 6 true 10h # kubectl scale --replicas=10 rec/example000-redis-cluster -nkube-system rediscluster.redis.middleware.hc.cn/example000-redis-cluster scaled # kubectl get rec --all-namespaces NAMESPACE NAME DESIRED PAUSE AGE kube-system example000-redis-cluster 10 true 10h # kubectl get rec example000-redis-cluster -n kube-system -o jsonpath='{.spec.replicas}' 10
從 Kubernetes 1.11 開始,kubectl 使用服務器端打印。服務器決定 kubectl get
命令顯示哪些列。您可使用 CustomResourceDefinition 自定義這些列。下面的示例將輸出 Spec
、Replicas
和 Age
列。
將 CustomResourceDefinition保存到 redis-cluster-crd.yaml
。
apiVersion: apiextensions.k8s.io/v1beta1 kind: CustomResourceDefinition metadata: name: redisclusters.redis.middleware.hc.cn spec: group: redis.middleware.hc.cn versions: - name: v1alpha1 # Each version can be enabled/disabled by Served flag. served: true # One and only one version must be marked as the storage version.
scope: Namespaced names: kind: RedisCluster singular: rediscluster listKind: RedisClusterList plural: redisclusters shortNames: - rec # 執行kubectl get all時會查到pod、service、該crd等屬於all categories的資源對象 categories: - all additionalPrinterColumns: - name: DESIRED type: integer description: The number of statefulset managed by the this redisCluster JSONPath: .spec.replicas # boolean,date,integer,number,string - name: PAUSE type: boolean description: Whether this redisCluster's grandson (pod) will not be managed by statefulset JSONPath: .spec.pause
2. 建立 CustomResourceDefinition:
kubectl create -f redis-cluster-crd.yaml
3. 使用上面建立的 `redis-cluster-cr.yaml` 實例。 4. 調用服務器端打印:
kubectl get rec --all-namespaces
請注意 `NAME`、`NAMESPACE`, `DESIRED`、`PAUSE` 和 `AGE` 在輸出列,而且都被轉成了大寫字母:
[root@master-192 redis-container]# kubectl get rec --all-namespaces
NAMESPACE NAME DESIRED PAUSE AGE
kube-system example000-redis-cluster 6 true 10h
`NAME`和`NAMESPACE` 列是隱含的,不須要在 CustomResourceDefinition 中定義。 ## operator中應用該特性 在golang編寫的operator代碼中建立該結構體:
//建立CRD
func CreateRedisClusterCRD(extensionCRClient *extensionsclient.Clientset) error {
//add CustomResourceValidation due to guarantee redis operator work normally labelSelectorPath := ".status.labelSelector" replicasMinimum := float64(0) replicasMaximum := float64(50) replicasMultipleOf := float64(2) slotsMinimum := float64(1) slotsMaximum := float64(16384) assignStr := "AssignReceive" autoStr := "AutoReceive" assignJson, _ := json.Marshal(assignStr) autoJson, _ := json.Marshal(autoStr) crd := &v1beta1.CustomResourceDefinition{ ObjectMeta: metav1.ObjectMeta{ Name: "redisclusters." + v1alpha1.SchemeGroupVersion.Group, }, Spec: v1beta1.CustomResourceDefinitionSpec{ Group: v1alpha1.SchemeGroupVersion.Group, Versions: []v1beta1.CustomResourceDefinitionVersion { { // Served is a flag enabling/disabling this version from being served via REST APIs Served: true, Name: v1alpha1.SchemeGroupVersion.Version, // Storage flags the version as storage version. There must be exactly one flagged as storage version Storage: true, }, }, Scope: v1beta1.NamespaceScoped, Names: v1beta1.CustomResourceDefinitionNames{ Kind: "RedisCluster", ListKind: "RedisClusterList", Plural: "redisclusters", Singular: "rediscluster", ShortNames: []string{"rec"}, Categories: []string{"all"}, }, Subresources: &v1beta1.CustomResourceSubresources { Status: &v1beta1.CustomResourceSubresourceStatus {}, Scale: &v1beta1.CustomResourceSubresourceScale { SpecReplicasPath: ".spec.replicas", StatusReplicasPath: ".status.replicas", LabelSelectorPath: &labelSelectorPath, }, }, AdditionalPrinterColumns: []v1beta1.CustomResourceColumnDefinition{ { Name: "DESIRED", Type: "integer", Description: "The number of statefulset managed by the this redisCluster", JSONPath: ".spec.replicas", }, { Name: "PAUSE", Type: "boolean", Description: "Whether this redisCluster's grandson (pod) will not be managed by statefulset", JSONPath: ".spec.pause", }, { Name: "AGE", Type: "date", JSONPath: ".metadata.creationTimestamp", }, }, Validation: &v1beta1.CustomResourceValidation { OpenAPIV3Schema: &v1beta1.JSONSchemaProps { Properties: map[string]v1beta1.JSONSchemaProps { "spec": { Required: []string{"replicas", "repository", "version"}, Properties: map[string]v1beta1.JSONSchemaProps{ "pause": { Type: "boolean", }, "replicas": { Type: "integer", Minimum: &replicasMinimum, Maximum: &replicasMaximum, MultipleOf: &replicasMultipleOf, }, "updateStrategy": { Type: "object", Properties: map[string]v1beta1.JSONSchemaProps{ "type": { Type: "string", Enum: []v1beta1.JSON { { //這裏必須是JSON格式的字符串 Raw: assignJson, }, { Raw: autoJson, }, }, }, "pipeline": { Type: "string", Pattern: `^([1-9][0-9]*){1,3}$`, }, "assignStrategies": { Type: "array", Items: &v1beta1.JSONSchemaPropsOrArray{ Schema: &v1beta1.JSONSchemaProps{ Type: "object", Properties: map[string]v1beta1.JSONSchemaProps{ "slots": { Type: "integer", Minimum: &slotsMinimum, Maximum: &slotsMaximum, }, "fromReplicas": { Type: "string", Pattern: `^[a-z0-9,]{3,}$`, }, }, }, }, }, }, }, }, }, "pod": { Type: "array", Items: &v1beta1.JSONSchemaPropsOrArray { Schema: &v1beta1.JSONSchemaProps { Type: "object", Required: []string{"replicas", "repository", "version"}, Properties: map[string]v1beta1.JSONSchemaProps{ "configmap": { Type: "string", }, "monitorImage": { Type: "string", }, "initImage": { Type: "string", }, "middlewareImage": { Type: "string", }, }, }, }, }, }, }, }, }, } _, err := extensionCRClient.ApiextensionsV1beta1().CustomResourceDefinitions().Create(crd) return err
}
## 參考 官方Extend the Kubernetes API with CustomResourceDefinitions: https://kubernetes.io/docs/tasks/access-kubernetes-api/custom-resources/custom-resource-definitions/ feature-gates參數說明: https://kubernetes.io/docs/reference/command-line-tools-reference/feature-gates/ CustomResourceDefinition中文文檔: https://kubernetes.feisky.xyz/cha-jian-kuo-zhan/api/customresourcedefinition swagger和openAPI: 數據類型: https://www.breakyizhan.com/swagger/2969.html 正則表達式: https://www.cnblogs.com/afarmer/archive/2011/08/29/2158860.html ---- --- 本公衆號免費提供csdn下載服務,海量IT學習資源,若是你準備入IT坑,勵志成爲優秀的程序猿,那麼這些資源很適合你,包括但不限於java、go、python、springcloud、elk、嵌入式 、大數據、面試資料、前端 等資源。同時咱們組建了一個技術交流羣,裏面有不少大佬,會不定時分享技術文章,若是你想來一塊兒學習提升,能夠公衆號後臺回覆【2】,免費邀請加技術交流羣互相學習提升,會不按期分享編程IT相關資源。 **掃碼關注,精彩內容第一時間推給你**