歡迎光臨
每天分享高質量文章

Shell-operator:用於簡化Kubernetes operator的建立

我們很高興在此介紹我們新的開源方案,它能將Kubernetes中Operator的開發提升到一個全新的更簡單的水平。它可以讓你在15分鐘內將你的小指令碼變成完全成熟的Operator而吸引你。歡迎shell-operator[1]!
標的

 

shell-operator的思路很簡單,它訂閱來自Kubernetes物件的事件,併在事件發生後執行外部程式,為其提供有關事件的資訊:
在我們執行Kubernetes叢集期間,很多小任務開始顯現。在Flant[2],我們迫切想要以正確的方式來自動化它們,因此我們覺得需要更智慧的解決方案。通常你可以使用基本的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:
  1. #!/bin/bash
  2. if [[ $1 == "--config" ]] ; then
  3. cat <<EOF
  4. {
  5. "onKubernetesEvent": [
  6. {
  7. "kind": "namespace",
  8. "event": [ "add" ]
  9. }
  10. ]
  11. }
  12. EOF
  13. fi
在這裡,我們定義我們關註的 namespace型別的新增( add)物件事件。
現在我們需要新增當事件發生時需要執行的程式碼:
  1. #!/bin/bash
  2. if [[ $1 == "--config" ]] ; then
  3. # configuration
  4. cat <<EOF
  5. {
  6. "onKubernetesEvent": [
  7. {
  8. "kind": "namespace",
  9. "event": [ "add" ]
  10. }
  11. ]
  12. }
  13. EOF
  14. else
  15. # response:
  16. # find out what namespace has emerged
  17. createdNamespace=$(jq -r '.[0].resourceName' $BINDING_CONTEXT_PATH)
  18. # create the appropriate secret in it
  19. kubectl create -n ${createdNamespace} -f - <<EOF
  20. apiVersion: v1
  21. kind: Secret
  22. metadata:
  23. ...
  24. data:
  25. ...
  26. EOF
  27. fi
真棒!我們現在已有一個簡潔且漂亮的指令碼,想讓它能真正發揮作用,我們需要準備一個映象並將其跑在叢集中。
使用鉤子製作我們的映象
你可以很輕易觀察到我們在指令碼裡面使用了 kubectl和 jq命令。這意味著映象中需要包含鉤子,shell-operator二進位制檔案(它將監視事件並執行這個鉤子),以及鉤子需要用到的命令( kubectl和 jq)。hub.docker.com上已提供了包含shell-operator,kubectl和jq的即用型映象。現在是時候使用 Dockerfile來新增一個鉤子:
  1. $ cat Dockerfile
  2. FROM flant/shell-operator:v1.0.0-beta.1-alpine3.9
  3. ADD namespace-hook.sh /hooks
  4. $ docker build -t registry.example.com/my-operator:v1 .
  5. $ docker push registry.example.com/my-operator:v1
在叢集中執行
我們再來看看這個鉤子,這次我們將關註具體的操作以及它在叢集中執行的物件:
  1. 它訂閱了 namespace的建立事件;

  2. 它在不與它所執行的名稱空間相同的空間建立一個secret。

這裡我們會發現執行這個映象的Pod需要有執行這些操作的許可權。你可以授權給一個 ServiceAccount。由於我們是關註整個叢集中的物件,那麼許可權需要使用 ClusterRole和 ClusterRoleBinding形式來配置。
YAML最終配置描述如下:
  1. ---
  2. apiVersion: v1
  3. kind: ServiceAccount
  4. metadata:
  5. name: monitor-namespaces-acc
  6. ---
  7. apiVersion: rbac.authorization.k8s.io/v1beta1
  8. kind: ClusterRole
  9. metadata:
  10. name: monitor-namespaces
  11. rules:
  12. - apiGroups: [""]
  13. resources: ["namespaces"]
  14. verbs: ["get", "watch", "list"]
  15. - apiGroups: [""]
  16. resources: ["secrets"]
  17. verbs: ["get", "list", "create", "patch"]
  18. ---
  19. apiVersion: rbac.authorization.k8s.io/v1beta1
  20. kind: ClusterRoleBinding
  21. metadata:
  22. name: monitor-namespaces
  23. roleRef:
  24. apiGroup: rbac.authorization.k8s.io
  25. kind: ClusterRole
  26. name: monitor-namespaces
  27. subjects:
  28. - kind: ServiceAccount
  29. name: monitor-namespaces-acc
  30. namespace: example-monitor-namespaces
你可以將建立的映象部署為一個簡單的Deployment:
  1. apiVersion: extensions/v1beta1
  2. kind: Deployment
  3. metadata:
  4. name: my-operator
  5. spec:
  6. template:
  7. spec:
  8. containers:
  9. - name: my-operator
  10. image: registry.example.com/my-operator:v1
  11. serviceAccountName: monitor-namespaces-acc
為方便起見,我們將建立一個單獨的名稱空間,用於執行shell-operator並應用建立的部署清單:
  1. $ kubectl create ns example-monitor-namespaces
  2. $ kubectl -n example-monitor-namespaces apply -f rbac.yaml
  3. $ kubectl -n example-monitor-namespaces apply -f deployment.yaml
好了,shell-operator啟動,它將訂閱名稱空間建立事件併在需要時執行鉤子。
這樣一個簡單的shell指令碼就變成了Kubernetes中一個真正的Operator,併成為叢集的一部分。這樣做的好處是我們避免了使用Golang來開發Operator的複雜過程:
過濾

 

關於物件的觀察很棒,但我們通常需要響應物件中某些屬性的更改,例如,增加/減少部署中的副本數量或物件對標簽中的任何更新。
當一個事件發生時,shell-operator接收該物件的JSON清單。在此JSON中,你可以選擇要監視的屬性,並僅在更改時啟動鉤子。jqFilter欄位可以幫助你完成這點:你應該輸入將應用於JSON清單的jq運算式。
舉個例子,要響應Deployment物件標簽中的修改,你必須從 metadata欄位中提取 labels欄位。這個例子中你將需要如下的配置:
  1. cat <<EOF
  2. {
  3. "onKubernetesEvent": [
  4. {
  5. "kind": "deployment",
  6. "event":["update"],
  7. "jqFilter": ".metadata.labels"
  8. }
  9. ]
  10. }
  11. EOF
jqFilter運算式將Deployment的長長的JSON清單轉換成帶有標簽的簡短的JSON:
shell-operator將只會在這個簡短的JSON發生變化時執行鉤子。其它屬性的變更將會被忽略。
鉤子的執行背景關係

 

鉤子的配置允許你指定幾種事件。例如你可以定義兩個Kubernetes事件和兩個計劃排程:
  1. {
  2. "onKubernetesEvent": [
  3. {
  4. "name": "OnCreatePod",
  5. "kind": "pod",
  6. "event": [
  7. "add"
  8. ]
  9. },
  10. {
  11. "name": "OnModifiedNamespace",
  12. "kind": "namespace",
  13. "event": [
  14. "update"
  15. ],
  16. "jqFilter": ".metadata.labels"
  17. }
  18. ],
  19. "schedule": [
  20. {
  21. "name": "every 10 min",
  22. "crontab": "0 */10 * * * *"
  23. },
  24. {
  25. "name": "on Mondays at 12:10",
  26. "crontab": "0 10 12 * * 1"
  27. }
  28. ]
  29. }
註意:shell-operator支援以crontab樣式執行指令碼!你可以在檔案[3]中找到額外的資訊。
為了區分鉤子執行的原因,shell-operator會建立一個臨時檔案並將其路徑儲存到 BINDING_CONTEXT_TYPE變數中。此檔案包含了執行鉤子的原因的JSON描述。例如,每隔10分鐘將會使用以下內容啟動鉤子:
  1. [{ "binding": "every 10 min" }]
在週一的話它將以以下內容啟動:
  1. [{ "binding": "every 10 min" }, { "binding": "on Mondays at 12:10" }]
同時將有 onKubernetesEvent呼叫的更詳細的JSON,因為它包含了物件的描述:
  1. [
  2. {
  3. "binding": "onCreatePod",
  4. "resourceEvent": "add",
  5. "resourceKind": "pod",
  6. "resourceName": "foo",
  7. "resourceNamespace": "bar"
  8. }
  9. ]
你能透過名稱來全面瞭解欄位的內容(更多詳細資訊可在檔案[4]中找到)。使用jq從 resourceName獲取資源名稱的示例已經在複製secret的鉤子中展示:
  1. jq -r '.[0].resourceName' $BINDING_CONTEXT_PATH
你可以透過類似的方式去獲取到其它欄位。
下一步是什麼呢?

 

在該專案倉庫中的/examples directory[5]目錄裡包含了一些可以直接在叢集中使用的示例。你可以將它們用作你開發自己的鉤子的基礎。
Shell-operator同樣支援使用Prometheus來收集指標。METRICS[6]章節已描述了這些可用的指標。
你能輕易想到,shell-operator是使用Go編寫的,並根據開源許可證(Apache 2.0)的條款進行分發。我們非常感謝任何關於開發在GitHub上的這個專案[7]的幫助。你可以透過給我們點Star,反饋問題或者是PR來支援我們!
相關連結:
  1. https://github.com/flant/shell-operator

  2. https://flant.com/

  3. https://github.com/flant/shell-operator/blob/master/HOOKS.md

  4. https://github.com/flant/shell-operator/blob/master/HOOKS.md#binding-context

  5. https://github.com/flant/shell-operator/tree/master/examples

  6. https://github.com/flant/shell-operator/blob/master/METRICS.md

  7. https://github.com/flant/shell-operator

原文連結:https://medium.com/flant-com/kubernetes-shell-operator-76c596b42f23


    已同步到看一看
    贊(0)

    分享創造快樂