Kubernetes 新玩法:在 yaml 中编程

简介: 如何做性能测试?要么是通过编码的方式完成,写一堆脚本,用完即弃;要么是基于平台,在平台定义的流程中进行。对于后者,通常由于目标场景的复杂性,如部署特定的 workload、观测特定的性能项、网络访问问题等,往往导致性能测试平台要以高成本才能满足不断变化的开发场景的需求。在云原生的背景下,是否可以更好解决这种问题?

头图.png

作者 | 悟鹏

引子

性能测试在日常的开发工作中是常规需求,用来摸底服务的性能。

那么如何做性能测试?要么是通过编码的方式完成,写一堆脚本,用完即弃;要么是基于平台,在平台定义的流程中进行。对于后者,通常由于目标场景的复杂性,如部署特定的 workload、观测特定的性能项、网络访问问题等,往往导致性能测试平台要以高成本才能满足不断变化的开发场景的需求。

在云原生的背景下,是否可以更好解决这种问题?

先看两个 yaml 文件:

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

    1. 创建测试用的 Namespace
    2. 启动针对 Deployment 创建效率和创建成功率的监控
    3. 下述动作重复 N 次:① 使用 workload 模板创建 Deployment;② 等待 Deployment 变为 Ready
    4. 删除测试用的 Namespace
  • basic-1-pod-deployment.yaml 描述使用的 workload 模板

performance-test.yaml :

apiVersion: aliyun.com/v1alpha1
kind: Beidou
metadata:name: performancenamespace: beidou
spec:steps:- name: "Create Namespace If Not Exits"operations:- name: "create namespace"type: Taskop: CreateNamespaceargs:- name: NSvalue: beidou- name: "Monitor Deployment Creation Efficiency"operations:- name: "Begin To Monitor Deployment Creation Efficiency"type: Taskop: DeploymentCreationEfficiencyargs:- name: NSvalue: beidou- name: "Repeat 1 Times"type: Taskop: RepeatNTimesargs:- name: TIMESvalue: "1"- name: ACTIONreference:id: deployment-operation- name: "Delete namespace"operations:- name: "delete namespace"type: Taskop: DeleteNamespaceargs:- name: NSvalue: beidou- name: FORCEvalue: "false"references:- id: deployment-operationsteps:- name: "Prepare Deployment"operations:- name: "Prepare Deployment"type: Taskop: PrepareBatchDeploymentsargs:- name: NSvalue: beidou- name: NODE_TYPEvalue: ebm- name: BATCH_NUMvalue: "1"- name: TEMPLATEvalue: "./templates/basic-1-pod-deployment.yaml"- name: DEPLOYMENT_REPLICASvalue: "1"- name: DEPLOYMENT_PREFIXvalue: "ebm"- name: "Wait For Deployments To Be Ready"type: Taskop: WaitForBatchDeploymentsReadyargs:- name: NSvalue: beidou- name: TIMEOUTvalue: "3m"- name: CHECK_INTERVALvalue: "2s"

basic-1-pod-deployment.yaml:

apiVersion: apps/v1
kind: Deployment
metadata:labels:app: basic-1-pod
spec:selector:matchLabels:app: basic-1-podtemplate:metadata:labels:app: basic-1-podspec:containers:- name: nginximage: registry-vpc.cn-hangzhou.aliyuncs.com/xxx/nginx:1.17.9imagePullPolicy: Alwaysresources:limits:cpu: 2memory: 4Gi

然后通过一个命令行工具执行 performance-test.yaml:

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

执行效果如下 (每个 Deployment 创建耗时,所有 Deployment 创建耗时的 TP95 值,每个 Deployment 是否创建成功):

1.png

这些 metrics 是按照 Prometheus 标准输出,可以被 Prometheus server 收集走,再结合 Grafana 可以可视化展示性能测试数据。

通过在 yaml 中表达想法,编排对 K8s 资源的操作、监控,再也不用为性能测试的实现头疼了 :D

为什么要在 yaml 中编程?

性能测试、回归测试等对于服务质量保障有很大帮助,需要做,但常规的实现方法在初期需要投入较多的时间和精力,新增变更后维护成本比较高。

通常这个过程是以代码的方式实现原子操作,如创建 Deployment、检测 Pod 配置等,然后再组合原子操作来满足需求,如 创建 Deployment -> 等待 Deployment ready -> 检测 Pod 配置等。

有没有办法在实现的过程中既可以尽量低成本实现,又可以复用已有的经验?

可以将原子操作封装为原语,如 CreateDeployment、CheckPod,再通过 yaml 的结构表达流程,那么就可以通过 yaml 而非代码的方式描述想法,又可以复用他人已经写好的 yaml 文件来解决某类场景的需求。

即在 yaml 中编程,减少重复性代码工作,通过 声明式 的方式描述逻辑,并以 yaml 文件来满足场景级别的复用。

业界有很多种类型的 声明式操作 服务,如运维领域中的 Ansible、SaltStack,Kubernetes 中的Argo Workflow、clusterloader2。它们的思想整体比较类似,将高频使用的操作封装为原语,使用者通过原语来表述操作逻辑。

通过声明式的方法,将面向 K8s 的操作抽象成 yaml 中的关键词,在 yaml 中提供串行、并行等控制逻辑,那么就可以通过 yaml 文件完整描述想要进行的工作。

这种思想和 Argo Workflow 比较像,但粒度比 Argo 更细,关注在操作函数上:

2.png

下面简单描述该服务的设计和实现。

设计和实现

1. 服务形态

  • 使用者在 yaml 中,通过 声明式 的方式描述操作逻辑;
  • 以 all-in-one 的二进制工具或 Operator 的方式交付;
  • 服务内置常见原语的实现,以关键字的方式在 yaml 中提供;
  • 支持配置原生 K8s 资源。

2. 设计

该方案的核心在于配置管理的设计,将操作流程配置化,自上而下有如下概念:

  • Service:Modules 或 Tasks 的编排;
  • Module:一种任务场景,是操作单元的集合(其中包含 templates/ 目录,表征模板文件的集合,可用来配置 K8s 原生资源);
     
  • Task:操作单元,使用 plugin 及参数执行操作;
     
  • Plugin:操作指令,类似开发语言中的函数。

抽象目标场景中的通用操作,这些通用操作即为可在 yaml 中使用的原语,对应上述 Plugin:

  • K8s 相关

    • CreateNamespace
    • DeleteNamespace
    • PrepareSecret
    • PrepareConfigMap
    • PrepareBatchDeployments
    • WaitForBatchDeploymentsReady
    • etc.
  • 观测性相关

    • DeploymentCreationEfficiency
    • PodCreationEfficiency
    • etc.
  • 检测项相关

    • CheckPodAnnotations
    • CheckPodObjectInfo
    • CheckPodInnerStates
    • etc.
  • 控制语句相关

    • RepeatNTimes
    • etc.

上述 4 个概念的关系如下:

3.png

示例可参见文章开头的 yaml 文件,对应形式二。

3. 核心实现

CRD 设计:

package v1alpha1import (corev1 "k8s.io/api/core/v1"metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)// BeidouType is the type related to Beidou execution.
type BeidouType stringconst (// 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 boolfor 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 stringfor _, 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 intvar steps []v1alpha1.BeidouStepvar err errorfor _, 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.Stepsbreak}}}}return RepeatNTimes(ctx, times, steps)}return ErrNotImplemented
}

操作原语的实现示例:

// 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
}

后续

目前阿里云容器服务团队内部已经实现了初版,已用于部分云产品的内部性能测试以及常规的回归测试,很大程度上提升了我们的工作效率。

在 yaml 中编程,是对云原生场景下声明式操作的体现,也是对声明式服务的一种实践。对于常规工作场景中重复编码或重复操作,可考虑类似的方式进行满足。

欢迎大家对这样的服务形态和项目进行讨论,探索这种模式的价值。

 

 

原文链接
本文为阿里云原创内容,未经允许不得转载。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/515027.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

对话猿辅导:阿里云远程办公零信任落地创新安全

简介: 大型实战场景验证,灵活应对复杂环境。 2020,一场突如其来的新冠疫情,引发了史上最大规模的远程办公。疫情让安全问题暴露得更加突出,与疫情的对抗也是阿里云安全的战场。 9月18日,2020云栖大会技术…

阿里云发布边缘计算视频上云解决方案 为海量视图处理提供城市级云基础设施

简介: 2020云栖大会在云上成功召开,此次大会汇聚行业领袖,共同见证数智未来的重构进程。在9月18日的产品发布大厅,阿里云正式发布边缘计算视频上云解决方案,旨在依托城市级云计算基础设施,实现海量视图数据…

数据湖元数据服务的实现和挑战

简介: 数据湖元数据服务为大数据而生,为互通生态而生,期望后续继续完善其服务能力和支撑更多的大数据引擎,通过开放的服务能力、存储能力、统一的权限及元数据管理能力,为客户节省管理/人力/存储等各项成本&#xff0c…

美国燃油“动脉”被黑客切断,网络安全走向哪里?专访山石网科|拟合

从无序中寻找踪迹,从眼前事探索未来。 > 2021 年正值黄金十年新开端,CSDN 以中立技术社区专业、客观的角度,深度探讨中国前沿 IT 技术演进,推出年度重磅企划栏目——「拟合」,通过对话企业技术高管大咖,…

云时代的智能运维平台,助力企业创新迭代

简介: 智能的运维平台,帮助企业业务平稳、智能、高效地运行。 每一起严重事故背后,必然有29次轻微事故和300起未遂先兆以及1000起事故隐患。—— 海恩法则(Heinrich‘s Law) 随着云计算时代的到来,大量企业将自己的业务逐步迁移…

数据湖架构,为什么需要“湖加速”?

简介: 湖加速即为数据湖加速,是指在数据湖架构中,为了统一支持各种计算,对数据湖存储提供适配支持,进行优化和缓存加速的中间层技术。那么为什么需要湖加速?数据湖如何实现“加速”?本文将从三个…

“阿里日”102 对新人举办集体婚礼,张勇证婚:“又热,又爱”!

整理 | 王晓曼出品 | 程序人生 (ID:coder _life)2021年5月10日,是第十七个“阿里日”,同时也是第十六次阿里巴巴举办员工集体婚礼。据悉,今年共有1541对新人报名集体婚礼,其中,双职工…

阿里巴巴电商搜索推荐实时数仓演进之路

简介: 自建实时数仓到底难在哪里?实时数仓应该怎么建?阿里巴巴搜索团队告诉您答案 作者:张照亮(士恒)阿里巴巴搜索事业部高级技术专家 1. 业务背景 阿里巴巴电商搜索推荐实时数据仓库承载了阿里巴巴集团…

多数据源一站式入湖

简介: 通过一站式入湖,将不同数据源的数据统一归并到以OSS对象存储为基础架构的集中式数据湖存储中,解决了企业面临的数据孤岛问题,为统一的数据分析打好了基础. 背景 数据湖作为一个集中化的数据存储仓库,支持的数据…

一文说透架构设计的本质

作者 | 猿码架构来源 | 云时代架构头图 | 下载于视觉中国前言:在软件研发领域,程序员的终极目标都是想成为一名合格的架构师。然而梦想很美好,但现实却很曲折。在实际工作中,程序员会分很多种,有的擅长编码实现&#x…

“视频云营业厅”发布,蚂蚁 mPaaS 加速金融机构数字化转型

简介: 线下业务线上办,便民服务不间断 在业务和数据争相“上云”的今天,更多金融机构加速数字化转型进程,将原有的人对人、线下、柜面核心业务进一步线上化,扩充全新的业务触点,开启远程业务办理的新模式。…

被Python「苦虐」的日子太惨了!

Python因为其优越的特性广泛应用于数据分析、人工智能、Web开发、后端开发、自动化测试/运维、爬虫等领域,也得到了很多企业的青睐。甚至连BATZJ的技术大牛,都无可否认Python现在对于一个程序员发展的重要性!最近一两年,我身边也有…

阿里云肖力:原生安全打造云上绿洲

2020年9月17日-18日,一年一度的云栖大会在云上如约而至。疫情加速数字化转型大背景之下,云原生以一种高能见度为各行业带来了一个更动态多变、更具效率和生命力的架构。云原生安全具有什么优势,能否解决线下业务场景的安全困局?作…

Nacos Go 微服务生态系列(一)| Dubbo-go 云原生核心引擎探索

简介: 作为微服务框架的核心引擎--注册中心,是必不可缺少的组件,市面已经有多款注册中心支持 Go 语言,应该如何选择呢?我们可以对目前主流的支持 Go 语言的注册中心做个对比。 作者 | 李志鹏 近几年,随着 …

微软每年豪砸安全研发 10 亿美元,聊聊背后的技术密码

从无序中寻找踪迹,从眼前事探索未来。正值 IT 黄金十年新开端, CSDN 欲以中立技术社区专业、客观的角度,深度探讨中国前沿 IT 技术演进,现在推出年度重磅企划——「拟合」,通过对话企业高管大咖,跟踪报道企…

Chrome浏览器直接下载pdf文件的设置步骤

使用Google Chrome浏览器,在点击网页中的pdf文件时,浏览器会直接将pdf文件打开并显示,要下载pdf文件的话,还需要进行另存操作。 有的时候我们点击pdf文件就是为了直接下载,而不是为了在浏览器中查看pdf文件。可以按以…

Bilibili资深运维工程师:DCDN在游戏应用加速中的实践

简介: bilibili资深运维工程师李宁分享《DCDN在游戏应用加速中的实践》从bilibili游戏应用的效果和成本入手,深入浅出地分享DCDN全站加速在游戏加速场景中的应用。 日前,云栖大会新一代CDN的技术突破与应用实践专场中,bilibili资…

Gartner:云安全的未来——中国的安全访问服务边缘架构

作者 | Gartner高级研究总监 Evan Zeng 编辑 | 宋 慧 头图 | 付费下载于东方IC Gartner最新的“安全领域新兴技术及趋势影响雷达”(Emerging Technologies and Trends Impact Radar: Security)显示,安全服务及接入边缘技术具有极高重要性&am…

springboot word excel ppt 图片aspose 转换PDF 在线预览

文章目录1. 引入依赖2. 注册工具类3. 文件转换工具类4. 文件预览工具类5. 文件处理实现类6. 文件处理入口7. 配置类8. 配置文件9. 依赖目录文件列表10. 图片转换工具类11. 测试连接12. 效果图12. 执行shell命令工具类补充1. 引入依赖 下载 Aspose 的依赖 Jar 包可以通过一下仓…

架构制图:工具与方法论

简介: 软件工程也是工程,因此传统工程制图的一些基本理论,在软件行业同样适用。但另一方面,软件与实体制造业之间还是有着本质区别,所以在制图方面的需求和方式也大相径庭,无法直接套用。作为软件行业的从业…