开发者学堂课程【Kubernetes 云原生管理实践: Kubernetes 应用管理领域总揽和入门实践】学习笔记,与课程紧密联系,让用户快速学习知识。
课程地址:https://developer.aliyun.com/learning/course/751/detail/13220
快速学习 Kubernetes 应用管理领域总揽和入门实践
内容介绍:
四、OAM 关注点分离概念
五、CUE 的关注点
六、CUE 在 Kubernetes 上的实践
四、OAM 关注点分离概念
1、逐步分析 OAM 的整个流程。
在这个流程里面,首先会有一个应用开发的角色,他会去写相应的应用代码,并且他还会定义相应的 workload,也就是如何去拷它的作用代码。比如可以选择用 content 方式,或者是 serverless 的方式,或者是 stateful 方式来去拷它的作用代码。他的代码和目录的最后会主要成 component 交付给下一个环节。在这个环节里,应用的运维人员会把他的运维能力通过 Trait 的方式附加在 component 上面。多个 component 和 Trait 最后组合在一起成为 Application Configuration,并且作为最终交付物,可以拷在任何的 OAM 平台上,而 OAM 平台负责管理和对接不同的底层资源,包括 Kubernetes 原声的自定义的资源、云资源等等。这些基础设施则由基础设施运维来去创建和维护。
五、CUE 的关注点
1、CUE configuration language
(1)K8s yaml 管理的窘境
Templating, Overlay,DSL
简单统一→复杂多样→简单统一
(2)CUE 用法入门
(3)用 CUE 管理 k8s yaml
2、CUE 跟 OAM 的关系
(1)OAM 是服务器端的更高级别抽象,达到以应用为中心。
(2)CUE 是客户端工具,为了更高效准确地编写和管理配置文件。
(3)OAM 可以通过CUE透出更简单易用的模板: CUE templates=>GitOps/IaC=OAM
前面学习了 OAM 是如何作为一个 Kubernetes 的应用层抽象,但是 OAM 本身还是一个服务器端的能力,开发者使用的时候往往更需要一些简单的模板,这就是接下来讲解的 CUE 主要关注的点。 先分享一篇文章。
在这篇文章里面很好地描述了当前 Kubernetes 配置管理的一些解决方法。这里对比了三种解决方法,第一种叫 templating,这个是最简单的解法,他直接在原配置的基础上,暴露几个参数,比如 han 就采用了这种方法。第二种叫 layering,也叫over lay,他能够保留原配置,并通过叠加的方式去做更改,比如 kustomize 就是采用了这种方法。第三种就是 DCL,也叫 DSL。
他比较偏向于跟编程语言一样,提供抽象和库从而达到高度重合的目的,比如目录里面的 brogcfg,brogcfg 在外面有一个开源版叫 jsonnet,都是采用了这种方法。CUE 可以归类为第三种,他是从 brogcfg 和 jsonnet 自然演进的下一代技术。给大家讲一个配置管理领域很有趣的趋势,一开始希望用一个简单统一的方式去管理配置,比如 YAML,后来大家发现重复的内容多了,或者有更好的管理思路,于是又开发了一系列的工具和格式,最后又导致整个趋势变得多样化和不统一。最后,大家又希望回到一个简单统一的方式,又回到开发下一代 YAML 格式。如此循环往复,迭代下去,而 CUE 刚好出现在有复杂多样到简单统一的时间节点上。
CUE 怎么样能够统一管理配置呢?下面将通过实战深入了解 CUE并回答问题。如下图是 CUE 的官方教程,
只要进入 CUE 的官方网站,点击 Tutorials,就会出现一系列相应的教程。将通过教程逐步了解 CUE。
3、JSON Superset
CUE 是 JSON Superset。先创建一个 CUE,里面创建一个 CUE 的文件叫 test. CUE,然后把下面内容复制到文件里。
one: 1 {
two: 2
//A field using quotes.
"two-and-a-half":2.5
list:
【
1
,
2
,
3,
]
上图返回了 JSON 的文件,这说明 CUE 可以返回一个 JSON。
4、Types are values
把下图所示内容添入进去
通过这两个例子,可以很好地了解它。在 CUE 里面 string 都是一个集合,使用 CUE 是对他不同的集合取一个交集。比如说现在去 export,他是失败的。因为第八行是一个 type,必须要很具体的数字,这种东西是没办法获得的。
但是可以把它变成一个定义,现在 export 就成功了,但他只是 export 的 moscow。可以把它的定义跟他的值取一个交集,现在 export 也是成功的,因为每一个值都对应了 type。
如果把 type 改了,export 就不成功了,这说明 CUE 里面的 type 也是一个值。一个 type 和一个 value 怎么去取一个交集呢?CUE 里面 value 是更具体的 type,CUE 取交集的操作就是把这个集合不断更加的具体化。如果把 name 改成42,他是一个 int,这时候 export 就成功了。也可以把它变成 string|int,这时候也是成功的。或者说就用原来的 Moscow,string|int 也是可以的。也可以定义另外一个叫 lity 2,也是可以的。但是如果把它改成 int,这时候就失败了。
5、Duplicate Fields
CUE 里面允许多个字段重复,CUE 就是被 string 这些集合不断的取一个交集,一模一样不会产生冲突,所以是没问题的。
6、Constraints
CUE 里面可以表达 constrains,一些限制。这其实和刚才表达的 type 和定义有些类似关系的。viola 是从 schema 里继承过来,取一个交集。如果这个时候 export,他是不成功的,要把它变成一个定义。
他跟一个定义取交集的时候,把定义里面的值自动赋予进来。复制
schema:{
name;string
age: int
human:true//always true
}
viola:schema
viola:{
name:"Viola"
age: 38
}
这个时候说明如果把前面没有定义的字段暴露出来,然后去 export,他会说这个字段他不认识。这时候,如果加上 “…”,这时候就相当于说明允许可以加任意字段。
7、Validation
把下面内容验证一下,相当于用 CUE 作为 YAML 验证文件的一种方式。
这个时候第六行变成了 no,no 变成了不,如果把它变成 string,这个时候再 bet 一下,就没了。第五行改成大写开头 Dutch,就没有错误了。
8、Order is irrelevant
最后要说明,它的顺序并没有关系,CUE 只是对他的内容取一个交集,对于谁先谁后在交集的操作里面是没有太大关系的。像 Jsonnet 这种以继承为手段的工具最大的区别,那些工具关注顺序,在大型协作里面,反而是不方便的,不关注顺序的,反而是更容易去协作的。
9、Folding of Single-Field Structs
CUE 里面能看到的其实是 Next start 这样一种方式,但是 next start 写起来也很麻烦,CUE 里面多层结构可以更快速的编写单层结构。也会把它自动 next 进去,这跟刚才那个是一样的东西,然后再 export 一下,这样 introduction 就走完了。
六、CUE 在 Kubernetes 上的实践
1、CUE 在 Kubernetes 上的一些实践。需要先把 CUE 克隆下来,然后把它打开。
到 original 文件夹里,复制 $ tree ./original I head
。copy 一份 original,在 tmp 里面做这个实验。第一步先 init cue mod,这相当于他会把相应的 configuration 文件下载下来。
2、接下来就可以坐 import 相关的操作,复制到 service 文件夹里,看一下 cue mod 里面有什么。
cue import 相当于是把这里面所有的 yaml 都变成 cue 的一种定义或者是 CUE 的一种文件,但现在 cue import 是失败的,这是因为有多个包和这个文件。所以要给所有文件定义一个统一的 p kube 名字,这还是失败的。因为他一个文件里面其实包含了多个 Kubernetes 的 object,所以不同 object 都在同一个 cue 里,其实是产生冲突了,所以这个时候还要给每一个 object 加一个前缀。加 kind metadata,做个前缀,看他发生什么了。
cue import ./
… -p kube -l ’string.ToCame(kind)’ -l metadata.name -f
这个时候 cue 文件都生成了,打开一个 cue 文件来看一下。
可以看到,这里面的定义都变成了 cue 的定义,yaml 都变成 cue 的定义。刚才加的前缀就是 kind,他的名字来作为前缀,这时候每个集合都有一个不同的前缀。
3、下一步复制$ cat mon/promethesu/configmap.cue
,这时候还有一个问题,比如说这个文件里面不仅有 yaml 格式,
还有 prometheus 相关的格式,这个 prometheus 格式在原来 yaml 当成一个裸的 string 类型,这也是 yaml 的一个弊端。在 cue 里面可以自动去检测 yaml 和 json 的strings,自动把它们转化成 cue 的格式,这也是非常先进的一个工具。复制
$ cue import ./
… -p kube -l ’string.ToCame(kind)’ -l metadata.name -f -R
这时再来看刚才存的 stream 都变成了 cue 的格式,然后再把这些所有的内容通过一个叫 yaml marshal 的方式放到原来字段里。
这样相当于用 cue 统一管理了不仅是 Kubernetes 配置还有 prometheus 配置,这是非常好的事情。
4、最后复制 $ cue eval ./mon/Prometheus -e configMap.
prometheus
,他整个内容跟原来的 yaml 文件是一模一样的。
接下来去 Create top-level template,复制$ cue eval -c ./…> snapshot
,因为现在在这个文件里面存在大量重复的内容,现在尝试去去除这些重复的内容。复制$ cp frontend/breaddispatcher /kube.cue.
,先把cue 的文件复制进去,创建一个 cue 的文件,
service:[10= 1:{
apiVersion: "v1"
kind: "Service"
metadata:{
name: ID
labels:{
app: ID //by convention
domain: "prod"//always the same in the given files
component:string//varies per directory
}
}
spec: {
//Any port has the following properties.
ports: [
…{
port: int
protocol: *"TCP" | "UDP" //from the Kubernetes definition
name: string|*"client"
}]
selector:metalata. labels//we want those to be the same
}
}
deployment:[ID= ]:{
apiVersion:
“apps/v1”
kind:4 "Deployment"
metadata:name:ID
spec: {
//1 is the default, but we allow any number
replicas:*1|int
template:{
metadata:labels:{
app: ID
domain: "prod"
component:string
}
//we always have one namespace container
spec:containers:[{name: ID}]
}
这个文件在 kube.cue 里面,
他相当于定义了两个 string,尝试去减少 service 和 development 里面的内容,每个 service 和 Deployment 都有不同的名字,把它定义为类似于一个参数,但是他会把 label、ports、template 这些重复的内容给削减掉。现在有了这个东西之后,看一下他有什么问题,
复制 $ cue eval ./… -c > snapshot2
报了很多错。因为定义的 label,但是 alertmanager 里面没有 label,明显的不符合要求,所以要想办法给他们都加上 label。
5、首先,要把 component 的 label,统一定义成一个新的 string 类型。
然后输入下面的命令
$ ls -d */ | sed 's/,$/ /' | xargs-I DIR sh -c ' cd DIR ; echo "
package kube
#Component: \"DIR\"
">kube, cue; cd ..
这行命令相当于给所有目录下的每一个目录,根据目录的名字加上一个 cue 文件,cue 会定义 component 具体的值。刚加的一个目录
目录名字根据 mod 加上一个 component 值,加上一个值相当于给这些 cue 文件自动加上 label,这样 cue eval 就成功了。eval 一遍,刚才是出错的,但是现在成功了。
6、复制 diff snopshot snopshot2,可以看到他加上了一些通用的 label。
复制cp snopshot2 snopshot
,保存。使用一个工具叫 cue trim,因为现在相当于在 kube.cue 统一了定义了 label 和 port、template 这些内容,用 cue trim 的时候相当于会自动地调整,把重复的东西削减掉,这个时候,整个文件的行数就少了很多。复制 cue eval -c ./...> snopshot2,diff snopshot snopshot2
是没有任何区别的,是一样的结果。这里面进一步把配置相同的字段定义出来,削减配置的行数。
这时候需要添加这些内容,相当于把 spec 这些内容都添加进来,每个 demonSet 等都有一个 spec,
spec 是一个通用的东西,把这些内容统一配置了。然后在 cue 里面加上下面那段内容
$ cat <> kube. cue
//Define the export option and set the default to true
//for all ports defined in all containers.
spec:spec:template:spec:containers:[
…{
ports: [....{
export:*true|false//include the port in the service
}]
}]
for x in [deployment,daemonSet,statefulSet] for k,v in x {service:
“\(k)": {
spec:selector:v. spec. template. metadata. labels
spec:ports: [
for c in v. spec. template. spec. containers
for p in c. ports
if p. export{
let Port=p.containerPort//Port is an alias
port: *Port| int
target Port:*Port|int
]
}
}
EOF
$cue fmt
这里面有一些逻辑,它允许多个 export 存在,为相应的 service 生成的port 的字段,里面的 port 与service 的 port 主动对应起来,有一个 if eles 的操作相应的service会给 port 填上。
7、复制下面内容,
$ cat <>infra/events/kube. cue
deployment:events:spec:template:spec:containers:[{ports:[{ export:false},
_]}]
EOF
$ cat <>infra/tasks/kube. cue
deployment:tasks:spec:template:spec:containers:[{ports:[{ export:false},
_ ]}l
EOF
$ cat <>infra/watcher/kube. cue
deployment:watcher:spec;template:spec;containers:[[ export:false},
_]}]
EOF
使用 cue trim,把配置进一步的减少。有一个 Simplify 的操作,相当于把很多行变成了一行。还有更多的减少配置的方法,这里就不再赘述了。
8、整个过程充分了解了如何通过 cue 更好地管理和减少重复的配置,让我们使用起 Kubernetes 和 OAM 相关配置的文件更加舒服,通过刚才的样例可以看到 cue 本身,既可以作为配置,也可以作为这个配置的验证和管理工具。CUE 也可以把各种各样的格式统一起来,比如 Kubernetes、YAML 或者是 istio 这种格式,或者是 promethesu 格式,CUE 和OAM 关系是 OAM 服务器端更高级别的抽象,达到以应用为中心的目的;CUE 是客户端的工具,为了更高效准确的编写和管理配置文件,而 OAM 其实可以通过 CUE 透出更简单易用的模板,现在通过 CUE 的模板可以达到用 GitOps 和 IaC 的目的,最后运行在任何一个 OAM 平台上。另外一个项目叫 Villa,也是利用了相关的能力。