TT Lab
开始
学习 学习路径 课程

Istio 服务网格

难的不是装上边车,而是和它一起过日子

在 TT Lab 中继续学习

一句话总结

已注入代理的资源、启动顺序和终止方式,由几个 Pod 注解决定;这些注解实际会变成哪些字段,可以在上传到集群之前用 istioctl kube-inject 确认。

为什么接入之后更难

边车注入很容易学会。给命名空间打上标签,每个 Pod 就会多出一个容器。问题出在之后。代理不是免费的,它必须与应用一起启动、一起退出,而且对某些工作负载来说是个妨碍。

先看资源。默认的代理请求 100m CPU 和 128Mi 内存,上限是 2 个 CPU 核和 1Gi 内存。对一个 Pod 来说不大,但有 2000 个 Pod 的话,仅请求量就要预留 200 个 CPU 核。反过来,如果把请求压得太低,流量涌来时代理会先变慢,引发表现为应用延迟的故障。这个旋钮必须因工作负载而异,所以通过 Pod 注解来调整。

第二是启动顺序。容器原则上是同时启动的。如果应用很快而代理很慢,那么应用最初几秒内发出的请求,在没有代理的情况下只经过 iptables 规则,就直接失败了。只在部署之后才出现的 5xx 就是这个样子。开启 holdApplicationUntilProxyStarts 后,注入器会把代理放在容器列表的最前面,并附加 postStart 钩子,在代理就绪之前,拦住下一个容器的启动。

第三个是最古老的陷阱。如果 Job 的 Pod 中代理以普通容器的身份存在,那么即使主体任务结束,代理仍然活着。Pod 无法转为完成状态,批处理流水线就卡在那里。以前的绕过办法,是在任务脚本的最后调用代理的退出端点——但它有个缺陷:任务失败的话,那一行就不会执行。现在 Kubernetes 支持带有重启策略的初始化容器,所以把代理移到那里。它先启动,在 Pod 存活期间一直存活,主体结束后一并清理。

注解会变成哪些字段

注解 注入结果中发生变化的位置
sidecar.istio.io/proxyCPU、proxyMemory istio-proxy 的 resources.requests
sidecar.istio.io/proxyCPULimit、proxyMemoryLimit istio-proxy 的 resources.limits
proxy.istio.io/config istio-proxy 的环境变量 PROXY_CONFIG(会被转换为 JSON)
proxy.istio.io/config 的 holdApplicationUntilProxyStarts 容器顺序 + lifecycle.postStart 钩子
traffic.sidecar.istio.io/excludeOutboundPorts istio-init 的命令行参数
sidecar.istio.io/inject: "false" 代理和初始化容器完全不会被放入
sidecar.istio.io/nativeSidecar: "true" 代理移到 initContainers 中,并获得 restartPolicy: Always

这里有一个重要的特性——proxy.istio.io/config 的值是字符串,里面装的是 YAML。这意味着人写的格式与代理读取的格式不同,所以即使写错,清单语法也能通过。这就是必须养成习惯、亲自打开结果中的环境变量来查看的原因。

注解加在什么位置也经常搞错。注入的对象是 Pod,所以注解必须加在 Pod 模板上。如果加在 Deployment 的元数据上,语法是对的,但什么都不会发生。

在现场相遇的样子

最常见的是“部署之后只有几秒钟报错”这类报告。日志里只留下连接被拒绝,无法复现。开启启动顺序保证后就消失了。这个设置之所以不是默认值,是因为它会让 Pod 的启动相应变慢,所以在哪里开启,要根据服务的性质来决定。

第二种是“夜间批处理从昨天起一直不结束”。Pod 是 Running,应用日志也打印了正常退出,只有代理还活着。这样的 Pod 堆积起来,批处理队列就会堵住,如果不知道原因,人工手动删除 Pod 的运维方式就会固化下来。

第三种是数据库连接。如果代理认错了协议(端口没有名称,或不符合规则),连接就会中断或变得异常缓慢。此时临时使用的旋钮是排除出站端口——根本的解决办法是按协议给端口加上名称,但在故障期间,这一个注解能争取到时间。

本实验环境的局限

在实验 Pod 中,无法启动真正的代理。 因此,启动顺序保证实际救下第一个请求的场景、Job 的 Pod 无法转为完成状态的场景,以及代理用了多少内存,都看不到。本实验涉及的是声明会变成什么样的 Pod 规格。好在这个阶段能抓到的错误,占现场事故的很大一部分——注解加错了位置、值的格式错误,或者以为已经开启的东西其实没有开启。

下一项实验要做什么

先阅读没有任何注解的默认注入结果,再逐个添加注解,确认结果清单中哪些字段发生变化。依次查看资源、代理配置、启动顺序保证、排除出站端口和排除注入,并以两种方式注入 Job,比较代理会进入哪个列表。最后制作一个脚本,一次性重新注入八份清单,并把结果固化成表。