溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

如何給你的K8s PaaS 上線一個新功能

發(fā)布時間:2021-12-16 10:02:16 來源:億速云 閱讀:116 作者:柒染 欄目:云計算

這期內(nèi)容當(dāng)中小編將會給大家?guī)碛嘘P(guān)如何給你的K8s PaaS 上線一個新功能,文章內(nèi)容豐富且以專業(yè)的角度為大家分析和敘述,閱讀完這篇文章希望大家可以有所收獲。

下面講解一下如何在 20 分鐘內(nèi),為你基于 KubeVela 的 PaaS “上線“一個新能力。

在正式開始之前,請確保你本地已經(jīng)正確安裝了 KubeVela 及其依賴的 K8s 環(huán)境。

KubeVela 擴(kuò)展的基本結(jié)構(gòu)

KubeVela 的基本架構(gòu)如圖所示:

如何給你的K8s PaaS 上線一個新功能

簡單來說,KubeVela 通過添加 Workload TypeTrait 來為用戶擴(kuò)展能力,平臺的服務(wù)提供方通過 Definition 文件注冊和擴(kuò)展,向上通過 Appfile 透出擴(kuò)展的功能。官方文檔中也分別給出了基本的編寫流程,其中 2 個是 Workload 的擴(kuò)展例子,一個是 Trait 的擴(kuò)展例子:

  • OpenFaaS 為例的 Workload Type 擴(kuò)展

  • 云資源 RDS 為例的 Workload Type 擴(kuò)展

  • KubeWatch 為例的 Trait 擴(kuò)展

我們以一個內(nèi)置的 WorkloadDefinition 為例來介紹一下 Definition 文件的基本結(jié)構(gòu):

apiVersion: core.oam.dev/v1alpha2
kind: WorkloadDefinition
metadata:
  name: webservice
  annotations:
    definition.oam.dev/description: "`Webservice` is a workload type to describe long-running, scalable, containerized services that have a stable network endpoint to receive external network traffic from customers.
    If workload type is skipped for any service defined in Appfile, it will be defaulted to `Web Service` type."
spec:
  definitionRef:
    name: deployments.apps
  extension:
    template: |
      output: {
          apiVersion: "apps/v1"
          kind:       "Deployment"
          spec: {
              selector: matchLabels: {
                  "app.oam.dev/component": context.name
              }
              template: {
                  metadata: labels: {
                      "app.oam.dev/component": context.name
                  }
                  spec: {
                      containers: [{
                          name:  context.name
                          image: parameter.image
                          if parameter["cmd"] != _|_ {
                              command: parameter.cmd
                          }
                          if parameter["env"] != _|_ {
                              env: parameter.env
                          }
                          if context["config"] != _|_ {
                              env: context.config
                          }
                          ports: [{
                              containerPort: parameter.port
                          }]
                          if parameter["cpu"] != _|_ {
                              resources: {
                                  limits:
                                      cpu: parameter.cpu
                                  requests:
                                      cpu: parameter.cpu
                              }}
                      }]
              }}}
      }
      parameter: {
          // +usage=Which image would you like to use for your service
          // +short=i
          image: string

          // +usage=Commands to run in the container
          cmd?: [...string]

          // +usage=Which port do you want customer traffic sent to
          // +short=p
          port: *80 | int
          // +usage=Define arguments by using environment variables
          env?: [...{
              // +usage=Environment variable name
              name: string
              // +usage=The value of the environment variable
              value?: string
              // +usage=Specifies a source the value of this var should come from
              valueFrom?: {
                  // +usage=Selects a key of a secret in the pod's namespace
                  secretKeyRef: {
                      // +usage=The name of the secret in the pod's namespace to select from
                      name: string
                      // +usage=The key of the secret to select from. Must be a valid secret key
                      key: string
                  }
              }
          }]
          // +usage=Number of CPU units for the service, like `0.5` (0.5 CPU core), `1` (1 CPU core)
          cpu?: string
      }

乍一看挺長的,好像很復(fù)雜,但是不要著急,其實細(xì)看之下它分為兩部分:

  • 不含擴(kuò)展字段的 Definition 注冊部分

  • 供 Appfile 使用的擴(kuò)展模板(CUE Template)部分

我們拆開來慢慢介紹,其實學(xué)起來很簡單。

不含擴(kuò)展字段的 Definition 注冊部分

apiVersion: core.oam.dev/v1alpha2
kind: WorkloadDefinition
metadata:
  name: webservice
  annotations:
    definition.oam.dev/description: "`Webservice` is a workload type to describe long-running, scalable, containerized services that have a stable network endpoint to receive external network traffic from customers.
    If workload type is skipped for any service defined in Appfile, it will be defaulted to `Web Service` type."
spec:
  definitionRef:
    name: deployments.apps

這一部分滿打滿算 11 行,其中有 3 行是在介紹 webservice 的功能,5行是固定的格式。只有 2 行是有特定信息:

  definitionRef:
    name: deployments.apps

這兩行的意思代表了這個 Definition 背后用的 CRD 名稱是什么,其格式是 <resources>.<api-group>。了解 K8s 的同學(xué)應(yīng)該知道 K8s 中比較常用的是通過 api-group, versionkind 定位資源,而 kind 在 K8s restful API 中對應(yīng)的是 resources。以大家熟悉 Deploymentingress 為例,它的對應(yīng)關(guān)系如下:

如何給你的K8s PaaS 上線一個新功能

這里補充一個小知識,為什么有了 kind 還要加個 resources 的概念呢? 因為一個 CRD 除了 kind 本身還有一些像 status,replica 這樣的字段希望跟 spec 本身解耦開來在 restful API 中單獨更新, 所以 resources 除了 kind 對應(yīng)的那一個,還會有一些額外的 resources,如 Deployment 的 status 表示為 deployments/status

所以相信聰明的你已經(jīng)明白了不含 extension 的情況下,Definition 應(yīng)該怎么寫了,最簡單的就是根據(jù) K8s 的資源組合方式拼接一下,只要填下面三個尖括號的空格就可以了。

apiVersion: core.oam.dev/v1alpha2
kind: WorkloadDefinition
metadata:
  name: <這里寫名稱>
spec:
  definitionRef:
    name: <這里寫resources>.<這里寫api-group>

針對運維特征注冊(TraitDefinition)也是這樣。

apiVersion: core.oam.dev/v1alpha2
kind: TraitDefinition
metadata:
  name: <這里寫名稱>
spec:
  definitionRef:
    name: <這里寫resources>.<這里寫api-group>

所以把 Ingress 作為 KubeVela 的擴(kuò)展寫進(jìn)去就是:

apiVersion: core.oam.dev/v1alpha2
kind: TraitDefinition
metadata:
  name:  ingress
spec:
  definitionRef:
    name: ingresses.networking.k8s.io

除此之外,TraitDefinition 中還增加了一些其他功能模型層功能,如:

  • appliesToWorkloads: 表示這個 trait 可以作用于哪些 Workload 類型。

  • conflictWith: 表示這個 trait 和哪些其他類型的 trait 有沖突。

  • workloadRefPath: 表示這個 trait 包含的 workload 字段是哪個,KubeVela 在生成 trait 對象時會自動填充。 ...

這些功能都是可選的,本文中不涉及使用,在后續(xù)的其他文章中我們再給大家詳細(xì)介紹。

所以到這里,相信你已經(jīng)掌握了一個不含 extensions 的基本擴(kuò)展模式,而剩下部分就是圍繞 CUE 的抽象模板。

供 Appfile 使用的擴(kuò)展模板(CUE Template)部分

對 CUE 本身有興趣的同學(xué)可以參考這篇 CUE 基礎(chǔ)入門 多做一些了解,限于篇幅本文對 CUE 本身不詳細(xì)展開。

大家知道 KubeVela 的 Appfile 寫起來很簡潔,但是 K8s 的對象是一個相對比較復(fù)雜的 YAML,而為了保持簡潔的同時又不失可擴(kuò)展性,KubeVela 提供了一個從復(fù)雜到簡潔的橋梁。 這就是 Definition 中 CUE Template 的作用。

CUE 格式模板

讓我們先來看一個 Deployment 的 YAML 文件,如下所示,其中很多內(nèi)容都是固定的框架(模板部分),真正需要用戶填的內(nèi)容其實就少量的幾個字段(參數(shù)部分)。

apiVersion: apps/v1
kind: Deployment
meadata:
  name: mytest
spec:
  template:
    spec:
      containers:
      - name: mytest
        env:
        - name: a
          value: b
        image: nginx:v1
    metadata:
      labels:
        app.oam.dev/component: mytest
  selector:
    matchLabels:
      app.oam.dev/component: mytest

在 KubeVela 中,Definition 文件的固定格式就是分為 outputparameter 兩部分。其中output中的內(nèi)容就是“模板部分”,而 parameter 就是參數(shù)部分。

那我們來把上面的 Deployment YAML 改寫成 Definition 中模板的格式。

output: {
    apiVersion: "apps/v1"
    kind:       "Deployment"
    metadata: name: "mytest"
    spec: {
        selector: matchLabels: {
            "app.oam.dev/component": "mytest"
        }
        template: {
            metadata: labels: {
                "app.oam.dev/component": "mytest"
            }
            spec: {
                containers: [{
                    name:  "mytest"
                    image: "nginx:v1"
                    env: [{name:"a",value:"b"}]
                }]
            }}}
}

這個格式跟 json 很像,事實上這個是 CUE 的格式,而 CUE 本身就是一個 json 的超集。也就是說,CUE的格式在滿足 JSON 規(guī)則的基礎(chǔ)上,增加了一些簡便規(guī)則, 使其更易讀易用:

  • C 語言的注釋風(fēng)格。

  • 表示字段名稱的雙引號在沒有特殊符號的情況下可以缺省。

  • 字段值結(jié)尾的逗號可以缺省,在字段最后的逗號寫了也不會出錯。

  • 最外層的大括號可以省略。

CUE 格式的模板參數(shù)--變量引用

編寫好了模板部分,讓我們來構(gòu)建參數(shù)部分,而這個參數(shù)其實就是變量的引用。

parameter: {
    name: string
    image: string
}
output: {
    apiVersion: "apps/v1"
    kind:       "Deployment"
    spec: {
        selector: matchLabels: {
            "app.oam.dev/component": parameter.name
        }
        template: {
            metadata: labels: {
                "app.oam.dev/component": parameter.name
            }
            spec: {
                containers: [{
                    name:  parameter.name
                    image: parameter.image
                }]
            }}}
}

如上面的這個例子所示,KubeVela 中的模板參數(shù)就是通過 parameter 這個部分來完成的,而 parameter 本質(zhì)上就是作為引用,替換掉了 output 中的某些字段。

完整的 Definition 以及在 Appfile 使用

事實上,經(jīng)過上面兩部分的組合,我們已經(jīng)可以寫出一個完整的 Definition 文件:

apiVersion: core.oam.dev/v1alpha2
kind: WorkloadDefinition
metadata:
  name: mydeploy
spec:
  definitionRef:
    name: deployments.apps
  extension:
    template: |
        parameter: {
            name: string
            image: string
        }
        output: {
            apiVersion: "apps/v1"
            kind:       "Deployment"
            spec: {
                selector: matchLabels: {
                    "app.oam.dev/component": parameter.name
                }
                template: {
                    metadata: labels: {
                        "app.oam.dev/component": parameter.name
                    }
                    spec: {
                        containers: [{
                            name:  parameter.name
                            image: parameter.image
                        }]
                    }}}
        }

為了方便調(diào)試,一般情況下可以預(yù)先分為兩個文件,一部分放前面的 yaml 部分,假設(shè)命名為 def.yaml 如:

apiVersion: core.oam.dev/v1alpha2
kind: WorkloadDefinition
metadata:
  name: mydeploy
spec:
  definitionRef:
    name: deployments.apps
  extension:
    template: |

另一個則放 cue 文件,假設(shè)命名為 def.cue

parameter: {
    name: string
    image: string
}
output: {
    apiVersion: "apps/v1"
    kind:       "Deployment"
    spec: {
        selector: matchLabels: {
            "app.oam.dev/component": parameter.name
        }
        template: {
            metadata: labels: {
                "app.oam.dev/component": parameter.name
            }
            spec: {
                containers: [{
                    name:  parameter.name
                    image: parameter.image
                }]
            }}}
}

先對 def.cue 做一個格式化,格式化的同時 cue 工具本身會做一些校驗,也可以更深入的通過 cue 命令做調(diào)試:

cue fmt def.cue

調(diào)試完成后,可以通過腳本把這個 yaml 組裝:

./hack/vela-templates/mergedef.sh def.yaml def.cue > mydeploy.yaml

再把這個 yaml 文件 apply 到 K8s 集群中。

$ kubectl apply -f mydeploy.yaml
workloaddefinition.core.oam.dev/mydeploy created

一旦新能力 kubectl apply 到了 Kubernetes 中,不用重啟,也不用更新,KubeVela 的用戶可以立刻看到一個新的能力出現(xiàn)并且可以使用了:

$ vela worklaods
Automatically discover capabilities successfully ? Add(1) Update(0) Delete(0)

TYPE           CATEGORY    DESCRIPTION
+mydeploy      workload    description not defined

NAME        DESCRIPTION
mydeploy    description not defined

在 Appfile 中使用方式如下:

name: my-extend-app
services:
  mysvc:
    type: mydeploy
    image: crccheck/hello-world
    name: mysvc

執(zhí)行 vela up 就能把這個運行起來了:

$ vela up -f docs/examples/blog-extension/my-extend-app.yaml
Parsing vela appfile ...
Loading templates ...

Rendering configs for service (mysvc)...
Writing deploy config to (.vela/deploy.yaml)

Applying deploy configs ...
Checking if app has been deployed...
App has not been deployed, creating a new deployment...
? App has been deployed ????????????
    Port forward: vela port-forward my-extend-app
             SSH: vela exec my-extend-app
         Logging: vela logs my-extend-app
      App status: vela status my-extend-app
  Service status: vela status my-extend-app --svc mysvc

我們來查看一下應(yīng)用的狀態(tài),已經(jīng)正常運行起來了(HEALTHY Ready: 1/1):

$ vela status my-extend-app
About:

  Name:          my-extend-app
  Namespace:     env-application
  Created at:    2020-12-15 16:32:25.08233 +0800 CST
  Updated at:    2020-12-15 16:32:25.08233 +0800 CST

Services:

  - Name: mysvc
    Type: mydeploy
    HEALTHY Ready: 1/1

Definition 模板中的高級用法

上面我們已經(jīng)通過模板替換這個最基本的功能體驗了擴(kuò)展 KubeVela 的全過程,除此之外,可能你還有一些比較復(fù)雜的需求,如條件判斷,循環(huán),復(fù)雜類型等,需要一些高級的用法。

結(jié)構(gòu)體參數(shù)

如果模板中有一些參數(shù)類型比較復(fù)雜,包含結(jié)構(gòu)體和嵌套的多個結(jié)構(gòu)體,就可以使用結(jié)構(gòu)體定義。

  1. 定義一個結(jié)構(gòu)體類型,包含 1 個字符串成員、1 個整型和 1 個結(jié)構(gòu)體成員。

#Config: {
 name:  string
 value: int
 other: {
   key: string
   value: string
 }
}
  1. 在變量中使用這個結(jié)構(gòu)體類型,并作為數(shù)組使用。

parameter: {
 name: string
 image: string
 config: [...#Config]
}
  1. 同樣的目標(biāo)中也是以變量引用的方式使用。

output: {
   ...
         spec: {
             containers: [{
                 name:  parameter.name
                 image: parameter.image
                 env: parameter.config
             }]
         }
    ...
}
  1. Appfile 中的寫法就是按照 parameter 定義的結(jié)構(gòu)編寫。

name: my-extend-app
services:
mysvc:
 type: mydeploy
 image: crccheck/hello-world
 name: mysvc
 config:
 - name: a
   value: 1
   other:
     key: mykey
     value: myvalue

條件判斷

有時候某些參數(shù)加還是不加取決于某個條件:

parameter: {
    name:   string
    image:  string
    useENV: bool
}
output: {
    ...
    spec: {
        containers: [{
            name:  parameter.name
            image: parameter.image
            if parameter.useENV == true {
                env: [{name: "my-env", value: "my-value"}]
            }
        }]
    }
    ...
}

在 Appfile 就是寫值。

name: my-extend-app
services:
  mysvc:
    type: mydeploy
    image: crccheck/hello-world
    name: mysvc
    useENV: true

可缺省參數(shù)

有些情況下參數(shù)可能存在也可能不存在,即非必填,這個時候一般要配合條件判斷使用,對于某個字段不存在的情況,判斷條件是是 _variable != _|_。

parameter: {
    name: string
    image: string
    config?: [...#Config]
}
output: {
    ...
    spec: {
        containers: [{
            name:  parameter.name
            image: parameter.image
            if parameter.config != _|_ {
                config: parameter.config
            }
        }]
    }
    ...
}

這種情況下 Appfile 的 config 就非必填了,填了就渲染,沒填就不渲染。

默認(rèn)值

對于某些參數(shù)如果希望設(shè)置一個默認(rèn)值,可以采用這個寫法。

parameter: {
    name: string
    image: *"nginx:v1" | string
}
output: {
    ...
    spec: {
        containers: [{
            name:  parameter.name
            image: parameter.image
        }]
    }
    ...
}

這個時候 Appfile 就可以不寫 image 這個參數(shù),默認(rèn)使用 "nginx:v1":

name: my-extend-app
services:
  mysvc:
    type: mydeploy
    name: mysvc

循環(huán)

Map 類型的循環(huán)

parameter: {
    name:  string
    image: string
    env: [string]: string
}
output: {
    spec: {
        containers: [{
            name:  parameter.name
            image: parameter.image
            env: [
                for k, v in parameter.env {
                    name:  k
                    value: v
                },
            ]
        }]
    }
}

Appfile 中的寫法:

name: my-extend-app
services:
  mysvc:
    type: mydeploy
    name:  "mysvc"
    image: "nginx"
    env:
      env1: value1
      env2: value2

數(shù)組類型的循環(huán)

parameter: {
    name:  string
    image: string
    env: [...{name:string,value:string}]
}
output: {
  ...
     spec: {
        containers: [{
            name:  parameter.name
            image: parameter.image
            env: [
                for _, v in parameter.env {
                    name:  v.name
                    value: v.value
                },
            ]
        }]
    }
}

Appfile 中的寫法:

name: my-extend-app
services:
  mysvc:
    type: mydeploy
    name:  "mysvc"
    image: "nginx"
    env:
    - name: env1
      value: value1
    - name: env2
      value: value2

KubeVela 內(nèi)置的 context 變量

大家可能也注意到了,我們在 parameter 中定義的 name 每次在 Appfile中 實際上寫了兩次,一次是在 services 下面(每個service都以名稱區(qū)分), 另一次則是在具體的name參數(shù)里面。事實上這里重復(fù)的不應(yīng)該由用戶再寫一遍,所以 KubeVela 中還定義了一個內(nèi)置的 context,里面存放了一些通用的環(huán)境上下文信息,如應(yīng)用名稱、秘鑰等。 直接在模板中使用 context 就不需要額外增加一個 name 參數(shù)了, KubeVela 在運行渲染模板的過程中會自動傳入。

parameter: {
    image: string
}
output: {
  ...
    spec: {
        containers: [{
            name:  context.name
            image: parameter.image
        }]
    }
  ...
}

KubeVela 中的注釋增強(qiáng)

KubeVela 還對 cuelang 的注釋做了一些擴(kuò)展,方便自動生成文檔以及被 CLI 使用。

 parameter: {
          // +usage=Which image would you like to use for your service
          // +short=i
          image: string

          // +usage=Commands to run in the container
          cmd?: [...string]
       ...
      }

其中,+usgae 開頭的注釋會變成參數(shù)的說明,+short 開頭的注釋后面則是在 CLI 中使用的縮寫。

上述就是小編為大家分享的如何給你的K8s PaaS 上線一個新功能了,如果剛好有類似的疑惑,不妨參照上述分析進(jìn)行理解。如果想知道更多相關(guān)知識,歡迎關(guān)注億速云行業(yè)資訊頻道。

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI