1. Service 端口命名约束

Istio 支持多平台,不过 Istio 和 Kubernetes 的兼容性是最优的,不管是设计理念,核心团队还是社区,都有一脉相承的意思。但 Istio 和 Kubernetes 的适配并非完全没有冲突,一个典型问题就是 Istio 需要 Kubernetes service 按照协议进行端口命名(port naming)。

端口命名不满足约束而导致的流量异常,是使用 mesh 过程中最常见的问题,其现象是协议相关的流控规则不生效,这通常可以通过检查该 port LDS 中 filter 的类型来定位。

原因

Kubernetes 的网络对应用层是无感知的,Kubernetes 的主要流量转发逻辑发生在 node 上,由 iptables/ipvs 来实现,这些规则并不关心应用层里是什么协议。

Istio 的核心能力是对 7 层流量进行管控,但前提条件是 Istio 必须知道每个受管控的服务是什么协议,istio 会根据端口协议的不同,下发不同的流控功能(envoy filter),而 Kubernetes 资源定义里并不包括七层协议信息,所以 Istio 需要用户显式提供。

istio 的解决方案:Protocol sniffing

协议嗅探概要:

  • 检测 TLS CLIENT_HELLO 提取 SNI、ALPN、NPN 等信息
  • 基于常见协议的已知典型结构,尝试检测应用层 plaintext 内容 a. 基于HTTP2 spec: Connection Preface,判断是否为 HTTP/2 b. 基于 HTTP header 结构,判断是否是 HTTP/1.x
  • 过程中会设置超时控制和检测包大小限制, 默认按照协议 TCP 处理

最佳实践

Protocol sniffing 减少了新手使用 istio 所需的配置,但是可能会带来不确定的行为。不确定的行为在生产环境中是应该尽量避免的。

一些嗅探失效的例子:

  • 客户端和服务端使用着某类非标准的七层协议,客户端和服务端都可以正确解析,但是不能确保 istio 自动嗅探逻辑认可这类非标准协议。比如对于 http 协议,标准的换行分隔是用 CRLF (0x0d 0x0a), 但是大部分 http 类库会使用并认可 LF (0x0a)作为分隔。
  • 某些自定义私有协议,数据流的起始格式和 http 报文格式类似,但是后续数据流是自定义格式:
  • 未开启嗅探时:数据流按照 L4 TCP 进行路由,符合用户期望。
  • 如果开启嗅探:数据流最开始会被认定为 L7 http 协议,但是后续数据不符合 http 格式,流量将被中断。

建议生产环境不使用协议嗅探, 接入 mesh 的 service 应该按照约定使用协议前缀进行命名。

2. 流控规则下发顺序问题

异常描述

在批量更新流量规则的过程中,偶尔会出现流量异常(503),envoy 日志中 RESPONSE_FLAGS 包含「NR」标志(No route configured),持续时间不长,会自动恢复。

原因分析

当用户使用 kubectl apply -f multiple-virtualservice-destinationrule.yaml 时,这些对象的传播和生效先后顺序是不保证的,所谓最终一致性,比如 VirtualService 中引用了某一个 DestinationRule 定义的子版本,但是这个 DestinationRule 资源的传播和生效可能在时间上落后于 该 VirtualService 资源。

最佳实践:make before break

将更新过程从批量单步拆分为多步骤,确保整个过程中不会引用不存在的 subset:

当新增 DestinationRule subset 时,应该先 apply DestinationRule subset,等待 subset 生效后,再 apply 引用了该 subset 的 VirtualService。

当删除 DestinationRule subset 时,应该先 删除 VirtualService 中对 该 subset 的引用,等待 VirtualService 的修改生效后,在执行删除 DestinationRule subset。

3. 请求中断分析

请求异常,到底是 istio 流控规则导致,还是业务应用的返回,流量断点出现在哪个具体的 pod?

这是使用 mesh 最常见的困境,在微服务中引入 envoy 作为代理后,当流量访问和预期行为不符时,用户很难快速确定问题是出在哪个环节。客户端收到的异常响应,诸如 403、404、503 或者连接中断等,可能是链路中任一 sidecar 执行流量管控的结果, 但也有可能是来自某个服务的合理逻辑响应。

Envoy 流量模型

Envoy 接受请求流量叫做 Downstream,Envoy 发出请求流量叫做Upstream。在处理Downstream 和 Upstream 过程中, 分别会涉及2个流量端点,即请求的发起端和接收端:

在这个过程中, envoy 会根据用户规则,计算出符合条件的转发目的主机集合,这个集合叫做 UPSTREAM_CLUSTER, 并根据负载均衡规则,从这个集合中选择一个 host 作为流量转发的接收端点,这个 host 就是 UPSTREAM_HOST

以上就是 envoy 请求处理的 流量五元组信息, 这是 envoy 日志里最重要的部分,通过这个五元组我们可以准确的观测流量「从哪里来」和「到哪里去」。

  • UPSTREAM_CLUSTER
  • DOWNSTREAM_REMOTE_ADDRESS
  • DOWNSTREAM_LOCAL_ADDRESS
  • UPSTREAM_LOCAL_ADDRESS
  • UPSTREAM_HOST

日志分析示例

通过日志重点观测 2 个信息:

  • 断点是在哪里 ?
  • 原因是什么?

示例一:一次正常的 client-server 请求:

可以看到 2 端日志包含相同的 request ID,因此可以将流量分析串联起来。

示例二:no healthy upstream, 比如目标 deployment 健康副本数为 0

日志中 flag「UH」表示 upstream cluster 中没有健康的 host。

示例三:No route configured , 比如 DestinationRule 缺乏对应的 subset

日志中 flag「NR」表示找不到路由。

示例四,Upstream connection failure,比如服务未正常监听端口。

日志中 flag「UF」表示 Upstream 连接失败,据此可以判断出流量断点位置。

4. sidecar 和 user container 启动顺序

异常描述

Sidecar 模式在 kubernetes 世界很流行,在1.18中之后有了 sidecar 的概念,sidecar 容器的角色是用户主观赋予的。

对 Istio 用户来说,一个常见的困扰是:sidecar 和用户容器的启动顺序:

sidecar(envoy) 和用户容器的启动顺序是不确定的,如果用户容器先启动了,envoy 还未完成启动,这时候用户容器往外发送请求,请求仍然会被拦截,发往未启动的 envoy,请求异常。

在 Pod 终止阶段,也会有类似的异常,根源仍然是 sidecar 和普通容器的生命周期的不确定性。

解决方案

目前常规的规避方案主要是有这样几种:

  • 业务容器延迟几秒启动, 或者失败重试
  • 启动脚本中主动探测 envoy 是否ready,如 127.0.0.1:15020/healthz/ready

无论哪种方案都显得很蹩脚,为了彻底解决上述痛点,从 kubernetes 1.18版本开始,kubernetes 内置的 Sidecar 功能将确保 sidecar 在正常业务流程开始之前就启动并运行,即通过更改pod的启动生命周期,在init容器完成后启动sidecar容器,在sidecar容器就绪后启动业务容器,从启动流程上保证顺序性。而 Pod 终止阶段,只有当所有普通容器都已到达终止状态(Succeeded for restartPolicy=OnFailure 或 Succeeded/Failed for restartPolicy=Never),才会向sidecar 容器发送 SIGTERM 信号。

5. Ingress Gateway 和 Service 端口联动

Ingress Gateway 规则不生效的一个常见原因是:Gateway 的监听端口在对应的 kubernetes Service 上没有开启,首先我们需要理解 Istio Ingress Gateway 和 kubernetes Service 的关系:

上图中,虽然 gateway 定义期望管控端口 b 和 c,但是它对应的 service (通过腾讯云CLB)只开启了端口 a 和 b,因此最终从 LB 端口 b 进来的流量才能被 istio gateway 管控。

  • Istio Gateway 和 kubernetes Service 没有直接的关联,二者都是通过 selector 去绑定 pod,实现间接关联。
  • Istio CRD Gateway 只实现了将用户流控规则下发到网格边缘节点,流量仍需要通过 LB 控制才能进入网格。
  • 腾讯云 tke mesh 实现了 Gateway-Service 定义中的 Port 动态联动,让用户聚焦在网格内的配置。

6. VirtualService 作用域

VirtualService 包含了大部分 outbound 端的流量规则,它既可以应用到网格内部数据面代理中, 也可以应用到网格边缘的代理中。

VirtualService 的属性gateways用于指定 VirtualService 的生效范围:

  • 如果 VirtualService.gateways为空,则 istio 为其赋默认值 mesh, 代表生效范围为网格内部。
  • 如果希望 VirtualService 应用到具体边缘网关上,则需要显示为其赋值:gateway-name1,gateway-name2...
  • 如果希望 VirtualService 同时应用到网格内部和边缘网关上,则需要显示地把mesh值加入VirtualService.gateways, 如 mesh,gateway-name1,gateway-name2...

一个常见的问题是以上的第三种情况,VirtualService 最开始作用于网关内部,后续要将其规则扩展到边缘网关上,用户往往只会添加具体 gateway name,而遗漏 mesh:

Istio 自动给VirtualService.gateways设置默认值, 本意是为了简化用户的配置,但是往往会导致用户应用不当,一个 feature 一不小心会被用成了 bug。

7. VirtualService 不支持 host fragment

异常案例:

对某一 host 新增、修改 VirtualService,发现规则始终无法生效,排查发现存在其他 VirtualService 也对该 host 应用了其他规则,规则内容可能不冲突,但还是可能出现其中一些规则无法生效的情况。

背景:

  • VirtualService 里的规则,按照 host 进行聚合。
  • 随着业务的增长,VirtualService 的内容会快速增长,一个 host 的流控规则,可能会由不同的团队分布维护。如安全规则和业务规则分开,不同业务按照子 path 分开。

目前 istio 对 cross-resource VirtualService 的支持情况:

  • 在网格边缘(gateway),同一个 host 的流控规则,支持分布到多个 VirtualService 对象中,istio 自动聚合,但依赖定义顺序以及用户自行避免冲突。
  • 在网格内部(for sidecar),同一个 host 的流控规则,不支持分布到多个 VirtualService 对象中,如果同一个 host 存在多个 VirtualService,只有第一个 VirtualService 生效,且没有冲突检测。

VirtualService 不能很好支持 host 规则分片,使得团队的维护职责不能很好的解耦,配置人员需要知悉目标 host 的所有流控规则,才有信心去修改 VirtualService。

Istio 解决方案:VirtualService chaining(plan in 1.6)

Istio 在 1.6 中支持了 VirtualService 代理链:

  • VirtualService 支持分片定义 + 代理链
  • 支持团队对同一 host 的 VirtualService 进行灵活分片,比如按照 SecOps/Netops/Business 特性分离,各团队维护各种独立的 VirtualService

8. 全链路跟踪并非完全透明接入

异常案例

微服务接入后 service mesh 后,链路跟踪数据没有形成串联。

原因

service mesh 遥测系统中,对调用链跟踪的实现,并非完全的零入侵,需要用户业务作出少量的修改才能支持,具体地,在用户发出(http/grpc) RPC 时, 需要主动将上游请求中存在的 B3 trace headers写入下游 RPC 请求头中,这些 headers 包括:

有部分用户难以理解:既然 inbound 流量和 outbound 流量已经完全被拦截到 envoy,envoy 可以实现完全的流量管控和修改,为什么还需要应用显示第传递 headers?

对于 envoy 来说,inbound 请求和 outbound 请求完全是独立的,envoy 无法感知请求之间的关联。实际上这些请求到底有无上下级关联,完全由应用自己决定。

举一个特殊的业务场景,如果 Pod X 接收到 请求 A,触发的业务逻辑是:每隔 10 秒 发送一个请求到 Pod Y,如 B1,B2,B3,那么这些扇出的请求 Bx(x=1,2,3...),和请求 A 是什么关系?业务可能有不同的决策:认为 A 是 Bx 的父请求,或者认为 Bx 是独立的顶层请求。

9. mTLS 导致连接中断

在开启 istio mTLS 的用户场景中,访问出现 connection termination 是一个高频的异常:

1
2
3
4
5
# curl helloworld:4000/hello -i
HTTP/1.1 503 Service Unavailable

upstream connect error or disconnect/reset before headers
reset reason: connection termination

Envoy 访问日志中可以看到 "UC" 错误标识:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
{
"upstrean_local_address": "-",
"duration": "0",
"downstrean_local_address": "172.16.254.233:4000",
"route_name": "-",
"response_codo": "503",
"user_agent": "curl/7.64.0",
"response_flags": "UC",
"start_time": "2020-02-12T04:30:21.628Z",
"method": "GET",
"request.id": "e116814a-e689-9d26-81eb-5455fal09571",
"upstream_host": "172.16.0.15:5000",
"upstream_cluster": "outbound|4000|vl|helloworld.default.svc.cluster.local"
......
}

这个异常的原因和 DestinationRule 中的 mTLS 配置有关,是 istio 中一个不健壮的接口设计。

  • 当通过 MeshPolicy 开启全局 mTLS, 如果网格中没有定义其他的 DestinationRule,mTLS 正常运行
  • 如果后续网格中新增了 DestinationRule,而 DestinationRule 中可以覆盖子版本的 mTLS 值(默认是不开启!), 用户在使用 DestinationRule 时,往往很少去关注 mTLS 属性(留空)。最终导致增 DestinationRule 后 mTLS 变成了不开启,导致connection termination
  • 为了修复以上问题,用户不得不在所有 DestinationRule 中增加 mTLS 属性并设置为开启
1
2
3
4
5
6
7
8
9
10
11
12
13
apiVersion: networking.istio.io/vlalpha3
kind: DestinationRule
metadata:
name: hello
spec:
host: helloworld
trafficPolicy:
tls:
mode: ISTIO_MUTUAL
subsets:
- name: vl
labels:
version: vl

这种 istio mtls 用户接口极度不友好,虽然 mtls 默认做到了全局透明, 业务感知不到 mtls 的存在, 但是一旦业务定义了 DestinationRule,DestinationRule 就必须要知道当前 mtls 是否开启,并作出调整。试想 mtls 配置交由安全团队负责,而业务团队负责各自的 DestinationRule,团队间的耦合会非常严重。

10. 用户服务监听地址限制

异常描述

如果用户容器中业务进程监听的地址是具体ip (pod ip),而不是0.0.0.0, 该用户容器无法正常接入 istio,流量路由失败。

这是又一个挑战 Istio 最大透明化(Maximize Transparency)设计目标 的场景。

原因分析

istio-proxy 中的一段 iptables:

1
2
3
4
5
6
7
8
Chain ISTIO_OUTPUT {1 references)
target port opt source destination
1. RETURN all —- 127.0.0.6 anywhere
2. ISTIO_IN_REDIRECT all -— anywhere !localhost
3. RETURN all -— anywhere anywhere owner UID match istio-proxy
4. RETURN all -— anywhere anywhere owner GID match istio-proxy
5. RETURN all -— anywhere localhost
6. ISTIO_REDIRECT all -— anywhere anywhere

其中,ISTIO_IN_REDIRECT 是 virtualInbound, 端口 15006;ISTIO_REDIRECT 是 virtualOutbound,端口 15001。

关键点是规则二:如果destination不是127.0.0.1/32, 转给15006(virtualInbound, envoy监听),这里导致了对 pod ip 的流量始终会回到 envoy。

对该规则的解释:

1
2
Redirect app calls back to itself via Envoy when using the service VIP or endpoint
address, e.g. appN => Envoy (client) => Envoy (server) => appN.

该规则是希望在这里起作用: 假设当前Pod a属于service A, Pod 中用户容器通过服务名访问服务A, envoy中负载均衡逻辑将这次访问转发到了当前的pod ip, istio 希望这种场景服务端仍然有流量管控能力. 如图示:

改造建议

建议应用在接入 istio 之前,调整服务监听地址,使用 0.0.0.0 而不是具体 IP。 如果业务方认为改造难度大,可以参考另一篇istio中问题解决方案:服务监听pod ip 在 istio 中路由异常分析

11. 状态码404: Not Found, 或是nginx下游服务路由规则不生效

背景

前端pod1静态资源访问正常,但请求通过pod1中的nginx配置转发到后端pod2后,返回404;

有问题的nginx配置

1
2
3
4
location /v1/ {
proxy_set_header Host $http_host;
proxy_pass http://ppap.test.svc.cluster.local.:8080/v1/;
}

方案1: 修改nginx配置

1
2
3
4
location /v1/ {
proxy_set_header Host "ppap";
proxy_pass http://ppap.test.svc.cluster.local.:8080/v1/;
}

方案2: 修改VS配置, 配置多host支持

1
2
3
4
5
6
7
8
9
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
name: ppap
namespace: test
spec:
hosts:
- ppap.test.svc.cluster.local
- $http_host(这里的httphost代指对外提供访问的域名/ip)

总结

将$http_host修改为对应service名后,问题解决了,但这个问题因人而异,只能提供一个思路

参考资料

Nginx Proxy Pass to Istio Ingress Gateway 404

12. istio 常见问题: 返回 426 状态码

本文摘自 istio 学习笔记

背景

Istio 使用 Envoy 作为数据面转发 HTTP 请求,而 Envoy 默认要求使用 HTTP/1.1 或 HTTP/2,当客户端使用 HTTP/1.0 时就会返回 426 Upgrade Required

常见的 nginx 场景

如果用 nginx 进行 proxy_pass 反向代理,默认会用 HTTP/1.0,你可以显示指定 proxy_http_version1.1:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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 "";
...
}
}

压测场景

ab 压测时会发送 HTTP/1.0 的请求,Envoy 固定返回 426 Upgrade Required,根本不会进行转发,所以压测的结果也不会准确。可以换成其它压测工具,如 wrk

让 istio 支持 HTTP/1.0

有些 SDK 或框架可能会使用 HTTP/1.0 协议,比如使用 HTTP/1.0 去资源中心/配置中心拉取配置信息,在不想改动代码的情况下让服务跑在 istio 上,也可以修改 istiod 配置,加上 PILOT_HTTP10: 1 的环境变量来启用 HTTP/1.0。

使用istioctl manifest apply --set values.pilot.env.PILOT_HTTP10=1

参考:

参考资料

13. 前端js文件报错

背景

前端两个版本的deployment,由一个service代理;不通过istio网关时,访问前端页面js报错,且每次刷新错误的js不同

原因

两个版本的前端podA和podB不通过istio网关对版本进行路由时,静态资源请求会负载均衡的进入podA和podB。

但是podB中并没有podA的静态资源,podA中也没有podB的静态资源,所以当对podA访问podB的静态资源时会报错。

14. no healthy upstream

背景

upstream是Envoy中的术语,Envoy就是istio所使用的sidecar

  • Downstream/下游:下游主机连接到 Envoy,发送请求并接收响应。

  • Upstream/上游:上游主机接收来自 Envoy 的连接和请求,并返回响应。

原因

no healthy upstream的原因有很多,但是归根结底是Envoy找不到目标了~

原因之一

  • 两个VirtualService都配了gateways,导致流量进来不知道去哪个svc,因此删掉一个gateways即可

例子

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
---
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: test-frontend
namespace: ppap
spec:
hosts:
- "*"
gateways:
- ppap-gateway
http:
- match:
- headers:
user_id:
exact: 952795279527
route:
- destination:
host: test-frontend
subset: v2
- route:
- destination:
host: test-frontend
subset: v1
---
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: test-backend
namespace: ppap
spec:
hosts:
- test-backend
http:
- match:
- headers:
user_id:
exact: 952795279527
route:
- destination:
host: test-backend
subset: v2
- route:
- destination:
host: test-backend
subset: v1
---

两个VirtualService区别之处就在

1
2
3
4
hosts:
- "*"
gateways:
- ppap-gateway

当两个VirtualService挂载同一个gateways便会报错

参考

15. 如何在隔离环境安装istio

通常现网机器不能直连外网,所以istio的安装是个问题,下面是一个思路

  • 将镜像传到可以访问的镜像仓库
  • 通过设定istioctl安装的hub进行安装
  • istioctl本身通过文件直接传到对应的机器上即可

例子

1
istioctl install --set hub=my-hub.cn/istio --set namespace=istio-system --set components.pilot.k8s.hpaSpec.minReplicas=2 --set components.ingressGateways[0].name=istio-ingressgateway --set components.ingressGateways[0].k8s.hpaSpec.minReplicas=2 --set components.ingressGateways[0].k8s.service.type=NodePort -y

16. 解决istio-proxy使用了资源配额导致新建pod处于pending状态的问题(No preemption victims found for incoming pod)

问题背景

创建的pod一直在pending状态

使用 kubectl describe po 查看下原因

1
kubectl describe po istio-springboot-demo-b-v1-7cf6979bbd-ct8kr -n istio-demos

输出的原因部分如下:

1
2
3
4
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Warning FailedScheduling 3m default-scheduler 0/1 nodes are available: 1 Insufficient cpu. preemption: 0/1 nodes are available: 1 No preemption victims found for incoming pod.

从Message看,很明显是cpu不够不了,无法调度。为什么会这样呢?
通过下面的步骤查看下node的信息
先查看node

1
2
3
# kubectl get nodes
NAME STATUS ROLES AGE VERSION
xxx Ready control-plane 21d v1.24.1

再通过下面的命令的查看po使用cpu和内在的情况

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
kubectl describe node xxx
Capacity:
cpu: 2
ephemeral-storage: 61255492Ki
hugepages-1Gi: 0
hugepages-2Mi: 0
memory: 8049416Ki
pods: 110
Allocatable:
cpu: 2
ephemeral-storage: 56453061334
hugepages-1Gi: 0
hugepages-2Mi: 0
memory: 7947016Ki
pods: 110
System Info:
Machine ID: 7679dc44-ba20-41de-afe7-6131f6cdfb01
System UUID: 25cc4672-0000-0000-aaf7-f9f722a48b3c
Boot ID: 72f1a678-b1e3-435a-a235-409592f5bf2b
Kernel Version: 5.10.104-linuxkit
OS Image: Docker Desktop
Operating System: linux
Architecture: amd64
Container Runtime Version: docker://20.10.17
Kubelet Version: v1.24.1
Kube-Proxy Version: v1.24.1
Non-terminated Pods: (20 in total)
Namespace Name CPU Requests CPU Limits Memory Requests Memory Limits Age
--------- ---- ------------ ---------- --------------- ------------- ---
istio-demos demo-istio-h5-latest-848b989c99-7rbv7 100m (5%) 2 (100%) 128Mi (1%) 1Gi (13%) 6m34s
istio-demos demo-istio-h5-v1-7fc5575845-5r28k 100m (5%) 2 (100%) 128Mi (1%) 1Gi (13%) 6m34s
istio-demos istio-springboot-demo-a-latest-64d5cf969f-6r7pf 100m (5%) 2 (100%) 128Mi (1%) 1Gi (13%) 6m22s
istio-demos istio-springboot-demo-a-v1-7d64cc6578-n6rs7 100m (5%) 2 (100%) 128Mi (1%) 1Gi (13%) 6m22s
istio-system grafana-6787c6fb46-jklbh 0 (0%) 0 (0%) 0 (0%) 0 (0%) 6d13h
istio-system istio-egressgateway-679565ff87-g8lq7 100m (5%) 2 (100%) 128Mi (1%) 1Gi (13%) 6d14h
istio-system istio-ingressgateway-b67d7948d-wfxgb 100m (5%) 2 (100%) 128Mi (1%) 1Gi (13%) 6d14h
istio-system istiod-645c8d8598-qmslz 500m (25%) 0 (0%) 2Gi (26%) 0 (0%) 6d14h
istio-system jaeger-5b994f64f4-gvcqt 10m (0%) 0 (0%) 0 (0%) 0 (0%) 6d13h
istio-system kiali-565bff499-bfrhr 0 (0%) 0 (0%) 0 (0%) 0 (0%) 6d13h
istio-system prometheus-74cd54db7f-flppp 0 (0%) 0 (0%) 0 (0%) 0 (0%) 6d13h
kube-system coredns-6d4b75cb6d-5rqbc 100m (5%) 0 (0%) 70Mi (0%) 170Mi (2%) 21d
kube-system coredns-6d4b75cb6d-wdh6b 100m (5%) 0 (0%) 70Mi (0%) 170Mi (2%) 6d14h
kube-system etcd-docker-desktop 100m (5%) 0 (0%) 100Mi (1%) 0 (0%) 21d
kube-system kube-apiserver-docker-desktop 250m (12%) 0 (0%) 0 (0%) 0 (0%) 21d
kube-system kube-controller-manager-docker-desktop 200m (10%) 0 (0%) 0 (0%) 0 (0%) 21d
kube-system kube-proxy-tnb9q 0 (0%) 0 (0%) 0 (0%) 0 (0%) 21d
kube-system kube-scheduler-docker-desktop 100m (5%) 0 (0%) 0 (0%) 0 (0%) 21d
kube-system storage-provisioner 0 (0%) 0 (0%) 0 (0%) 0 (0%) 21d
kube-system vpnkit-controller 0 (0%) 0 (0%) 0 (0%) 0 (0%) 21d
Allocated resources:
(Total limits may be over 100 percent, i.e., overcommitted.)
Resource Requests Limits
-------- -------- ------
cpu 1960m (98%) 12 (600%)
memory 3056Mi (39%) 6484Mi (83%)
ephemeral-storage 0 (0%) 0 (0%)
hugepages-1Gi 0 (0%) 0 (0%)
hugepages-2Mi 0 (0%) 0 (0%)
Events: <none>

可以看到几个用于istio测试的demo的pod都有使用配额,从其中挑选一个pod进行查看

1
kubectl describe po istio-springboot-demo-a-latest-64d5cf969f-6r7pf -n istio-demos

找到了使用配额的容器,原来是istio的代理容器(sidecar) : istio-proxy

调整istio关于sidecar的资源配额参数

查找参数配置

后来在istio安装包找到关于proxy相关的资源配额参数,文件路径:https://github.com/istio/istio/tree/master/manifests/charts/istio-control/istio-discovery/values.yaml
resources相关参数:

动态调整

然后找到了参数所在的文件对于已经在运行的istio环境也无能为力,因为改文件中的参数并不能应用于正在运行的istio环境中,后面找了好久,终于找到一篇华为的文章: 如何调整istio-proxy容器resources requests取值?

这里记录一下

方法一:调整网格中的所有服务

调整后,对已经创建的istio-proxy并不会做改动,但是之后创建的istio-proxy将会使用调整后的参数。

  1. 执行以下命令修改comfigmap

    1
    kubectl edit cm istio-sidecar-injector -n istio-system

可以调整配额大小,也可以删除掉, 这里最好是调整配额, 我使用rancher安装istio, 删除了这里后启动服务失败了。

  1. 重启istio-sidecar-injector Pod。
  2. 重启业务服务Pod,多实例滚动升级不会断服。

方法二:调整网格中的某个服务

  1. 通过value.yaml发现,默认资源使用几个annoation调整的,如下

如果是通过rancher安装,可以按如下步骤找到values.yaml

  1. 登录 Rancher 界面,选择 Istio 所在的项目;
  2. 在左侧导航栏中选择 "Apps & Marketplaces",然后选择 "Installed Apps";
  3. 找到 Istio 应用程序,然后单击它;
  4. 在右侧的面板中,选择 "View YAML";
  5. 在打开的 YAML 文件中,你可以找到和修改 values.yaml 文件。
  1. 所以我们可以修改服务的yaml文件
    命令格式:

    1
    kubectl edit deploy <nginx> -n <namespace>

    本次实验在namespace istio-demos进行,查看namespace下的deployment

    1
    kubectl get deploy -n istio-demos

    输出如下:

    1
    2
    3
    4
    5
    6
    7
    8
    saleson@SalesondeMacBook-Pro istio-discovery % kubectl get deploy -n istio-demos
    NAME READY UP-TO-DATE AVAILABLE AGE
    demo-istio-h5-latest 1/1 1 1 3h59m
    demo-istio-h5-v1 1/1 1 1 3h59m
    istio-springboot-demo-a-latest 1/1 1 1 3h58m
    istio-springboot-demo-a-v1 1/1 1 1 3h58m
    istio-springboot-demo-b-latest 1/1 1 1 27m
    istio-springboot-demo-b-v1 1/1 1 1 27m

    使用下命令进入istio-springboot-demo-b-v1的编辑面板:

    1
    2
    kubectl edit deploy istio-springboot-demo-b-v1 -n istio-demos
    在spec.template.metadata.annotations下添加如下配置(大小仅供参考,请自行替换)
    1
    2
    3
    4
    sidecar.istio.io/proxyCPU: 100m
    sidecar.istio.io/proxyCPULimit: 100m
    sidecar.istio.io/proxyMemory: 500Mi
    sidecar.istio.io/proxyMemoryLimit: 500Mi

    编辑保存后的结果如下:

  2. 修改后服务滚动升级,确保不会断服
    执行如下命令重启deployment

    1
    kubectl rollout restart deployment istio-springboot-demo-b-v1 -n istio-demos
  3. 查看修改后的结果
    使用下面的命令查看修改后的配额

    1
    kubectl describe node docker-desktop

提前规划

先调整 https://github.com/istio/istio/tree/master/manifests/charts/istio-control/istio-discovery/values.yaml 中关于proxy的配额,再安装istio-discovery

17. 在Istio中使用preStop钩子时,可能会出现sleep命令不起作用的情况

在Kubernetes中,Pod中的preStop钩子会在Pod被终止之前被调用。preStop钩子可以用来在Pod终止之前执行一些清理操作,例如保存状态、关闭连接、删除临时文件等等。

在Istio中使用preStop钩子时,如果在preStop钩子中使用了sleep命令,可能会出现sleep命令不起作用的情况。这是由于Istio的sidecar代理会在preStop钩子执行期间发送SIGTERM信号给Pod,以便Pod可以进行优雅的终止。然而,sleep命令在收到SIGTERM信号时会立即退出,而不是等待指定的时间。

为了解决这个问题,可以使用trap命令来捕获SIGTERM信号,并在收到信号时执行一些操作。例如,可以使用以下命令来在preStop钩子中等待30秒钟,然后执行一些清理操作:

注:trap指令的详细用法可以参考:https://cloud.tencent.com/developer/article/1640249

1
2
3
4
5
6
preStop:
exec:
command:
- "/bin/sh"
- "-c"
- "trap 'echo \"Caught SIGTERM signal\" && sleep 30 && echo \"Performing cleanup...\"' SIGTERM"

在上面的示例中,我们使用trap命令捕获了SIGTERM信号,并打印了一条消息。然后,我们使用sleep命令等待30秒钟,然后打印另一条消息,表示我们正在执行清理操作。

请注意,如果的清理操作需要更长的时间来完成,可以根据需要增加sleep命令的等待时间。同时,还可以使用kubectl logs命令来查看Pod的日志,以确定preStop钩子是否执行成功。

18. istio envoy log显示block_all

1
2
3
4
5
# nginx-ingress日志
5ab10.133.0.10 -- [31/Aug/2023:03:03:48 +0000] "GET HTTP/.1" 502 0 """Mozila/5.0 (windows NT 10.0; Win64; x64) ApleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36Eda/116.0.1938.62" 923 0001 [kevcloak-kevcloak-http-htt] 7 10.130 7.82:8080 0 0 004 502 42ecdfa48c7c186f4d494cda9ae320310.133.0.10 - -[31/Aug/2023:03:03:49 +0000] "GET HTTP/1" 502 0 """Mozilla/5.0 (windo.s NT 10.0; Win64; x64) ApleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36Edg/116.0.1938.62" 923 .001 [keycloak-keycloak-http-htt] [] 10.130.7.82:8080 0 0.000 502 122c4ff573cae0e9bcb235fef198282200 205"""Mozilla/5.0 (windows NT 10.0; Win64; x64) AppleWebKi10.130.5.96 - - [31/Aug/2023:03:03:49 +0000]"GET /proxy/otification/notifications/unread-count?channel=WEB HTTP/1.1

# nainx-ingress istio-proxy日志
10.0; Win64; x64) AppleWebKit/537.36 (KHTML,Tike Gecko)Chrome/116.0.0.0 Safari/537.36" "c3785224a5b953b12250d0b691a35ab""hkaa-cargo-auth-proxy-appchart.app.svc.cluster.local""10rt.app.svc.cluster.local 10.130.20.170:36466 172.20.151.10313041.210:8080" outboundl80l hkaa-cargo-auth-proxy-appchi:80 10.130.5.96:0 - default2023-08-31T03:03:48.229Z] "GET HTTP/1.1" 502 directesponse - "" 0 0 0 - "10.133.0.10" "Mozilla/5.0 (windows T 10.0: Win64: x64) AppleWebkit/537.36 (KHTML,like Gecko) Chrome/116 0 0 0 Safari/537 36 Eda/116 0 1038 62" "42ocdfa48c7c18fAd404cda00ao3203" "owcloak.bttn kowcloak swc cluctor 10 130 7 82:8080 10 133 0 10:0 - 61ock a171oc[2023-08-31T03:03:49.458Z] "GET HTTP/1.1" 502 - directesponse - "" 0 0 0 - "10.133.0.10" "Mozilla/5.0 (windowsT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36 Edg/116.0.1938.62" "0122c4ff573caee9bcb235fef198282""keycloak-http.keycloak.svc.cluster.Toc0- - 10.130.7.82:8080 10.133.0.10:0 - block all

文章中提到如果global.outboundTrafficPolicy.mode设置为REGISTRY_ONLY,这种模式下除非为每个服务显式的添加了service entries,否则所有到外部服务的流量都会被阻止

ServiceEntry需要列出与HTTP请求匹配正确的Host

19.upstream_reset_before_response_started{connection_failure,delayed_connect_error:_111}或upstream_reset_before_response_started{connection_termination}

参考:https://github.com/envoyproxy/envoy/issues/14981

此问题可能是因为下游服务主动断开连接导致。我的场景则是服务发生堆分配不足错误