Istio 从懵圈到熟练:二分之一活的微服务
作者 | 声东 阿里云售后技术专家
<关注阿里巴巴云原生公众号,回复 排查 即可下载电子书>
《深入浅出 Kubernetes》一书共汇集 12 篇技术文章,帮助你一次搞懂 6 个核心原理,吃透基础理论,一次学会 6 个典型问题的华丽操作!
Istio is the future!基本上,我相信对云原生技术趋势有些微判断的同学,都会有这个觉悟。其背后的逻辑其实是比较简单的:当容器集群,特别是 Kubernetes 成为事实上的标准之后,应用必然会不断的复杂化,服务治理肯定会成为强需求。
Istio 的现状是,聊的人很多,用的人其实很少。所以导致我们能看到的文章,讲道理的很多,讲实际踩坑经验的极少。阿里云售后团队作为一线踩坑团队,分享问题排查经验,我们责无旁贷。这篇文章,我就跟大家聊一个简单 Istio 问题的排查过程,权当抛砖。
二分之一活的微服务
问题是这样的,用户在自己的测试集群里安装了 Istio,并依照官方文档部署 bookinfo 应用来上手 Istio。部署之后,用户执行 kubectl get pods 命令,发现所有的 Pod 都只有二分之一个容器是 READY 的。
# kubectl get pods
NAME READY STATUS RESTARTS AGE
details-v1-68868454f5-94hzd 1/2 Running 0 1m
productpage-v1-5cb458d74f-28nlz 1/2 Running 0 1m
ratings-v1-76f4c9765f-gjjsc 1/2 Running 0 1m
reviews-v1-56f6855586-dplsf 1/2 Running 0 1m
reviews-v2-65c9df47f8-zdgbw 1/2 Running 0 1m
reviews-v3-6cf47594fd-cvrtf 1/2 Running 0 1m
如果从来都没有注意过 READY 这一列的话,我们大概会有两个疑惑:2 在这里是什么意思,以及 1/2 到底意味着什么。
简单来讲,这里的 READY 列,给出的是每个 Pod 内部容器的 Readiness,即就绪状态。每个集群节点上的 kubelet 会根据容器本身 Readiness 规则的定义,分别是 tcp、http 或 exec 的方式,来确认对应容器的 Readiness 情况。
更具体一点,kubelet 作为运行在每个节点上的进程,以 tcp/http 的方式(节点网络命名空间到 Pod 网络命名空间)访问容器定义的接口,或者在容器的 namespace 里执行 exec 定义的命令,来确定容器是否就绪。
这里的 2 说明这些 Pod 里都有两个容器,1/2 则表示,每个 Pod 里只有一个容器是就绪的,即通过 Readiness 测试的。关于 2 这一点,我们下一节会深入讲,这里我们先看一下,为什么所有的 Pod 里,都有一个容器没有就绪。
使用 kubectl 工具拉取第一个 details pod 的编排模板,可以看到这个 Pod 里两个容器,只有一个定义了 readiness probe。对于未定义 readiness probe 的容器, kubelet 认为,只要容器里的进程开始运行,容器就进入就绪状态了。所以 1/2 个就绪 Pod,意味着,有定义 readiness probe 的容器,没有通过 kubelet 的测试。
没有通过 readiness probe 测试的是 istio-proxy 这个容器。它的 readiness probe 规则定义如下:
readinessProbe:
failureThreshold: 30
httpGet:
path: /healthz/ready
port: 15020
scheme: HTTP
initialDelaySeconds: 1
periodSeconds: 2
successThreshold: 1
timeoutSeconds: 1
我们登录这个 Pod 所在的节点,用 curl 工具来模拟 kubelet 访问下边的 uri,测试 istio-proxy 的就绪状态。
# curl http://172.16.3.43:15020/healthz/ready -v
* About to connect() to 172.16.3.43 port 15020 (#0)
* Trying 172.16.3.43...
* Connected to 172.16.3.43 (172.16.3.43) port 15020 (#0)
> GET /healthz/ready HTTP/1.1
> User-Agent: curl/7.29.0
> Host: 172.16.3.43:15020
> Accept: */*>
< HTTP/1.1 503 Service Unavailable< Date: Fri, 30 Aug 2019 16:43:50 GMT
< Content-Length: 0
< *
Connection #0 to host 172.16.3.43 left intact
绕不过去的大图
上一节我们描述了问题现象,但是留下一个问题,就是 Pod 里的容器个数为什么是 2。虽然每个 Pod 本质上至少有两个容器:一个是占位符容器 pause,另一个是真正的工作容器,但是我们在使用 kubectl 命令获取 Pod 列表的时候,READY 列是不包括 pause 容器的。
这里的另外一个容器,其实就是服务网格的核心概念 sidercar。其实把这个容器叫做 sidecar,某种意义上是不能反映这个容器的本质的。Sidecar 容器本质上是反向代理,它本来是一个 Pod 访问其他服务后端 Pod 的负载均衡。
然而,当我们为集群中的每一个 Pod,都“随身”携带一个反向代理的时候,Pod 和反向代理就变成了服务网格。正如下边这张经典大图所示。这张图实在有点难画,所以只能借用,绕不过去。
所以 sidecar 模式,其实是“自带通信员”模式。这里比较有趣的是,在我们把 sidecar 和 Pod 绑定在一块的时候,sidecar 在出流量转发时扮演着反向代理的角色,而在入流量接收的时候,可以做超过反向代理职责的一些事情。这点我们会在其他文章里讨论。
Istio 在 Kubernetes 基础上实现了服务网格,Isito 使用的 sidecar 容器就是第一节提到的,没有就绪的容器。所以这个问题,其实就是服务网格内部,所有的 sidecar 容器都没有就绪。
代理与代理的生命周期管理
上一节我们看到,Istio 中的每个 Pod,都自带了反向代理 sidecar。我们遇到的问题是,所有的 sidecar 都没有就绪。我们也看到 readiness probe 定义的,判断 sidecar 容器就绪的方式就是访问下边这个接口:
http://<pod ip>:15020/healthz/ready
接下来,我们深入看下 Pod,以及其 sidecar 的组成及原理。在服务网格里,一个 Pod 内部除了本身处理业务的容器之外,还有 istio-proxy 这个 sidecar 容器。正常情况下,istio-proxy 会启动两个进程:pilot-agent 和 Envoy。
如下图,Envoy 是实际上负责流量管理等功能的代理,从业务容器出、入的数据流,都必须要经过 Envoy;而 pilot-agent 负责维护 Envoy 的静态配置,以及管理 Envoy 的生命周期。这里的动态配置部分,我们在下一节会展开来讲。
我们可以使用下边的命令进入 Pod 的 istio-proxy 容器做进一步排查。这里的一个小技巧,是我们可以以用户 1337,使用特权模式进入 istio-proxy 容器,如此就可以使用 iptables 等只能在特权模式下运行的命令。
docker exec -ti -u 1337 --privileged <istio-proxy container id> bash
这里的 1337 用户,其实是 sidecar 镜像里定义的一个同名用户 istio-proxy,默认 sidecar 容器使用这个用户。如果我们在以上命令中,不使用用户选项 u,则特权模式实际上是赋予 root 用户的,所以我们在进入容器之后,需切换到 root 用户执行特权命令。
进入容器之后,我们使用 netstat 命令查看监听,我们会发现,监听 readiness probe 端口 15020 的,其实是 pilot-agent 进程。
istio-proxy@details-v1-68868454f5-94hzd:/$ netstat -lnpt
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
tcp 0 0 0.0.0.0:15090 0.0.0.0:* LISTEN 19/envoy
tcp 0 0 127.0.0.1:15000 0.0.0.0:* LISTEN 19/envoy
tcp 0 0 0.0.0.0:9080 0.0.0.0:* LISTEN -
tcp6 0 0 :::15020 :::* LISTEN 1/pilot-agent
我们在istio-proxy内部访问readiness probe接口,一样会得到503的错误。
就绪检查的实现
了解了 sidecar 的代理,以及管理代理生命周期的 pilot-agent 进程,我们可以稍微思考一下 pilot-agent 应该怎么去实现 healthz/ready 这个接口。显然,如果这个接口返回 OK 的话,那不仅意味着 pilot-agent 是就绪的,而必须确保代理是工作的。
实际上 pilot-agent 就绪检查接口的实现正是如此。这个接口在收到请求之后,会去调用代理 Envoy 的 server_info 接口。调用所使用的 IP 是 Localhost。这个非常好理解,因为这是同一个 Pod 内部进程通信。使用的端口是 Envoy 的 proxyAdminPort,即 15000。
有了以上的知识准备之后,我们来看下 istio-proxy 这个容器的日志。实际上,在容器日志里,一直在重复输出一个报错,这句报错分为两部分,其中 Envoy proxy is NOT ready 这部分是 pilot agent 在响应 healthz/ready 接口的时候输出的信息,即 Envoy 代理没有就绪;而剩下的 config not received from Pilot (is Pilot running?): cds updates: 0 successful, 0 rejected; lds updates: 0 successful, 0 rejected 这部分,是 pilot-agent 通过 proxyAdminPort 访问 server_info 的时候带回的信息,看起来是 Envoy 没有办法从 Pilot 获取配置。
Envoy proxy is NOT ready: config not received from Pilot (is Pilot running?): cds updates: 0 successful, 0 rejected; lds updates: 0 successful, 0 rejected.
到这里,建议大家回退看下上一节的插图,在上一节我们选择性的忽略是 Pilot 到 Envoy 这条虚线,即动态配置。这里的报错,实际上是 Envoy 从控制面 Pilot 获取动态配置失败。
控制面和数据面
目前为止,这个问题其实已经很清楚了。在进一步分析问题之前,我聊一下我对控制面和数据面的理解。控制面数据面模式,可以说无处不在。我们这里举两个极端的例子。
第一个例子,是 DHCP 服务器。我们都知道,在局域网中的电脑,可以通过配置 DHCP 来获取 IP 地址,这个例子中,DHCP 服务器统一管理,动态分配 IP 地址给网络中的电脑,这里的 DHCP 服务器就是控制面,而每个动态获取 IP 的电脑就是数据面。
第二个例子,是电影剧本,和电影的演出。剧本可以认为是控制面,而电影的演出,包括演员的每一句对白,电影场景布置等,都可以看做是数据面。
我之所以认为这是两个极端,是因为在第一个例子中,控制面仅仅影响了电脑的一个属性,而第二个例子,控制面几乎是数据面的一个完整的抽象和拷贝,影响数据面的方方面面。Istio 服务网格的控制面是比较靠近第二个例子的情况,如下图:
Istio 的控制面 Pilot 使用 gRPC 协议对外暴露接口 istio-pilot.istio-system:15010,而 Envoy 无法从 Pilot 处获取动态配置的原因,是在所有的 Pod 中,集群 DNS 都无法使用。
简单的原因
这个问题的原因其实比较简单,在 sidecar 容器 istio-proxy 里,Envoy 不能访问 Pilot 的原因是集群 DNS 无法解析 istio-pilot.istio-system 这个服务名字。在容器里看到 resolv.conf 配置的 DNS 服务器是 172.19.0.10,这个是集群默认的 kube-dns 服务地址。
istio-proxy@details-v1-68868454f5-94hzd:/$ cat /etc/resolv.conf
nameserver 172.19.0.10
search default.svc.cluster.local svc.cluster.local cluster.local localdomain
但是客户删除重建了 kube-dns 服务,且没有指定服务 IP,这导致,实际上集群 DNS 的地址改变了,这也是为什么所有的 sidecar 都无法访问 Pilot。
# kubectl get svc -n kube-system
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kube-dns ClusterIP 172.19.9.54 <none> 53/UDP,53/TCP 5d
最后,通过修改 kube-dns 服务,指定 IP 地址,sidecar 恢复正常。
# kubectl get pods
NAME READY STATUS RESTARTS AGE
details-v1-68868454f5-94hzd 2/2 Running 0 6d
nginx-647d5bf6c5-gfvkm 2/2 Running 0 2d
nginx-647d5bf6c5-wvfpd 2/2 Running 0 2d
productpage-v1-5cb458d74f-28nlz 2/2 Running 0 6d
ratings-v1-76f4c9765f-gjjsc 2/2 Running 0 6d
reviews-v1-56f6855586-dplsf 2/2 Running 0 6d
reviews-v2-65c9df47f8-zdgbw 2/2 Running 0 6d
reviews-v3-6cf47594fd-cvrtf 2/2 Running 0 6d
结论
这其实是一个比较简单的问题,排查过程其实也就几分钟。但是写这篇文章,有点感觉是在看长安十二时辰,短短几分钟的排查过程,写完整背后的原理,前因后果,却花了几个小时。这是 Istio 文章的第一篇,希望在大家排查问题的时候,有所帮助
Istio 从懵圈到熟练:二分之一活的微服务的更多相关文章
- Kubernetes从懵圈到熟练:读懂这一篇,集群节点不下线
排查完全陌生的问题,完全不熟悉的系统组件,是售后工程师的一大工作乐趣,当然也是挑战.今天借这篇文章,跟大家分析一例这样的问题.排查过程中,需要理解一些自己完全陌生的组件,比如systemd和dbus. ...
- Kubernetes 从懵圈到熟练:集群服务的三个要点和一种实现
作者 | 声东 阿里云售后技术专家 文章来源:Docker,点击查看原文. 以我的经验来讲,理解 Kubernetes 集群服务的概念,是比较不容易的一件事情.尤其是当我们基于似是而非的理解,去排查服 ...
- K8s 从懵圈到熟练 – 集群网络详解
作者 | 声东 阿里云售后技术专家 导读:阿里云 K8S 集群网络目前有两种方案:一种是 flannel 方案:另外一种是基于 calico 和弹性网卡 eni 的 terway 方案.Terway ...
- K8s 从懵圈到熟练 – 镜像拉取这件小事
作者 | 声东 阿里云售后技术专家 导读:相比 K8s 集群的其他功能,私有镜像的自动拉取,看起来可能是比较简单的.而镜像拉取失败,大多数情况下都和权限有关.所以,在处理相关问题的时候,我们往往会轻松 ...
- Kubernetes 从懵圈到熟练 – 集群网络详解(转)
阿里云K8S集群网络目前有两种方案,一种是flannel方案,另外一种是基于calico和弹性网卡eni的terway方案.Terway和flannel类似,不同的地方在于,terway支持Pod弹性 ...
- 如何使用Istio 1.6管理多集群中的微服务?
假如你正在一家典型的企业里工作,需要与多个团队一起工作,并为客户提供一个独立的软件,组成一个应用程序.你的团队遵循微服务架构,并拥有由多个Kubernetes集群组成的广泛基础设施. 由于微服务分布在 ...
- 【连载】微服务网格Istio(一)
Istio基础 服务网格是用于描述构成应用程序的微服务网络以及应用之间的交互,服务网格的功能包括服务发现.负载均衡.故障恢复.指标和监控以及更加复杂的运维工作,例如A/B测试.金丝雀发布.限流.访问控 ...
- 云容器云引擎:容器化微服务,Istio占C位出道
在精彩的软件容器世界中,当新项目涌现并解决你认为早已解决的问题时,这感觉就像地面在你的脚下不断地移动.在许多情况下,这些问题很久以前被解决,但现在的云原生架构正在推动着更大规模的应用程序部署,这就需要 ...
- Istio微服务架构初试
感谢 http://blog.csdn.net/qq_34463875/article/details/77866072 看了一些文档,有些半懂不懂,所以还是需要helloworld一下.因为isti ...
- Google、IBM和Lyft开源其大型微服务系统管理工具Istio
Istio 的优势 集群规模可视性:在故障状况出现时,运营人员需要利用多种工具以始终关注集群运行状况并分析微服务状态图表.Istio 项目能够监控与应用程序及网络活动相关的数据,利用 Promethe ...
随机推荐
- Java 22正式发布,一文了解全部新特性
就在昨晚,Java 22正式发布!该版本提供了 12 项功能增强,其中包括 7 项预览功能和 1 项孵化器功能.它们涵盖了对 Java 语言.API.性能以及 JDK 中包含的工具的改进. 下面就来一 ...
- HTTP与WebSocket/WebDAV
WebSocket WebDAV
- MySQL数据库维护和改善性能
备份数据 由于MySQL数据库是基于磁盘的文件,普通的备份系统和例程就能备份MySQL的数据.但是,由于这些文件总是处于打开和使用状态,普通的文件副本备份不一定总是有效.下面列出这个问题的可能解决 ...
- Bochs调试指令
Bochs Bochs就像一台真机一样,处理器在加电之后,要开始取指令并执行指令. jmpf f000:e05b ;转移目标位置ROM-BIOS 如图在左侧显示了该指令所在的物理内存地址 0x ...
- #启发式合并,LIS,平衡树#洛谷 4577 [FJOI2018]领导集团问题
题目 在一棵树上选择最多的点,使得存在祖先关系的点满足\(w_x\leq w_y\),其中\(x\)是\(y\)的祖先 分析 祖先链上要满足\(LIS\),考虑将子节点的LIS序列合并至节点\(x\) ...
- Windows开发的瑞士军刀,NewSPYLite发布
宗旨:延续了SpyLite的方便易用,同时也丰富了更多的功能,更适合开发者使用. 在SpyLite的基础功能上,开发了更多人性化并且好用的功能,增强的信息的可读性,以及软件总体的可用性. New Sp ...
- js实现电子白板
功能:使用画笔绘制笔迹(线条).橡皮檫 <!DOCTYPE html> <html lang="en"> <head> <meta cha ...
- openGauss2.1.0新特性-账本数据库实验
openGauss2.1.0 新特性-账本数据库实验 账本数据库融合了区块链思想,将用户操作记录至两种历史表中:用户历史表和全局区块表.当用户创建防篡改用户表时,系统将自动为该表添加一个 hash 列 ...
- 冒泡排序的基本实现【数据结构与算法—TypeScript 实现】
笔记整理自 coderwhy 『TypeScript 高阶数据结构与算法』课程 概念 本质:相邻元素两两比较并交换位置,使整个序列按照特定的顺序排列 特性 复杂度分析 时间复杂度: 最好情况:O(n) ...
- stack smashing--canary报错利用
stack smashing--canary报错利用 一般这种都是考察点比较狭窄,因为这个漏洞在libc2.23以上就被修复了,漏洞产生的原因是因为当覆盖掉canary的时候程序会报错,程序会执行 _ ...