🧰Helm 的作用
在開始前需要先對 kubernetes ?Operator 有個簡單的認識。
以為我們在編寫部署一些簡單 Deployment
的時候只需要自己編寫一個 yaml 文件然后 kubectl apply
即可。
apiVersion:?apps/v1??
kind:?Deployment??
metadata:??labels:??app:?k8s-combat??name:?k8s-combat??
spec:??replicas:?1??selector:??matchLabels:??app:?k8s-combat??template:??metadata:??labels:??app:?k8s-combat??spec:??containers:??-?name:?k8s-combat??image:?crossoverjie/k8s-combat:v1??imagePullPolicy:?Always??resources:??limits:??cpu:?"1"??memory:?300Mi??requests:??cpu:?"0.1"??memory:?30Mi
kubectl?apply?-f?deployment.yaml
這對于一些并不復雜的項目來說完全夠用了,但組件一多就比較麻煩了。
這里以 Apache Pulsar 為例:它的核心組件有:
Broker
Proxy
Zookeeper
Bookkeeper
Prometheus(可選)
Grafana(可選) 等組件,每個組件的啟動還有這依賴關系。
必須需要等 Zookeeper 和 Bookkeeper 啟動之后才能將流量放進來。
此時如何還繼續使用 yaml 文件一個個部署就會非常繁瑣,好在社區有提供 Helm 一鍵安裝程序,使用它我們只需要在一個同意的 yaml 里簡單的配置一些組件,配置就可以由 helm 來部署整個復雜的 Pulsar 系統。
components:??#?zookeeper??zookeeper:?true??#?bookkeeper??bookkeeper:?true??#?bookkeeper?-?autorecovery??autorecovery:?true??#?broker??broker:?true??#?functions??functions:?false??#?proxy??proxy:?true??#?toolset??toolset:?true??#?pulsar?manager??pulsar_manager:?false??
monitoring:??#?monitoring?-?prometheus??prometheus:?true??#?monitoring?-?grafana??grafana:?true??#?monitoring?-?node_exporter??node_exporter:?true??#?alerting?-?alert-manager??alert_manager:?false
比如在 helm 的 yaml 中我們可以選擇使用哪些 components,以及是否啟用監控組件。
最后直接使用這個文件進行安裝:
helm?install?pulsar?apache/pulsar?\--values?charts/pulsar/values.yaml?\--set?namespace=pulsar?\--set?initialize=true
它就會自動生成各個組件的 yaml 文件,然后統一執行。
所以 helm 的本質上和 kubectl apply yaml
一樣的,只是我們在定義 value.yaml 時幫我們處理了許多不需要用戶低頻修改的參數。
我們可以使用 helm 將要執行的 yaml 輸出后人工審核
helm?install?pulsar?apache/pulsar?--dry-run?--debug?>?debug.yaml
🤔Operator 是什么
💔Helm 的痛點
Helm 雖然可以幫我們部署或者升級一個大型應用,但他卻沒法幫我們運維這個應用。
舉個例子:比如我希望當 Pulsar Broker 的流量或者內存達到某個閾值后就指定擴容 Broker,閑時再自動回收。
或者某個 Bookkeeper 的磁盤使用率達到閾值后可以自動擴容磁盤,這些僅僅使用 Helm 時都是無法實現的。
以上這些需求我們目前也是通過監控系統發出報警,然后再由人工處理。
其中最大的痛點就是進行升級:
升級ZK
關閉auto recovery
升級Bookkeeper
升級Broker
升級Proxy
開啟auto recovery
因為每次升級是有先后順序的,需要依次觀察每個組件運行是否正常才能往后操作。
如果有 Operator 理性情況下下我們只需要更新一下鏡像版本,它就可以自動執行以上的所有步驟最后將集群升級完畢。
所以相對于 Helm 來說 Operator 是可以站在一個更高的視角俯視整個應用系統,它能發現系統哪個地方需要它從而直接修復。
💎CRD(Custom Resource Definitions)
而提到 Operator 那就不得不提到 CRD(Custom Resource Definitions)翻譯過來就是自定義資源。
這是 kubernetes 提供的一個 API 擴展機制,類似于內置的 Deployment/StatefulSet/Services
資源,CRD 是一種自定義的資源。
這里以我們常用的 prometheus-operator
和 VictoriaMetrics-operator
為例:
Prometheus:
**
Prometheus
**:用于定義 Prometheus 的 Deployment**
Alertmanager
**:用于定義Alertmanager
**
ScrapeConfig
**:用于定會抓取規則
apiVersion:?monitoring.coreos.com/v1alpha1
kind:?ScrapeConfig
metadata:name:?static-confignamespace:?my-namespacelabels:prometheus:?system-monitoring-prometheus
spec:staticConfigs:-?labels:job:?prometheustargets:-?prometheus.demo.do.prometheus.io:9090
使用時的一個很大區別就是資源的 kind: ScrapeConfig
為自定義的類型。
VictoriaMetrics 的 CRD:
VMPodScrape:Pod 的抓取規則
VMCluster:配置 VM 集群
VMAlert:配置 VM 的告警規則
等等
#?vmcluster.yaml
apiVersion:?operator.victoriametrics.com/v1beta1
kind:?VMCluster
metadata:name:?demo
spec:retentionPeriod:?"1"replicationFactor:?2vmstorage:replicaCount:?2storageDataPath:?"/vm-data"storage:volumeClaimTemplate:spec:resources:requests:storage:?"10Gi"resources:limits:cpu:?"1"memory:?"1Gi"vmselect:replicaCount:?2cacheMountPath:?"/select-cache"storage:volumeClaimTemplate:spec:resources:requests:storage:?"1Gi"resources:limits:cpu:?"1"memory:?"1Gi"requests:cpu:?"0.5"memory:?"500Mi"vminsert:replicaCount:?2
以上是用于創建一個 VM 集群的 CRD 資源,應用之后就會自動創建一個集群。
Operator 原理
Operator 通常是運行在 kubernetes API server 的
webhook
之上,簡單來說就是在一些內置資源的關鍵節點 API-server 會調用我們注冊的一個 webhook
,在這個 webhook
中我們根據我們的 CRD 做一些自定義的操作。
理論上我們可以使用任何語言都可以寫 Operator,只需要能處理 api-server 的回調即可。
只是 Go 語言有很多成熟的工具,比如常用的 kubebuilder 和 operator-sdk.
他們內置了許多命令行工具,可以幫我們節省需要工作量。
這里以 operator-sdk 為例:
$?operator-sdk?create?webhook?--group?cache?--version?v1alpha1?--kind?Memcached?--defaulting?--programmatic-validation
會直接幫我們創建好一個標準的 operator 項目:
├──?Dockerfile
├──?Makefile
├──?PROJECT
├──?api
│???└──?v1alpha1
│???????├──?memcached_webhook.go
│???????├──?webhook_suite_test.go
├──?config
│???├──?certmanager
│???│???├──?certificate.yaml
│???│???├──?kustomization.yaml
│???│???└──?kustomizeconfig.yaml
│???├──?default
│???│???├──?manager_webhook_patch.yaml
│???│???└──?webhookcainjection_patch.yaml
│???└──?webhook
│???????├──?kustomization.yaml
│???????├──?kustomizeconfig.yaml
│???????└──?service.yaml
├──?go.mod
├──?go.sum
└──?main.go
其中 Makefile 中包含了開發過程中常用的工具鏈(包括根據聲明的結構體自動生成 CRD 資源、部署k8s 環境測試等等)、Dockerfile 等等。
這樣我們就只需要專注于開發業務邏輯即可。
因為我前段時間給 https://github.com/open-telemetry/opentelemetry-operator 貢獻過兩個 feature,所以就以這個 Operator 為例:
它有一個 CRD: kind: Instrumentation
,在這個 CRD 中可以將 OpenTelemetry 的 agent 注入到應用中。
apiVersion:?opentelemetry.io/v1alpha1??
kind:?Instrumentation??
metadata:??name:?instrumentation-test-ordernamespace:?test??
spec:??env:??-?name:?OTEL_SERVICE_NAME??value:?orderselector:??matchLabels:??app:?order??java:??image:?autoinstrumentation-java:2.4.0-release??extensions:??-?image:?autoinstrumentation-java:2.4.0-release??dir:?/extensions??env:??-?name:?OTEL_RESOURCE_ATTRIBUTES??value:?service.name=order??-?name:?OTEL_INSTRUMENTATION_MESSAGING_EXPERIMENTAL_RECEIVE_TELEMETRY_ENABLED??value:?"true"??-?name:?OTEL_TRACES_EXPORTER??value:?otlp??-?name:?OTEL_METRICS_EXPORTER??value:?otlp??-?name:?OTEL_LOGS_EXPORTER??value:?none??-?name:?OTEL_EXPORTER_OTLP_ENDPOINT??value:?http://open-telemetry-opentelemetry-collector.otel.svc.cluster.local:4317??-?name:?OTEL_EXPORTER_OTLP_COMPRESSION??value:?gzip??-?name:?OTEL_EXPERIMENTAL_EXPORTER_OTLP_RETRY_ENABLED??value:?"true"
它的運行規則是當我們的 Pod 在啟動過程中會判斷 Pod 的注解中是否開啟了注入 OpenTelemetry 的配置。
如果開啟則會將我們在 CRD 中自定義的鏡像里的 javaagent 復制到業務容器中,同時會將下面的那些環境變量也一起加入的業務容器中。
要達到這樣的效果就需要我們注冊一個回調 endpoint。
mgr.GetWebhookServer().Register("/mutate-v1-pod",?&webhook.Admission{??Handler:?podmutation.NewWebhookHandler(cfg,?ctrl.Log.WithName("pod-webhook"),?decoder,?mgr.GetClient(),??[]podmutation.PodMutator{??sidecar.NewMutator(logger,?cfg,?mgr.GetClient()),??instrumentation.NewMutator(logger,?mgr.GetClient(),?mgr.GetEventRecorderFor("opentelemetry-operator"),?cfg),??}),})
當 Pod 創建或有新的變更請求時就會回調我們的接口。
func?(pm?*instPodMutator)?Mutate(ctx?context.Context,?ns?corev1.Namespace,?pod?corev1.Pod)?(corev1.Pod,?error)?{??logger?:=?pm.Logger.WithValues("namespace",?pod.Namespace,?"name",?pod.Name)}
在這個接口中我們就可以拿到 Pod 的信息,然后再獲取 CRD Instrumentation
做我們的業務邏輯。
var?otelInsts?v1alpha1.InstrumentationList??
if?err?:=?pm.Client.List(ctx,?&otelInsts,?client.InNamespace(ns.Name));?err?!=?nil?{??return?nil,?err??
}//?從 CRD 中將數據復制到業務容器中。
pod.Spec.InitContainers?=?append(pod.Spec.InitContainers,?corev1.Container{Name:??????javaInitContainerName,Image:?????javaSpec.Image,Command:???[]string{"cp",?"/javaagent.jar",?javaInstrMountPath?+?"/javaagent.jar"},Resources:?javaSpec.Resources,VolumeMounts:?[]corev1.VolumeMount{{Name:??????javaVolumeName,MountPath:?javaInstrMountPath,}},
})for?i,?extension?:=?range?javaSpec.Extensions?{pod.Spec.InitContainers?=?append(pod.Spec.InitContainers,?corev1.Container{Name:??????initContainerName?+?fmt.Sprintf("-extension-%d",?i),Image:?????extension.Image,Command:???[]string{"cp",?"-r",?extension.Dir?+?"/.",?javaInstrMountPath?+?"/extensions"},Resources:?javaSpec.Resources,VolumeMounts:?[]corev1.VolumeMount{{Name:??????javaVolumeName,MountPath:?javaInstrMountPath,}},})
}
不過需要注意的是想要在測試環境中測試 operator 是需要安裝一個 cert-manage,這樣
webhook
才能正常的回調。
要使得 CRD 生效,我們還得先將 CRD 安裝進 kubernetes 集群中,不過這些 operator-sdk 這類根據已經考慮周到了。
我們只需要定義好 CRD 的結構體:
然后使用 Makefile 中的工具 make bundle
就會自動將結構體轉換為 CRD。
參考鏈接:
https://github.com/VictoriaMetrics/operator
https://github.com/prometheus-operator/prometheus-operator
往期推薦
如何找到并快速上手一個開源項目
OpenTelemetry 深度定制:跨服務追蹤的實戰技巧
從 Prometheus 到 OpenTelemetry: 指標監控的演進與實踐
從 Dapper 到 OpenTelemetry:分布式追蹤的演進之旅
實操 OpenTelemetry:通過 Demo 掌握微服務監控的藝術
點分享
點收藏
點點贊
點在看