引入 shell-operator:為 Kubernetes 建立操作符變得更加容易

我們的部落格上已經有文章討論過 Kubernetes 中的算子能力 如何 自己寫一個簡單的運算符。 這次我們想向您介紹我們的開源解決方案,它將運算符的創建提升到超級簡單的水平 - 請查看 外殼操作符!

為什麼呢?

shell-operator 的想法非常簡單:訂閱來自 Kubernetes 物件的事件,當收到這些事件時,啟動外部程序,為其提供有關事件的資訊:

引入 shell-operator:為 Kubernetes 建立操作符變得更加容易

當在叢集運行期間,開始出現我們真正希望以正確的方式自動化的小任務時,就出現了對它的需求。 所有這些小任務都是使用簡單的 bash 腳本解決的,儘管如您所知,最好在 Golang 中編寫運算符。 顯然,為每個這樣的小任務投資運營商的全面開發是無效的。

15分鐘內操作員

讓我們來看一個範例,了解 Kubernetes 叢集中可以實現哪些自動化以及 shell 操作員如何提供協助。 範例如下:複製金鑰以存取 docker 註冊表。

使用私有註冊表中的映像的 Pod 必須在其清單中包含指向包含用於存取註冊表的資料的金鑰的連結。 在建立 pod 之前,必須在每個命名空間中建立此金鑰。 這可以手動完成,但是如果我們設定動態環境,那麼一個應用程式的命名空間就會變得很多。 如果也沒有 2-3 個應用程式…秘密的數量就會變得非常大。 關於秘密的另一件事是:我想不時更改存取註冊表的金鑰。 最終, 手動操作 作為解決方案 完全無效 ——我們需要自動化秘密的創建和更新。

簡單的自動化

讓我們編寫一個每 N 秒執行一次的 shell 腳本,並檢查名稱空間是否有秘密,如果不存在秘密,則建立它。 這個解決方案的優點是它看起來像 cron 中的 shell 腳本——這是一種經典且每個人都可以理解的方法。 缺點是,在其啟動之間的時間間隔內,可以創建一個新的命名空間,並且在一段時間內它將保持沒有秘密,這將導致啟動 Pod 時出錯。

使用 shell 操作符實現自動化

為了讓我們的腳本正常工作,經典的 cron 啟動需要替換為新增命名空間時的啟動:在這種情況下,您可以在使用它之前建立一個秘密。 讓我們看看如何使用 shell-operator 來實現這一點。

首先,我們來看看腳本。 shell 操作符術語中的腳本稱為鉤子。 帶有標誌運行時的每個鉤子 --config 通知 shell 操作符它的綁定,即應該啟動哪些事件。 在我們的例子中,我們將使用 onKubernetesEvent:

#!/bin/bash
if [[ $1 == "--config" ]] ; then
cat <<EOF
{
"onKubernetesEvent": [
  { "kind": "namespace",
    "event":["add"]
  }
]}
EOF
fi

這裡描述了我們有興趣添加事件(add) 類型的對象 namespace.

現在您需要新增事件發生時將執行的程式碼:

#!/bin/bash
if [[ $1 == "--config" ]] ; then
  # конфигурация
cat <<EOF
{
"onKubernetesEvent": [
{ "kind": "namespace",
  "event":["add"]
}
]}
EOF
else
  # реакция:
  # узнать, какой namespace появился
  createdNamespace=$(jq -r '.[0].resourceName' $BINDING_CONTEXT_PATH)
  # создать в нём нужный секрет
  kubectl create -n ${createdNamespace} -f - <<EOF
apiVersion: v1
kind: Secret
metadata:
  ...
data:
  ...
EOF
fi

偉大的! 結果是一個小而漂亮的腳本。 要「恢復」它,還需要兩個步驟:準備映像並在叢集中啟動它。

準備帶有鉤子的圖像

如果你看一下腳本,你可以看到使用了這些命令 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

在叢集中運行

讓我們再次查看該鉤子,這次記下它在叢集中執行的操作以及物件:

  1. 訂閱命名空間創建事件;
  2. 在啟動它的命名空間以外的命名空間中建立一個秘密。

事實證明,將啟動我們的映像檔的 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

您可以將組裝的映像作為簡單的部署啟動:

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-operator:為 Kubernetes 建立操作符變得更加容易

因此, 一個簡單的 shell 腳本變成了 Kubernetes 的真正操作符 並作為集群的一部分工作。 而這一切都不需要在 Golang 中開發操作符的複雜過程:

引入 shell-operator:為 Kubernetes 建立操作符變得更加容易

關於這個問題還有另一個例子...引入 shell-operator:為 Kubernetes 建立操作符變得更加容易

我們將在以下出版物之一中更詳細地揭示其含義。

過濾

追蹤物體固然很好,但通常需要對物體做出反應 改變一些物件屬性,例如,更改 Deployment 中的副本數量或更改物件標籤。

當事件到達時,shell 操作符會收到物件的 JSON 清單。 我們可以在此 JSON 中選擇我們感興趣的屬性並運行掛鉤 當他們改變時。 有一個字段可用於此目的 jqFilter,您需要在其中指定將套用於 JSON 清單的 jq 表達式。

例如,要回應 Deployment 物件標籤的更改,您需要過濾該欄位 labels 場外 metadata。 配置將是這樣的:

cat <<EOF
{
"onKubernetesEvent": [
{ "kind": "deployment",
  "event":["update"],
  "jqFilter": ".metadata.labels"
}
]}
EOF

此 jqFilter 表達式將 Deployment 的長 JSON 清單轉換為帶有標籤的短 JSON:

引入 shell-operator:為 Kubernetes 建立操作符變得更加容易

shell-operator 只有當這個簡短的 JSON 變更時才會執行鉤子,並且其他屬性的變更將被忽略。

鉤子啟動上下文

掛鉤配置可讓您為事件指定多個選項 - 例如,來自 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":"* */10 * * * *"
}, {"name":"on Mondays at 12:10",
"crontab": "* 10 12 * * 1"
]}

一個小題外話:是的,shell-operator 支持 運行 crontab 風格的腳本。 更多詳細資訊可以參見 文件.

為了區分啟動鉤子的原因,shell 操作員建立一個臨時檔案並將其路徑通過變數傳遞給鉤子 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"
 }
]

欄位的內容可以從它們的名稱中了解到,更詳細的內容可以閱讀 文件。 從欄位取得資源名稱的範例 resourceName 使用 jq 已經在複製秘密的鉤子中顯示:

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

其他欄位也可以用類似的方法取得。

接下來是什麼?

在專案儲存庫中, /範例目錄,有一些可以在叢集上運行的掛鉤範例。 當你編寫自己的鉤子時,你可以使用它們作為基礎。

支援使用 Prometheus 收集指標 - 可用指標在 參考資料 部分中有描述 指標.

正如您可能猜到的,shell 運算子是用 Go 編寫的,並根據開源許可證 (Apache 2.0) 分發。 我們將感謝任何發展援助 GitHub 上的項目:還有星星、問題和拉取請求。

揭開秘密的面紗,我們也會通知您 shell-operator 是 我們系統的一部分,可以讓 Kubernetes 叢集中安裝的附加元件保持最新狀態並執行各種自動操作。 了解有關該系統的更多信息 告訴 確切地說,將於週一在聖彼得堡舉行的 HighLoad++ 2019 大會上 - 我們很快就會發布本報告的影片和文字記錄。

我們計劃開放系統的其餘部分:插件操作符以及我們的鉤子和模組集合。 順便說一下,addon-operator 已經是 在 github 上可用,但它的文檔仍在路上。 模組集合計劃於夏季發布。

敬請關注!

聚苯乙烯

另請閱讀我們的博客:

來源: www.habr.com

添加評論