简介
应用场景
对服务进行全面管理,除了需要具备服务治理功能,还需要知道服务到底运行得怎么样,有没有问题,哪里有问题,这一般是 APM(Application Performance Management,应用性能管理) 的职能,其中涉及采集数据,存储数据和检索数据.
实现方式
Istio 将 Envoy 的遥测和策略功能提取出来,放到一个服务端组件 Mixer 上,在逻辑上将 Envoy 和各种遥测数据的收集解耦,并将 Envoy 和真正的遥测后端解耦.Envoy 和控制面组件 Mixer 的单条连接
基于 Mixer Adapter 提供的扩展机制,可以做到在遥测和策略执行时对业务代码的无侵入,解耦数据面 Envoy 和遥测与策略执行的后端服务,并开发自己的 Adapter,提供扩展和定制的能力,提供满足用户特定场景的服务运行监控和控制
原理
工作流程
简单来说,该流程主要有两步:
- Envoy 生成数据并将数据上报给 Mixer
 
- Mixer 调用对应的服务后端处理收到的数据
 
每个经过 Envoy 的请求都会调用 Mixer上报数据,Mixer将上报的这些数据作为策略和遥测报告的一部
分发送出来,并转换为对后端服务的调用
属性
属性定义
Envoy 上报的数据在 Istio 中被称为属性(Attribute).严格来讲,在以上 Mixer 处理流程的两个阶段,从Envoy到Mixer及从Mixer到后端服务,处理的
对象都是属性
属性表达式
1 2 3
   | destination_service: destination.service response_code: response.code destination_version: destination.labels["version"] | "unknown"
   | 
 
更多属性表达式见 <云原生服务网格 istio> 表 4-1
Mixer的配置模型
Istio 主要通过 Handler(业务处理),Instance(数据定义)和 Rule(关联规则)这三个资源对象来描述对 Adapter 的配置
Handler
Handler 描述定义的 Adapters 及其配置,不同的 Adapter 有不同的配置.Handler 是 Adapter 定义的模板的实现,通过给模板上的参数赋值来进行实例化
示例如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
   | apiVersion: "config.istio.io/v1alpha2" kind: stdio metadata:   name: handler   namespace: istio-system spec:   outputAsJson: true --- apiVersion: "config.istio.io/v1alpha2" kind: prometheus metadata:   name: handler   namespace: istio-system spec:   metrics:   - name: requests_total     instance_name: requestcount.metric.istio-system     kind: COUNTER     label_names:     - reporter     - source_app     - source_principal     - source_workload     - source_workload_namespace     - source_version     - destination_app     - destination_principal     - destination_workload     - destination_workload_namespace     - destination_version     - destination_service     - destination_service_name     - destination_service_namespace     - request_protocol     - response_code     - connection_security_policy   - name: request_duration_seconds     instance_name: requestduration.metric.istio-system     kind: DISTRIBUTION     label_names:     - reporter     - source_app     - source_principal     - source_workload     - source_workload_namespace     - source_version     - destination_app     - destination_principal     - destination_workload     - destination_workload_namespace     - destination_version     - destination_service     - destination_service_name     - destination_service_namespace     - request_protocol     - response_code     - connection_security_policy     buckets:       explicit_buckets:         bounds: [0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1, 2.5, 5, 10]
   | 
 
Instance
Instance 定义了 Adapter 要处理的数据对象,通过模板为 Adapter 提供对元数据的定义.Mixer 通过 Instance 把来自代理的属性拆分并分发给不通的适配器
示例如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73
   | apiVersion: "config.istio.io/v1alpha2" kind: logentry metadata:   name: accesslog   namespace: istio-system spec:   severity: '"Info"'   timestamp: request.time   variables:     sourceIp: source.ip | ip("0.0.0.0")     sourceApp: source.labels["app"] | ""     sourcePrincipal: source.principal | ""     sourceName: source.name | ""     sourceWorkload: source.workload.name | ""     sourceNamespace: source.namespace | ""     sourceOwner: source.owner | ""     destinationApp: destination.labels["app"] | ""     destinationIp: destination.ip | ip("0.0.0.0")     destinationServiceHost: destination.service.host | ""     destinationWorkload: destination.workload.name | ""     destinationName: destination.name | ""     destinationNamespace: destination.namespace | ""     destinationOwner: destination.owner | ""     destinationPrincipal: destination.principal | ""     apiClaims: request.auth.raw_claims | ""     apiKey: request.api_key | request.headers["x-api-key"] | ""     protocol: request.scheme | context.protocol | "http"     method: request.method | ""     url: request.path | ""     responseCode: response.code | 0     responseSize: response.size | 0     requestSize: request.size | 0     requestId: request.headers["x-request-id"] | ""     clientTraceId: request.headers["x-client-trace-id"] | ""     latency: response.duration | "0ms"     connection_security_policy: conditional((context.reporter.kind | "inbound") == "outbound", "unknown", conditional(connection.mtls | false, "mutual_tls", "none"))     requestedServerName: connection.requested_server_name | ""     userAgent: request.useragent | ""     responseTimestamp: response.time     receivedBytes: request.total_size | 0     sentBytes: response.total_size | 0     referer: request.referer | ""     httpAuthority: request.headers[":authority"] | request.host | ""     xForwardedFor: request.headers["x-forwarded-for"] | "0.0.0.0"     reporter: conditional((context.reporter.kind | "inbound") == "outbound", "source", "destination")   monitored_resource_type: '"global"' --- apiVersion: "config.istio.io/v1alpha2" kind: metric metadata:   name: requestcount   namespace: istio-system spec:   value: "1"   dimensions:     reporter: conditional((context.reporter.kind | "inbound") == "outbound", "source", "destination")     source_workload: source.workload.name | "unknown"     source_workload_namespace: source.workload.namespace | "unknown"     source_principal: source.principal | "unknown"     source_app: source.labels["app"] | "unknown"     source_version: source.labels["version"] | "unknown"     destination_workload: destination.workload.name | "unknown"     destination_workload_namespace: destination.workload.namespace | "unknown"     destination_principal: destination.principal | "unknown"     destination_app: destination.labels["app"] | "unknown"     destination_version: destination.labels["version"] | "unknown"     destination_service: destination.service.host | "unknown"     destination_service_name: destination.service.name | "unknown"     destination_service_namespace: destination.service.namespace | "unknown"     request_protocol: api.protocol | context.protocol | "unknown"     response_code: response.code | 200     connection_security_policy: conditional((context.reporter.kind | "inbound") == "outbound", "unknown", conditional(connection.mtls | false, "mutual_tls", "none"))   monitored_resource_type: '"UNSPECIFIED"'
   | 
 
Rule
Rule 配置了一组规则,告诉 Mixer 有哪个 Instance 在什么时候被发送给哪个 Handler 来处理,一般包括一个匹配的表达式和执行动作(action)匹配表达式控制在什么时候调用 Adapter,在 Action 里配置 Adapter 和 Instance 的名称
字段如下
| 字段 | 
是否必选 | 
解释 | 
| match | 
是 | 
表示匹配条件,如果未定义条件,则判定为总是匹配 | 
| actions | 
是 | 
表示满足条件后执行的动作,是一个数组.包含 handler 和 instance 两个字段,用于指定 handler 和 instance 的名称(必须是全名,格式一般为 <name.kind>) | 
示例如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
   | apiVersion: "config.istio.io/v1alpha2" kind: rule metadata:   name: stdio   namespace: istio-system spec:   match: context.protocol == "http" || context.protocol == "grpc"   actions:   - handler: handler.stdio     instances:     - accesslog.logentry --- apiVersion: "config.istio.io/v1alpha2" kind: rule metadata:   name: promhttp   namespace: istio-system spec:   match: context.protocol == "http" || context.protocol == "grpc"   actions:   - handler: handler.prometheus     instances:     - requestcount.metric     - requestduration.metric     - requestsize.metric     - responsesize.metric
   | 
 
遥测适配器配置
Prometheus 适配器
工作流程
- Envoy 通过 Report 接口上报数据给 Mixer
 
- Mixer 根据配置将请求分发给 Prometheus Adapter
 
- Prometheus Adapter 通过 HTTP 接口发 布Metric 数据
 
- Prometheus 服务作为 Addon 在集群中进行安装,拉取并存储 Metric 数据,提供 Query 接口进行检索
 
- 集群内的 Dashboard(如Grafana)通过 Prometheus 的检索 API 访问 Metric 数据
 
handler 配置定义
handler 配置中最主要的字段是 metrics,用于在 Prometheus 中定义 metrics.它是一个数组,每个元素都具有如下属性
name: metric 的名称 
instance_name: instance 的全名称,格式为 <instance_name.kind.namespace> 
kind: 定义指标类型,请求计数类型为 COUNTER,请求耗时类型为 DISTRIBUTION.DISTRIBUTION 类型的指标可以定义其 buckets 
label_names: 定义指标的标签,一般与 instance 中维度相同 
instance 配置定义
instance 配置中最主要的字段是 dimensions 和 value,分别用于记录数据的维度及对应的值.这两个字段均支持属性表达式.其中维度中的 key 多用于 prometheus-metrics 的标签
rule 配置定义
Rule 可以将 Handler 和 Instance建立关系,最主要的字段是 match 和 actions,分别用于匹配规则及匹配后的动作