云服务器价格_云数据库_云主机【优惠】最新活动-搜集站云资讯

数据库服务器_荣耀战魂服务器_促销

小七 141 0

使用Jsonnet模板语言的声明性基础设施

这篇博文是我们一系列关于Databricks平台、基础设施管理、集成、工具、监控和供应的内部工程博客的一部分。在Databricks engineering,我们是Kubernetes的狂热粉丝。我们的大部分平台基础设施都在Kubernetes中运行,无论是在AWS云中还是在更受监管的环境中。然而,我们发现,单靠Kubernetes还不足以管理复杂的服务基础设施,它可能包括Kubernetes中创建的资源(例如pods、服务)和外部资源(如IAM角色)。管理的复杂性来自于(1)对基础设施当前状态的可见性的需要(2)关于如何对基础设施进行更改的推理。为了帮助减少管理的复杂性,基础设施最好是声明性的(即,由一组配置文件或模板描述)。第一个优点是可以通过读取配置文件轻松检查基础结构的目标状态。其次,由于基础设施完全由文件描述,因此可以作为标准软件开发工作流的一部分提出、审阅和应用更改。少了什么?kubernetesyaml配置文件已经实现了对Kubernetes中对象的声明性更新。用户只需编辑对象的YAML文件,然后运行$kubectl apply-f对象.yaml同步对Kubernetes的更改。这些YAML文件可以签入到源代码管理中,如果需要,用户可以查询Kubernetes来检查实时版本和本地文件之间的差异。但是,在尝试将此方法应用于更大的生产环境时,会遇到一些困难:公共结构不能跨YAML文件共享。您通常不需要一次而是多次设置服务,可能是在{dev,staging,prod}环境中,跨越不同的地理区域,比如{uswest,useast,asia-pacific},以及云提供商{AWS,Azure,GCE}。YAML文件必须经常引用有关外部定义的实体(如关系数据库、网络配置或SSL证书)的元数据。复杂的多层服务部署可能需要组合许多不同的资源或YAML文件。更新由许多这样的文件组成的部署会变得很麻烦。在这篇博客文章中,我们描述了如何使用Google的Jsonnet配置语言来解决这些问题,通过一个使用Jsonnet模板化服务部署的示例,并为基础设施模板化用例提出了一个Jsonnet风格的指南。我们发现Jsonnet很容易入门,并且可以很好地扩展到复杂的用例。Databricks的Jsonnet使用情况我们在2015年为我们的免费教育版的Apache Spark尝试了一部分。从那时起,Jsonnet在Databricks engineering中迅速普及,在1000多个不同的文件中有40000多行Jsonnet被检入到我们的主开发库中。这些模板扩展到成百上千行的原始山药。Databricks的团队目前使用Jsonnet来管理Kubernetes资源的配置(包括运行在Kubernetes上的服务的内部配置)、AWS CloudFormation、Terraform、Databricks作业,以及TLS证书管理和定义Prometheus警报等任务。在过去的两年里,Jsonnet已经发展成为工程中事实上的标准配置语言。Jsonnet基础知识Jsonnet是一种配置语言,可以帮助您定义JSON数据。基本思想是一些JSON字段可以作为变量或表达式留在编译时进行计算。例如,JSON对象{"count":4}在Jsonnet中可以表示为{count:2+2}。您还可以使用":"声明隐藏字段,这些字段在编译期间可以被引用,例如{x::2,y::2,count:$.x+$.y}也可以计算为{"count":4}。Jsonnet对象可以通过连接("+")对象来覆盖字段值来进行子类化,例如,假设我们定义了以下内容。本地基={x: :error"您必须定义'x'的值',y: :2,//此字段有默认值计数:$.x+$.y};然后,Jsonnet表达式(base+{x::10})将编译为{"count":12}。实际上,Jsonnet编译器要求您重写x,因为默认值会引发错误。因此,您可以将base看作是在Jsonnet中定义一个抽象基类。Jsonnet编译是完全确定的,不能执行外部I/O,因此非常适合定义配置。我们发现Jsonnet在限制性和灵活性之间找到了一个恰当的平衡点——以前我们使用Scala代码生成配置,这在灵活性方面犯了太多错误,导致了很多麻烦。我们可以直接把它作为扩展的工具。在内部,它将Jsonnet编译为普通JSON/YAML,然后将数据发送给Kubernetes。然后Kubernetes根据上传的配置创建或更新对象。用Jsonnet组合Kubernetes对象为了更好地理解Jsonnet如何与Kubernetes一起使用,让我们考虑为企业客户部署一个理想化的[1]单租户"Databricks平台"的任务。在这里,我们要创建两个不同但相关的服务部署:一个Webapp(用于交互式工作区)和clustermanager(用于管理Spark集群)。此外,这些服务需要访问awsrds数据库来存储持久数据。[1] :请注意,实际上我们并没有为每个客户创建单独的Jsonnet文件,这将创建数量惊人的Jsonnet文件,这本身就是一个问题。用于定义Kubernetes部署的模板对于这些示例,我们将使用服务-deployment.jsonnet.TEMPLATE,这是我们的一个内部基本模板的简化版本,它将Kubernetes服务和部署作为一对定义在一起。在Kubernetes中,实例化的服务和部署一起构成了一个独立的"生产服务",可以接收网络流量。请注意,除了几个可选参数外,模板还有两个必需参数,包括传递给服务二进制文件本身的可选配置:服务-deployment.jsonnet.TEMPLATE://用于服务部署的配对(Kuetes)模板。{//此模板的必需参数serviceName::error"必须指定serviceName",dockerImage::error"必须指定dockerImage",//此模板的可选参数。服务配置::{}...}示例1:每个服务部署一个文件提供服务-deployment.jsonnet.TEMPLATE,最简单的选择是为Webapp和Cluster manager创建单独的Jsonnet文件。在每个文件中,我们必须导入基本模板,将其子类化,并填写所需的参数。我们指定服务名称、包含服务二进制文件的Docker映像以及一些特定于服务的配置(包括RDS地址)。在这个例子中,RDS地址是硬编码的,但是稍后我们将展示如何从运行CloudFormation脚本的输出文件导入元数据。下面的文件描述了管理器服务(在这里,服务具有其标准含义,我们将明确地引用Kubernetes服务)。我们还通过serviceConf::字段在Jsonnet模板中构造特定于服务的配置,该字段将作为环境变量传递给pod。我们发现通过这种方式统一服务和Kubernetes配置非常有用:简单/食品公司-jsonnet经理:local serviceDeployment=导入"../service"-deployment.jsonnet.TEMPLATE";//foocorp的集群管理器部署。服务部署+{服务名称::"foocorp管理器",文件摘要:经理:2.42-rc1",服务配置:{客户名称:"foocorp",数据库:"用户-数据库数据库-西部2号。rds.amazonaws.com网站",},}要使webapp服务创建群集,它必须指定群集管理器的Kubernetes DNS地址,该地址可以通过Kubernetes服务名称提前确定:简单/食品公司-webapp.jsonnet:local serviceDeployment=导入"../service"-deployment.jsonnet.TEMPLATE";//foocorp的webapp部署。服务部署+{服务名称::"foocorp webapp",文件摘要:网络应用程序:2.42-rc1",服务配置:{客户名称:"foocorp",数据库:"用户-数据库数据库-西部2号。rds.amazonaws.com网站",经理地址:"foocorp-生产服务经理.群集.local",},}如果您克隆了示例代码repo,则可以通过在文件上运行jsonnet编译器来查看这些模板的具体化输出,例如:$jsonnet示例/databricks/simple/foocorp-webapp.jsonnet这给我们带来了什么?我们已经设法至少删除了一些关于定义服务的标准Kubernetes样板文件,将每个部署定义从100多行减少到大约10行。但是,我们可以做得更好—如果有许多不同的客户或每个客户需要更多的服务,那么仍然存在重复的参数,这将使此模式难以维护。示例2:在单个文件中组合展开由于Webapp和clustermanager服务都是作为一个单元或每个客户的切分部署在一起的,因此每个客户应该只有一个文件来描述他们的独特需求。确实可以定义一个模板,shard.jsonnet.TEMPLATE,它将Webapp和Cluster manager部署组合在一起,而不重复param。这里的诀窍是模板定义了一个Kubernetes"List"对象,它可以在一个JSON对象中包含多个Kubernetes资源。我们使用Jsonnet标准库函数合并服务部署模板生成的子列表标准扁平阵列:碎片-v1/shard.jsonnet.TEMPLATE:local serviceDeployment=导入"../service"-deployment.jsonnet.TEMPLATE";{//必需的参数customerName::error"必须定义customerName",release::error"必须定义释放",//可选参数有限公司