从 Helm 到 Operator:Kubernetes应用管理的进化

helm,operator,kubernetes · 浏览次数 : 46

小编点评

Helm(简称Helmfile)是一个开源的Kubernetes包管理器,用于简化Kubernetes应用的部署和管理。Helmfile允许开发者通过声明式的方式定义Kubernetes资源,并将其存储在Helm chart中。这些chart包含了组成应用程序的所有配置、依赖项和设置,使得部署过程更加一致和可管理。 Helmfile的主要作用: 1. **声明式配置**:Helmfile使用YAML或JSON文件来描述应用程序的配置,这使得配置更加直观和易于管理。 2. **图表管理**:Helmfile通过图表(charts)来组织和管理Kubernetes资源。图表是预配置好的Kubernetes应用程序模板,包含了应用程序的所有必要配置。 3. **版本控制**:Helmfile支持版本控制,可以轻松回滚到之前的版本或升级到新版本。 4. **插件系统**:Helmfile具有插件系统,可以通过安装插件来扩展其功能,例如支持Helmfile的云服务提供商插件。 5. **自动化部署**:Helmfile可以自动化地部署应用程序,减少了手动操作的需要。 Helmfile相对于直接使用kubectl的显著优势在于: - **一致性**:Helmfile提供了一种统一的方式来定义和部署Kubernetes应用程序,避免了手动编辑YAML文件的不便和错误。 - **可重用性**:通过创建和共享图表,可以实现应用程序配置的重用,减少重复的工作。 - **协作**:Helmfile支持团队协作,团队成员可以共享和使用相同的图表。 Helmfile的缺点可能包括: - **学习曲线**:对于新手来说,Helmfile可能需要一定的学习和适应时间。 - **复杂性**:对于复杂的Kubernetes应用程序,手动配置可能会更加直观和可控。 - **性能**:与直接使用kubectl相比,Helmfile可能在某些情况下导致性能下降,尤其是在处理大量图表和大型集群时。 总的来说,Helmfile是一个强大的工具,可以帮助开发者和运维人员更高效地部署和管理Kubernetes应用程序。

正文

🧰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-operatorVictoriaMetrics-operator 为例:

Prometheus:

  • Prometheus:用于定义 Prometheus 的 Deployment
  • Alertmanager:用于定义 Alertmanager
  • ScrapeConfig:用于定会抓取规则
apiVersion: monitoring.coreos.com/v1alpha1
kind: ScrapeConfig
metadata:
  name: static-config
  namespace: my-namespace
  labels:
    prometheus: system-monitoring-prometheus
spec:
  staticConfigs:
    - labels:
        job: prometheus
      targets:
        - 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: 2
  vmstorage:
    replicaCount: 2
    storageDataPath: "/vm-data"
    storage:
      volumeClaimTemplate:
        spec:
          resources:
            requests:
              storage: "10Gi"
    resources:
      limits:
        cpu: "1"
        memory: "1Gi"
  vmselect:
    replicaCount: 2
    cacheMountPath: "/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 语言有很多成熟的工具,比如常用的 kubebuilderoperator-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-order
  namespace: test  
spec:  
  env:  
    - name: OTEL_SERVICE_NAME  
      value: order
  selector:  
    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。

参考链接:

与从 Helm 到 Operator:Kubernetes应用管理的进化相似的内容:

从 Helm 到 Operator:Kubernetes应用管理的进化

Helm 的作用 在开始前需要先对 kubernetes Operator 有个简单的认识。 以为我们在编写部署一些简单 Deployment 的时候只需要自己编写一个 yaml 文件然后 kubectl apply 即可。 apiVersion: apps/v1 kind: Deploymen

Rancher 系列文章-K3S 集群升级

概述 书接上回:《Rancher 系列文章-Rancher 升级》, 我们提到:将 Rancher 用 Helm 从 v2.6.3 升级到 v2.6.4. 接下来开始进行 K3S 集群的升级:将 K3S 集群从 v1.21.7+k3s1 升级到 v1.22.5+k3s2 相关信息 本次升级的 K3S

从DDPM到DDIM (一) 极大似然估计与证据下界

从DDPM到DDIM (一) 极大似然估计与证据下界 现在网络上关于DDPM和DDIM的讲解有很多,但无论什么样的讲解,都不如自己推到一遍来的痛快。笔者希望就这篇文章,从头到尾对扩散模型做一次完整的推导。本文的很多部分都参考了 Calvin Luo[1] 和 Stanley Chan[2] 写的经典

从Mybatis-Plus开始认识SerializedLambda

从Mybatis-Plus开始认识SerializedLambda 背景 对于使用过Mybatis-Plus的Java开发者来说,肯定对以下代码不陌生: @TableName("t_user") @Data public class User { private String id; private

从基础到高级应用,详解用Python实现容器化和微服务架构

本文分享自华为云社区《Python微服务与容器化实践详解【从基础到高级应用】》,作者: 柠檬味拥抱。 Python中的容器化和微服务架构实践 在现代软件开发中,容器化和微服务架构已经成为主流。容器化技术使得应用程序可以在任何环境中一致运行,而微服务架构通过将应用拆分成多个独立的服务,从而提升了系统的

从DDPM到DDIM

现在网络上关于DDPM和DDIM的讲解有很多,但无论什么样的讲解,都不如自己推到一边来的痛快。笔者希望就这篇文章,从头到尾对扩散模型DDPM及其加速方法DDIM做一次完整的推导。

PixiJS源码分析系列: 第一章 从最简单的例子入手

从最简单的例子入手分析 PixiJS 源码 我一般是以使用角度作为切入点查看分析源码,例子中用到什么类,什么方法,再入源码。 高屋建瓴的角度咱也做不到啊,毕竟水平有限 pixijs 的源码之前折腾了半天都运行不起来,文档也没有明确说明如何调式 我在 github 上看到过也有歪果仁在问如何本地调式最

突破自我认知的壁垒

从之前非常迷茫到现在慢慢变清晰,其实我发现很多时候看似难以逾越的问题下要突破自我认知的壁垒,需要你有打破了重建的自我革命精神!你所看到的世界并不一定是真实的世界,都是在自我认知固化和以你的生活为蓝本的大数据编织的信息茧房中,就如同黑客帝国中的Matix一样,现实迷茫的时候你必须要突破自己的理解误区。

从一个双非本学渣到自学前端上岸,我都做了些什么

这个世界上其实大部分人还没有到那种需要拼天赋的程度,大家都是普通人,只要你想,别人能做的你也能做。这是我一直相信的。

从安装到配置,教你用Argo CD对接CCE集群完成测试、生产部署

本文使用两个CCE集群模拟测试及生产环境,使用gitlab仓库作为应用部署yaml文件存储仓库,通过Argo CD对接不同CCE集群完成测试、生产环境业务部署。