查看完整视频
小黑屋思过中,禁止观看!
评论并刷新后可见

您需要在视频最下面评论并刷新后,方可查看完整视频

积分观看

您支付积分,方可查看完整视频

{{user.role.value}}
付费视频

您支付费用,方可查看完整视频

¥{{user.role.value}}
专属视频

只允许以下等级用户查看该视频

升级
会员专享

视频选集

.NET云原生挑战赛直播课-第十一课【陈计节-使用 .NET Core 开发 Kubernetes 基础组件】

  • 视频介绍
  • 视频选集
  • 交流讨论
内容纲要

导师介绍:陈计节老师,现为腾讯高级云原生架构师。讲师、作者、译者,前红帽中国、ThoughtWorks 高级咨询顾问。在敏捷、 云原生 DevOps 和跨平台 .NET 开发等方面拥有丰富的经验。多次在 .NET 大会、.NET 地区和线上活动中分享话题、组织工作坊课程。 最爱的编程语言是 C#,曾以 C# 作为工具实现容器镜像仓库开发、类 gRPC 协议和服务端,以及 Kubernetes 和 Istio 基础组件开发等。 翻译有《.NET 性能优化》、《ASP.NET Core 微服务开发》等著作,在各类公众号和网站发表大量有关 .NET 技术、职业素养等技术文章。

其实我在 .NET 社区当中,应当说朋友们对我还是比较熟悉的。不过由于我近些年工作上比较忙一些,所以主要还是在私下跟大家交流的比较多。当然这个 .NET 技术,以及像我最近这两年主要深耕的是 Kubernetes 和 Isito 相关的这些领域,也都是我非常感兴趣的,也愿意跟大家在 .NET 社区一起去做更多的探讨。

今天我们的主题是:使用 .NET Core 开发 Kubernetes 基础组件。在我们前几期的直播课程中,贵老师讲解过我们如何使用 .NET 去开发容器应用,那么开发容器应用可以说是我们所有开发者这两年比较兴奋的一个方向了,那么除了容器应用之外,我们跟 Kubernetes 这样的平台还有哪些比较有趣的方向我们可以去一起探讨呢?实际上我们今天就是来关注一下这些方面。

我们今天的主题会分成三个方面来讨论:

  1. 首先就是简单的介绍 .NET Core 和 Kubernetes 这两个比较庞大的项目
  2. 其次我们会来探讨如何使用 .NET Core 与 Kubernetes 集群直接进行交互,为运维集群或是一些开发场景提供直接的支持。
  3. 最后一部分也是我们今天主题的重点,使用 .NET Core 开发 Kubernetes 组件,所谓 Kubernetes 组件,是指它能够形成一个类似于系统软件的结构。

第一部分:.NET Core 和 Kubernetes 简介

.NET Core 从 2016 年正式宣布开源以后,至今已经六年了,虽然对于很多新技术而言,六年可能还显得比较年轻,但实际上 .NET 这项技术已经是历史悠久了,我们今年正好是 .NET 技术发布的 20 周年,在这样的时间节点上我们在线上来探讨 .NET 和 .NET Core 技术,实际上是一个非常好的机会。因为 .NET Core 对于 .NET 来说,本身它就是一个很完美的继承,同时也是一次新生。

我简单的介绍一下 .NET Core 在 Linux 原生方面的几个内容。以前我们对于 .NET 的印象更多会觉得它是一个 Windows 原生的开发技术栈,但实际上对于 .NET Core 来说,它已经不再是一个Windows 原生的了,如果大家关注过 .NET Core 本身的开发,包括它的运行时、编译器、周边的工具、开发框架等,你就会知道它的整个开发过程都是在 Github 上面,它的所有框架、跟操作系统的交互等等,就会发现其实它对 Linux 的支持都是非常到位的,它在第一时间就会去考虑跟 Linux 的兼容性。其实 .NET Core 的开发团队也是一直在用 CI 持续集成的,包括 Windows、Linux 以及 Mac OS 在内的各个平台都是包含在他们的 CI 平台里面的。所以你会发现,.Net Core 不再仅仅是一个 Windows only 的,它现在还是一个 Linux 原生平台,这是一个很关键的点。因为如果我们认为它只是一个兼容模式,那么可能会对它在 Linux 或者是 Mac OS 这样平台进行使用会有些担心,但既然它是原生的,那么我们会对它有更多的信心。

原生的意思是指我原本就在这个平台、为它而设计、为它而生。我们今天讨论的是 .NET Core 跟 Kubernetes 平台的结合,所以容器原生是非常关键的。实际上我们会发现 .NET Core 对于容器的支持也是非常完美的,包括它的运行时、运行框架,对于我们现在各种 Linux 容器、Windows 容器都提供了一个非常好的支持。

最后一个方面是 AOT 预先编译。什么是预先编译呢?大家在之前学习 .NET 的时候就会了解,它作为一个托管程序运行时的运行状态,所谓托管运行时是指它在目标平台之上会先运行一个叫做 CLR 的运行时,然后接着才开始运行 .NET 字节码。而现在,AOT 的相关功能在 .NET 5 中就逐步开始提供,在 .NET 6 中已经非常成熟,在 .NET 7 中它逐步开始去覆盖更多的场景。那么它提供的 AOT 编译,就是指直接将程序编译成目标平台的原生代码,你可以理解为它编译完之后,就跟 C++ 或 Go 这种程序编译效果是一样的,你可以使用类似于 GCC、GDB、或是 LLDB 这样的工具去做一些调试,而这取决于你目标平台字节码的格式。最后一旦用上 AOT 的编译,你会发现 .NET Core 或是 .NET 生态其实已经统一了。.NET Core 是我们去强调它的一个说法,实际上在 .NET Core 5 开始,它已经不再强调 .NET Core 了。所以在使用 .NET 运行软件时你会发现,编写的时候是以非常舒畅的 C# 的语法和内库去开发软件,接着你是以这种平台原生的程序去运行。那么它的性能、启动效率,包括内存的分配和回收效率,跟 C++、Go 这样的一些底层语言是没有什么差别的。这个可以说是很长时间以来我们一直在渴望的一个能力。

那么聊完 .NET Core 的这些新特性之外,我们再来聊一下它的开发效率。其实最让我喜欢 .NET Core 的一个原因是它各个方面的搭配非常均衡,使得我们总能以一个比较高效的方式去关注要开发的东西。尽管 .NET Core 在国内社区不算是最流行的一个开发技术栈,但是 C# 可以说是全世界最好的编程语言,这是没有任何异议的。那么 C# 这个编程语言,不管是业务还是技术方面的一个描述能力,以及它的各方面语法和一些特性,其实给我们带来了非常大的帮助。

开发工具方面,我们有早就号称宇宙第一 IDE 的 Visual Studio,以及它的小弟 VS Code,还有实际上我个人非常喜欢的 Rider,这些工具都能够完美的满足日常的开发使用。编译工具方面的 dotnet CLI MSBuild,以及内库管理工具 NuGet 和它的私有部署版 MyGet 、BaGet 等。可以说工具链是非常齐全的,你会发现基于这样的一整套开发技术工具和框架,实际上我们在这些方面可以说是非常成熟了,那么整个 .NET Core 它现在已经变成了一个非常高效率的开发平台。

我们聊完 .NET Core 之后,来看一下今天要探讨的第二个话题。Kubernetes 其实非常有趣,跟 .NET Core 可以说是差不多同一时间在 2017 年左右开始流行的,但是它的发布也是在 2015 年、2016 年那个时间点。在一开始的时候,人们比较担心的是:这个平台它的复杂度是不是有点太高了?我们很多人是不是玩不转?现在呢,事实表明实际上 Kubernetes 几乎已经完整的赢得了云原生这个容器编排市场,这个市场现在已经几乎变成了云原生的一个代名词了。那么这里有一份来自 Redhat 红帽的统计数据,他们去采访了全球一些著名的大型企业,采访问他们:你们有没有在用 Kubernetes?以及如果在用的话,你们有没有在生产环境中去使用?我们可以看到 88% 的企业都说他们已经在使用了,在已经使用的企业里面,有 70% 已经用在生产环境。那么当然有些人就会问,Kubernetes 除了在生产环境使用,在其它环境也可以用吗?是的,实际上 Kubernetes 是一个很大的平台,它除了在生产环境去用来运行我们的业务应用,运行各种面向大众的、或者是面向消费者的产品之外,它还可以在企业内部更多的场景里面为我们提供帮助。

比如我们这里简单列举到,实际上你基于这个 Kubernetes 去做一个 CICD 的持续交付平台是非常容易的。我们可以简单的设想一下,比如 Github Actions 这样的工具是非常酷的,但是如果你仔细去看它的代码,你会发现 Github Actions 里面每一个 Action 动作,它其实都是一个容器镜像,那么可想而知它背后很有可能就是一个容器品牌,当然它不见得是 Kubernetes 的,但是你基于 Kubernetes 这样的一个容器平台去构建一个 CICD 平台是比较容易的。一会儿我们会看到如何用代码去操作这个平台。当然这只是一个举例,其实 Kubernetes 适合的还有很多,比如像这里列举到的基础运维平台、业务应用托管、大数据、机器学习、以及边缘计算等等,Kubernetes 现在已经变成了一个非常通用的运维平台了。其实基本上可以说 Kubernets 适合运行各类软件,包括在线的、离线的业务,尤其是集群类软件,它都是可以去做的。

我个人认为在未来十年,可以想象的是 Kubernetes 可能将是我们所有的开发、测试和运维人员几乎没有办法去避免、逃脱的一个平台。当然也有可能会有新的技术产生,这个是完全有可能的。但是新的技术产生,它不是突然一下子蹦出来了,然后把原来的完全推翻掉。新的技术从来不是以这种形式出现的,新的技术从来都是在以前的创新基础之上去进行的一个新的技术。所以在现在我们来了解 Kubernetes、怎么样去跟 Kubernets 打交道,是一个非常好的时机。

上图是 Kubernetes 的一个系统结构。在这个结构的中间部分,主控节点其实是少量的机器,一般来说主控节点如果是比较小的集群有可能就是一台集群,然后中等集群可能是三台服务器,或者大型的集群就是五到七台机器,一般而言七台及以上的集群是比较少见的。工作节点就是真正用来运行我们刚才讲的各类业务应用、各类的包括 CICD 工作负载的虚机所在,这个机器我们这里称为节点,其实可以是虚机,可以是物理机,这个当然都是没有问题的,在节点上真正去运行业务容器。

Kubernetes 集群中每个节点之间是如何通信的呢?如果大家看过 Kubernetes 代码的话,你会发现它其实是非常符合我们现在主流的所谓微服务、模块化的一个设计风格。那么集群内部从主控节点的设计来讲,它的内部是通过 API 进行调用的,这个如何理解呢?就是我们外面的人去调用包括 Kubectl 命令行,或是直接用 Restful API 去调用直接处理请求的 ApiServer,那么你在写入一些资源对象之后,比如我们稍后提到的 Pod,这些资源对象会被写入到 DB 里面,最终它会由这个集群的调度器来使用,调度器会把它下发到工作节点里面去,然后去运行。

工作节点想要配合主控节点工作的话,它就必须要有跟主控节点协作的一些组件,具体来讲,像 Kubelet、Kube-proxy 还有 Docker,也有可能是其它的容器运行时以及其它的一些扩展软件。那么这些组件它在工作节点上就能够与主控节点配合,来接收主控节点下发的相关指令,完成容器的一些启动,还有一些运维的指令等等。

那么主控节点除了刚才讲的把 Pod 作为一个调度之外呢,它还会需要去针对 API 里面的一些资源对象来做一些交互,比如稍后我们会接触到的一些编程的东西去做交互。所以控制管理器 Controller 是 Kubernetes 内部一个比较关键的概念,它是用来解析我们功能模块的一个关键逻辑所在,所以它会有一个 Control Manager,就是控制管理器。

Etcd 其实是它的数据库。如果把主控节点分成一个三层的架构,上面就是 Web API 层,中间就是逻辑层,下面就是数据库。其实也很简单的,这样一个结构看起来很简单,那么我们怎么样去用它呢?

一般来说,我们使用 Kubernetes 来部署一个应用时,通常分成这样的几个步骤:首先,我们需要手动编写 Yaml 文件。在一个团队里面,一般你的应用的结构会是比较固定的,所以你会倾向于把这些 Yaml 文件做成一个模板。当你需要更新这个模板时,你才会需要去修改它。而平常你会把它存储在一个仓库里面,这个模板也可以进行打包,现在社区里面有 Helm 工具可以帮助我们打包。它需要把模板先取下来做一个压缩,然后填充模板里面的参数,形成最终用来往集群上安装的 Yaml 文件内容,最后使用这些 Yaml 文件在集群中进行部署。如果是安装 .NET 应用的话,它最终就会被安装到我们的主控节点上面去运行。最终我们站点的用户会通过集群中的Service 或是 Load Balance 这种机制来访问到运行在容器里面的 Pod,大概是这样的一个模式。

总的来说,大概分成三个步骤:

1.第一个步骤就是制作 Yaml 模板。

2.第二个步骤就是我们将 Yaml 模板做一个参数化。参数化中有一个关键过程,我们需要去填写应用的容器镜像,因为容器镜像才是真正要用来部署的东西,所以你每次升级代码时,容器镜像版本都会改变。

3.第三个步骤就是把 Yaml 文件安装到集群上,完成部署。

所以在容器平台上去部署一个应用,基本上就是这三个阶段。

Kubernetes 平台其实除了可以部署应用之外,实际上它还提供了非常多的周边的一些功能。正是这些周边功能,使得它比起其它的容器编排平台而言,显得更加吸引人。在上图中我们可以看到:

1.容器周边服务:指它本身为运行 Pod 容器准备了很多抽象。比如说它的安全的上下文封装、网络模型、存储对接等等。

2.批量容器调度:它允许我们根据设置的 Label 标签、CPU 的情况来做一些调度,允许我们自己去写调度算法,所以它是一个具体高可扩展性的平台。

3.高级应用部署:在 Kubernetes 集群中,应用部署有很多种类型。比如你可以非常容易的做一个无停机的部署,包括滚动更新。还可以非常方便地去做灰度发布管理,当然前提是你需要配合一些流量管理软件,像 Isito 这样的软件在 Kubernetes 上面就配合的非常好。

4.应用运维:比如自动扩缩容,监控,故障的自动处置等等。其实这个平台都提供了非常好的工具。

5.集群定制接口:Kubernets 本身是一个非常具有可扩展性、可定制性的平台,就像一个操作系统一样,你可以在它里面去编程、去安装一些新的东西。

我们在谈 Kubernetes 基础组件开发的时候,我们就需要对 Kubernetes 基础组件与它上面的应用软件来做一个区分。

所谓的应用软件,它是相对于我们刚才谈的基础组件。如果我这里换一个词叫系统软件,可能大家会熟悉一点。以前大家在上计算机基础课程时,就知道电脑里面的软件通常分成两种,第一种叫做系统软件,第二种叫做应用软件。其实应用软件是我们大多数人经常接触的,比如电脑里面的 Word、PPT、Excel、浏览器等这些软件,其实它就属于应用软件。应用软件就是说你可以在它里面去实现一系列定制性的业务流程的软件,它不是操作系统本身自带的一些功能,它通常是提供这个软件里面的功能,它只在这个里面进行使用。

而系统软件作为对比,它是一种平台级别的东西。它是提供给这个电脑上、这个计算机里面、很多的其它应用软件可以共用的一种技术组件。

那么对于 Kubernetes 是一样的,在上图中列到了,我们称之为 Kubernetes OS。这其实是一种类比,就是说我们几乎可以把 Kubernetes 当做一个操作系统,如果你以这种视角来看待它,我们今天其实需要探讨是:如何在 Kubernetes 上面去开发系统软件。

要开发系统软件,就要考虑到日常我们是如何使用它的。实际上,Kubernetes 在使用的时候有很多种方法,一般作为业务开发人员,或是集群的业务运维人员,通常我们使用的一些工具就是可视化界面,命令行,还有 API 等等。但其实我们真正要去增加它里面的功能,更多是需要与 SDK 打交道,所以 SDK 面向的是它的生态开发者。从上到下我们会发现,越往上是越面向入门级的用户,越往下是越面向专业用户

作为开发者,我们就知道这几种方式的软件开发难度自然是不一样的。面向界面的软件开发相对肯定是更难的。我们都知道如果你想去做一个 UI 的自动化测试本身就是很难的,而且实际上对于我们跟 Kubernetes 打交道做基础开发的话,就没必要用界面了,我们更多的反而是使用命令行、API 和 SDK 的方式,稍后我们会来介绍。

如果我们要对它来做开发,我们要关注些什么呢?就是基础知识,那么我们来简单的看一下上图的基础模型。

现在假如我是一个用户,我在最上面开始往集群中去 create 一个 pod 时,它会发生什么呢?pod.yaml 文件中包含了我要创建的这些内容,那么这些内容它首先会被 API Server 所接收到,然后尝试存到 DB 里面去。当然稍后我会介绍,它实际上不会直接存储还有一些动作的,那么它存储完了之后,会由 Scheduler 调度器来完成 Pod 的一个调度,然后把相关指令给到工作节点,工作节点会去运行我们的容器,有可能是 Docker,也有可能是 Containerd 等其它的容器运行时。Pod 启动之后的同时,就需要去为它配置一些网络、存储,当整个配置都就绪之后,我们的 Pod 才能够运行起来,以上就是它的一个概要的工作机制。

在了解了概要的工作机制之后,我们要考虑到接下来要对它做开发,我们可以在哪些场景对它做开发呢?在上图可以看到有三种切入点:

第一种是客户端

1.我们可以开发 kubectl 的一个命令行插件,直接往里面增加或是定制客户端的行为。

第二种是主控节点,我们做的事情可以说非常多:

1.webhook:指的是我们对它资源的一个处理方式来做一些定制。

2.CRD:指的是我们能够为它提供一些定制的资源类型。比如我们可以给它开发一个 C# APP 运行 C# 代码。

3.Customized Scheduler:定制调度策略的开发。

4.APIService:把 API 的处理过程委托给另外一个程序。

5.CloudProvider:作为云厂商也可以对集群的托管行为进行一些定制。

第三种是工作节点

1.DaemonSet:它是一种运行在所有工作节点上的应用,通常可以用来做一些在每个节点上都要做的功能,比如用来部署 CNI,或者做 Log 日志的收集等等。

2.CRI:运行容器的一个容器运行时。

3.CSI:为容器提供存储功能的驱动程序。

4.CNI:是为容器提供联网功能的一些机制。

以上这些东西都是我们可以使用第三方的代码来开发的。除了 kubectl,由于 kubectl 它本身是由 go 语言写的,所以 .NET 是没法在里面去开发的,因为开发的话它需要编译的,所以它不会把我们的代码编译进去。除了这个之外,其它的所有种类我们都可以用 C# 来开发。我们可以看到整个这个 Kubernetes 的一个可编程性是非常灵活、非常强大的

上面我们花了这么大的篇幅来探讨什么是 Kubernets,以及它的可编程能力。那我们来简单的挑其中的几个场景来讲一下,在 .NET Core 里面,我们如何跟它去做一些这种程序上的交互:

1.如果我们把 Kubernetes 当做一个操作系统的话,我们可以开发它的客户端应用,对吧?能不能适合用 .NET Core 来开发呢?非常适合。因为 .NET Core 拥有完整的跟操作系统的进程打交道的能力,你可以去启动一个进程,你可以去调用相关的 Restful API,这些都是可以非常方便地完成我们的能力的。

2.Webhook 我们在上面有简单的介绍,它程序的格式,其实是一个这种 Web API,那 .NET Core 的话用来开发 Web API 可以说是我们最擅长这个工作之一。

3.CRD 它是一种运行在集群之内,其实也可以运行在集群之外的一种自定义程序,它其实就是一个你能运行的二进制程序就可以了。当然我们 .NET Core 是非常擅长于做这类的程序的。

4.DaemonSet 它往往是运行在宿主机上,或者是运行在宿主机上的容器,所以 .NET Core 都是非常擅长做这方面的开发。

第二部分:使用 .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 组件

我们来看一下如何去开发集群上的一些基础组件。其实我们很多场景都会用到这些基础组件。比如说作为公司的安全策略,可能公司哪一天突然会要求说现在不允许下载某个镜像仓库里面的镜像。这其实是很正常的,有时候你的集群部署在内网的,你不希望用你集群的同事一不小心就引用了外面公网上的一些镜像。你希望大家只能用内部的镜像。那我们如何去实现这样的一个需求呢?

第四个演示 Kncs.Webhook

我们集群中目前有 5 个 Pod 在正常运行,现在进入 Kncs.Webhook/manifests/test-pods 文件夹内,在终端执行 kubectl create -f ./validatee.yaml 命令,validatee.yaml 文件内容其实就是一个用于验证的 Pod。

在 validatee.yaml 文件中,它用的是外部镜像 abcd.com/centos:7 ,而这个域名的镜像正好是被公司的拦截策略命中的,所以上面执行创建命令时,报错意思为在创建资源对象的时候发生了一个拦截,提示为 Pod should not use this images。现在查看 Pod 数量的话就可以看到名为 webhook-tester-validating 的 Pod 没有被创建出来,集群现在依然还是 5 个 Pod。

这就是我们刚才讲的一个拦截项目,而这个项目其实在 Istio 软件中是很常见的,叫做自动注入 Sidecar 的一个能力。Istio 其实是一个叫做服务网格的产品,它的作用就是帮助我们在集群里面去管理各类的流量,可以实现流量灰度、熔断限流等功能。它的特点是允许我们在业务开发的过程当中,不需要去关注刚才我们讲的这些东西,而当你把程序部署到 Kubernetes 集群上的时候,它就通过自动注入的机制,帮我们把相关的容器注入进去。这种情况下容器就会接管我们的流量,帮助完成流量治理的功能。它的过程跟我们前面讲的内容很相关,前面讲的是拦截,这里讲的是自动注入。它其实是另外一种我们叫做 webhook 的东西。

我们看上图,用户创建的 Pod 里面就是一个名为 myapp 的容器,然后由 Istiod 做了处理之后,增加了一个名为 istio-proxy 的容器。这就叫做 webhook。

在前面的 Yaml 文件中可以看到我们只声明了一个容器,但是实际运行的 Pod 却有两个容器,实际上我们在这里就实现了自动注入。我们来做一个简单的演示。在 Kncs.Webhook/manifests/test-pods 文件夹内,有一个名为 injectee.yaml 的文件,这个文件默认也只有一个容器,现在来看一下我们如何向其中再注入一个容器。在终端执行 kubectl create -f ./injectee.yaml 命令。

看上图 Pod 已经创建好了。可以看到 webhook-tester-mutating Pod 里面确实运行两个容器,通过 describe 命令可以查看 Pod 的详细信息。

在 containers 里面,第一个容器为 centos 7 是处于 sleep 状态,第二个容器为 runtime 也是处于 sleep 状态。而第二个容器就是我们注入进去的。这就被称为自动注入能力。

上面两个演示都是 Kubernetes webhook 的功能,它其实是指用户在界面或是命令行工具写入的各类型的 Kubernetes 资源在写入到 DB 之前,Kubernetes 会调用外面的一些 webhook。比如你做 CI 的话,你希望一提交代码就能触发 CI 的自动运行,这个就是像 git 这种代码托管工具,它可以去调用 CI 的 URL。

在 Kubernetes 里面是一个类似的机制,它可以在写入到数据库之前先调用两种类型的 webhook:

1.第一种类型叫做 Mutating:表示由用户输入的资源可以做一些修改。

2.第二种类型叫做 Validating:表示在修改完之后,可以允许我们以编程的方式对结果来做一些验证。

我们上面的演示,拦截的功能用的就是第二种类型的 webhook,注入的功能就是用的第一种类型的 webhook。

webhook 这种请求实际上也是 JSON 的格式,我们可以用 .NET 程序去编写,整体流程分为三步:

1.获取 Webhook OpenAPI,用 nswag 将模型转换为 C# 类。模型链接在上面的图片中已经给出。

2.根据需求完成 Webhook 的开发。

3.最后将 Webhook 部署到集群中。

Kncs.Webhook 的代码逻辑是非常的简单,而且采用了 Minimal API 的方式去编写的。大家有兴趣的话可以自己去看相关的代码。

上图是其中一些值得单独拎出来说一说的代码片段:

1.在 Validating Webhook 里面会去检查你使用了哪些镜像,我们也用到了人见人爱的 LINQ 语法去完成检查。

2.在 Mutating Webhook 里面我们用到了匿名对象,有时候你在不经意当中就开始使用它了。

对于 Webhook,之前提到过第三个步骤是写完之后,你需要把它安装到集群里面去。具体是有一些步骤的:

1.需要把程序打包成一个容器镜像。

2.去编写相关的 Yaml 文件,包括 Deployment、Service。

3.自签证书:Kubernetes 作为一个运维平台是非常关注安全的,所以要求通讯过程一定是要加密的,我们要做一个自签的证书,然后在相关的 Webhook 配置中添加上证书,它会帮助我们完成相关的调用。

4.在集群中进行部署。

第五个演示 Kncs.CrdController

在今天课程的最后,我们带领大家做一个非常有意思的演示开开眼界。在前面的内容中我们都是使用的 Kubernetes 现成的功能,那我们有没有办法直接增加一个集群的功能呢?像我们日常创建Deployment、Service 等这些东西又是哪来的呢?所以实际上 Kubernetes 可以帮助我们去开发自定义的这种应用类型。

在上图中可以看到有一种 kind 叫做 CSharpApp,那我们能不能在集群里面直接让它运行 C# 代码呢?我们想实现一些具体的目标,比如说:

1.将 C# 代码部署为 Pod:支持直接修改代码,它自动就能运行。

2.能支持多个副本。

3.支持暴露服务端口:如果它是一个 web 应用的话,它还能够暴露出 Service,我能够直接通过本地去访问它。

4.代码变化后,重新部署:它还可以检测到我的代码的修改,然后帮我进行重新部署。

5.不接受编译错误的代码:如果有编译错误就提前拦截。

我们先清理掉之前创建的 Pod,然后来看一下刚才讲的 CSharpAPP,这是一个需要我们去关注的比较有趣的样例。进入 Kncs.CrdController/manifests/samples 目录下,在 console.yaml 文件中有一个叫做 hello-console 的 CSharpApp,它里面运行的是一个 Hello world。

目前集群中没有任何的 Pod,然后我们准备在集群中创建一些 Pod。我们在右边屏幕中执行 kubectl get pods -w 命令来监听应用的运行过程。在左边屏幕中执行 kubectl create -f ./console.yaml 命令。这个 console Pod 里面运行的是一个 Hello word。然后运行 Kncs.CrdController 程序。

Yaml 文件创建成功后,我们执行 kubectl get csharpapp 命令,可以看到输出了 hello-console,并且已经创建了 51 秒。然后执行 kubectl get csharpapp hello-console -o yaml 命令查看 Pod 详细信息。同时右边屏幕也输出了 Pod 的事件信息,表示用来运行 C# 代码的容器已经在创建之中了。

执行命令 kubectl get pods csa-hello-console-181119-rapp 可以查看创建进度。

查看 Pod 的详细信息,可以发现这个 Pod 里面还是挺复杂的:有一个 csharp-app-runner,然后它里面会将相关的 C# 代码给挂载进去。

现在 Pod 已经运行起来了,那我们来看一下它里面运行的结果是什么。当然我希望它运行的就是我的 CSharp APP 代码,这个代码会输出 Hello world from CSharp APP 。如何验证这一点呢?查看它的 Logs 就可以了。

执行命令 kubectl logs csa-hello-console-181119-rsa -c csa,在打印出来的日志中可以看到 Hello world from CSharp APP,打印的整个日志信息其实就是 .NET 自动 build 的过程。所以它会自动帮我们去完成编译,那么有没有可能我现在去给它做一些修改呢?

执行命令 kubectl edit csa hello-console,把 Hello world from CSharp APP 改为 Hello world from Jijie Chen’s CSharp APP。可以看到当我修改了程序之后,它又一次帮我们重新去运行了程序。而且把我们之前的 Pod 自动删掉了,这是不是很像 Deployment 的效果?

现在我们来看一下它在新的容器里面运行的效果是什么,从打印出来的日志中就可以看到,这时候我们的代码就已经被重新的编译和运行了。所以你可以看到,这基本上就实现了一个我们日常当中非常熟悉的 Deployment 的效果。

最后我们来看一个 Web 的效果。在命令行执行 kubectl create -f ./web.yaml 命令,在右侧屏幕可以看到删除 Pod 是需要一定时间的,它中间会逐步删除各种事件。web.yaml 文件有什么不一样的吗?web 我们知道它有一个关键的能力,就是让我们从外面要能访问到,所以配置了 type 为 LoadBalance 类型,把 80 端口暴露出来。那么如何访问 80 端口呢?

我们可以去获取 service,执行命令 kubectl get svc。看到创建了一个名为 hello-web 的 service,它的 EXTERNAL-IP 为 192.168.1.156,我们在浏览器里面来访问一下这个 IP:

可以看到页面显示 Hello world!,这表示已经访问成功了。

那么这就是它帮我们做了一个自动化的部署,同样的,类似的一个能力我们能不能去做一个 edit 呢?执行命令 kubectl edit csa hello-web。同样的,我们对 web 应用也做一个修改,把 Hello World 改为 Hello World from Jijie Chen。

可以想象,如果未来你公司的程序能够实现以这种方式去部署,那么我们可以任务这是一种自动化能力。等新 Pod 创建好了之后,我们去刷新页面,可以看到它现在已经帮我更新成了新版的页面。那么这整个过程中,我们所有的代码都是使用 C# 去完成的,这就是整个 Crd.Controller,我们使用 C# 去实现一个运行器,它可以完成应用的自动化部署。所以这就是 .NET Core 程序程序在 Kubernetes 上能够帮助我们实现各种类型的应用开发。

那我们今天呢,其实跟大家一起聊了很多。一开始我们聊了 .NET Core 和 Kubernetes 的一些介绍,以及我们聊了如何去使用进程的方式,用 ClientCs 去跟集群交互,模拟我们日常使用的一个场景。还有呢就是我们后面去关注的怎么样使用 ClientCs 去开发一些 Webhook、去开发 CrdController,然后以各种方式来定制集群的行为。

因为最后这个 CrdController 的代码是比较复杂的,它里面就留给大家一个家庭作业,去看一下它的运行的过程。如果你去看我的代码,你会发现我到目前还没有实现最后一个功能,就是去拒绝会编译错误的代码。那么这个功能大家有兴趣的话,我非常的鼓励你们去用之前讲的 webhook 的功能去把它完成。这样的话,基本上这个 CSharp APP 就可以成为一个小团队里面可以使用起来的自动化小工具了,可以说是比较有趣的一个东西吧。那我们今天的课程到这里就结束了,非常感谢大家的观看!

第四部分:答疑

第一个问题:怎么学习 Kubernetes?

我觉得这个问题显得比较宽泛了,那我也宽泛的回答一下:就是你最好要多练习。Linux 的特点就是如果你只看书或看视频,那你肯定是学不会的。所以如果你想学习的话,还是需要多加练习。在社区中有很多的交互式学习网站提供了练习素材。

第二个问题:有没有适合部署 Kubernetes 的精简系统呢?可以附加虚机管理的有吗?

这类我不是特别清楚,至少集群类的软件是肯定有的。而虚机管理的话,其实有很多虚机管理软件,你直接安装使用就可以了,这类软件也很多的。

第三个问题:怎么理解 Ingress?

新版本 Kubernetes 中增加了一个功能叫做 APIService。而在这之前呢,Ingress 其实是一种广泛使用的入口流量方法。因为 Kubernetes 它本身内置的 Service 有多种类型,比如 LoadBalance 类型,但这种类型更多还是需要依赖云厂商的实现或是你在本机进行一些配置才能够支持的。那么 Ingress 其实给我们提供了一种,一般来讲叫做七层流量 HTTP 或是 GRPC 这种接入的方法。而 LoadBalance 类型一般都是四层流量接入,而 Ingress 可以使得我们直接以七层的方式去使用。而它的底层可以去共享使用一个 LoadBalance Service,或是共享使用一个云平台的 CRB 这种设施。使得我们以单个的 CLB 共享 IP 的方式去用多个域名来接入到我们的网站。这样的话对于很多小体量的网站是非常好的。有一些 Ingress 也支持七层的转发规则。比如路径前缀,有一些还支持流量灰度的能力,这个都是比较有用的。以上就是 Ingress 相比于 LoadBalance Service来讲的话,它的一些特性。

第四个问题:Yaml 文件是通过接口去修改的吗?需要安装额外的组件来实现吗?

是的,我们今天演示了很多,既有通过 C# 去修改 Yaml,也有通过 C# 生成 Yaml 的,还有通过 C# 不生成 Yaml 直接去操作 API 的。所以今天演示的内容可以说是非常的丰富的。今天演示的代码不需要安装其它额外的组件,只需要 C# 代码加 NewGet 加 IDE 编辑器就可以了。不需要在本地安装 Kubectl,我今天演示的东西里面只需要代码。

第五个问题:Ingress 与 API Gateway 的区别?

其实它们两者有很多相似的地方:Ingress 是一个流量接入措施;API Gateway 除了流量接入之外,通常它还有更加丰富的、对流量的定制和控制能力。通常在它上面还会增加比如像登录或是 head 来做灰度,或是你对流量比例做一些控制。所以 API Gateway 相比于 Ingress 的流量接入能力之外,它还有对流量更细粒度的控制能力。

第六个问题:C# 语言开发 Kubernetes 生态产品和用纯 Go 开发相比有哪些优势呢?

C# 的优势就是它的开发效率比较高,尤其是对 .NET 社区而言,如果大家现有的很多业务已经是在用 C# 写的话,那这时候如果要与现有的业务进行交互的话,它的兼容性非常好,这就是它的独特优势。相对于 Go 语言而言,C# 在面向对象、性能、异步开发等这些方面,实际上都是有它的优势。由于 Kubernetes 生态更多是用 Go 语言开发的,所以使用 C# 也有一些需要注意的地方。如果你想用 C# 去写一些 Kubernetes 底层的东西,比如 CNI、CSI 等许多与操作系统做接口交互的,那么这时候 C# 能使用的周边工具会少一点,也会比较难利用 Go 语言生态已有的这些工具和内库。所以我们不能做一个非常武断的比较,说谁优谁劣的问题。我相信会有很多的厂商或者企业,在自己的这个场景里面会选择一个比较合适的东西来用。

如果是 C 语言的话,我也简单说一下。其实 C 语言相对于 C++ 或是 Go 语言来说,它是比较底层一点的开发语言了。如果你使用 C 语言,就会有很多东西可能需要自己做封装。因为 Kubernetes 它可以用 API 去调、用 SDK 去做,所以社区里面相关的工具都是有的,它其实跟 C# 就很像了,但是它的语法又可能反而还不如 C# 那么方便。

所以如果你只是要做相关的基础组件开发,而又不排斥 Go 语言的话。在 C 和 Go 语言之间选择的话,我比较推荐 Go;而如果是在 Go 和 C# 中间选择的话,就看大家业务的实际情况,然后去做出选择。

第七个问题:调用 Docker 生态是否有相关的 SDK 呢?

.NET 是可以直接调用 Docker API 的,社区中有一个名为 Docker .NET 的项目。Docker 是一个产品,它的 API 现在也已经抽象出来成为一个标准的东西了,所以也就是你与 Docker API 的一套标准打交道。你可以通过 .NET 程序运行一个容器,然后附加到它里面去查看一些进程和各种环境的情况吧,这个还是非常好玩的。

给TA打赏
共{{data.count}}人
人已打赏
0 条回复 A文章作者 M管理员
    暂无讨论,说说你的看法吧
个人中心
今日签到
有新私信 私信列表
搜索