shell-operator的发布用以简化Kubernetes operator的创建

栏目: 编程工具 · 发布时间: 5年前

我们很高兴在此介绍我们新的开源方案,它能将Kubernetes中Operator的开发提升到一个全新的更简单的水平。它能提供给你一个非常吸引人的机会,让你可以在15分钟内将你的小脚本变成完全成熟的Operator。欢迎[shell-operator](https://github.com/flant/shell-operator)!

![img](https://cdn-images-1.medium.com/max/1600/1*VJvWuXg8pk8c2Y7lCCnNng.png)

### **目标**

shell-operator的思路很简单,它订阅来自Kubernetes对象的事件,并在事件发生后执行外部程序,为其提供有关事件的信息:

![img](https://cdn-images-1.medium.com/max/1600/1*oiiHmowii4KdL7NR9hBZVg.gif)

在我们运行Kubernetes集群期间,很多小任务开始显现。在[Flant](https://flant.com/),我们迫切想要以正确的方式来自动化它们,因此我们觉得需要更智能的解决方案。通常你可以使用基本的bash脚本来解决所有这些任务,但是如你所知,更推荐的方式是使用Golang来编写Operators。很显然,为每个小任务分别开发成熟的operator将会很低效。

### 15内创建一个Operator

我们将举一个在Kubernetes集群中可以被自动化的例子,以及shell-operator可以如何帮助我们。我们将尝试复制用于访问 Docker 仓库的凭证。

使用私有仓库镜像的Pod应在其清单中包含指定的用于访问仓库的secret。这个secret必须在创建pod之前先创建在每个命名空间中。你可以手动执行此操作,但是,如果我们将配置动态的多个环境,我们将为单个应用程序创建许多命名空间。在多个应用程序(甚至两个或三个)的情况下,secret的数量会变得巨大。关于secret还有一个需求:我们希望能够偶尔更改(注册表)仓库的访问密钥。因此,手动解决方案变得非常低效,你必须自动创建和更新secret。

#### 简单的自动化

我们来写一个脚本,每N秒运行一次,并检查命名空间中secret是否存在。如果secret不存在,那么它将会被创建。这个解决方案的优势是它看起来就像是cron中的一个 shell 脚本,一种经典且易于理解的方法。缺点是在此脚本的两次启动之间的间隔期间可能会出现一些新的命名空间,因此在一段时间内它将不会持有这个secret。这种情况会导致启动pod的过程中出错。

#### 使用shell-operator进行自动化

为了使我们的脚本准确运行,经典的cron执行应该被当有新增命名空间事件发生时的执行所取代。在这种情况下,你可以在使用之前创建一个secret。让我们看看如何使用shell-operator来实现这个功能。

首先,我们先分析一下脚本,就shell-operator而言,脚本都被称之为“钩子“。每个钩子在使用`--config`标志执行时都会通知shell-operator将其绑定(即需要执行哪些事件)。在我们的例子中,我们将使用`onKubernetesEvent`:

{{{

#!/bin/bash

if [[ $1 == "--config" ]] ; then

cat <

{

"onKubernetesEvent": [

{

"kind": "namespace",

"event": [ "add" ]

}

]

}

EOF

fi

}}}

在这里,我们定义我们关注的`namespace`类型的添加(`add`)对象事件。

现在我们需要添加当事件发生时需要执行的代码:

{{{

#!/bin/bash

if [[ $1 == "--config" ]] ; then

# configuration

cat <

{

"onKubernetesEvent": [

{

"kind": "namespace",

"event": [ "add" ]

}

]

}

EOF

else

# response:

# find out what namespace has emerged

createdNamespace=$(jq -r '.[0].resourceName' $BINDING_CONTEXT_PATH)

# create the appropriate secret in it

kubectl create -n ${createdNamespace} -f - <

apiVersion: v1

kind: Secret

metadata:

...

data:

...

EOF

fi

}}}

真棒!我们现在已有一个简洁且漂亮的脚本,想让它能真正发挥作用,我们需要准备一个镜像并将其跑在集群中。

#### 使用钩子制作我们的镜像

你可以很轻易观察到我们在脚本里面使用了`kubectl`和`jq`命令。这意味着镜像中需要包含钩子,shell-operator二进制文件(它将监视事件并执行这个钩子),以及钩子需要用到的命令(`kubectl`和`jq`)。[hub.docker.com](https://hub.docker.com/r/flant/shell-operator)上已提供了包含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

}}}

#### 在集群中运行

我们再来看看这个钩子,这次我们将关注具体的操作以及它在集群中执行的对象:

1. 它订阅了`namespace`的创建事件;

2. 它在不与它所运行的命名空间相同的空间创建一个secret

这里我们会发现运行这个镜像的pod需要有执行这些操作的权限。你可以授权给一个`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-operator并应用创建的部署清单:

{{{

$ 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-operator启动,它将订阅命名空间创建事件并在需要时执行钩子。

这样一个**简单的shell脚本就变成了Kubernetes中一个真正的operator**,并成为集群的一部分。这样做的好处是我们避免了使用Golang来开发operator的复杂过程:

![img](https://cdn-images-1.medium.com/max/1600/1*QgCDjqjARgpMZjqF25vUMQ.png)

### 过滤

关于对象的观察很棒,但我们通常需要响应对象中某些属性的更改,例如,增加/减少部署中的副本数量或对象对标签中的任何更新。

当一个事件发生时,shell-operator接收该对象的JSON清单。在此JSON中,你可以选择要监视的属性,并**仅在更改时**启动钩子。`jqFilter`字段可以帮助你完成这点:你应该输入将应用于JSON清单的jq表达式。

举个例子,要响应Deployment对象标签中的修改,你必须从`metadata`字段中提取`labels`字段。这个例子中你将需要如下的配置:

{{{

cat <

{

"onKubernetesEvent": [

{

"kind": "deployment",

"event":["update"],

"jqFilter": ".metadata.labels"

}

]

}

EOF

}}}

jqFilter表达式将Deployment的长长的JSON清单转换成带有标签的简短的JSON:

![img](https://cdn-images-1.medium.com/max/1600/1*GzDt1eYk4du3dFEIlTw_tw.png)

shell-operator将只会在这个简短的JSON发生变化时执行钩子。其它属性的变更将会被忽略。

### 钩子的执行上下文

钩子的配置允许你指定几种事件。例如你可以定义两个Kubernetes事件和两个计划调度:

{{{

{

"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**样式运行脚本!你可以在[文档](https://github.com/flant/shell-operator/blob/master/HOOKS.md)中找到额外的信息。

为了区分钩子执行的原因,shell-operator会创建一个临时文件并将其路径保存到`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"

}

]

}}}

你能通过名称来全面了解字段的内容(更多详细信息可在[文档](https://github.com/flant/shell-operator/blob/master/HOOKS.md#binding-context)中找到)。使用jq从`resourceName`获取资源名称的示例已经在复制secret的钩子中展示:

{{{

jq -r '.[0].resourceName' $BINDING_CONTEXT_PATH

}}}

你可以通过类似的方式去获取到其它字段。

### 下一步是什么呢?

在该项目仓库中的[/examples directory](https://github.com/flant/shell-operator/tree/master/examples)目录里包含了一些可以直接在集群中使用的示例。你可以将它们用作你开发自己的钩子的基础。

Shell-operator同样支持使用Prometheus来收集指标。[METRICS](https://github.com/flant/shell-operator/blob/master/METRICS.md)章节已描述了这些可用的指标。

你能轻易想到,shell-operator是使用 Go 编写的,并根据开源许可证(Apache 2.0)的条款进行分发。我们非常感谢任何关于开发在Github上的这个[项目](https://github.com/flant/shell-operator)的帮助。你可以通过给我们点[Star](https://github.com/flant/shell-operator/star),反馈问题或者是PR来支持我们!

【原文链接】:[Announcing shell-operator to simplify creating of Kubernetes operators](https://medium.com/flant-com/kubernetes-shell-operator-76c596b42f23) 翻译:冯旭松


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

JavaScript编程精解

JavaScript编程精解

Marijn Haverbeke / 徐涛 / 机械工业出版社华章公司 / 2012-10-1 / 49.00元

如果你只想阅读一本关于JavaScript的图书,那么本书应该是你的首选。本书由世界级JavaScript程序员撰写,JavaScript之父和多位JavaScript专家鼎力推荐。本书适合作为系统学习JavaScript的参考书,它在写作思路上几乎与现有的所有同类书都不同,打破常规,将编程原理与运用规则完美地结合在一起,而且将所有知识点与一个又一个经典的编程故事融合在一起,读者可以在轻松的游戏式......一起来看看 《JavaScript编程精解》 这本书的介绍吧!

HTML 编码/解码
HTML 编码/解码

HTML 编码/解码

Markdown 在线编辑器
Markdown 在线编辑器

Markdown 在线编辑器

正则表达式在线测试
正则表达式在线测试

正则表达式在线测试