<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          使用 shell-operator 實現(xiàn) Operator

          共 1978字,需瀏覽 4分鐘

           ·

          2020-09-12 21:08

          在本文我們將介紹簡化 Kubernetes Operator 創(chuàng)建的方法,并展示如何使用 shell-operator 輕松實現(xiàn)自己的 Operator。本文基于我們在 KubeCon Europe 2020上的最新演講,這是此演講的完整視頻[1]

          Kubernetes API 和控制器

          我們可以將 Kubernetes API 看成包含每種對象文件夾的文件服務(wù)器,這些資源對象通過服務(wù)器上的 YAML 文件來表示。APIServer 有一個基本的 HTTP API,使我們可以對這些對象執(zhí)行三件事。我們可以:

          • 根據(jù)資源類型和名稱獲取資源
          • 更改資源
          • watch 資源

          換句話說,我們可以將 Kubernetes 看作基本上是具有三種通用方法的YAML 文件服務(wù)器(當(dāng)然還有其他方法,我們現(xiàn)在可以先忽略它們)。

          但是,服務(wù)端本身只能存儲信息,為了使其正常工作,我們需要一個控制器 - Kubernetes 中第二重要的基礎(chǔ)工具。

          通常,有兩種類型的控制器,第一種類型從 Kubernetes 讀取信息,使用某種邏輯對其進行處理,然后將其寫回到 Kubernetes。第二種類型也從 Kubernetes 讀取數(shù)據(jù),但是與第一種類型不同,它改變了某些外部資源的狀態(tài)。

          我們先看看用戶創(chuàng)建 Kubernetes Deployment 時會發(fā)生什么:

          • Deployment 控制器(kube-controller-manager 的一部分)獲取對應(yīng)的資源信息并創(chuàng)建一個 ReplicaSet。
          • 然后,ReplicaSet 使用對應(yīng)的信息來創(chuàng)建兩個 Pod 副本,但是還沒有調(diào)度這些 Pod。
          • 然后才是調(diào)度程序調(diào)度 Pod 并將調(diào)度結(jié)果的節(jié)點信息更新回YAML。
          • 最后 Kubelets watch 到 Pod 數(shù)據(jù)后去啟動對應(yīng)的容器。

          然后以相反的順序重復(fù)所有操作:kubelet 檢查容器,計算容器的狀態(tài),然后將其發(fā)送回去。ReplicaSet 控制器 接收它并更新副本集的狀態(tài)。Deployment 控制器也發(fā)生了同樣的事情,用戶最終獲得了當(dāng)前狀態(tài)。

          Shell-operator

          事實上 Kubernetes 完全就是各種控制器一起運行實現(xiàn)的(Operator 也是控制器)。為了能夠輕松創(chuàng)建一個控制器呢,我們引入了一個工具 shell-operator[2],它可以讓系統(tǒng)管理員使用他們習(xí)慣的方法來創(chuàng)建 Operator。

          簡單的示例:復(fù)制 Secrets

          讓我們看一個簡單的例子,假設(shè)我們有一個 Kubernetes 集群。其中有一個默認(rèn)的名稱空間,其中包含一些 Secret(mysecret)資源對象。此外,集群中還有其他名稱空間。這些名稱空間中有幾個具有額外的特定標(biāo)簽。我們的目標(biāo)是將 Secret 復(fù)制到帶有此標(biāo)簽的名稱空間中。

          新的命名空間可以出現(xiàn)在集群中,并且其中一些可能帶有此標(biāo)簽,這一事實使任務(wù)變得復(fù)雜。另一方面,如果標(biāo)簽被刪除,則 Secret 也必須被刪除。Secret 本身也可以更改,在這種情況下,新的 Secret 必須傳播到所有帶標(biāo)簽的命名空間中去。如果 Secret 在某個命名空間中被意外刪除,則 Operator 必須立即將其還原。

          現(xiàn)在我們已經(jīng)了解了需要實現(xiàn)的需求,接下來我們來使用 shell-oprerator 來真正實現(xiàn)它。

          運行原理

          與其他 Kubernetes 工作負(fù)載類似,shell-operator 部署在 Pod中。在 Pod 中有一個 /hooks 的一個子目錄,其中存儲了可執(zhí)行文件,它們可以用 Bash、Python、Ruby等編寫的,我們稱這些可執(zhí)行文件為hooks

          Shell-opeator 訂閱 Kubernetes?事件并執(zhí)行這些鉤子來響應(yīng)我們感興趣的事件。

          但是,shell-operator 如何知道何時執(zhí)行鉤子呢?事實上每個鉤子都有兩個階段。在啟動過程中,shell-operator 使用-config參數(shù)運行每個鉤子。一旦配置階段結(jié)束,鉤子將以“正常”方式執(zhí)行:響應(yīng)附加給它們的事件。在這種情況下,鉤子會獲取綁定上下文

          使用 Bash 實現(xiàn)

          現(xiàn)在,如果我們使用 Bash,我們需要實現(xiàn)兩個函數(shù)(強烈建議使用shell_lib[3] 庫,因為它大大簡化了 Bash 中鉤子的編寫):

          • 第一個用于配置階段,并且應(yīng)該輸出綁定上下文;
          • 第二個包含鉤子的核心邏輯。
          #!/bin/bash
          source?/shell_lib.sh
          function?__config__()?{
          ??cat?<????configVersion:?v1
          ????#?BINDING?CONFIGURATION
          EOF
          }
          function?__main__()?{
          ??#THE?LOGIC
          }
          hook::run?"$@"

          下一步是確定我們感興趣的對象,在我們的示例中,我們需要跟蹤:

          • 變更的 Secret 對象;
          • 集群中的所有命名空間,以查看帶有標(biāo)簽的命名空間;
          • 目標(biāo) Secret,以驗證它們是否已和源 Secret 同步了。

          訂閱源 Secret

          綁定配置非常簡單,這里我們的mysecretdefault 命名空間中的 Secrets 感興趣。

          function?__config__()?{
          ??cat?<????configVersion:?v1
          ????kubernetes:
          ????-?name:?src_secret
          ??????apiVersion:?v1
          ??????kind:?Secret
          ??????nameSelector:
          ????????matchNames:
          ????????-?mysecret
          ??????namespace:
          ????????nameSelector:
          ??????????matchNames:?["default"]
          ??????group:?main
          EOF

          結(jié)果會根據(jù)源 Secret(src_secret)中的更新執(zhí)行該鉤子,它將獲得以下綁定上下文:

          可以看到該綁定上下文具有其名稱和完整的對象信息。

          處理命名空間

          接下來我們需要訂閱命名空間,這是所需的綁定配置:

          -?name:?namespaces
          ??group:?main
          ??apiVersion:?v1
          ??kind:?Namespace
          ??jqFilter:?|
          ????{
          ??????namespace:?.metadata.name,
          ??????hasLabel:?(
          ???????.metadata.labels?//?{}?|???
          ?????????contains({"secret":?"yes"})
          ??????)
          ????}
          ??group:?main
          ??keepFullObjectsInMemory:?false

          可以看到的在配置中有一個新的字段,叫做 jqFilter。顧名思義,jqFilter 就是過濾掉所有不必要的信息,并提供一個新的 JSON 對象,其中包含我們感興趣的字段。以這種方式配置的鉤子會收到以下綁定上下文:

          它由集群中每個命名空間的 filterResults 數(shù)組組成,布爾變量hasLabel顯示相關(guān)的命名空間是否具有mysecret標(biāo)簽,keepFullObjectsInMemory: false選擇器表示將刪除內(nèi)存中的完整對象。

          追蹤目的 Secret

          我們訂閱所有具有 managed-secret: "yes"注釋的 Secrets?(這些就是是我們的dst_secrets):

          -?name:?dst_secrets
          ??apiVersion:?v1
          ??kind:?Secret
          ??labelSelector:
          ????matchLabels:
          ??????managed-secret:?"yes"
          ??jqFilter:?|
          ????{
          ??????"namespace":
          ????????.metadata.namespace,
          ??????"resourceVersion":
          ????????.metadata.annotations.resourceVersion
          ????}
          ??group:?main
          ??keepFullObjectsInMemory:?false

          在這種情況下,jqFilter過濾掉除命名空間名稱和resourceVersion參數(shù)之外的所有信息。創(chuàng)建此目標(biāo) Secret 時,我們將該參數(shù)傳遞給注釋。

          以這種方式配置的鉤子在執(zhí)行時將獲得上述三個綁定上下文,你可以將它們視為集群的某種快照

          我們可以使用所有這些信息來設(shè)計一種最基本的算法,它遍歷所有命名空間,如果當(dāng)前命名空間 hasLabeltrue,則進行迭代:

          • 比較源和目標(biāo) Secret
          • 如果它們相同,則什么都不做
          • 如果它們不同 - 執(zhí)行 kubectl replace 或者 create 操作。

          如果當(dāng)前命名空間 hasLabelfalse,則:

          • 確保命名空間中沒有 Secret
          • 如果目標(biāo) Secret 存在 - 執(zhí)行kubectl delete
          • 如果目標(biāo) Secret 不存在,則不執(zhí)行任何操作。

          在我們的示例倉儲庫中[4],可以找到上述算法的完整 Bash 實現(xiàn)。

          35 行 YAML 和相同數(shù)量的 Bash 組成了一個簡單的 Kubernetes 控制器!Shell-operator 的工作是將它們?nèi)拷壎ㄔ谝黄稹?/p>

          顯然,使用 Shell-operator 并不是只能復(fù)制 Secrets,我們還會用更多示例來了解它的用法。

          示例1:更新 ConfigMap

          比如現(xiàn)在我們有一個具有三個 Pod 的 Deployment,這些 Pods 使用ConfigMap 來存儲一些配置,當(dāng)這些 Pod 啟動時,ConfigMap 處于某種狀態(tài)(我們將其稱為版本1:v.1),我們所有的 Pod 都具有相同的 v.1 版本的 ConfigMap。

          現(xiàn)在,假設(shè) ConfigMap 更改為另一個版本 v.2,在這種情況下,我們的Pod 仍將使用 ConfigMap 的早期版本 v.1。

          在這種情況下我們通常怎么做呢?是的,我們可以在 Pod 的模板中添加一些內(nèi)容。因此,讓我們將 checksum 注解添加到 Deployment 定義的模板部分:

          現(xiàn)在,我們所有的 Pod 都有 checksum,并且與 Deployment 的 checksum 相同。接下來,我們應(yīng)該更新注釋來響應(yīng) ConfigMap 的更改。這就是 shell-operator 可能派上用場的時候,我們只需要編寫一個鉤子即可訂閱 ConfigMap 并更新 checksum

          當(dāng)用戶修改 ConfigMap 時,shell-operator 會 watch 到變更并更新 checksum。然后,Kubernetes 會殺死 Pod,創(chuàng)建一個新 Pod,等到準(zhǔn)備就緒后再進行下一個 Pod。因此,我們的 Deployment 可以完美同步并與更新的 ConfigMap 一起運行。

          示例2:使用 CRD

          我們知道 Kubernetes 允許我們創(chuàng)建自定義類型的對象。例如,我們可以創(chuàng)建一個名為 MysqlDatabase 的資源,假設(shè)這種類型只有兩個元數(shù)據(jù)參數(shù):namenamespace

          apiVersion:?example.com/v1alpha1
          kind:?MysqlDatabase
          metadata:
          ??name:?foo
          ??namespace:?bar

          因此,我們可以在 Kubernetes 集群中創(chuàng)建 MySQL 數(shù)據(jù)庫,在這種情況下,可以使用 shell-operator 來 watch MysqlDatabase這類資源,將它們連接到 MySQL 數(shù)據(jù)庫服務(wù)器,并同步所需狀態(tài)和 watch 到的狀態(tài)。

          示例3:監(jiān)控集群網(wǎng)絡(luò)

          如您所知,ping 是監(jiān)視網(wǎng)絡(luò)的最簡單方法,當(dāng)然我們也可以使用 shell-operator 來實現(xiàn)。

          首先,我們需要訂閱節(jié)點,shell-operator 需要每個節(jié)點的名稱和 IP 地址,以循環(huán)瀏覽節(jié)點列表并 ping 它們中的每一個。

          configVersion:?v1
          kubernetes:
          -?name:?nodes
          ??apiVersion:?v1
          ??kind:?Node
          ??jqFilter:?|
          ????{
          ??????name:?.metadata.name,
          ??????ip:?(
          ???????.status.addresses[]?|???
          ????????select(.type?==?"InternalIP")?|
          ????????.address
          ??????)
          ????}
          ??group:?main
          ??keepFullObjectsInMemory:?false
          ??executeHookOnEvent:?[]
          schedule:
          -?name:?every_minute
          ??group:?main
          ??crontab:?"*?*?*?*?*"

          executeHookOnEvent: []參數(shù)可防止響應(yīng)任何事件而調(diào)用該鉤子(更新、添加或刪除節(jié)點時將不執(zhí)行掛鉤)。但是,它將根據(jù) schedule 字段每分鐘運行一次(并更新節(jié)點列表)。

          我們?nèi)绾未_定丟包之類的問題?讓我們看一下如下所示代碼:

          function?__main__()?{
          ??for?i?in?$(seq?0?"$(context::jq?-r?'(.snapshots.nodes?|?length)?-?1')");?do
          ????node_name="$(context::jq?-r?'.snapshots.nodes['"$i"'].filterResult.name')"
          ????node_ip="$(context::jq?-r?'.snapshots.nodes['"$i"'].filterResult.ip')"
          ????packets_lost=0
          ????if?!?ping?-c?1?"$node_ip"?-t?1?;?then
          ??????packets_lost=1
          ????fi
          ????cat?>>?"$METRICS_PATH"?<??????{
          ????????"name":?"node_packets_lost",
          ????????"add":?$packets_lost,
          ????????"labels":?{
          ??????????"node":?"$node_name"
          ????????}
          ??????}
          END
          ??done
          }

          我們遍歷節(jié)點列表,獲取節(jié)點名稱和 IP 地址,對節(jié)點執(zhí)行 ping 操作,然后將結(jié)果寫入 Prometheus 指標(biāo)端點。Shell-operator 可以通過將指標(biāo)寫入存儲在 $METRICS_PATH 環(huán)境變量中指定路徑下的文件中來將指標(biāo)暴露到 Prometheus。

          這樣我們就使用最少的代碼[5]在群集中實現(xiàn)了基本網(wǎng)絡(luò)監(jiān)視的方式。

          排隊機制

          如果不討論 shell-operator 必不可少的排隊機制,那么將是不完整的。想象一下,shell-operator 響應(yīng)集群中的某些事件而執(zhí)行了一個鉤子。

          • 如果集群中發(fā)生了另一個事件,將會怎樣?
          • shell-operator 會運行該鉤子的另一個實例嗎?
          • 例如,如果集群中同時發(fā)生五個事件,該怎么辦?
          • shell-operator 會并行運行它們嗎?
          • 消耗的資源(如內(nèi)存和CPU)又如何呢?

          幸運的是,shell-operator 具有內(nèi)置的排隊機制,所有事件都放入隊列并順序處理。

          假設(shè)我們有兩個鉤子,第一個事件轉(zhuǎn)到第一個鉤子,處理完成后,隊列前進。接下來的三個事件是另一個鉤子,它們從隊列中彈出并作為批處理傳遞給鉤子。因此,該鉤子接收事件數(shù)組?-更準(zhǔn)確地說是綁定上下文數(shù)組。

          另一種選擇是將這些事件合并為一個較大的事件,綁定配置的group參數(shù)對此負(fù)責(zé)。

          此外,您可以根據(jù)需要獲取任意數(shù)量的隊列或鉤子及其組合,例如,您可以在一個隊列中使用兩個鉤子,反之亦然。

          您要做的就是將queue字段插入綁定配置中,如果queue省略該名稱,則鉤子在default隊列中運行,這種排隊機制可以整體解決所有資源管理問題。

          總結(jié)

          在本文中,我們解釋了什么是 shell-operator,展示了如何快速簡單地創(chuàng)建它的 Kubernetes Operator,并提供了使用它的一些示例。

          有關(guān)我們工具的詳細信息以及快速入門指南,請參考其 GitHub 存儲庫。另外也可以看看我們的其他項目,例如,addon-operator[6] ,它可以綁定 Helm Charts,對其進行升級,監(jiān)視各種 Chart 參數(shù)/值(以及控制 Helm Chart 的安裝)并根據(jù)集群事件進行更新。

          原文鏈接:https://medium.com/flant-com/meet-the-shell-operator-kubecon-36c14ba2f8fe

          此外后臺回復(fù) shell 可以獲取 shell-operator 完整的 PDF 文檔。

          參考資料

          [1]

          shell-operator 演講視頻: https://www.youtube.com/watch?v=we0s4ETUBLc

          [2]

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

          [3]

          shell_lib: https://github.com/flant/shell-operator/blob/master/shell_lib.sh

          [4]

          示例倉儲庫: https://github.com/flant/examples/tree/master/2020/08-kubecon

          [5]

          集群網(wǎng)絡(luò)監(jiān)控代碼: https://github.com/flant/examples/blob/master/2020/08-kubecon/container/ping_exporter.sh

          [6]

          addon-operator: https://github.com/flant/addon-operator





          K8S進階訓(xùn)練營,點擊下方圖片了解詳情


          瀏覽 108
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  免费 无码 国产真人视频九色 | 免费观看一级a片 | 伊人国产在线 | 丁香六月激情 | 撸撸一撸撸操逼视频。 |