
4月8日,在
Saint HighLoad ++ 2019大会上,在DevOps和Operations部分的框架内,由Flant的三名员工创建了一份题为“扩展和补充Kubernetes”的报告。 在其中,我们谈到了许多情况,在这些情况下我们想扩展和补充Kubernetes的功能,但是对于这些情况,我们没有找到现成的简单解决方案。 必要的解决方案以开源项目的形式出现,本演示文稿也专门针对这些项目。
按照传统,我们很高兴为您提供一个
带有报告的
视频 (50分钟,比文章多得多),并以文本形式进行主要压缩。 走吧
K8s内核和附加组件
Kubernetes正在改变悠久的行业和管理方法:
- 由于他的抽象 ,我们不再使用诸如配置配置或运行命令(Chef,Ansible ...)之类的概念,而是使用容器,服务等的分组。
- 我们可以准备应用程序而无需考虑将在其上启动的特定平台的细微差别:裸机,提供商之一的云等。
- 有了K8,组织基础架构的最佳实践变得比以往任何时候都更容易获得:扩展,自我修复,容错等。
但是,当然,事情并不是那么顺利:Kubernetes带来了他们自己的新挑战。
Kubernetes不能解决所有用户的所有问题。 Kubernetes
核心仅负责
每个集群中存在的最低限度必需功能集:

在Kubernetes的核心中,定义了一组基本的原语-用于对容器进行分组,管理流量等。 我们在
两年前的
一份报告中更详细地讨论了它们。

另一方面,K8提供了扩展可用功能的绝佳机会,有助于满足其他
特定用户的需求。 集群管理员负责添加Kubernetes,他们必须安装和配置所有必要的东西,以使集群“具有必要的形状”(以解决其特定问题)。 这些是什么添加物? 让我们看一些例子。
附加示例
安装了Kubernetes之后,我们可能会感到惊讶的是,网络对于节点内部以及节点之间的Pod交互非常必要,因此它本身无法运行。 Kubernetes核心不保证必要的连接-而是为第三方插件定义了一个网络
接口 (
CNI )。 我们必须安装这些附加组件之一,这些附加组件将负责网络配置。

一个很好的例子是数据存储解决方案(本地磁盘,网络块设备,Ceph ...)。 最初,它们在内核中,但是随着
CSI的出现
,情况变成了已经描述过的类似情况:在Kubernetes,接口及其实现,在第三方模块中。
在其他示例中:
- 入口控制器(有关回顾,请参阅我们的最新文章 ) 。
- 证书经理 :

- 运算符是一整套附加程序(包括提到的cert-manager),它们定义了原语和控制器。 他们的工作逻辑仅受我们的想象力限制,并且使我们能够将现成的基础结构组件(例如DBMS)转换为原语,而原语比使用一组容器及其设置要容易得多。 已经编写了大量操作员-尽管其中许多操作员尚未准备好投入生产,但这只是时间问题:

- 指标是Kubernetes如何将接口(Metrics API)与实现(第三方插件,例如Prometheus适配器,Datadog集群代理...)分开的另一个说明。
- 为了进行监视和统计 ,实际上不仅需要Prometheus和Grafana ,还需要 kube-state-metrics,node-exporter等。
而且这还不是附件的完整列表。例如,我们今天在Flant公司为每个Kubernetes集群安装
29个附件 (它们总共创建249个Kubernetes对象)。 简而言之,没有添加,我们看不到集群的生命。
自动化技术
运营商旨在使我们日常面临的日常操作自动化。 以下是一些适合编写操作员的生活示例:
- 有一个私有(即要求登录)注册表,其中包含应用程序的图像。 假定每个吊舱都绑定到允许在注册表中进行身份验证的特殊机密。 我们的任务是确保在机密名称空间中找到此机密,以便Pod可以下载图像。 可能有很多应用程序(每个应用程序都需要一个秘密),定期更新秘密本身很有用,这样一来,用手发现秘密的选项就消失了。 在这里,操作员急忙解决:我们创建一个控制器,它将等待名称空间出现并为此事件向名称空间添加秘密。
- 假设默认情况下,禁止从Pod到Internet的访问。 但是有时可能需要这样做:访问权限机制可以简单地工作而无需特定技能,这是合乎逻辑的,例如,通过在名称空间中存在某个标签。 操作员将如何在这里帮助我们? 将创建一个控制器,该控制器期望标签出现在名称空间中,并添加用于访问Internet的适当策略。
- 相似的情况:如果节点具有类似的标签(带有某种前缀),则需要在节点上添加特定的污点 。 操作员的动作很明显...
在任何集群中,有必要解决日常任务,并
正确地做到这一点-使用运算符。
总结所有描述的故事,我们得出结论,
为了在Kubernetes中
进行舒适的工作,需要 :a)
安装附加组件 ,b)
开发操作员 (用于解决日常管理任务)。
如何为Kubernetes编写声明?
通常,该方案很简单:

...但是事实证明:
- Kubernetes API是一件相当平凡的事情,需要大量时间来掌握。
- 编程也不是每个人都适合(Go被选为首选语言,因为它有一个特殊的框架-Operator SDK );
- 与这样的框架类似的情况。
底线:
要编写一个控制器 (操作员),您必须
花费大量资源来学习材料。 对于“大”运算符(例如MySQL DBMS),这是合理的。 但是,如果我们回想起上述示例(泄露机密,pod访问Internet ...),而我们也想正确地做这些事情,那么我们将了解花费的精力将超过现在所需的结果:

通常,会出现一个难题:花费大量资源并找到合适的工具来编写语句或采取“旧方法”(但很快)。 为了解决这个问题-在这些极端之间找到折衷方案,我们创建了自己的项目:
shell-operator (另请参见他最近在集线器上发布的消息 ) 。
壳运算符
他如何工作? 在集群中有一个吊舱,其中带有shell-operator的Go-binary所在。 一组
钩子存储在它旁边
(有关它们的更多详细信息,请参见下文) 。 shell运算符本身订阅Kubernetes API中的某些
事件 ,然后启动相应的钩子。
Shell运算符如何理解在哪些事件下触发哪些钩子? 这些信息由钩子本身传递给shell运算符,它们使之非常简单。
挂钩是Bash脚本或任何支持单个参数
--config
并返回JSON作为响应的可执行文件。 后者确定哪些对象引起他的兴趣,哪些事件(对于这些对象)应作出反应:

我将举例说明我们的示例之一的shell-operator实现-揭示使用应用程序映像访问私有注册表的秘密。 它包括两个阶段。
练习:1.写一个钩子
挂钩的第一步是处理
--config
,这表明我们对名称空间(特别是创建名称空间的时间)感兴趣:
[[ $1 == "--config" ]] ; then cat << EOF { "onKubernetesEvent": [ { "kind": "namespace", "event": ["add"] } ] } EOF …
逻辑将是什么样? 也很简单:
… else createdNamespace=$(jq -r '.[0].resourceName' $BINDING_CONTEXT_PATH) kubectl create -n ${createdNamespace} -f - << EOF Kind: Secret ... EOF fi
第一步是找出创建了哪个名称空间,第二步是通过
kubectl
为该名称空间创建一个秘密。
练习:2.将图像放在一起
剩下的就是将创建的钩子转移到shell运算符-该怎么做? Shell-operator本身作为Docker映像提供,因此我们的任务是在该映像的特殊目录中添加一个钩子:
FROM flant/shell-operator:v1.0.0-beta.1 ADD my-handler.sh /hooks
它仍然可以收集并推送:
$ docker build -t registry.example.com/my-operator:v1 . $ docker push registry.example.com/my-operator:v1
最后的任务是将图像嵌入群集中。 为此,编写
Deployment :
apiVersion: extensions/v1beta1 kind: Deployment metadata: name: my-operator spec: template: spec: containers: - name: my-operator image: registry.example.com/my-operator:v1 # 1 serviceAccountName: my-operator # 2
在其中,您需要注意两点:
- 指示刚创建的图像;
- 这是一个系统组件,(至少)需要权限来订阅Kubernetes中的事件并按名称空间显示秘密,因此我们为该钩子创建了一个ServiceAccount(和一组规则)。
结果-我们以Kubernetes
固有的方式解决了我们的问题,创建了一个揭露秘密的操作员。
其他Shell操作员功能
要限制该挂钩将使用的所选类型的对象,
可以通过按特定标签
过滤 (或使用
matchExpressions
)
来过滤它们 :
"onKubernetesEvent": [ { "selector": { "matchLabels": { "foo": "bar", }, "matchExpressions": [ { "key": "allow", "operation": "In", "values": ["wan", "warehouse"], }, ], } … } ]
提供了一种
重复数据删除机制 ,该
机制使用jq过滤器使您可以将对象的大型JSON转换为小型JSON,仅保留那些我们要监视更改的参数。
调用该钩子时,shell运算符将
有关对象的数据传递给它,该
数据可用于任何需要。
触发挂钩的事件不仅限于Kubernetes事件:shell运算符支持
及时调用挂钩 (类似于传统调度程序中的crontab),以及特殊的
onStartup事件。 所有这些事件都可以组合并分配给同一钩子。
Shell运算符的另外两个功能:
- 它异步工作 。 由于Kubernetes事件(例如,对象的创建),其他事件(例如,同一对象的移除)可能会在集群中发生,因此必须在挂钩中将其考虑在内。 如果挂钩失败,则默认情况下将再次调用它,直到成功完成(可以更改此行为)。
- 它导出Prometheus的度量标准 ,您可以使用该度量标准了解shell运算符是否正在运行,找出每个挂钩的错误数量以及队列的当前大小。
总结报告的这一部分:

附加组件安装
为了使Kubernetes舒适地工作,还提到需要安装附加组件。 我将以我们公司目前的运作方式为例来进行讨论。
我们开始使用带有多个集群的Kubernetes,唯一添加的是Ingress。 有必要将其不同地放在每个群集中,并且我们针对不同的环境进行了几种YAML配置:裸机,AWS ...
集群更多-配置更多。 另外,我们改进了这些配置本身,结果它们变得非常不同:

为了使一切井井有条,我们从脚本(
install-ingress.sh
)开始,该脚本将要部署的集群类型作为参数,生成了所需的YAML配置并将其部署到Kubernetes。
简而言之,我们的进一步路径和与之相关的论据如下:
- 要使用YAML配置,需要模板引擎(在第一阶段它是一个简单的sed);
- 随着集群数量的增加,需要进行自动更新(最早的解决方案是将脚本放入Git,由cron更新它并运行它);
- Prometheus(
install-prometheus.sh
)也需要类似的脚本,但是值得注意的是,它需要更多的输入数据,以及它们的存储(以一种很好的方式,集中在集群中),并且一些数据(密码)可以自动生成:

- 将错误信息放入越来越多的集群中的风险不断增加,因此我们意识到安装程序(即,两个脚本:Ingress和Prometheus)需要阶段设置(Git中的几个分支,几个cron在相应的分支中进行更新):稳定或测试集群);
- 使用
kubectl apply
变得困难,因为它不是声明性的,只能创建对象,而不能决定它们的状态/删除它们; - 缺少一些我们当时没有意识到的功能:
- 完全控制群集更新的结果,
- 根据可从集群获得的数据自动确定某些参数(安装脚本的输入)(发现),
- 它以不断发现的形式进行逻辑发展。
我们在其他项目
addon-operator的框架中实现了所有这些积累的经验。
附加运算符
它基于已经提到的shell运算符。 整个系统如下:
在shell-operator钩子中添加了:
- 值存储
- 舵图
- 监视值存储库的组件,并在发生任何更改时要求Helm重新滚动图表。

因此,我们可以响应Kubernetes中的事件,启动一个钩子,然后从该钩子对存储库进行更改,然后将重新抽出图表。 在生成的方案中,我们将一组挂钩和一个图表选择为一个组件,我们将其称为
模块 :

可以有许多模块,并且向它们添加了全局挂钩,全局值存储和监视此全局存储的组件。
现在Kubernetes中正在发生某些事情,我们可以使用全局钩子对此做出响应并在全局存储库中进行更改。 此更改将引起注意,并将导致群集中所有模块的回滚:

该方案满足上面宣布的安装附加组件的所有要求:
- Helm负责标准化和声明性。
- 自动更新问题已在全局挂钩的帮助下得以解决,该全局挂钩按计划进入注册表,并且如果在该注册表中看到系统的新映像,则重新启动它(即“自身”)。
- 集群中设置的存储使用ConfigMap实现,其中记录了存储的主要数据(启动时将它们加载到存储中)。
- 使用钩子可以解决密码生成,发现和连续发现的问题。
- 借助Docker开箱即用支持的标签可以实现暂存。
- 使用可以了解状态的指标来监视结果。
整个系统在Go上实现为单个二进制文件,称为addon-operator。 因此,该方案看起来更简单:

该图中的主要组件是一组模块
(在下面显示为
灰色) 。 现在,我们可以为所需的附加组件付出一些努力,并确保将其安装在每个集群中,将对其进行更新并响应集群中所需的事件。
Flant在70多个Kubernetes集群上使用
addon-operator 。 当前状态为
Alpha版本 。 现在,我们正在准备发布Beta的文档,但目前
,存储库
中提供了
示例 ,您可以基于这些
示例创建自己的插件。
哪里可以自己获取addon-operator模块? 图书馆的出版对我们来说是下一个阶段,我们计划在夏天进行。
影片和幻灯片
表演视频(〜50分钟):
报告介绍:
聚苯乙烯
我们博客上的其他报告:
您可能也对以下出版物感兴趣: