运维调试:Istio


可观察性

Istio 生成以下类型的 telemetry(遥测信息) ,以提供整体服务网格可观察性:

  • Metrics
    Istio 根据监视的四个“黄金信号”( latency(延迟) 、 traffic(流量) 、 errors 、 saturation(饱和度) ) 为服务生成一组指标数据。
    Istio 还为网格控制平面 提供了的详细的指标数据。
    还基于这些指标构建了一组内置的网格监视仪表板。
    metrics的收集从sidecar代理(Envoy)开始, Envoy自身也有很丰富的metrics统计信息。这被称为 Proxy-level metrics
    除了 Proxy-level metrics , Istio 还提供了一组 面向服务 的metrics,就是上述的 latency(延迟) 、 traffic(流量) 、 errors 、 saturation(饱和度) 。 这被称为 Service-level metrics
    启用 Service-level metrics 是可选的,运维人员可以选择关闭这些指标的生成和收集,以满足他们的个性化需求。
  • Distributed Traces
    Istio 为每个服务生成分布式追踪span,从而使运维人员能够详细了解网格内的服务调用链和服务之间的依赖性

  • Access Logs
    当流量进入网格内的服务时, Istio 可以为每个请求生成完整的记录,包含源和目标的metadata。
    该信息可以使运维人员审计在单个工作负载的级别上审计服务的行为。


启用 Envoy的访问日志

IstioOperator CR 中增加如下配置:

spec:
  meshConfig:
    accessLogFile: /dev/stdout


流量管理方面的常见问题


Requests被Envoy拒绝

Requests可能由于各种原因而被拒绝。 理解为什么拒绝请求的最佳方法是检查 Envoy的访问日志 。 默认情况下,访问日志输出到容器的标准输出。 运行以下命令以查看日志:

kubectl logs PODNAME -c istio-proxy -n NAMESPACE

在默认访问日志默认格式中,在返回码后面有 Envoy response flagsMixer policy status
如果您使用的是自定义日志格式,请确保包括 %RESPONSE_FLAGS%%DYNAMIC_METADATA(istio.mixer:status)%

常见的 response flags 有:

  • NR : No Route configured , 检查 DestinationRule 或 VirtualService
  • UO : Upstream overflow with circuit breaking, 检查 DestinationRule 中的断路器配置
  • UF : Upstream Failed . 如果你正在使用 Istio 认证,检查 mTLS配置冲突
  • UAEX : 表示request是被 Mixer 拒绝的(Mixer policy status的值不能是 -

常见的 Mixer Policy status 有:

  • UNAVAILABLE : Envoy不能连接到Mixer并且Policy被配置为Fail Close
  • UNAUTHENTICATED : 请求被 Mixer authentication 拒绝
  • PERMISSION_DENIED : 请求被 Mixer authorization 拒绝
  • RESOURCE_EXHAUSTED : 请求被 Mixer quota 拒绝
  • INTERNAL : 请求被拒绝因为 Mixer内部错误

路由规则似乎没有影响traffic flow

Envoy的当前版本实现中,可能需要多达100个请求才能遵守按权重分发流量
如果路由规则对于Bookinfo示例而言运行良好,但是类似的“版本路由规则”对您自己的应用程序没有影响,则可能需要您对您的Kubernetes服务需要稍作更改。
Kubernetes服务必须遵守某些限制,才能利用 Istio 的L7路由功能。 有关详细信息,请参阅Pod和服务要求。

另一个潜在的可能是路线规则的生效速度有些缓慢。 Kubernetes上的 Istio实现 使用 最终一致的算法 来确保所有Envoy sidecar具有一致的配置,包括所有路由规则。 配置更改将需要一些时间才能传播到所有 sidecars。 在大型部署中,传播将花费更长的时间,并且可能会有几秒钟的延迟时间。


在设置了Destination rule之后返回HTTP 503错误

如果在应用DestinationRule后对服务的请求立即生成 HTTP 503错误 ,并且错误一直持续到您删除或还原DestinationRule,则DestinationRule可能会导致服务的 TLS冲突
例如,如果你在集群全局配置了双向TLS,那么 DestinationRule必须包含如下的 trafficPolicy :

trafficPolicy:
  tls:
    mode: ISTIO_MUTUAL

否则, mode 的默认值是 DISABLE , 将导致客户端的 proxy sidecar 发出纯HTTP请求而不是TLS加密请求。 因为服务端的 proxy sidecar 期望加密的请求,所以客户端代理发出的请求与服务端代理期望接受的请求发生冲突。

每当您应用DestinationRule时,请确保 trafficPolicy TLS模式 与全局配置匹配。


路由规则对ingress gateway上的请求无效

假设您使用 ingress gateway 和相应的VirtualService访问内部服务。 例如,您的VirtualService看起来像这样:

apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
  name: myapp
spec:
  hosts:
  - "myapp.com" # or maybe "*" if you are testing without DNS using the ingress-gateway IP (e.g., http://1.2.3.4/hello)
  gateways:
  - myapp-gateway
  http:
  - match:
    - uri:
        prefix: /hello
    route:
    - destination:
        host: helloworld.default.svc.cluster.local

你还有一个 VirutalService 定义将流量路由到特定的subset,如下所示:

apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
  name: helloworld
spec:
  hosts:
  - helloworld.default.svc.cluster.local
  http:
  - route:
    - destination:
        host: helloworld.default.svc.cluster.local
        subset: v1

在这种情况下,您会注意到,通过入口网关向helloworld服务的请求将不会定向到子集v1,而是将继续使用默认的 round-robin routing(轮询路由)
以 gateway host(例如myapp.com)作为入口的请求将激活 myapp VirtualService 中的规则,该规则会将流量路由到helloworld服务的任意endpoint。
以 helloworld.default.svc.cluster.local 作为入口的的内部请求才会激活 helloworld VirtualService 中的规则,该规则会将流量路由到 subset v1
要控制以 gateway host(例如myapp.com)作为入口的请求,还需要在 myapp VirtualService 中包括子集规则:

apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
  name: myapp
spec:
  hosts:
  - "myapp.com" # or maybe "*" if you are testing without DNS using the ingress-gateway IP (e.g., http://1.2.3.4/hello)
  gateways:
  - myapp-gateway
  http:
  - match:
    - uri:
        prefix: /hello
    route:
    - destination:
        host: helloworld.default.svc.cluster.local
        subset: v1

或者将这两个 VirtualService 合并成一个:

apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
  name: myapp
spec:
  hosts:
  - myapp.com # cannot use "*" here since this is being combined with the mesh services
  - helloworld.default.svc.cluster.local
  gateways:
  - mesh # applies internally as well as externally
  - myapp-gateway
  http:
  - match:
    - uri:
        prefix: /hello
      gateways:
      - myapp-gateway #restricts this rule to apply only to ingress gateway
    route:
    - destination:
        host: helloworld.default.svc.cluster.local
        subset: v1
  - match:
    - gateways:
      - mesh # applies to all services inside the mesh
    route:
    - destination:
        host: helloworld.default.svc.cluster.local
        subset: v1

Envoy is crashing under load

检查 ulimit -a 。 很多系统有默认最多打开 1024个fd(file descriptor) 的限制,这会导致 Envoy 的 assert失败并crash,错误信息如下:

[2017-05-17 03:00:52.735][14236][critical][assert] assert failure: fd_ != -1: external/envoy/source/common/network/connection_impl.cc:58

请确保提高了该限制,例如 ulimit -n 16384


Envoy无法连接到我的 HTTP/1.0 服务

Envoy需要upstream服务是 HTTP/1.1HTTP/2
例如当你使用Nginx作为上游服务的web server时,你需要设置nginx的 proxy_http_version 1.1 指令,因为nginx的该指令默认值是 1.0
示例

upstream http_backend {
    server 127.0.0.1:8080;

    keepalive 16;
}

server {
    ...

    location /http/ {
        proxy_pass http://http_backend;
        proxy_http_version 1.1;
        proxy_set_header Connection "";
        ...
    }
}

TLS配置错误类问题

很多traffic管理问题都是由不正确的 TLS配置 导致的。

发送HTTPS到HTTP端口

如果你将 HTTPS请求 发送到声明为HTTP的服务,则 Envoy sidecar 将在转发请求时尝试将请求解析为HTTP,这将失败,因为HTTP意外地被加密。

例如你有如下的 ServiceEntry 定义:

apiVersion: networking.istio.io/v1beta1
kind: ServiceEntry
metadata:
  name: httpbin
spec:
  hosts:
  - httpbin.org
  ports:
  - number: 443
    name: http
    protocol: HTTP
  resolution: DNS

当你发送 HTTPS请求,例如 curl https://httpbin.org , 你会得到一个错误如下 curl: (35) error:1408F10B:SSL routines:ssl3_get_record:wrong version number
Envoy的 access log 会显示一个 400 DPE 的错误
这是因为你将 protocol 声明成了 HTTP 而不是 HTTPS
要修复这个问题,修改 ServiceEntry 定义如下:

apiVersion: networking.istio.io/v1beta1
kind: ServiceEntry
metadata:
  name: httpbin
spec:
  hosts:
  - httpbin.org
  ports:
  - number: 443
    name: https
    protocol: HTTPS
  resolution: DNS

Gateway to virtual service TLS mismatch

virtual service 绑定到 gateway 时,可能会发生两种常见的TLS不匹配:

  • gateway terminate(终结了) TLS , 而 virtual service 配置了 TLS路由
  • gateway passthrough TLS ,而 virtual service 没有配置 TLS路由

gateway终结了TLS而virtual service配置了TLS路由示例
假设你有如下的配置:

apiVersion: networking.istio.io/v1beta1
kind: Gateway
metadata:
  name: gateway
  namespace: istio-system
spec:
  selector:
    istio: ingressgateway
  servers:
  - port:
      number: 443
      name: https
      protocol: HTTPS
    hosts:
      - "*"
    tls:
      mode: SIMPLE
      credentialName: sds-credential
---
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
  name: httpbin
spec:
  hosts:
  - "*.example.com"
  gateways:
  - istio-system/gateway
  tls:
  - match:
    - sniHosts:
      - "*.example.com"
    route:
    - destination:
        host: httpbin.org

上面的这个配置, gateway 终结了TLS,而 virtual service 使用了基于TLS的路由规则, 这个路由规则不会生效 ,因为当路由规则被 evaluted(评估) 时,TLS已经被终结。
使用这个配置,你会得到 404状态码
你可以通过 istioctl proxy-config routes 命令来确认这个信息

gateway passthrough TLS ,而 virtual service 没有配置 TLS路由示例
假如你有如下的配置

apiVersion: networking.istio.io/v1beta1
kind: Gateway
metadata:
  name: gateway
spec:
  selector:
    istio: ingressgateway
  servers:
  - hosts:
    - "*"
    port:
      name: https
      number: 443
      protocol: HTTPS
    tls:
      mode: PASSTHROUGH
---
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
  name: virtual-service
spec:
  gateways:
  - gateway
  hosts:
  - httpbin.example.com
  http:
  - route:
    - destination:
        host: httpbin.org

上面的这个配置, gateway passthrough TLS,而 virtual service 没有使用基于TLS的路由规则, 这个路由规则不会生效
你可以通过 istioctl proxy-config listeneristioctl proxy-config routes 命令来确认这个信息

Double TLS (TLS origination for a TLS request)

当配置 Istio 执行 TLS origination 时,你需要确保发送到 sidecar 的请求是纯文本的, sidecar 随后发起 TLS origination

假定你有如下的配置

apiVersion: networking.istio.io/v1beta1
kind: ServiceEntry
metadata:
  name: httpbin
spec:
  hosts:
  - httpbin.org
  ports:
  - number: 443
    name: https
    protocol: HTTPS
  resolution: DNS
---
apiVersion: networking.istio.io/v1beta1
kind: DestinationRule
metadata:
  name: originate-tls
spec:
  host: httpbin.org
  trafficPolicy:
    tls:
      mode: SIMPLE

使用此配置, Sidecar 希望应用程序在端口443上发送TLS流量(例如 curl https://httpbin.org ),但它还将在转发请求之前执行 TLS origination 。 这将导致请求被双重加密。
你会得到一个错误 (35) error:1408F10B:SSL routines:ssl3_get_record:wrong version number
要修复这个问题,你可以将 ServiceEntry 中的 protocol 更改为 HTTP
然后用 curl http://httpbin.org:443 的方式发出请求
幸运的是从 Istio 1.8 开始,你可以向80端口发出请求,然后将请求重定向到 443以进行TLS发起, 如下所示:

apiVersion: networking.istio.io/v1beta1
kind: ServiceEntry
metadata:
  name: httpbin
spec:
  hosts:
  - httpbin.org
  ports:
  - number: 80
    name: http
    protocol: HTTP
    targetPort: 443

当多个网关配置了相同的TLS证书时发生404错误

使用相同的TLS证书配置多个网关 将导致利用 HTTP/2连接重用 的浏览器(即大多数浏览器) 在与一主机的建立连接之后 访问第二主机时产生 404错误

假定你有如下配置:

  • 安装在 istio-ingressgateway 中的通配符证书 *.test.com
  • Gateway gw1 配置了 host = service1.test.com , selector = istio: ingressgateway , 并且使用网关已安装的通配符证书的TLS
  • Gateway gw2 配置了 host = service2.test.com , selector = istio: ingressgateway , 并且使用网关已安装的通配符证书的TLS
  • VirtualService vs1 绑定 gw1 和 host = service1.test.com
  • VirtualService vs2 绑定 gw2 和 host = service2.test.com

由于两个网关由相同的工作负载(即 selector = istio: ingressgateway )提供服务,因此对两个服务( service1.test.comservice2.test.com )的请求都将解析为相同的IP。
如果首先访问 service1.test.com ,它将返回通配符证书( *.test.com ),指示与 service2.test.com 的连接可以使用同一证书。 因此,Chrome和Firefox等浏览器将重复使用现有连接来请求对 service2.test.com 的请求。 由于 Gateway gw1 没有 service2.test.com 的路由,因此它将返回 404响应

要避免这个问题,你可以配置一个单独的 Gateway , 而不是两个( gw1 和 gw2 ) 。就像下面这样 :

  • Gateway gw 配置了 host = *.test.com , selector = istio: ingressgateway , 并且使用网关已安装的通配符证书的TLS
  • VirtualService vs1 绑定 gw 和 host = service1.test.com
  • VirtualService vs2 绑定 gw 和 host = service2.test.com

在网关中配置多个TLS主机时端口冲突

当使用相同的 selector 配置多个 Gateway 时,如果它们都暴露了相同的 HTTPS 端口,你必须确保它们使用了不同的端口名称。否则后创建的Gateway会无效( 虽然不会报错

加入你有如下配置:

apiVersion: networking.istio.io/v1beta1
kind: Gateway
metadata:
  name: mygateway
spec:
  selector:
    istio: ingressgateway # use istio default ingress gateway
  servers:
  - port:
      number: 443
      name: https
      protocol: HTTPS
    tls:
      mode: SIMPLE
      serverCertificate: /etc/istio/ingressgateway-certs/tls.crt
      privateKey: /etc/istio/ingressgateway-certs/tls.key
    hosts:
    - "myhost.com"
---
apiVersion: networking.istio.io/v1beta1
kind: Gateway
metadata:
  name: mygateway2
spec:
  selector:
    istio: ingressgateway # use istio default ingress gateway
  servers:
  - port:
      number: 443
      name: https
      protocol: HTTPS
    tls:
      mode: SIMPLE
      serverCertificate: /etc/istio/ingressgateway-certs/tls.crt
      privateKey: /etc/istio/ingressgateway-certs/tls.key
    hosts:
    - "myhost2.com"

使用这个配置,发送到 myhost2.com 的请求会失败,因为两个 Gateway 使用了相同的端口名称 httpscurl 请求会产生如下的错误:

curl: (35) LibreSSL SSL_connect: SSL_ERROR_SYSCALL in connection to myhost2.com:443

你可以通过检查 Pilot的日志来确认发生了什么,如下所示:

$ kubectl logs -n istio-system $(kubectl get pod -l istio=pilot -n istio-system -o jsonpath={.items..metadata.name}) -c discovery | grep "non unique port"
2018-09-14T19:02:31.916960Z info    model   skipping server on gateway mygateway2 port https.443.HTTPS: non unique port name for HTTPS port

要避免这个问题,可以将第二个gateway的端口名称修改(例如 https2


安全方面的常见问题

(TODO 待补充)
https://istio.io/latest/docs/ops/common-problems/security-issues/


可观察性方面的常见问题

Zipkin中没有数据

Istio被安装,并且所有的事情看起来都正常,除了Zipkin中没有追踪数据

这可能是由一个已知的Docker问题引起的,就是容器内部的时间可能与主机上的时间显著不同。(这个问题在 OSX 操作系统上出现)

你可以通过在容器内部和外部执行 date 命令来确认。

要解决这个问题,你需要关闭并重启 Docker ,然后重新安装 Istio

Grafana输出丢失

同样可能是时间的问题
当你从本地web客户端连接到远端的Grafana时,如果你不能获取Grafana的输出,请确认本地客户端时间和服务端的时间是否相同
本地web客户端(例如 Chrome)影响Grafana的输出

验证Istio CNI Pod是否正在运行(如果使用)

Istio CNI插件 在Kubernetes Pod生命周期的网络设置阶段执行 Istio Mesh Pod流量重定向 ,从而消除了将Pod部署到Istio Mesh中的用户对 NET_ADMINNET_RAW 功能的要求。
Istio CNI插件 取代了 istio-init容器 提供的功能。

  1. 验证istio-cni-node容器是否正在运行:
kubectl -n kube-system get pod -l k8s-app=istio-cni-node
  1. 如果在群集中强制执行 PodSecurityPolicy ,请确保 istio-cni ServiceAccount 可以使用允许 NET_ADMINNET_RAW 功能的 PodSecurityPolicy

Sidecar注入方面的常见问题

(TODO 待补充)
https://istio.io/latest/docs/ops/common-problems/injection/


配置验证方面的常见问题

(TODO 待补充)
https://istio.io/latest/docs/ops/common-problems/validation/


诊断工具 - istioctl

获取服务网格的overview信息

istioctl proxy-status
istioctl ps

如果输出列表中缺少某个proxy,则表示该proxy当前未连接到Pilot实例,因此不会接收任何配置。
此外,如果proxy被标记为 stale(陈旧) ,则可能意味着存在网络问题或需要 扩充Pilot实例的数量 。

获取proxy的配置信息

istioctl proxy-config cluster  [flags]
istioctl pc cluster  [flags]

查阅pod信息

istioctl experimental describe pod [.]
istioctl x describe pod [.]

分析istio配置

istioctl analyze --all-namespaces

# 分析文件
istioctl analyze samples/bookinfo/networking/

Istiod Introspection(内省、自我检查)

Istiod 使用灵活的 Introspection(自省)框架(称为 ControlZ )构建,这使得它很容易检查和操作 istiod 实例 的内部状态。
Istiod 打开一个web端口,可以通过浏览器使用交互视图更改其状态,或者通过REST API的方式。
istiod 启动时,通过它的日志信息可以看到连接到 ControlZ 的IP和端口
你可以通过在本地使用 istioctl dashboard 命令开启对 Istio组件 的访问

istioctl dashboard controlz  -n istio-system

然后打开浏览器 http://localhost:9876

istio组件的日志

Istio组件使用灵活的日志框架构建,该日志框架提供了许多功能和控件,以帮助操作这些组件并促进诊断。
启动组件时,可以通过传递 命令行选项 来控制这些日志记录功能。

1. Logging scopes

组件输出的日志消息按 scopes 分类。 scope 代表一组可以整体控制的相关日志消息。 根据组件提供的功能,不同的组件具有不同的 scopes 。 所有组件都有 default scope ,该scope用于未分类的日志消息。

例如,在撰写本文时, istioctl 具有24个 scope ,代表命令中的不同功能区域: ads, adsc, analysis, attributes, authn, authorization, cache, cli, default, grpcAdapter, installer, mcp, model, patch, processing, resource, secretfetcher, source, spiffe, tpath, translator, util, validation, validationController

Pilot-Agent,Pilot-Discovery和Istio Operator 具有它们自己的scopes,您可以通过查看它们的参考文档来发现它们。

每个scope都有一个唯一的log level 为以下之一:
none error warning info debug

none 不产生任何输出, debug 则产生最多的输出
所有scopes的默认level是 info

要控制输出的level,可以使用 --log_output_level 命令行选项,如下所示:

istioctl analyze --log_output_level attributes:debug,cli:warn

2. 控制输出位置

日志消息通常发送到组件的标准输出流。 使用 --log_target 选项,您可以将输出定向到任意数量的不同位置。 您可以为该选项提供一个用逗号分隔的文件系统路径列表,以及特殊值 stdout和stderr ,分别表示标准输出和标准错误流。

日志消息通常以人类友好的格式输出。 --log_as_json 选项可用于将输出强制转换为JSON,这样工具可以更轻松地进行处理。

3. Log rotation

Istio控制平面组件 可以自动管理 Log rotation ,这使将大型日志分解为较小的日志文件变得很简单。 使用 --log_rotate 选项,您可以指定用于rotation的基本文件名。 派生名称将用于单个日志文件。

使用 --log_rotate_max_age 选项可以指定文件rotation之前的最大天数,而使用 --log_rotate_max_size 选项可以指定文件rotation之前的最大大小(以megabytes为单位)。 最后, --log_rotate_max_backups 选项使您可以控制要保留的最大rotation文件数,较旧的文件将被自动删除。

3. 组件调试

使用 --log_caller--log_stacktrace_level 选项,您可以控制日志信息是否包括 programmer-level 的信息。 当试图跟踪组件中的bug时,这很有用,但通常在日常操作中不使用。


诊断工具 - pilot-discovery

该命令被 pilot 使用, pilotistiod 的一部分,所以你可以在 istiod pod内执行该命令

kubectl exec -it istiod-76655fb8dd-8x7xk -c discovery -- bash

诊断工具 - operator

该命令被 istio-operator namespace中的 istio-operator deployment 使用,所以你可以在 istio-operator pod内执行该命令

kubectl exec -it -n istio-operator istio-operator-86d8595474-chkwf -- bash

诊断工具 - pilot-agent

该命令被注入的pod中的 istio-proxy container 使用,所以你可以在任意被注入的pod的 istio-proxy container 内执行该命令

kubectl exec -it  -c istio-proxy -- bash

你可能感兴趣的:(运维调试:Istio)