Istio Mixer Adapter

 20th August 2020 at 2:19pm

Mixer 这块,主要看官方文档中的 Istio Mixer: Adapter Developer's Guide。这里我举一些实例辅助理解。

Template 中定义的字段,像是一个 placeholder,供运行时填写实际请求相关的字段,比如:

# mixer/template/listentry/template.proto
message Template {
    // Specifies the entry to verify in the list.
    string value = 1;
}
# mixer/testdata/config/listcheck.yaml
apiVersion: "config.istio.io/v1alpha2"
kind: listentry
metadata:
  name: appversion
  namespace: istio-system
spec:
  value: source.labels["version"]

adapter 需要指定使用哪(几)个 template,在 GetInfo()SupportedTemplate 中定义。Template 的 Protobuf 文件中有 template_variety,表明它是 Check 还是 Report 等,比如:

// mixer/template/checknothing/template.proto
option (istio.mixer.adapter.model.v1beta1.template_variety) = TEMPLATE_VARIETY_CHECK;

Template 里提供了一个 value 字段,供运维配置运行时时,用哪个请求相关的字段填充。这个例子中,会用 source.labels["version"] 填充。然后运行时就会生成一个 instance 给 adapter 去取 value,也就是调用方的版本号:

// mixer/adapter/list/list.go
func (h *handler) HandleListEntry(_ context.Context, entry *listentry.Instance) (adapter.CheckResult, error) {
	// 此时你就可以从 entry 中拿到调用方的版本号,做相关的逻辑
}

adapter 也有自己的配置,主要跟运行时的业务逻辑有关:

# mixer/adapter/list/config/config.proto
message Params {
    // Where to find the list to check against. This may be ommited for a completely local list.
    string provider_url = 1;

    // Determines how often the provider is polled for
    // an updated list
    google.protobuf.Duration refresh_interval = 2 [(gogoproto.nullable) = false, (gogoproto.stdduration) = true];

    // Indicates how long to keep a list before discarding it.
    // Typically, the TTL value should be set to noticeably longer (> 2x) than the
    // refresh interval to ensure continued operation in the face of transient
    // server outages.
    google.protobuf.Duration ttl = 3 [(gogoproto.nullable) = false, (gogoproto.stdduration) = true];
    
    // ...
    
    // List entries that are consulted first, before the list from the server
    repeated string overrides = 6;

	// ...
    // Whether the list operates as a blacklist or a whitelist.
    bool blacklist = 8;
}

这上面的 refresh_interval 表示 listchecker 这个 adapter,它会在多长时间去访问 provider_url 去刷新黑白名单列表。这上面的配置,可以由运维通过配置文件配置:

apiVersion: "config.istio.io/v1alpha2"
kind: listchecker
metadata:
  name: staticversion
  namespace: istio-system
spec:
  # providerUrl: ordinarily black and white lists are maintained
  # externally and fetched asynchronously using the providerUrl.
  overrides: ["v1", "v2"]  # overrides provide a static list
  blacklist: false

同时你可以在代码中配置默认值:

// mixer/adapter/list/list.go
func GetInfo() adapter.Info {
	return adapter.Info{
		Name:               "listchecker",
		Impl:               "istio.io/istio/mixer/adapter/list",
		Description:        "Checks whether an entry is present in a list",
		SupportedTemplates: []string{listentry.TemplateName},
		DefaultConfig: &config.Params{
			RefreshInterval: 60 * time.Second,
			Ttl:             300 * time.Second,
			CachingInterval: 300 * time.Second,
			CachingUseCount: 10000,
			EntryType:       config.STRINGS,
			Blacklist:       false,
		},

		NewBuilder: func() adapter.HandlerBuilder { return &builder{} },
	}
}

配置好了 template 所定义的 instance 数据,adapter 所定义的业务逻辑配置,你需要再写一个 rule 把它们串起来,就完成了:

apiVersion: "config.istio.io/v1alpha2"
kind: rule
metadata:
  name: checkwl
  namespace: istio-system
spec:
  # If an attribute could be potentially absent, use the '|' operator
  # to provide a default value. In the below example if 'destination.labels'
  # is absent, "unknown" is used in its place (this happens in non kubernetes env).
  match: (destination.labels["app"]|"unknown") == "ratings"
  actions:
  - handler: staticversion.listchecker  # 对应上面配置的 listchecker,里面写了 adapter 配置
    instances:
    - appversion.listentry              # 对应上面配置的 listentry,里面配置了 instance 数据

这上面所有可以配置的内容,最终会由运维写进一个 yaml 文件中,再由 istioctl 命令应用进这套系统中。

Note
listentrylistchecker 都可以配置多个。多个 listcheck 时,运行时就有多个 adapter 实例存在 mixer 中。多个 listentry 时,每个请求就有多个 instance 生成给 adapter(当然也看匹配规则)。