我们的博客中已经有关于
Kubernetes中运算符功能以及如何
自己编写简单运算符的文章 。 这次,我们希望引起您的注意,我们的开源解决方案将操作员的创建带到了一个非常容易的程度-熟悉
shell操作符 !
怎么了
shell运算符的想法非常简单:从Kubernetes对象订阅事件,并在接收到这些事件时启动一个外部程序,并为其提供有关事件的信息:

在集群运行期间,当我们真正希望以正确的方式实现自动化的小任务开始出现时,对我们的需求就随之而来。 所有这些次要任务都是通过简单的bash脚本解决的,尽管您知道,操作员最好用Golang编写。 显然,为每个如此小的任务投资全面的运营商开发将是低效的。
15分钟内即可操作
让我们看一个示例,该示例可以在Kubernetes集群中实现自动化,以及shell运算符将如何提供帮助。 示例如下:复制密钥以访问Docker注册表。
使用私有注册表中的映像的Pod必须在其清单中包含指向包含访问注册表的数据的秘密的链接。 在创建Pod之前,必须在每个命名空间中创建此秘密。 完全有可能手动执行此操作,但是如果我们设置动态环境,则一个应用程序将有很多名称空间。 而且如果应用程序也不是2-3 ...,那么秘密的数量将变得非常大。 关于机密的另一件事:我想更改密钥以不时访问注册表。 结果,
手动操作作为解决方案是
完全无效的 -您需要自动创建和更新机密。
易于自动化
我们将编写一个Shell脚本,该脚本每N秒运行一次,并检查名称空间中的秘密,如果没有秘密,则将创建该秘密。 该解决方案的优点是,它看起来像cron中的shell脚本-一种经典且易于理解的方法。 缺点是,在两次启动之间可以创建一个新的命名空间,并且在一段时间内它将保持秘密状态,这将导致启动Pod时出错。
使用Shell-operator进行自动化
为了使脚本正常工作,经典的cron启动需要替换为添加了事件名称空间的启动:在这种情况下,您可以设法在使用秘密之前创建一个秘密。 让我们看看如何使用shell-operator来实现这一点。
首先,让我们分析脚本。 用shell-operator命名的脚本称为钩子。 启动时每个带有
--config
标志的钩子都会告诉Shell操作员有关其绑定的信息,即 通过什么事件需要启动它。 在我们的例子中,我们将使用
onKubernetesEvent
:
这里描述的是,我们对添加(
add
)
namespace
类型的对象的事件感兴趣。
现在,您需要添加事件发生时将执行的代码:
太好了! 结果是一个小的,漂亮的脚本。 要“恢复”,剩下两个步骤:准备映像并在集群中运行。
用挂钩准备图像
如果看一下脚本,可以看到使用了
kubectl
和
jq
。 这意味着该映像必须具有以下内容:我们的钩子,一个将监视事件并启动该钩子的shell运算符,以及该钩子使用的命令(kubectl和jq)。
Hub.docker.com已经有一个现成的映像,其中打包了shell-operator,kubectl和jq。 仍然可以通过一个简单的
Dockerfile
添加钩子:
$ cat Dockerfile FROM flant/shell-operator:v1.0.0-beta.1-alpine3.9 ADD namespace-hook.sh /hooks $ docker build -t registry.example.com/my-operator:v1 . $ docker push registry.example.com/my-operator:v1
集群启动
再次让我们看一下挂钩,这一次写出集群中它将执行哪些操作以及执行哪些对象:
- 订阅名称空间事件
- 在运行位置以外的名称空间中创建一个机密。
事实证明,将在其中启动映像的容器必须具有执行这些操作的权限。 这可以通过创建自己的ServiceAccount来完成。 必须以ClusterRole和ClusterRoleBinding的形式进行许可,因为 我们对整个集群中的对象感兴趣。
YAML中的最终描述是这样的:
--- apiVersion: v1 kind: ServiceAccount metadata: name: monitor-namespaces-acc --- apiVersion: rbac.authorization.k8s.io/v1beta1 kind: ClusterRole metadata: name: monitor-namespaces rules: - apiGroups: [""] resources: ["namespaces"] verbs: ["get", "watch", "list"] - apiGroups: [""] resources: ["secrets"] verbs: ["get", "list", "create", "patch"] --- apiVersion: rbac.authorization.k8s.io/v1beta1 kind: ClusterRoleBinding metadata: name: monitor-namespaces roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: monitor-namespaces subjects: - kind: ServiceAccount name: monitor-namespaces-acc namespace: example-monitor-namespaces
您可以以简单的Deployment形式运行组装的映像:
apiVersion: extensions/v1beta1 kind: Deployment metadata: name: my-operator spec: template: spec: containers: - name: my-operator image: registry.example.com/my-operator:v1 serviceAccountName: monitor-namespaces-acc
为了方便起见,创建了一个单独的名称空间,将在该名称空间中启动shell运算符并应用创建的清单:
$ kubectl create ns example-monitor-namespaces $ kubectl -n example-monitor-namespaces apply -f rbac.yaml $ kubectl -n example-monitor-namespaces apply -f deployment.yaml
就是这样:shell运算符将启动,订阅名称空间创建事件并在必要时启动挂接。

因此,一个
简单的shell脚本变成了Kubernetes的实际运算符,并成为集群的组成部分。 所有这些-无需在Golang上开发运算符的复杂过程:

筛选
跟踪对象是很好的方法,但是通常需要对
对象某些属性的更改做出响应,例如,对Deployment中副本数的更改或对象标签的更改做出响应。
当事件到达时,shell运算符将接收对象的JSON清单。 您可以在此JSON中选择我们感兴趣的属性,并
仅在它们更改时运行该挂钩。 为此,提供了
jqFilter
字段,您需要在其中指定将应用于JSON清单的jq表达式。
例如,要响应Deployment对象上的标签更改,您需要从
metadata
字段中过滤
labels
字段。 配置将是这样的:
cat <<EOF { "onKubernetesEvent": [ { "kind": "deployment", "event":["update"], "jqFilter": ".metadata.labels" } ]} EOF
jqFilter中的此表达式将Deployment的长JSON清单转换为带有标签的短JSON:

仅当此简短的JSON更改和对其他属性的更改被忽略时,shell运算符才会触发钩子。
挂钩启动上下文
钩子配置允许您为事件指定几个选项-例如,来自Kubernetes的事件的2个选项和2个时间表:
{"onKubernetesEvent":[ {"name":"OnCreatePod", "kind": "pod", "event":["add"] }, {"name":"OnModifiedNamespace", "kind": "namespace", "event":["update"], "jqFilter": ".metadata.labels" } ], "schedule": [ { "name":"every 10 min", "crontab":"0 */10 * * * *" }, {"name":"on Mondays at 12:10", "crontab": "0 10 12 * * 1" ]}
有点题外话:是的,shell-operator支持运行crontab样式的脚本 。 您可以在文档中阅读更多内容 。为了区分为什么启动钩子,shell操作员创建了一个临时文件,并将其路径传递给
BINDING_CONTEXT_TYPE
变量中的
BINDING_CONTEXT_TYPE
。 该文件包含挂钩启动原因的JSON描述。 例如,每10分钟,一个钩子将从以下内容开始:
[{ "binding": "every 10 min"}]
...并在周一开始:
[{ "binding": "every 10 min"}, { "binding": "on Mondays at 12:10"}]
对于
onKubernetesEvent
将有更多的JSON触发,因为 它包含对象的描述:
[ { "binding": "onCreatePod", "resourceEvent": "add", "resourceKind": "pod", "resourceName": "foo", "resourceNamespace": "bar" } ]
字段的内容可以从它们的名称中了解,并且可以更详细地阅读
文档中的
内容 。 在复制机密的挂钩中,已经显示了使用jq从
resourceName
字段获取资源名称的示例:
jq -r '.[0].resourceName' $BINDING_CONTEXT_PATH
同样,您可以获取其余字段。
接下来是什么?
在项目存储库中
的/ examples目录中 ,有可以在群集中运行的挂钩示例。 编写钩子时,可以将其作为基础。
支持使用Prometheus收集指标-可用的指标写在
METRICS部分中。
您可能会猜到,shell运算符是用Go编写的,并在开放源代码许可(Apache 2.0)下分发。 对于
在GitHub上开发
项目的任何帮助,我们将不胜感激:星号,问题和请求请求。
揭开保密的面纱,我们还告知shell操作系统仅是我们系统的一
小部分,它可以使Kubernetes集群中安装的附加组件保持最新状态,并执行各种自动操作。 我们在周一于圣彼得堡的HighLoad ++ 2019上更详细地
讨论了该系统-该报告的视频和成绩单将很快发布。
我们计划开放该系统的其余部分:addon-operator以及我们的钩子和模块集合。 顺便说一句,addon-operator已经
在GitHub上可用 ,但是有关它的文档仍在开发中。 计划在夏季发布模块集合。
敬请期待!
聚苯乙烯
另请参阅我们的博客: