陈计节-使用 .NET Core 开发 Kubernetes 基础组件(中篇)

在上一篇文章中,我们分享了本主题的第一部分内容:.NET Core 和 Kubernetes 简介,大家可以在《陈计节-使用 .NET Core 开发 Kubernetes 基础组件(上篇)》进行查看,今天的文章是本主题的第二篇文章。

第二部分:使用 .NET Core 与 Kubernetes 交互

 
在第一部分提到过,我们把 Kubernetes 视为客户端应用的一个形式如何去开发它。今天的主题,我们接下来会探讨第一部分提到的三种方式(k-os 客户端应用、webhook、CRD&CRD Controller),如何去跟集群打交道。

交互是我们的第一类。日常在使用集群时,我们通常是一个人类用户来跟集训打交道、进行交互。所以接下来我们说用 .NET Core 来交互,你可以理解为就是说把这个 .NET Core 来模拟我们日常做的各种操作,就好比你要 create 一个 port 用来部署业务,你要去监控这个 Pod 里面的日志,那么这些我们都是可以通过 .Net 来实现的。

我们用 .NET Core 来实现跟集群交互的一个重要目的,是希望这个程序能够为我们提供一种自动化的运行能力,它可以为我们做一些自动化的运维,甚至是做一些这种像提供 SaaS 服务或者 PaaS 服务。我们可以用 .NET 来做一个云厂商,或是我自己要提供云服务的时候,我可不可以跟集群打交道,这些场景都是非常合适的。

示例代码下载地址

接下来的内容就涉及到了代码演示,因此我们提前准备了一个代码库,大家可以通过链接 使用 CSHARP 开发 KUBERNETES 原生基础设施,开发辅助,Asp.net源码|- 51Aspx.com 下载。

这个项目总共包含 4 个文件示例,分别是:
 

1.kncs.CmdExecuter:展示如何在 C# 中读取、生成并编辑 YAML,最终传入 kubectl 命令行,动态地在 Kubernetes 集群中创建 Pod 。

2.kncs.ClientCs:展示如何以 C# 编程的方式与 Kubernetes 集群交互,读取和监视 Pod 资源的状态。

3.kncs.Webhook:展示如何用 C# 实现 Kubernetes Webhook,实现对 Pod 镜像的检查,并实现类似 Istio 中的容器自动注入的功能。

4.kncs.CrdController:展示如何实现自定义资源类型 CRD:为 Kubernetes 集群安装新的 CSharpApp 资源类型。

第一个演示 kncs.CmdExecuter

 
目前在本地 Kubernetes 集群中 default 命名空间下,执行 kubectl get pods 命令返回 No resources found in default namespace. 也就是没有任何 Pod。我们现在开启监控模式 kubectl get pods -w,如果有新的 Pod 产生就会第一时间推送到屏幕上。接下来我们进入 kncs.CmdExecuter 目录下,启动程序执行命令 dotnet run 运行写好的代码。我们预期的效果是当 .Net 代码运行后,能够在集群中去创建相关的容器。程序运行后输出的文字为 “已安装的 kubectl 版本:…”,表示我们的代码正在正常执行。后面的输出信息展示了: kubectl 的版本、准备创建的 pod 名称为 testpod-77983,创建 pod 的结果,获取 pod 的结果为 ContainerCreating。我们可以看到 .Net 程序运行输出信息跟右边屏幕中输出内容是完全一致的:成功的创建了一个 Pod。
 

 
上面展示的就是我们的 .Net 代码去调用命令行:启动一个外部进程,然后去执行相关的参数。
 

这个就是 kncs.CmdExecuter,跟我们直接在命令行中执行 kubectl 命令没有什么区别。
 

这里稍微提一个注意事项就是我们的 Yaml 操作。

 
在上图中的 GetPodYaml 函数,首先在 GetExecutingAssembly 方法中去获取 Yaml 文件的内容,然后往里面去修改了一下 podName,因为我们希望 podName 是随机的、而不是一个固定值。我们希望对读取出来的 Yaml 文件内容进行一个操作,在 .Net Core 里面,包括 JSON、Yaml 这一类的弱类型文件,有一个非常好用的语法叫做 dynamic。如果只是对文件进行一些非常局部的修改,dynamic 可以让你几乎不需要去声明任何的 C# 类,就可以直接去操作里面的内容,然后去完成赋值。再完成赋值后,再用序列化器给它序列化回来,变成 Yaml 的文本,最后再返回回去。这就是一个非常典型的 C# 风格开发。
 

 
另外,除了获取现有的 Yaml 文件之外,我们还能以创建 Pod 数据结构的方式来进行创建。把它创建好之后变成一个 Pod。我们继续在 kncs.CmdExecuter 目录下运行代码,与上次不同的是,这次我们给它传一个参数。当然这个参数其实没有任何用处,这里只是一个简单的判断,如果有参数就会进入到 ComposePodAsYaml 函数。
 

 
ComposePodAsYaml 函数其实就不再是通过 Yaml 去操作了,我们其实声明的是 new V1Pod 这样的一个强类型的对象,因此里面我们就可以完整的对 Pod 所有的内部结构来做编程。你可以对它的 Metadata 做赋值,添加一些 Label。还可以添加一个 Container 等等。设置完这些就算是组装好了一个 Pod。以上就是一个强类型编程,它的好处是编译器以及各种工具可以帮助我们做非常多的一个保护。形成 Pod 之后,依然是把它序列化成 Yaml、返回的仍然是一个 String。最后我们还是使用这个 String 将其创建到集群中。
 

 
从上面的图片中可以看到,新的 csharp-testpod 也创建成功了、处于运行之中。
 

 
以上就是我们的第一个 Demo,能看到使用 .NET Core 是可以非常方便的以各种方式去操作 Pod,我们这里举例的就有两种:一种是 Yaml 文本的方式生成,另一种是通过强类型对象去生成
 
 

第二个演示 kncs.ClientCs

 
 
ClientCs 就是以 C# 编写的客户端来完成与刚才类似的功能,而且相比之下我们会使用一些更高端的特性。
 
首先来看一下效果。进入到 kncs.ClientCs 目录下,执行 dotnet run 命令运行。运行的过程当中会有一个隐形的编译过程,然后我们可以看到运行结果有了输出:
 

 
输出内容是:列举出集群中现有的所有 Pod,同时它在里面执行命令,然后命令执行完后会输出结果。
 

 
可以看到它的代码其实非常简单:先在集群中 List 当前 Pod,如果我们有 Pod 的话,我们就在现有的Pod 里面调用 ExecInPod 方法,这个方法的作用是在 Pod 内部执行 hostname -I 命令,其实就是打印 Pod 的 IP 地址。
 

 
上图是 ExecInPod 方法的具体实现,其实非常简单,仍然是使用 IKubernetes 的 client,然后这个 client 启动了一个 webSocket 的通信隧道。那么接下来就可以在这个里面把我相关的指令写入进去,然后同时能够读取它的输出。那么这就是 ExecInPod 的一个操作。可想而知使用这个方法我们不仅可以获取 Pod 的主机名,当然也可以获取别的任何东西。所以你如果想借助这个东西来做一个你自己的 CI/CD 工具的话,也是比较容易的。
 
我们在每个 Pod 里面都执行了打印主机名的命令,同时在输出结果中也确实可以看到 Pod 的主机名被打印出来了。那么为了证实输出结果的正确性,我们可以通过常用的命令行模式来做一个对比,验证程序是否正确。
 

 
在上图中我们在终端执行 kubectl exec -it csharp-testpod-73405 — hostname -I 命令,根据输出可以看到 csharp-testpod 的主机名是 10.244.1.240,这个结果与左侧程序运行的输出一致,从而证明了我们的程序没有问题。
 
大家可以注意到,我们的程序运行后它还在等待新的 Pod 事件。这表明它不仅仅可以和我们终端命令行的方式操作一样与 Pod 进行交互,同时它还能做事件的监控。那么在这种场景下如何工作呢?比如现在我们模拟另一位用户向集群中创建一个 Pod,然后看一下运行效果。
 

 
在上图中,我们向集群中使用 test-pod.yaml 文件创建资源,终端输出显示 test-pod 已经创建完毕,左边程序也是非常灵敏,它已经检测到了一个新的 Pod Added 事件,以及后面紧跟着有三个 Modified事件。为什么会有三个更新事件呢?因为集群在创建 Pod 的过程当中,创建后还需要考虑到它的一些状态的变更,比如 Pod 已经被调度到一个节点了、Pod 已经运行成功等等。这些都会称为一个事件。我们的代码里面都可以获得相关的通知。
 
大家可以看到我们通过这样一个非常简单的代码,就可以去跟集群做一个非常深度的交互。我们编写的程序跟 kubectl 命令行来做对比的话,这个能力还是不一样的。使用命令行的话,很多东西需要我们自己去核对。但是如果你想对事件去做一些编程的话,就必须要用到一些比较深入的能力,以上就是 kncs.ClientCs 的一些能力。
 
 

第三个演示 让程序在 Pod 中运行

 
之前我们提到,如果我们希望 .NET Core 它能够以一种集群里面的自动化程序帮我们去跟集群打交道。上面的演示还是在我们本地电脑上运行程序,那么如何让程序在集群内部运行呢?
 

 
因为集群里面是 Linux 的格式,我们要将代码编译为 Linux 版本的。进入 Kncs.ClientCs 文件夹内,在终端执行 dotnet publish -r linux-x64 -n PublishSingleFile=true 命令。这个命令中,等程序编译完后,再来看一下它在集群内部的运行效果如何。
 

 
上图是我们编写的一个名为 copy-exe.sh 的脚本,用于将编译后的程序复制到 Pod 中,然后我们进入该 Pod,切换到 /tmp 目录下,运行程序 Kncs.ClientCs。
 

 
根据上图可以看到,我们的程序在 Linux 系统中已经运行成功了,运行结果跟我们之前运行的是完全一样的,程序最后也在等待新的 Pod 事件。
 

 
我们接下来进入 Kncs.CmdExecuter 目录,在终端执行 dotnet run 命令,这个程序的特点就是可以向集群中创建新的 Pod,然后我们来观察运行在 Pod 里面的 Kncs.ClientCs 程序是否也有我们预期的输出,能够获取到新 Pod 的事件。
 

 
根据 Kncs.CmdExecuter 的输出,可以看到它又开始创建了新的名为 testpod-22318 的 Pod,然后它会与集群进行通讯。根据上图右侧的输出,运行在 Pod 中的 Kncs.ClientCs 已经检测到新的事件。这也证明了我们可以将程序部署到集群中,成为在集群内部运维集群的一个工具。
 
这里有一个关键的功能,如果我们要把程序放在集群内部还能够正常运行。那么我如何进行授权呢?在本地电脑上,大家知道是用 KubeConfig 集群凭证文件进行授权的。在该文件内部就会设置我们的权限。那么在集群内部我们如何获得这个权限呢?其实 Kubernets 为我们提供了一个更加方便的做法就是 ServiceAccount,它可以帮助我们进行授权管理。ServiceAccount 是一个可以关联到集群角色的权限定义,其实就是我们非常熟悉的 RBAC 权限模型。
 

 
从我们上面的演示可以看到,我们的代码是有权限的。它能拿到权限的原因是,在创建 test-pod 的时候,我在 Yaml 文件中指定了 serviceAccountName 为 pod-operator,而这个 pod-operator 它是有相关权限的,它通过 RoleBinding 绑定到了名为 pod-operator-role 的 Role 角色,这个角色拥有了 Pod 相关的权限。于是它就能够完成我们上面讲的相关操作。
 
如果现在我们想控制 Pod 里面执行的跟集群相关的权限,那么我们如何通过 ServiceAccount 的权限来对它做一些定制呢?我们尝试进行一些修改。
 

 
通过前面的演示我们知道程序是没有问题的,假使我现在把权限拿掉。在终端执行 kubectl apply -f ./rbac-no-permission.yaml 命令,这个 no permission 表示它已经没有权限了。
 

 
然后我们在 Pod 里面再重新运行程序,看看效果怎么样。在终端执行 kubectl exec -it test-pod -c dotnet-helper — /bin/bash 命令进入 Pod,运行 Kncs.ClientCs 程序,可以看到程序执行就发生了异常,异常输出为 Operator returned an invalid status code ‘Forbidden’。这表示我们没有权限。而我刚刚做的所有操作只是修改了它的权限,一开始我们使用 rbac-good-permission.yaml 文件创建,这个文件是有权限的。后来我改用 rbac-no-permission.yaml 文件创建,它就没有权限了。
 

 
上图是 rbac-no-permission.yaml 文件,我们对 Pod 权限进行了限制,只能执行 create 操作了。它不再有之前的所有权限,所有权指的就是 List、Create、Delete 等。所以我们通过修改 ServiceAccount 的权限来实现了对 Pod 权限的管理。
 

 
正常情况下,Pod 里面使用 ServiceAccount 的话,集群会帮我们把这个 token 挂载到 Pod 里面去。那么我们开发的 ClientCs 程序它会帮助我们把 ServiceAccount 对应的 token 能够自动的生成为类似于 Kubectl 这样的一种配置,然后在程序里面去读取它、使用它,最终跟 ApiServer 去打交道。使用 ClientCs 这样的工具可以非常高效地帮助我们去完成跟集群相关的开发。
 
以上就是我们第二部分的全部内容,第一部分“.NET Core 和 Kubernetes 简介”的内容可以在《陈计节-使用 .NET Core 开发 Kubernetes 基础组件(上篇)》进行查看,第三部分 “使用 .NET Core 开发 Kubernetes 组件”的内容我们将会在后面的文章继续分享讲解。
 

给TA打赏
共{{data.count}}人
人已打赏
.Net Core

陈计节-使用 .NET Core 开发 Kubernetes 基础组件(上篇)

2022-6-2 11:54:44

.Net Core

陈计节-使用 .NET Core 开发 Kubernetes 基础组件(下篇)

2022-6-6 12:07:22

0 条回复 A文章作者 M管理员
    暂无讨论,说说你的看法吧
个人中心
今日签到
有新私信 私信列表
搜索