«

Istio 简明教程

istio-logo

一、什么是Service Mesh 和Istio

服务网格 是致力于解决服务间通讯的基础设施层。它负责在现代云原生应用程序的复杂服务拓扑来可靠地传递请求。实际上,Service Mesh 通常是通过一组轻量级网络代理(Sidecarproxy),与应用程序代码部署在一起来实现,而无需感知应用程序本身。

Istio 是Service Mesh的一种实现,Istio 允许您以一种无侵入性的方式连接、保护、控制和观测服务。

Istio 可以为云原生应用提供的功能列表:
1. 智能路由和负载均衡
2. 网络容错
3. 策略增强
4. 可观察性
5. 服务间通信安全

二、Istio架构

Istio组件实现遵循典型的service-mesh架构:
- 数据面 (Data plane): Envoy 代理以边车的形式和你的服务部署在一起 - 控制面 (Control plane): 负责管理和配置数据面 ( Envoy 服务代理); 另外控制面提供可以补充数据面的后端基础服务 (例如metrics, policy, 和 security engines)

在 service mesh 中通过与每个应用部署在一起的 Envoy 代理进行通信. 所有网络层的逻辑例如 (retries, timeouts, circuit breaking,etc) 都从服务中迁移到service mesh中。

三、Istio的核心概念

1、DestinationRule

DestinationRule 配置将流量转发到服务时应用的策略集。 这些策略应由服务提供者撰写,用于描述断路器,负载均衡设置,TLS 设置或者定义目标主机的 subset(命名的版本)等。

例如, 定义服务名为 recommendation 的两个版本:

apiVersion: networking.istio.io/v1alpha3  
kind: DestinationRule  
metadata:  
  name: recommendation
  namespace: tutorial
spec:  
  host: recommendation
  subsets:
  ­ labels:
      version: v1
    name: version­v1
  ­ labels:
      version: v2
    name: version­v2

在这个例子中, 通过在服务上打标签的方式来定义 subsets (对于Kubernetes平台来说,这个例子中subsets中的标签引用的是k8s部署中所打的标签)。

2、VirtualService

VirtualService 描述了一个或多个用户可寻址目标到网格内实际工作负载之间的映射。

例如, 定义一个"virtual service" 把90%的流量请求给版本1,把剩余的10%流量给版本2:

apiVersion: networking.istio.io/v1alpha3  
kind: VirtualService  
metadata:  
  name: recommendation
  namespace: tutorial
spec:  
  hosts:
  ­ recommendation
  http:
  ­ route:
    ­ destination:
        host: recommendation
        subset: version­v1
      weight: 90
    ­ destination:
        host: recommendation
        subset: version­v2
      weight: 10

3、ServiceEntry

ServiceEntry 用于将附加条目添加到 Istio 内部维护的服务注册表中。 它最常用于对访问网格外部依赖的流量进行建模,例如访问Web 上的 API 或遗留基础设施中的服务。 由于 ServiceEntry 配置只是将服务添加到网格内部的服务注册表中,因此它可以像注册表中的任何其他服务一样,与 VirtualService 和/或 DestinationRule 一起使用。

例如配置一个httpbin的外部服务:

apiVersion: networking.istio.io/v1alpha3  
kind: ServiceEntry  
metadata:  
  name: httpbin­egress­rule
  namespace: istioegress
spec:  
  hosts:
  ­ httpbin.org
  ports:
  ­ name: http­80
    number: 80
    protocol: http

4、Gateway

Gateway 用于为 HTTP / TCP 流量配置负载均衡器,并不管该负载均衡器将在哪里运行。可以使用 VirtualService 为边缘入口流量或目标出口流量指定路由规则 (利用Istio的路由能力) 。

下面这个简单的 Gateway 配置了一个 Load Balancer,以允许访问host foo.com 的 https 外部流量进入网格中:

apiVersion: networking.istio.io/v1alpha3  
kind: Gateway  
metadata:  
  name: foo­gateway
spec:  
  servers:
  ­ port:
      number: 443
      name: https
      protocol: HTTPS
    hosts:
    ­ foo.com
    tls:
      mode: SIMPLE
      serverCertificate: /tmp/tls.crt
      privateKey: /tmp/tls.key

四、Istio实践

可以使用 sidecar 自动注入的方式或者手动方式安装 Istio 。 我们推荐在开始学习的时候以手动注入的方式安装istio

1、安装Istio

首先你需要下载Istio并注册PATH环境变量:

open https://github.com/istio/istio/releases/

cd istio-1.2.2  
export ISTIO_HOME=`pwd`  
export PATH=$ISTIO_HOME/bin:$PATH  

你可以通过 helm install 或者 helm template 的方式在k8s集群上安装Istio. 通过 template 方式我们可以显示创建一个包含所有资源的文件,然后通过 kubectl 来安装Istio,参考如下:

$ helm template install/kubernetes/helm/istio \
    --name istio --namespace istio-system \
    --set sidecarInjectorWebhook.enabled=false \
    > $HOME/istio.yaml

kubectl create namespace istio-system  
kubectl create -f $HOME/istio.yaml  

等待所有的pod启动起来。

2、智能路由

以百分比的方式把流量路由到recommendation服务:

apiVersion: networking.istio.io/v1alpha3  
kind: VirtualService  
metadata:  
  name: recommendation
  namespace: tutorial
spec:  
  hosts:
  - recommendation
  http:
  - route:
    - destination:
        host: recommendation
        subset: version-v1
      weight: 75
    - destination:
        host: recommendation
        subset: version-v2
      weight: 25

根据URI前缀和cookie的正则匹配把流量路由给指定版本

spec:  
  hosts:
  - ratings
  http:
  - match:
    - headers:
        cookie:
          regex: "^(.*?;)?(user=jason)(;.*)?"
        uri:
          prefix: "/ratings/v2/"
    route:
    - destination:
        host: ratings
        subset: version-v2

一些 match 选项:

|字段|类型|描述| | --- | --- | --- | |uri|StringMatch|待匹配的URI值. exact , prefix , regex| |scheme|StringMatch|待匹配的URI Scheme. exact , prefix , regex| |method|StringMatch|待匹配的Http Method. exact , prefix , regex| |authority|StringMatch|待匹配的Http Authority value. exact , prefix , regex| |headers|map<string, StringMatch>|Headers key/value. exact , prefix , regex| |port|int|指定主机上的端口。有的服务只开放一个端口,有的服务会用协议作为前缀给端口命名,这两种情况下,都不需要显式的指明端口号。| |sourceLabels|map<string, string >|Caller labels to match| |gateways|string[]|规则所涉及的 Gateway 的名称列表。|

根据调用方标签来发送流量

- match:
  - sourceLabels:
      app: preference
      version: v2
  route:
  - destination:
      host: recommendation
      subset: version-v2
- route:
  - destination:
      host: recommendation
      subset: version-v1

当服务调用方包含标签 app=preferenceversion=v2 , 流量会被路由到包含标签 version-v2subset . 否则, 流量被路由到 version-v1

两个版本之间的流量镜像:

spec:  
  hosts:
  - recommendation
  http:
  - route:
    - destination:
        host: recommendation
        subset: version-v1
    mirror:
      host: recommendation
      subset: version-v2

对于路由目的的 VirtualService 来说也支持 redirects , rewrites , corsPolicies 或者 appending 的自定义headers。

除了HTTP 规则, VirtualService 也支持 TCP 级别的规则。

spec:  
  hosts:
  - postgresql
  tcp:
  - match:
    - port: 5432
      sourceSubnet: "172.17.0.0/16"
    route:
    - destination:
        host: postgresql
        port:
          number: 5555

一些 TCP 级别的 match 选项:

|字段|类型|描述| | --- | --- | --- | |destinationSubnet|string|目标的 IPv4 或者 IPv6 地址,可能带有子网标识,a.b.c.d/xx 或者 a.b.c.d 都有可能。| |port|int|指定主机上的端口。有的服务只开放一个端口,有的服务会用协议作为前缀给端口命名,这两种情况下,都不需要显式的指明端口号。| |sourceSubnet|string|源IPv4 或者 IPv6 地址,可能带有子网标识,a.b.c.d/xx 或者 a.b.c.d 都有可能。| |sourceLabels|map<string, string >|调用方标签| |gateways|string[]|规则所涉及的 Gateway 的名称列表。|

3、Resilience

Retry

如果请求报错在抛出错误之前重试3次.

spec:  
  hosts:
  - recommendation
  http:
  - retries:
      attempts: 3
      perTryTimeout: 4.000s
    route:
    - destination:
        host: recommendation
        subset: version-v1

Timeout

你可以设置通信超时时间,比如设置1秒超时。

spec:  
  hosts:
  - recommendation
  http:
  - route:
    - destination:
        host: recommendation
    timeout: 1.000s

异常检测/断路器

如果请求被转发到特定实例并且请求失败(例如:返回5xx错误码) 那么这个实例会被认为异常,并在一段时间内客户端请求不会路由到该实例。

在下一个例子中设置了每15秒异常检测,如果发生连续5次错误则主机会被拒绝服务2分钟。

apiVersion: networking.istio.io/v1alpha3  
kind: DestinationRule  
metadata:  
  name: recommendation
  namespace: tutorial
spec:  
  host: recommendation
  trafficPolicy:
    outlierDetection:
      baseEjectionTime: 2m
      consecutiveErrors: 5
      interval: 15.000s
      maxEjectionPercent: 100
  subsets:

trafficPolicy 可以应用在subset级别,这样该policy就会应用于整个subset.

你可以在 TCP 和 HTTP 级别创建连接池:

trafficPolicy:  
  connectionPool:
    http:
      http1MaxPendingRequests: 100
      http2MaxRequests: 100
      maxRequestsPerConnection: 1
    tcp:
      maxConnections: 100
      connectTimeout: 50ms

流量策略(Traffic Policy)的一些值:

|字段|类型|描述| | --- | --- | --- | |loadbalancer|LoadBalancerSettings|负载均衡算法| |connectionPool|ConnectionPoolSettings|连接池设置| |outlierDetection|OutlierDetection|驱逐不健康的主机| |tls|TLSSettings|TLS设置| |portLevelSettings|PortTrafficPolicy[]|针对指定端口的流量策略|

4、Policy Enforcement

Istio提供了一个增强授权的策略模型。 比如说你可以在服务间通信加上黑名单、白名单限制或者一些配额限制。

你可以配置只允许来自recommendation服务的请求:

apiVersion: "config.istio.io/v1alpha2"  
kind: listchecker  
metadata:  
  name: preferencewhitelist
spec:  
  overrides: ["recommendation"]
  blacklist: false
apiVersion: "config.istio.io/v1alpha2"  
kind: listentry  
metadata:  
  name: preferencesource
spec:  
  value: source.labels["app"]
---
apiVersion: "config.istio.io/v1alpha2"  
kind: rule  
metadata:  
  name: checkfromcustomer
spec:  
  match: destination.labels["app"] == "preference"
  actions:
  - handler: preferencewhitelist.listchecker
    instances:
    - preferencesource.listentry

Source 部分通过 listchecker (提供允许访问的服务) 和 listentry (配置如何从请求中获取到白名单校验的值) 元素来完成配置。 Destination 部分和规则通过rule来配置。

|字段|类型|描述| | --- | --- | --- | |providerUrl|string|Url 用来加载需要检查的列表,可以为空| |refreshInterval|Duration|指定provider被拉取的频率| |ttl|Duration|指定列表被保存多久,之后会被丢弃| |cachingInterval|Duration|指定调用方可以缓存结果的时间间隔| |cachingUseCount|int|指定调用方可以使用缓存结果的次数| |overrides|string[]|在providerUrl被调用前被查询的列表值| |entryType|ListEntryType|list entry 和 overrides的类别( STRINGS , CASE_INSENSITIVE_STRINGS , IP_ADDRESSES,REGEX` )| |blacklist|boolean|黑名单/白名单列表|

5、Telemetry, Monitoring and Tracing

Istio通过集成 Prometheus/Grafana and Jaeger (OpenAPI Spec)可提供开箱即用的可观测能力。

6、Service to Service Security

你可以通过启用双向TLS来保证服务间的通信安全

Mutual TLS

首先,启用mutual TLS.

你可以通过 MeshPolicy 进行全局启用

exact

或者通过 Policy 进行更精细的控制; 下面的例子是在namespace级别进行policy设置:

prefix

指定目标和端口启用mTLS:

regex

重要提示: 如果 ports 未设置,那么会对所有端口生效。

|字段|类型|描述| | --- | --- | --- | |peers|PeerAuthentication Method[]|peer auth的authentication methods列表| |peerIsOptional|boolean|当peer authentication methods不满足时仍接受请求| |targets|TargetSelector[]|指定策略的Destinations。默认所有目标服务| |origins|OriginAuthentication Method[]|origin auth的authentication methods列表| |originIsOptional|boolean|当origin authentication methods不满足时仍接受请求| |principalBinding|PrincipalBinding|Peer或origin identity. 默认 USE_PEER|

7、End-user Authentication

通过JWT认证终端用户 (origin authentication):

StringMatch

这次, Origins 只支持JWT. JWT的值如下:

|字段|类型|描述| | --- | --- | --- | |issuer|string|token的Issuer| |audiences|string[]|允许访问的JWT audiences 列表| |jwksUri|string|用于验签的公钥URL| |jwtParams|string[]|JWT以query parameter被发送| |jwtHeaders|string[]|如果Authorization: Bearer $token不存在则,JWT会在请求头中发送。|

开启 mTLS之后, 你需要在客户端侧通过 DestinationRule 进行相应配置. 通过 host 字段指定通过mTLS方式通信的hosts

exact

如果 ISTIO_MUTUAL 已经设置了, Istio会根据内部实现配置客户端证书, private key和CA证书.

|字段|类型|描述| | --- | --- | --- | |httpsRedirect|boolean|用 301 重定向指令响应 http 协议的请求。| |mode|TLSmode|这一字段的值决定了如何使用 TLS, PASSTHROUGH , SIMPLE , MUTUAL| |serverCertificate|string|指定了服务端的 TLS 证书| |privateKey|string|指定了服务端的 TLS 密钥| |caCertificates|string|用于验证客户端证书的 ca 证书| |subjectAltNames|string[]|用于验证客户端证书的一组认证主体名称|

8、Istio RBAC

Istio的授权特性提供了Istio Mesh中服务的访问控制.

开启 RBAC:

prefix

合法选项: ON , OFF , ON_WITH_INCLUSION , ON_WITH_EXCLUSION 。 当 WITH_INCLUSIONinclusion 条件会生效, 当 WITH_EXCLUSIONexclusion 条件会生效。

他们支持以下的属性列表:

|字段|类型|描述| | --- | --- | --- | |services|string[]|服务列表| |namspaces|string[]|命名空间列表|

赋予使用 GET 方法访问指定目标服务的所有源服务权限。

regex

|字段|类型|描述| | --- | --- | --- | |services|string[]|服务名列表.| |paths|string[]|HTTP paths 列表| |methods|string[]|HTTP methods 列表| |constraints|Constraint[]|其他约束|

Constraint 是一组 key ( string ) 和 values ( string[] )数组。 合法的 keys 如下表所示:

|键|值| | --- | --- | |destination.ip|["10.1.2.3", "10.2.0.0/16"]| |destination.port|["80", "443"]| |destination.labels[version]|["v1", "v2"]| |destination.name|["productpage*"]| |destination.namespace|["tutorial"]| |destination.user|["customer-tutorial"]| |request.headers[X-Custom-Token]|["345CFA3"]|

把角色( what )和主体( who )绑定

StringMatch

|字段|类型|描述| | --- | --- | --- | |user|string|用户名/ID ( Service Account ).| |properties|map|用于识别主体的属性|

支持的属性如下所示:

|键|值| | --- | --- | |source.ip|"10.1.2.3"| |source.namespace|"default"| |source.principal|"customer"| |request.headers[User-Agent]|"Mozilla/"| |request.auth.principal|"users.tutrial.org/654654"| |request.auth.audiences|"tutorial.org"| |request.auth.presenter|"654654.tutorial.org"| |request.auth.claims[iss]|"@redhat.com"|

最后一个属性引用了名叫 iss 的JWT claim. 当然, 你可以使用任何其他的claim. 通常来说, 你可能会使用 group claim 来授予指定组级别用户的访问权限。

参考资料:

  1. 中文版:Isito 简明教程
  2. 英文版:Istio cheat-sheet
  3. Istio 实战
  4. Istio Service Mesh 实现微服务
  5. Red Hat 开发者Istio教程
  6. Istio 中文社区
分享