可观察性
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 flags
和 Mixer 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.1
或 HTTP/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 listener
和 istioctl 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.com
和 service2.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
使用了相同的端口名称 https
, curl
请求会产生如下的错误:
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_ADMIN
和 NET_RAW
功能的要求。
Istio CNI插件
取代了 istio-init容器
提供的功能。
- 验证istio-cni-node容器是否正在运行:
kubectl -n kube-system get pod -l k8s-app=istio-cni-node
- 如果在群集中强制执行
PodSecurityPolicy
,请确保istio-cni ServiceAccount
可以使用允许NET_ADMIN
和NET_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
使用, pilot
是 istiod
的一部分,所以你可以在 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