Knative Eventing 中 Channel 如何注入默认 Provisioner

本文涉及的产品
容器镜像服务 ACR,镜像仓库100个 不限时长
简介: 在 Knative Eventing 中创建 Broker 时,如果不指定 provisioner, 系统会自动创建默认的 provisioner, 那么这个机制是如何实现的呢? 本文基于 Knative Eventing 0.5 版本,介绍了这个实现机制。

场景

通常的在创建Broker时,我们需要通过 spec.ChannelTemplate 指定使用某个具体的 Channel Provisioner。例如这样的Broker:

apiVersion: eventing.knative.dev/v1alpha1
kind: Broker
metadata:
  name: pubsub-channel
spec:
  channelTemplate:
    provisioner:
      apiVersion: eventing.knative.dev/v1alpha1
      kind: ClusterChannelProvisioner
      name: gcp-pubsub

这里通过spec.ChannelTemplate 指定了名称为gcp-pubsub的provisioner。那么我们也遇到过这样的Broker:

apiVersion: eventing.knative.dev/v1alpha1
kind: Broker
metadata:
  name: default

并没有指定使用某个具体的 channel, 但创建完Broker之后会发现已经创建出来了Channel:

apiVersion: eventing.knative.dev/v1alpha1
kind: Channel
metadata:
  ...
  name: default-broker-8ml79
  namespace: default
  ownerReferences:
  - apiVersion: eventing.knative.dev/v1alpha1
    blockOwnerDeletion: true
    controller: true
    kind: Broker
    name: default
    uid: 2e4c3332-6755-11e9-a81f-00163f005e02
spec:
  provisioner:
    apiVersion: eventing.knative.dev/v1alpha1
    kind: ClusterChannelProvisioner
    name: in-memory
...

分析

我们知道 Broker创建之后,会通过 reconcile controller 会创建相应的Channel, 也就是下面这段代码:

// newChannel creates a new Channel for Broker 'b'.
func newChannel(b *v1alpha1.Broker, l map[string]string) *v1alpha1.Channel {
    var spec v1alpha1.ChannelSpec
    if b.Spec.ChannelTemplate != nil {
        spec = *b.Spec.ChannelTemplate
    }

    return &v1alpha1.Channel{
        ObjectMeta: metav1.ObjectMeta{
            Namespace:    b.Namespace,
            GenerateName: fmt.Sprintf("%s-broker-", b.Name),
            Labels:       l,
            OwnerReferences: []metav1.OwnerReference{
                *metav1.NewControllerRef(b, schema.GroupVersionKind{
                    Group:   v1alpha1.SchemeGroupVersion.Group,
                    Version: v1alpha1.SchemeGroupVersion.Version,
                    Kind:    "Broker",
                }),
            },
        },
        Spec: spec,
    }
}

分析上面这段代码,我们可以很清楚得出这样的结论:如果Broker中设置了Spec.ChannelTemplate, 那么Channel中会直接使用ChannelTemplate所对应的provisioner。
但如果没有设置的话, 那么Channel中的spec应该设置为nil。但事实上设置了in-memory provisioner, 那么这个是在哪里注入的呢?

注入机制

经过定位源代码,我们发现在channel_defaults.go中,发现下面这段代码:

func (c *Channel) SetDefaults(ctx context.Context) {
    if c != nil && c.Spec.Provisioner == nil {
        // The singleton may not have been set, if so ignore it and validation will reject the
        // Channel.
        if cd := ChannelDefaulterSingleton; cd != nil {
            prov, args := cd.GetDefault(c.DeepCopy())
            c.Spec.Provisioner = prov
            c.Spec.Arguments = args
        }
    }
    c.Spec.SetDefaults(ctx)
}

分析一下,我们可以看到当c.Spec.Provisioner==nil时, 会设置默认的Provisioner。
进一步分析ChannelDefaulterSingleton, 我们可以在webhook中赋予了实现设置:

...
// Watch the default-channel-webhook ConfigMap and dynamically update the default
// ClusterChannelProvisioner.
channelDefaulter := channeldefaulter.New(logger.Desugar())
eventingv1alpha1.ChannelDefaulterSingleton = channelDefaulter
configMapWatcher.Watch(channeldefaulter.ConfigMapName, channelDefaulter.UpdateConfigMap)
...

接着分析发现 ChannelDefaulter 实现了 GetDefault 方法:

// GetDefault determines the default provisioner and arguments for the provided channel.
func (cd *ChannelDefaulter) GetDefault(c *eventingv1alpha1.Channel) (*corev1.ObjectReference, *runtime.RawExtension) {
    // Because we are treating this as a singleton, be tolerant to it having not been setup at all.
    if cd == nil {
        return nil, nil
    }
    if c == nil {
        return nil, nil
    }
    config := cd.getConfig()
    if config == nil {
        return nil, nil
    }

    // TODO Don't use a single default, instead use the Channel's arguments to determine the type of
    // Channel to use (e.g. it can say whether it needs to be persistent, strictly ordered, etc.).
    dp := getDefaultProvisioner(config, c.Namespace)
    cd.logger.Info("Defaulting the ClusterChannelProvisioner", zap.Any("defaultClusterChannelProvisioner", dp))
    return dp, nil
}

并且这里是通过一个ConfigMap设置使用的默认provisioner, 这个ConfigMap名称为default-channel-webhook, 没错可以在 Knative Eventing 安装文件中发现这个资源:

apiVersion: v1
data:
  default-channel-config: |
    clusterdefault:
      apiversion: eventing.knative.dev/v1alpha1
      kind: ClusterChannelProvisioner
      name: in-memory
    namespacedefaults:
      some-namespace:
        apiversion: eventing.knative.dev/v1alpha1
        kind: ClusterChannelProvisioner
        name: some-other-provisioner
kind: ConfigMap
metadata:
  name: default-channel-webhook
  namespace: knative-eventing

那么分析到此,我们梳理一下整个注入的流程:
image

结论

通过上面的分析, 我们现在了解了默认provisioner的注入机制, 同时我们也可以通过 webhook 修改默认的provisioner。

相关实践学习
巧用云服务器ECS制作节日贺卡
本场景带您体验如何在一台CentOS 7操作系统的ECS实例上,通过搭建web服务器,上传源码到web容器,制作节日贺卡网页。
容器应用与集群管理
欢迎来到《容器应用与集群管理》课程,本课程是“云原生容器Clouder认证“系列中的第二阶段。课程将向您介绍与容器集群相关的概念和技术,这些概念和技术可以帮助您了解阿里云容器服务ACK/ACK Serverless的使用。同时,本课程也会向您介绍可以采取的工具、方法和可操作步骤,以帮助您了解如何基于容器服务ACK Serverless构建和管理企业级应用。 学习完本课程后,您将能够: 掌握容器集群、容器编排的基本概念 掌握Kubernetes的基础概念及核心思想 掌握阿里云容器服务ACK/ACK Serverless概念及使用方法 基于容器服务ACK Serverless搭建和管理企业级网站应用
目录
相关文章
|
6月前
|
存储 JSON 监控
Higress Controller**不是将配置信息推送到Istio的内存存储里面的**。
【2月更文挑战第30天】Higress Controller**不是将配置信息推送到Istio的内存存储里面的**。
55 1
|
6月前
OpenKruise金丝雀发布过程中,创建出了canary service但没有canary deploy,新老实例均属于同一个deploy,类似多批次发布
OpenKruise金丝雀发布过程中,创建出了canary service但没有canary deploy,新老实例均属于同一个deploy,类似多批次发布
47 1
|
存储 Kubernetes 负载均衡
【Kubernetes系统原理、核心资源、Pod原理与创建及生命周期管理、Job、Cronjob、Statefulset、Service负载均衡Ingress】
【Kubernetes系统原理、核心资源、Pod原理与创建及生命周期管理、Job、Cronjob、Statefulset、Service负载均衡Ingress】
391 2
|
消息中间件 Kubernetes Cloud Native
【Kubernetes的Knative Servina、Knative Eventing 核心概念及Broker、Channel and Trigger使用】
【Kubernetes的Knative Servina、Knative Eventing 核心概念及Broker、Channel and Trigger使用】
122 0
|
6月前
OpenKruise金丝雀发布过程中,创建出了canary service
【1月更文挑战第11天】【1月更文挑战第51篇】OpenKruise金丝雀发布过程中,创建出了canary service
31 1
|
Kubernetes 负载均衡 容器
Kubernetes LoadBalancer使用场景例子及配置步骤
Kubernetes 的 LoadBalancer 类型的服务用于将流量从集群外部的负载均衡器路由到集群内的服务。它通常用于公共云提供商(如 AWS、GCP、Azure)或者其他支持这种负载均衡机制的环境中。 以下是一个使用场景的例子:假设你在 Kubernetes 集群中部署了一个 web 应用程序,并希望将该应用程序暴露给外部用户,以便他们可以通过公共网络访问这个应用程序。你可以使用 LoadBalancer 类型的服务来实现这一目标。 下面是配置步骤: 1. 创建一个 `Service` 对象,并设置 `type: LoadBalancer`。 ```yaml apiVersio
2096 0
|
Prometheus 监控 Kubernetes
(二)ACK prometheus-operator之 配置自定义组件监控
使用ack-prometheus-operator 在阿里云ACK专有版集群里,默认未采集 etcd / scheduler/ kcm/ccm/kube-proxy等管理组件的监控数据,需要手动配置证书、采集等配置。本文目的在于解决由于不正确的配置带来的监控异常,也顺便扫盲“更新Prometheus Server的配置prometheus.yml"这几个词在operator体系中的具体配置步骤。
1704 0
(二)ACK prometheus-operator之 配置自定义组件监控
|
Prometheus 监控 Kubernetes
在ASM中为应用服务启用SLO(4):导入生成的规则到Prometheus中执行SLO
服务等级目标SLO可以用于衡量服务的水平。用户可以基于Prometheus指标手动定义SLO,但过程相对繁琐。ASM服务网格提供了生成SLO以及配套的告警规则的能力,能够通过自定义资源YAML配置的方式简化这一流程。本文将介绍如何将生成的规则导入到Prometheus中以及如何执行SLO。
317 0
在ASM中为应用服务启用SLO(4):导入生成的规则到Prometheus中执行SLO
|
Kubernetes 应用服务中间件 API
K8S 集群核心概念 Controller_使用资源清单文件创建 Deployment 控制器类型应用 | 学习笔记
快速学习 K8S 集群核心概念 Controller_使用资源清单文件创建 Deployment 控制器类型应用
109 0
Knative Eventing 之 Sequence 介绍
在处理数据时,往往会涉及到一个数据需要进行多次加工,这时候我们一般是通过Pipeline的方式进行处理。那么在Knative Eventing中是否也能支持对一个事件进行分步骤多次处理? 这个还真有。从 0.7 版本开始,Knative Eventing中提供了一个 Sequence 资源模型,可用于事件Pipeline处理。
1976 0