Istio入门

一、背景

随着单片应用程序向分布式微服务架构过渡 ,特别是服务之间呈现拓扑状的复杂关系,service mesh的提出就是为了简化管理微服务之间的通信问题。为了实现微服务 Service Mesh 模式和诸多理念,Google , IBM 和 Lyft 这三家公司协同研发,并于 2017 年 6 月 8 日( 根据 Github 最后一次提交的时间 )发布了 Istio 的第一个发行版——Istio 0.1 版本。

二、istio架构

istio分为控制面和数据面,架构如下图所示。

![](/tp/5de067fab481934666d3bb44090fadad/istio

数据面:由一组sidecar组成,对应具体的组件为envoy;通过给每用启动一个轻量级的网络代理,来执行对网络通信的控制和调整,Sidecar和外围代理,实现客户端和服务器之间的安全通信;

控制面:负责管理和配置代理流量。具体通过mixer组件下发策略给envoy,执行策略并对各个sidecar收集数据。Citadel用于密钥和证书管理;pilot将身份验证策略和安全命名信息分发到代理;mixer用于管理授权和审核。

三、核心功能

流量管理:

下图显示了pilot的服务发现过程。

stio根据Kubernetes的适配器发现并注册service后,流量规则会由pilot解析成envoy理解的格式传送给Sidecar,进而控制服务间的流量和 API 调用。Istio 简化了断路器、超时和重试等服务级别属性的配置,并且可以轻松设置 A/B 测试、金丝雀部署和基于百分比的流量分割的分阶段部署等重要任务。

安全:

Istio提供底层安全通信通道,使开发人员可以专注于应用程序级别的安全,并提供大规模管理服务通信的身份验证、授权和加密。使用Istio,服务通信在默认情况下是安全的,可以跨不同的协议和运行时一致地实施策略,所有这些都只需很少或根本不需要更改应用程序。

安全涉及的几个组件及架构如下图所示:

Citadel:用于密钥和证书管理。

Sidecar和外围代理:实现客户端和服务器之间的安全通信。

pilot:将身份验证策略和安全命名信息分发到代理。

mixer:用于管理授权和审核。

策略定制:为应用程序配置自定义策略,以在运行时强制执行规则,如动态限制服务的通信量,通过名单限制对服务的访问,也可以创建自己的策略适配器,添加自定义授权行为。

可观察性:Istio强大的跟踪、监控和日志记录功能可让人深入了解服务网格的部署。通过Istio的监控功能,可以真正了解服务性能对上下游的影响,同时其定制的仪表盘可查看所有服务的性能,并了解该性能对其他流程有何影响。

四、基本功能验证

本环境基于Kubernets1.14和istio1.13版本进行验证。其中下面的例子都在官方链接的samples目录中,官方链接[1]istio官方例子

1、流量管理:

为了填充自己的服务注册表,Istio连接到服务发现系统,而在Kubernetes集群上安装了Istio,Istio会自动检测该集群中的服务和端点,使用此服务注册表,代理就可以将流量定向到相关服务。默认情况下同一工作负载多个实例,流量会平均分发,而作为A/B测试的一部分,也可以将特定百分比的流量定向到服务的新版本,或者对特定服务实例子集的流量应用不同的负载平衡策略。还可以对进出Mesh的流量应用特殊规则,或者将Mesh的外部依赖项添加到服务注册表。

以官方的bookinfo为例,使用对同一程序多版本的流量管理,具体配置如下:

自注入使能

kubectl label namespace default istio-injection=enabled

部署bookinfo到default namespaces,bookinfo服务之间默认的调用关系如下图:

可创建virtualService全部流量导向reviews-v1,yaml文件中host指向的是reviews service,只指定了v1版本,因此流量全导向reviews v1。

virtualService yaml如下:

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: reviews
spec:
  hosts:
  - reviews
  http:
  - route:
    - destination:
        host: reviews
        subset: v1

结果如下图,可以看到图中绿色部分就是当前流量的走向,全部走向previews v1:

2、安全,主要提供服务网格之间安全访问,这里以enable TLS为例。

创建meshPolicy全局enable tls

kubectl apply -f - <<EOF
apiVersion: "authentication.istio.io/v1alpha1"
kind: "MeshPolicy"
metadata:
  name: "default"
spec:
  peers:
  - mtls: {}
EOF

因为使能了TLS,所以不带证书访问会报错,直接http访问结果如下:

for from in "foo" "bar"; do for to in "foo" "bar"; do kubectl exec $(kubectl get pod -l app=sleep -n ${from} -o jsonpath={.items..metadata.name}) -c sleep -n ${from} -- curl "http://httpbin.${to}:8000/ip" -s -o /dev/null -w "sleep.${from} to httpbin.${to}: %{http_code}\n"; done; done
sleep.foo to httpbin.foo: 503
sleep.foo to httpbin.bar: 503
sleep.bar to httpbin.foo: 503
sleep.bar to httpbin.bar: 503

创建 destination rules使能TLS,目标是所有的集群内部的服务,然后服务之间就可以正常的访问了,使能TLS的操作如下:

kubectl apply -f - <<EOF
apiVersion: "networking.istio.io/v1alpha3"
kind: "DestinationRule"
metadata:
  name: "default"
  namespace: "istio-system"
spec:
  host: "*.local"
  trafficPolicy:
    tls:
      mode: ISTIO_MUTUAL
EOF

根据destination rules,访问所有集群内部服务都会带上TLS证书进行访问,使能TLS的访问结果:

for from in "foo" "bar"; do for to in "foo" "bar"; do kubectl exec $(kubectl get pod -l app=sleep -n ${from} -o jsonpath={.items..metadata.name}) -c sleep -n ${from} -- curl "http://httpbin.${to}:8000/ip" -s -o /dev/null -w "sleep.${from} to httpbin.${to}: %{http_code}\n"; done; done
sleep.foo to httpbin.foo: 200
sleep.foo to httpbin.bar: 200
sleep.bar to httpbin.foo: 200
sleep.bar to httpbin.bar: 200

除了全局指定tls,也可以单独指定namespace使能TLS,操作如下:

kubectl apply -f - <<EOF
apiVersion: "authentication.istio.io/v1alpha1"
kind: "Policy"
metadata:
  name: "default"
  namespace: "foo"
spec:
  peers:
  - mtls: {}
EOF

kubectl apply -f - <<EOF
apiVersion: "networking.istio.io/v1alpha3"
kind: "DestinationRule"
metadata:
  name: "default"
  namespace: "foo"
spec:
  host: "*.foo.svc.cluster.local"
  trafficPolicy:
    tls:
      mode: ISTIO_MUTUAL
EOF

指定特定service tls,操作如下:

cat <<EOF | kubectl apply -n bar -f -
apiVersion: "authentication.istio.io/v1alpha1"
kind: "Policy"
metadata:
  name: "httpbin"
spec:
  targets:
  - name: httpbin
  peers:
  - mtls: {}
EOF
cat <<EOF | kubectl apply -n bar -f -
apiVersion: "networking.istio.io/v1alpha3"
kind: "DestinationRule"
metadata:
  name: "httpbin"
spec:
  host: "httpbin.bar.svc.cluster.local"
  trafficPolicy:
    tls:
      mode: ISTIO_MUTUAL
EOF

3、策略

这里还是以官方的bookinfo为例,指定应用拒绝访问。

首先修改istio configmap修改disablePolicyChecks为false,使能policy;然后制定策略拒绝v3版本的访问版本,匹配源为reviews v3和目的ratings制定rule对应handler为拒绝访问,yaml如下:

 code: 7
      message: Not allowed
---
apiVersion: "config.istio.io/v1alpha2"
kind: instance
metadata:
  name: denyreviewsv3request
spec:
  compiledTemplate: checknothing
---
apiVersion: "config.istio.io/v1alpha2"
kind: rule
metadata:
  name: denyreviewsv3
spec:
  match: destination.labels["app"] == "ratings" && source.labels["app"]=="reviews" && source.labels["version"] == "v3"
  actions:
  - handler: denyreviewsv3handler
    instances: [ denyreviewsv3request ]

4、可观察性

Istio为网格内的所有服务通信生成详细的telemetry信息。此telemetry提供服务行为的可观察性,使运维人员能够对应用程序进行故障排除、维护和优化, 具体通过三个方面表现,第一个是metrics即指标,Istio根据监控的四个维度(延迟、流量、错误和饱和度)生成一组服务指标,暴露给proetheus。第二个是访问日志,当流量流入网格内的服务时,Istio可以生成每个请求的完整记录,包括源和目标元数据。此信息使操作员能够审核服务行为,直至各个工作负载实例级别。第三个是分布式跟踪, Istio提供了一种通过监视流经网格的各个请求来监视和了解行为的方法,了解服务网状网内的服务依赖关系和延迟来源。

以bookinfo为例,配置istio自动收集服务指标,每次调用网格内的服务,都会有相应的指标生成。

配置收集metrics的yaml文件如下:

# Configuration for metric instances
apiVersion: config.istio.io/v1alpha2
kind: instance
metadata:
  name: doublerequestcount
  namespace: istio-system
spec:
  compiledTemplate: metric
  params:
    value: "2" # count each request twice
    dimensions:
      reporter: conditional((context.reporter.kind | "inbound") == "outbound", "client", "server")
      source: source.workload.name | "unknown"
      destination: destination.workload.name | "unknown"
      message: '"twice the fun!"'
    monitored_resource_type: '"UNSPECIFIED"'
---
# Configuration for a Prometheus handler
apiVersion: config.istio.io/v1alpha2
kind: handler
metadata:
  name: doublehandler
  namespace: istio-system
spec:
  compiledAdapter: prometheus
  params:
    metrics:
    - name: double_request_count # Prometheus metric name
      instance_name: doublerequestcount.instance.istio-system # Mixer instance name (fully-qualified)
      kind: COUNTER
      label_names:
      - reporter
      - source
      - destination
      - message
---
# Rule to send metric instances to a Prometheus handler
apiVersion: config.istio.io/v1alpha2
kind: rule
metadata:
  name: doubleprom
  namespace: istio-system
spec:
  actions:
  - handler: doublehandler
    instances: [ doublerequestcount ]

在prometheus graph界面搜索istio_double_request_count,结果如下:

日志功能,使用资源instance,handler,rule创建,具体内容如下:

# Configuration for logentry instances
apiVersion: config.istio.io/v1alpha2
kind: instance
metadata:
  name: newlog
  namespace: istio-system
spec:
  compiledTemplate: logentry
  params:
    severity: '"warning"'
    timestamp: request.time
    variables:
      source: source.labels["app"] | source.workload.name | "unknown"
      user: source.user | "unknown"
      destination: destination.labels["app"] | destination.workload.name | "unknown"
      responseCode: response.code | 0
      responseSize: response.size | 0
      latency: response.duration | "0ms"
    monitored_resource_type: '"UNSPECIFIED"'
---
# Configuration for a stdio handler
apiVersion: config.istio.io/v1alpha2
kind: handler
metadata:
  name: newloghandler
  namespace: istio-system
spec:
  compiledAdapter: stdio
  params:
    severity_levels:
      warning: 1 # Params.Level.WARNING
    outputAsJson: true
---
# Rule to send logentry instances to a stdio handler
apiVersion: config.istio.io/v1alpha2
kind: rule
metadata:
  name: newlogstdio
  namespace: istio-system
spec:
  match: "true" # match for all requests
  actions:
   - handler: newloghandler
     instances:
     - newlog

访问productpage,可以看到有对应的日志生成,操作如下:

kubectl logs -n istio-system -l istio-mixer-type=telemetry -c mixer | grep "newlog" | grep -v '"destination":"telemetry"' | grep -v '"destination":"pilot"' | grep -v '"destination":"policy"' | grep -v '"destination":"unknown"'
{"level":"warn","time":"2019-12-16T16:45:53.950607Z","instance":"newlog.instance.istio-system","destination":"ratings","latency":"1.494269ms","responseCode":200,"responseSize":48,"source":"reviews","user":"unknown"}

分布式跟踪,使用jaeger进行trace, 默认采样率为1%。至少需要发送100个请求,才能看到一个跟踪,访问productpage操作:

for i in `seq 1 100`; do curl -s -o /dev/null http://$GATEWAY_URL/productpage; done

可以在jaeger dashboard看到对应跟踪信息,如下图: