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

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)

    分享創造快樂