Shell-operator is a tool for running event-driven scripts in a Kubernetes cluster.
This operator is not an operator for a particular software product such as prometheus-operator
or kafka-operator
. Shell-operator provides an integration layer between Kubernetes cluster events and shell scripts by treating scripts as hooks triggered by events. Think of it as an operator-sdk
but for scripts.
Shell-operator is used as a base for more advanced addon-operator that supports Helm charts and value storages.
Shell-operator provides:
add
, update
or delete
events. Learn more about hooks.Contents:
You need to have a Kubernetes cluster, and the
kubectl
must be configured to communicate with your cluster.
The simplest setup of shell-operator in your cluster consists of these steps:
kubernetes
bindings)For more configuration options see RUNNING.
A hook is a script that, when executed with --config
option, outputs configuration to stdout in YAML or JSON format. Learn more about hooks.
Let's create a small operator that will watch for all Pods in all Namespaces and simply log the name of a new Pod.
kubernetes
binding is used to tell shell-operator about objects that we want to watch. Create the pods-hook.sh
file with the following content:
#!/usr/bin/env bash
if [[ $1 == "--config" ]] ; then
cat <<EOF
configVersion: v1
kubernetes:
- apiVersion: v1
kind: Pod
executeHookOnEvent: ["Added"]
EOF
else
podName=$(jq -r .[0].object.metadata.name $BINDING_CONTEXT_PATH)
echo "Pod '${podName}' added"
fi
Make the pods-hook.sh
executable:
chmod +x pods-hook.sh
You can use a prebuilt image flant/shell-operator:latest with bash
, kubectl
, jq
and shell-operator
binaries to build you own image. You just need to ADD
your hook into /hooks
directory in the Dockerfile
.
Create the following Dockerfile
in the directory where you created the pods-hook.sh
file:
FROM flant/shell-operator:latest
ADD pods-hook.sh /hooks
Build an image (change image tag according to your Docker registry):
docker build -t "registry.mycompany.com/shell-operator:monitor-pods" .
Push image to the Docker registry accessible by the Kubernetes cluster:
docker push registry.mycompany.com/shell-operator:monitor-pods
We need to watch for Pods in all Namespaces. That means that we need specific RBAC definitions for shell-operator:
kubectl create namespace example-monitor-pods
kubectl create serviceaccount monitor-pods-acc --namespace example-monitor-pods
kubectl create clusterrole monitor-pods --verb=get,watch,list --resource=pods
kubectl create clusterrolebinding monitor-pods --clusterrole=monitor-pods --serviceaccount=example-monitor-pods:monitor-pods-acc
Shell-operator can be deployed as a Pod. Put this manifest into the shell-operator-pod.yaml
file:
apiVersion: v1
kind: Pod
metadata:
name: shell-operator
spec:
containers:
- name: shell-operator
image: registry.mycompany.com/shell-operator:monitor-pods
imagePullPolicy: Always
serviceAccountName: monitor-pods-acc
Start shell-operator by applying a shell-operator-pod.yaml
file:
kubectl -n example-monitor-pods apply -f shell-operator-pod.yaml
Let's deploy a kubernetes-dashboard to trigger kubernetes
binding defined in our hook:
kubectl apply -f https://raw.githubusercontent.com/kubernetes/dashboard/master/aio/deploy/recommended.yaml
Now run kubectl -n example-monitor-pods logs po/shell-operator
and observe that the hook will print dashboard pod name:
...
INFO[0027] queue task HookRun:main operator.component=handleEvents queue=main
INFO[0030] Execute hook binding=kubernetes hook=pods-hook.sh operator.component=taskRunner queue=main task=HookRun
INFO[0030] Pod 'kubernetes-dashboard-775dd7f59c-hr7kj' added binding=kubernetes hook=pods-hook.sh output=stdout queue=main task=HookRun
INFO[0030] Hook executed successfully binding=kubernetes hook=pods-hook.sh operator.component=taskRunner queue=main task=HookRun
...
Note: hook output is logged with output=stdout label.
To clean up a cluster, delete namespace and RBAC objects:
kubectl delete ns example-monitor-pods
kubectl delete clusterrole monitor-pods
kubectl delete clusterrolebinding monitor-pods
This example is also available in /examples: monitor-pods.
Every hook should respond with JSON or YAML configuration of bindings when executed with --config
flag.
This binding defines a subset of Kubernetes objects that shell-operator will monitor and a jq expression to filter their properties. Read more about onKubernetesEvent
bindings here.
Example of YAML output from hook --config
:
configVersion: v1
kubernetes:
- name: execute_on_changes_of_namespace_labels
kind: Namespace
executeHookOnEvent: ["Modified"]
jqFilter: ".metadata.labels"
Note: it is possible to watch Custom Defined Resources, just use proper values for
apiVersion
andkind
fields.
Note: return configuration as JSON is also possible as JSON is a subset of YAML.
This binding has only one parameter: order of execution. Hooks are loaded at the start and then hooks with onStartup binding are executed in the order defined by parameter. Read more about onStartup
bindings here.
Example hook --config
:
configVersion: v1
onStartup: 10
This binding is used to execute hooks periodically. A schedule can be defined with a granularity of seconds. Read more about schedule
bindings here.
Example hook --config
with 2 schedules:
configVersion: v1
schedule:
- name: "every 10 min"
crontab: "*/10 * * * *"
allowFailure: true
- name: "Every Monday at 8:05"
crontab: "5 8 * * 1"
queue: mondays
Shell-operator provides a /metrics
endpoint. More on this in METRICS document.
More examples of how you can use shell-operator are available in the examples directory.
Prominent shell-operator use cases include:
Please find out & share more examples in Show & tell discussions.
Shell-operator has been presented during KubeCon + CloudNativeCon Europe 2020 Virtual (Aug'20). Here is the talk called "Go? Bash! Meet the shell-operator":
Official publications on shell-operator:
Other languages:
Please feel free to reach developers/maintainers and users via GitHub Discussions for any questions regarding shell-operator.
You're also welcome to follow @flant_com to stay informed about all our Open Source initiatives.
Apache License 2.0, see LICENSE.
前一段时间发了一篇 Shell Operator 的介绍,搓例子的时候,就想起个需求,我想把 Pod 所在节点上的特定标签复制给 Pod,例如机架、虚拟机节点所在的物理机等,都可以用标签的形式来表达,并可以用这些标签进行选择和统计等。 Shell Operator 的基本开发流程是: 编写配置文件,确定触发条件。 开发操作脚本,打包容器镜像。 确定操作权限,设置 RBAC。 运行和测试。 官方的例
shell 脚本的错误:代码如下: #!/bin/bash alive=`ps aux|grep youdun|grep -v grep|wc -l` if [ $alive -eq 0 ] then nohup /usr/local/php-7.1/bin/php /**/**.php >> /**/**.log 2>&1 & fi 开始的时候我时用的 if [ $alive -eq 0 ]
当$f4 为 <NE> 这种类型的字符串时 if [ -z $f4 ] 或 if [ $f4 != "<NE>" ] 会报错: Binary Operator Expected 解决方法:将$f4用双引号括起来 if [ -z "$f4" ] 或 if [ "$f4" != "<NE>" ] 原因:$f4的内容中的 "<" 会被理解为小于号,因此解析器会期待后面是数字,因此会报错.
shell脚本报错:"[: =: unary operator expected" 在匹配字符串相等时,我用了类似这样的语句: if [ $STATUS == "OK" ]; then echo "OK" fi 在运行时出现了 [: =: unary operator expected 的错误,就一直找不到原因,尝试了删除等号两侧的空格和括号里的空格都不管用,最后baidu了一下,才找到原因。把语
shell脚本报错:"[: =: unary operator expected" 在匹配字符串相等时,我用了类似这样的语句: if [ $STATUS == "OK" ]; then echo "OK" fi 在运行时出现了 [: =: unary operator expected 的错误,就一直找不到原因,尝试了删除等号两侧的空格和括号里的空格都不管用,最后baid
今天写了一个shell脚本,类似于下面这样 if [ $userId = “” ]; then echo "0” else echo "1" fi 出现报错unary operator expected 报错的原因是:如果变量userId的值为空,那么就if语句就变成了if [ ="" ],这不是一个合法的条件。为了避免出现这种情况,我们必须给变量加上引号if [ “$
unexpected operator:意外的运算符 出现这个问题,可以先使用以下步骤排查问题: (1) 检查语法是否正确+编码是否正确(使用utf8编码,不能是utf8-bom编码); (2)确认shell脚本中使用的是#!/bin/bash还是#!/bin/sh,如果是#!/bin/sh修改替换为#!/bin/bash试试
Shell排序是一种高效的排序算法,基于插入排序算法。 该算法避免了大的移位,如插入排序的情况,如果较小的值是最右边的并且必须移动到最左边。 该算法对广泛传播的元素使用插入排序,首先对它们进行排序,然后对间距较小的元素进行排序。 该间距称为interval 。 此间隔基于Knuth的公式计算为 - Knuth的公式 h = h * 3 + 1 where − h is interval wi
shell 模块提供了集成其他桌面客户端的关联功能. 在用户默认浏览器中打开URL的示例: var shell = require('shell'); shell.openExternal('https://github.com'); Methods shell 模块包含以下函数: shell.showItemInFolder(fullPath) fullPath String 打开文件所在文
使用默认应用程序管理文件和 url。 进程: Main, Renderer shell 模块提供与桌面集成相关的功能。 在用户的默认浏览器中打开 URL 的示例: const { shell } = require('electron') shell.openExternal('https://github.com') Manage files and URLs using their defau
Erlang shell用于测试表达式。 因此,在实际测试应用程序本身之前,可以非常轻松地在shell中进行测试。 以下示例展示了如何在shell中使用加法表达式。 这里需要注意的是表达式需要以点(。)分隔符结束。 执行命令后,shell会打印另一个提示符,这次是命令编号2(因为每次输入新命令时命令编号都会增加)。 以下函数是Erlang shell中最常用的函数。 b() - 打印当前变量绑定。
可能您早已能够熟练的使用 GUI(图形用户界面),例如您可以使用鼠标双击一个图标,来打开或者执行它。 我们来看这个过程: 您使用鼠标定位桌面上的一个程序图标,按下左键两次。系统读取鼠标指针的位置,并且判断该位置下图标的涵义,根据预设的双击动作,运行程序或者打开文件。 这一套 GUI 系统,便是一种 Shell,它的作用是实现人机交互。如果我们不能够控制电脑,那么电脑还不如电视机好玩,不是么?电视机
使用默认应用程序管理文件和 url。 Process: Main, Renderer (只能在非沙盒下使用) shell 模块提供与桌面集成相关的功能。 在用户的默认浏览器中打开 URL 的示例: const { shell } = require('electron') shell.openExternal('https://github.com') 注意: 虽然 shell 模块可以在渲染