<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Kubernetes 新玩法:在 YAML 中編程

          共 11053字,需瀏覽 23分鐘

           ·

          2020-09-23 19:08


          作者 |?悟鵬



          引子


          性能測(cè)試在日常的開(kāi)發(fā)工作中是常規(guī)需求,用來(lái)摸底服務(wù)的性能。


          那么如何做性能測(cè)試?要么是通過(guò)編碼的方式完成,寫一堆腳本,用完即棄;要么是基于平臺(tái),在平臺(tái)定義的流程中進(jìn)行。對(duì)于后者,通常由于目標(biāo)場(chǎng)景的復(fù)雜性,如部署特定的 workload、觀測(cè)特定的性能項(xiàng)、網(wǎng)絡(luò)訪問(wèn)問(wèn)題等,往往導(dǎo)致性能測(cè)試平臺(tái)要以高成本才能滿足不斷變化的開(kāi)發(fā)場(chǎng)景的需求。


          在云原生的背景下,是否可以更好解決這種問(wèn)題?


          先看兩個(gè) yaml 文件:

          • performance-test.yaml 描述了在 K8s 中的操作流程:

            1. 創(chuàng)建測(cè)試用的 Namespace

            2. 啟動(dòng)針對(duì) Deployment 創(chuàng)建效率和創(chuàng)建成功率的監(jiān)控

            3. 下述動(dòng)作重復(fù) N 次:① 使用 workload 模板創(chuàng)建 Deployment;② 等待 Deployment 變?yōu)?Ready

            4. 刪除測(cè)試用的 Namespace


          • basic-1-pod-deployment.yaml 描述使用的 workload 模板


          performance-test.yaml :


          apiVersion: aliyun.com/v1alpha1kind: Beidoumetadata:  name: performance  namespace: beidouspec:  steps:  - name: "Create Namespace If Not Exits"    operations:    - name: "create namespace"      type: Task      op: CreateNamespace      args:      - name: NS        value: beidou  - name: "Monitor Deployment Creation Efficiency"    operations:    - name: "Begin To Monitor Deployment Creation Efficiency"      type: Task      op: DeploymentCreationEfficiency      args:      - name: NS        value: beidou    - name: "Repeat 1 Times"      type: Task      op: RepeatNTimes      args:      - name: TIMES        value: "1"      - name: ACTION        reference:          id: deployment-operation  - name: "Delete namespace"    operations:    - name: "delete namespace"      type: Task      op: DeleteNamespace      args:      - name: NS        value: beidou      - name: FORCE        value: "false"  references:  - id: deployment-operation    steps:    - name: "Prepare Deployment"      operations:      - name: "Prepare Deployment"        type: Task        op: PrepareBatchDeployments        args:        - name: NS          value: beidou        - name: NODE_TYPE          value: ebm        - name: BATCH_NUM          value: "1"        - name: TEMPLATE          value: "./templates/basic-1-pod-deployment.yaml"        - name: DEPLOYMENT_REPLICAS          value: "1"        - name: DEPLOYMENT_PREFIX          value: "ebm"      - name: "Wait For Deployments To Be Ready"        type: Task        op: WaitForBatchDeploymentsReady        args:        - name: NS          value: beidou        - name: TIMEOUT          value: "3m"        - name: CHECK_INTERVAL          value: "2s"

          basic-1-pod-deployment.yaml:


          apiVersion: apps/v1kind: Deploymentmetadata:  labels:    app: basic-1-podspec:  selector:    matchLabels:      app: basic-1-pod  template:    metadata:      labels:        app: basic-1-pod    spec:      containers:      - name: nginx        image: registry-vpc.cn-hangzhou.aliyuncs.com/xxx/nginx:1.17.9        imagePullPolicy: Always        resources:          limits:            cpu: 2            memory: 4Gi


          然后通過(guò)一個(gè)命令行工具執(zhí)行 performance-test.yaml:


          $ beidou server -c ~/.kube/config services/performance-test.yaml


          執(zhí)行效果如下 (每個(gè) Deployment 創(chuàng)建耗時(shí),所有 Deployment 創(chuàng)建耗時(shí)的 TP95 值,每個(gè) Deployment 是否創(chuàng)建成功):



          這些 metrics 是按照 Prometheus 標(biāo)準(zhǔn)輸出,可以被 Prometheus server 收集走,再結(jié)合 Grafana 可以可視化展示性能測(cè)試數(shù)據(jù)。


          通過(guò)在 yaml 中表達(dá)想法,編排對(duì) K8s 資源的操作、監(jiān)控,再也不用為性能測(cè)試的實(shí)現(xiàn)頭疼了 :D



          為什么要在?yaml 中編程?


          性能測(cè)試、回歸測(cè)試等對(duì)于服務(wù)質(zhì)量保障有很大幫助,需要做,但常規(guī)的實(shí)現(xiàn)方法在初期需要投入較多的時(shí)間和精力,新增變更后維護(hù)成本比較高。


          通常這個(gè)過(guò)程是以代碼的方式實(shí)現(xiàn)原子操作,如創(chuàng)建 Deployment、檢測(cè) Pod 配置等,然后再組合原子操作來(lái)滿足需求,如 創(chuàng)建 Deployment -> 等待 Deployment ready -> 檢測(cè) Pod 配置等。


          有沒(méi)有辦法在實(shí)現(xiàn)的過(guò)程中既可以盡量低成本實(shí)現(xiàn),又可以復(fù)用已有的經(jīng)驗(yàn)?


          可以將原子操作封裝為原語(yǔ),如 CreateDeployment、CheckPod,再通過(guò) yaml 的結(jié)構(gòu)表達(dá)流程,那么就可以通過(guò) yaml 而非代碼的方式描述想法,又可以復(fù)用他人已經(jīng)寫好的 yaml 文件來(lái)解決某類場(chǎng)景的需求。


          即在 yaml 中編程,減少重復(fù)性代碼工作,通過(guò) 聲明式 的方式描述邏輯,并以 yaml 文件來(lái)滿足場(chǎng)景級(jí)別的復(fù)用。


          業(yè)界有很多種類型的 聲明式操作 服務(wù),如運(yùn)維領(lǐng)域中的AnsibleSaltStack,Kubernetes 中的Argo Workflow、clusterloader2它們的思想整體比較類似,將高頻使用的操作封裝為原語(yǔ),使用者通過(guò)原語(yǔ)來(lái)表述操作邏輯。


          通過(guò)聲明式的方法,將面向 K8s 的操作抽象成 yaml 中的關(guān)鍵詞,在 yaml 中提供串行、并行等控制邏輯,那么就可以通過(guò) yaml 文件完整描述想要進(jìn)行的工作。


          這種思想和Argo Workflow?比較像,但粒度比 Argo 更細(xì),關(guān)注在操作函數(shù)上:



          下面簡(jiǎn)單描述該服務(wù)的設(shè)計(jì)和實(shí)現(xiàn)。



          設(shè)計(jì)和實(shí)現(xiàn)


          1. 服務(wù)形態(tài)

          • 使用者在 yaml 中,通過(guò) 聲明式 的方式描述操作邏輯;

          • 以 all-in-one 的二進(jìn)制工具或 Operator 的方式交付;

          • 服務(wù)內(nèi)置常見(jiàn)原語(yǔ)的實(shí)現(xiàn),以關(guān)鍵字的方式在 yaml 中提供;

          • 支持配置原生 K8s 資源。


          2. 設(shè)計(jì)


          該方案的核心在于配置管理的設(shè)計(jì),將操作流程配置化,自上而下有如下概念:


          • ServiceModules 或 Tasks 的編排;

          ?

          • Module一種任務(wù)場(chǎng)景,是操作單元的集合(其中包含 templates/ 目錄,表征模板文件的集合,可用來(lái)配置 K8s 原生資源);

          ?

          • Task操作單元,使用 plugin 及參數(shù)執(zhí)行操作;

          ?

          • Plugin操作指令,類似開(kāi)發(fā)語(yǔ)言中的函數(shù)。


          抽象目標(biāo)場(chǎng)景中的通用操作,這些通用操作即為可在 yaml 中使用的原語(yǔ),對(duì)應(yīng)上述 Plugin:


          • K8s 相關(guān)

            • CreateNamespace

            • DeleteNamespace

            • PrepareSecret

            • PrepareConfigMap

            • PrepareBatchDeployments

            • WaitForBatchDeploymentsReady

            • etc.


          • 觀測(cè)性相關(guān)

            • DeploymentCreationEfficiency

            • PodCreationEfficiency

            • etc.


          • 檢測(cè)項(xiàng)相關(guān)

            • CheckPodAnnotations

            • CheckPodObjectInfo

            • CheckPodInnerStates

            • etc.


          • 控制語(yǔ)句相關(guān)

            • RepeatNTimes

            • etc.


          上述 4 個(gè)概念的關(guān)系如下:



          示例可參見(jiàn)文章開(kāi)頭的 yaml 文件,對(duì)應(yīng)形式二。


          3. 核心實(shí)現(xiàn)


          CRD 設(shè)計(jì):


          package v1alpha1
          import ( corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1")
          // BeidouType is the type related to Beidou execution.type BeidouType string
          const ( // BeidouTask represents the Task execution type. BeidouTask BeidouType = "Task")
          // +genclient// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
          // Beidou represents a crd used to describe serices.type Beidou struct { metav1.TypeMeta `json:",inline"` metav1.ObjectMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"`
          Spec BeidouSpec `json:"spec,omitempty" protobuf:"bytes,2,opt,name=spec"` Status BeidouStatus `json:"status,omitempty" protobuf:"bytes,3,opt,name=status"`}
          // BeidouSpec is the spec of a Beidou.type BeidouSpec struct { Steps []BeidouStep `json:"steps" protobuf:"bytes,1,opt,name=steps"` References []BeidouReference `json:"references" protobuf:"bytes,2,opt,name=references"`}
          // BeidouStep is the spec of step.type BeidouStep struct { Name string `json:"name" protobuf:"bytes,1,opt,name=name"` Operations []BeidouOperation `json:"operations" protobuf:"bytes,2,opt,name=operations"`}
          // BeidouOperation is the spec of operation.type BeidouOperation struct { Name string `json:"name" protobuf:"bytes,1,opt,name=name"` Type BeidouType `json:"type" protobuf:"bytes,2,opt,name=type"` Op string `json:"op" protobuf:"bytes,3,opt,name=op"` Args []BeidouArg `json:"args" protobuf:"bytes,4,opt,name=args"`}
          // BeidouArg is the spec of arg.type BeidouArg struct { Name string `json:"name" protobuf:"bytes,1,opt,name=name"` Value string `json:"value,omitempty" protobuf:"bytes,2,opt,name=value"` Reference BeidouOperationReference `json:"reference,omitempty" protobuf:"bytes,3,opt,name=reference"` Tolerations []corev1.Toleration `json:"tolerations,omitempty" protobuf:"bytes,4,opt,name=tolerations"` Checking []string `json:"checking,omitempty" protobuf:"bytes,5,opt,name=checking"`}
          // BeidouOperationReference is the spec of operation reference.type BeidouOperationReference struct { ID string `json:"id" protobuf:"bytes,1,opt,name=id"`}
          // BeidouReference is the spec of reference.type BeidouReference struct { ID string `json:"id" protobuf:"bytes,1,opt,name=id"` Steps []BeidouStep `json:"steps" protobuf:"bytes,2,opt,name=steps"`}
          // BeidouStatus represents the current state of a Beidou.type BeidouStatus struct { Message string `json:"message" protobuf:"bytes,1,opt,name=message"`}
          // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
          // BeidouList is a collection of Beidou.type BeidouList struct { metav1.TypeMeta `json:",inline"` metav1.ListMeta `json:"metadata" protobuf:"bytes,1,opt,name=metadata"`
          Items []Beidou `json:"items" protobuf:"bytes,2,opt,name=items"`}


          核心流程:


          // ExecSteps executes steps.func ExecSteps(ctx context.Context, steps []v1alpha1.BeidouStep, references []v1alpha1.BeidouReference) error {    logger, _ := ctx.Value(CtxLogger).(*log.Entry)
          var hasMonitored bool for i, step := range steps { for j, op := range step.Operations { switch op.Op { case "DeploymentCreationEfficiency": if !hasMonitored { defer func() { err := monitor.Output() if err != nil { logger.Errorf("Failed to output: %s", err) } }() } hasMonitored = true }
          err := ExecOperation(ctx, op, references) if err != nil { return fmt.Errorf("failed to run operation %s: %s", op.Name, err) } } }
          return nil}
          // ExecOperation executes operation.func ExecOperation(ctx context.Context, op v1alpha1.BeidouOperation, references []v1alpha1.BeidouReference) error { switch op.Type { case v1alpha1.BeidouTask: if !tasks.IsRegistered(op.Op) { return ErrNotRegistered }
          if !tasks.DoesSupportReference(op.Op) { return ExecTask(ctx, op.Op, op.Args) }
          return ExecTaskWithRefer(ctx, op.Op, op.Args, references) }
          return nil}
          // ExecTask executes a task.func ExecTask(ctx context.Context, opname string, args []v1alpha1.BeidouArg) error { switch opname { case tasks.CreateNamespace: var ns string for _, arg := range args { switch arg.Name { case "NS": ns = arg.Value } }
          return op.CreateNamespace(ctx, ns) // ... } // ...}
          // ExecTaskWithRefer executes a task with reference.func ExecTaskWithRefer(ctx context.Context, opname string, args []v1alpha1.BeidouArg, references []v1alpha1.BeidouReference) error { switch opname { case tasks.RepeatNTimes: var times int var steps []v1alpha1.BeidouStep var err error for _, arg := range args { switch arg.Name { case "TIMES": times, err = strconv.Atoi(arg.Value) if err != nil { return ErrParseArgs } case "ACTION": for _, refer := range references { if refer.ID == arg.Reference.ID { steps = refer.Steps break } } } }
          return RepeatNTimes(ctx, times, steps) }
          return ErrNotImplemented}


          操作原語(yǔ)的實(shí)現(xiàn)示例:


          // PodAnnotations is an operation used to check whether annotations of Pod are expected.func PodAnnotations(ctx context.Context, data PodAnnotationsData) error {  kclient, ok := ctx.Value(tasks.KubernetesClient).(kubernetes.Interface)  if !ok {    return tasks.ErrNoKubernetesClient  }
          pods, err := kclient.CoreV1().Pods(data.Namespace).List(metav1.ListOptions{}) if err != nil { return fmt.Errorf("failed to list pods in ns %s: %s", data.Namespace, err) }
          for _, pod := range pods.Items { if pod.Annotations == nil { return fmt.Errorf("pod %s in ns %s has no annotations", pod.Name, data.Namespace) }
          for _, annotation := range data.Exists { if _, exists := pod.Annotations[annotation]; !exists { return fmt.Errorf("annotation %s does not exist in pod %s in ns %s", annotation, pod.Name, data.Namespace) } }
          for k, v := range data.Equal { if pod.Annotations[k] != v { return fmt.Errorf("value of annotation %s is not %s in pod %s in ns %s", k, v, pod.Name, data.Namespace) } } }
          return nil}



          后續(xù)


          目前阿里云容器服務(wù)團(tuán)隊(duì)內(nèi)部已經(jīng)實(shí)現(xiàn)了初版,已用于部分云產(chǎn)品的內(nèi)部性能測(cè)試以及常規(guī)的回歸測(cè)試,很大程度上提升了我們的工作效率。


          在 yaml 中編程,是對(duì)云原生場(chǎng)景下聲明式操作的體現(xiàn),也是對(duì)聲明式服務(wù)的一種實(shí)踐。對(duì)于常規(guī)工作場(chǎng)景中重復(fù)編碼或重復(fù)操作,可考慮類似的方式進(jìn)行滿足。


          歡迎大家對(duì)這樣的服務(wù)形態(tài)和項(xiàng)目進(jìn)行討論,探索這種模式的價(jià)值。


          阿里云容器服務(wù)持續(xù)招聘,歡迎加入我們,一起在 K8s、邊緣計(jì)算、Serverless 等領(lǐng)域開(kāi)拓,讓當(dāng)前變得更美好,也為未來(lái)帶來(lái)可能性!聯(lián)系郵箱:[email protected]


          References


          • Ansible:https://docs.ansible.com/ansible/latest/index.html

          • SaltStack:https://docs.saltstack.com/en/latest/

          • Argo Workflow:https://github.com/argoproj/argo

          • clusterloader2:https://github.com/kubernetes/perf-tests/tree/master/clusterloader2





          K8S進(jìn)階訓(xùn)練營(yíng),點(diǎn)擊下方圖片了解詳情


          瀏覽 72
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  国内精品国产三级国产在线专 | 成人做爰免费视频观看 | 肏屄网站| sm在线| 日产无码久 |