【ArchSummit架构师峰会】探讨数据与人工智能相互驱动的关系>>> 了解详情
写点什么

深入浅出 Kubernetes 实践篇 (五):二分之一活的微服务

  • 2020-03-30
  • 本文字数:4961 字

    阅读完需:约 16 分钟

深入浅出Kubernetes 实践篇 (五):二分之一活的微服务

简介:Istio is the future!基本上,我相信对云原生技术趋势有些微判断的同学,都会有这个觉悟。其背后的逻辑其实是比较简单的:当容器集群,特别是 Kubernetes 成为事实上的标准之后,应用必然会不断的复杂化,服务治理肯定会成为强需求。


Istio is the future!基本上,我相信对云原生技术趋势有些微判断的同学,都会有这个觉悟。其背后的逻辑其实是比较简单的:当容器集群,特别是 Kubernetes 成为事实上的标准之后,应用必然会不断的复杂化,服务治理肯定会成为强需求。


Istio 的现状是,聊的人很多,用的人其实很少。所以导致我们能看到的文章,讲道理的很多,讲实际踩坑经验的极少。


阿里云售后团队作为一线踩坑团队,分享问题排查经验,我们责无旁贷。这篇文章,我就跟大家聊一个简单 Istio 问题的排查过程,权当抛砖。

二分之一活的微服务

问题是这样的,用户在自己的测试集群里安装了 Istio,并依照官方文档部署 bookinfo 应用来上手 Istio。部署之后,用户执行 kubectl get pods 命令,发现所有的 pods 都只有二分之一个容器是 READY 的。


# kubectl get podsNAME READY STATUS RESTARTS AGEdetails-v1-68868454f5-94hzd 1/2 Running 0 1mproductpage-v1-5cb458d74f-28nlz 1/2 Running 0 1mratings-v1-76f4c9765f-gjjsc 1/2 Running 0 1mreviews-v1-56f6855586-dplsf 1/2 Running 0 1mreviews-v2-65c9df47f8-zdgbw 1/2 Running 0 1mreviews-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 -lnptActive Internet connections (only servers)Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program nametcp        0      0 0.0.0.0:15090           0.0.0.0:*               LISTEN      19/envoytcp        0      0 127.0.0.1:15000         0.0.0.0:*               LISTEN      19/envoytcp        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.confnameserver 172.19.0.10search default.svc.cluster.local svc.cluster.local cluster.local localdomain
复制代码


但是客户删除重建了 kube-dns 服务,且没有指定服务 IP,这导致,实际上集群 dns 的地址改变了,这也是为什么所有的 sidecar 都无法访问 Pilot。


# kubectl get svc -n kube-systemNAME                      TYPE           CLUSTER-IP      EXTERNAL-IP     PORT(S)                      AGEkube-dns                  ClusterIP      172.19.9.54     <none>          53/UDP,53/TCP                5d
复制代码


最后,通过修改 kube-dns 服务,指定 IP 地址,sidecar 恢复正常。


# kubectl get podsNAME READY STATUS RESTARTS AGEdetails-v1-68868454f5-94hzd 2/2 Running 0 6dnginx-647d5bf6c5-gfvkm 2/2 Running 0 2dnginx-647d5bf6c5-wvfpd 2/2 Running 0 2dproductpage-v1-5cb458d74f-28nlz 2/2 Running 0 6dratings-v1-76f4c9765f-gjjsc 2/2 Running 0 6dreviews-v1-56f6855586-dplsf 2/2 Running 0 6dreviews-v2-65c9df47f8-zdgbw 2/2 Running 0 6dreviews-v3-6cf47594fd-cvrtf 2/2 Running 0 6d
复制代码

结论

这其实是一个比较简单的问题,排查过程其实也就几分钟。但是写这篇文章,有点感觉是在看长安十二时辰,短短几分钟的排查过程,写完整背后的原理,前因后果,却花了几个小时。这是 Istio 文章的第一篇,希望在大家排查问题的时候,有所帮助。


作者简介


罗建龙(花名声东),阿里云技术专家。多年操作系统和图形显卡驱动调试和开发经验。目前专注云原生领域,容器集群和服务网格。


相关阅读


深入浅出Kubernetes 实践篇 (一):节点就绪问题之一


深入浅出Kubernetes 实践篇 (二):节点就绪问题之二


深入浅出Kubernetes 实践篇 (三):命名空间删除问题


深入浅出Kubernetes 实践篇 (四):集群安全组配置管理


2020-03-30 17:141102

评论

发布
暂无评论
发现更多内容

Hacktoberfest 2022:Jenkins maven-snapshot-check Plugin 的改进实践

donghui

jenkins Hacktoberfest

爬虫的简介

二哈侠

Python语法 10月月更 爬虫简介

HTML的简介

二哈侠

HTML标准 10月月更 HTML元素

Zebec即将推出公链并开放节点申请,潜力几何?

EOSdreamer111

Spring之IOC

楠羽

笔记 spring 源码 10月月更

跟着卷卷龙一起学Camera--压缩与存储

卷卷龙

ISP camera 10月月更

跟着卷卷龙一起学Camera--BM3D

卷卷龙

ISP camera 10月月更

Surpass Day——Java 接口在开发中的作用、关于Object类、内部类

胖虎不秃头

Java 10月月更 se

企业的数据资产怎么盘?统筹规划,摸清家底

雨果

数据资产管理

大数据ELK(二十):FileBeat是如何工作的

Lansonli

Filebeat 10月月更

2022-10-09:我们给出了一个(轴对齐的)二维矩形列表 rectangles 。 对于 rectangle[i] = [x1, y1, x2, y2],其中(x1,y1)是矩形 i 左下角的坐

福大大架构师每日一题

算法 rust 福大大

爬虫练习题(四)

张立梵

Python. 10月月更 爬虫案例

一起学习设计模式:备忘录模式——软件的“后悔药”

宇宙之一粟

设计模式 备忘录模式 10月月更

什么是DataOps?DataOps只是Data加上Ops吗

雨果

DevOps

【算法作业】实验一:轮流报数与鸡兔同笼

清风莫追

面试突击89:事务隔离级别和传播机制有什么区别?

王磊

简述Docker改造传统应用的流程

穿过生命散发芬芳

Docker 10月月更

Android Coder浅谈队列同步器(AQS)

子不语Any

后端 java; 10月月更

强化学习发现矩阵乘法工赋开发者社区 | DeepMind再登Nature封面推出AlphaTensor

工赋开发者社区

【玩物立志-scratch少儿编程】骑上小摩托(动态背景+摄像头控制操作)

清风莫追

10月月更

_fitoa_word的实现:一个整型数据是如何转成字符串的呢?

桑榆

源码刨析 10月月更 C++

六类网线、七类网线、八类网线区别有哪些?

wljslmz

10月月更 弱电 以太网线 综合布线

应用监控可视化工具Grafana&Kibana对比

阿泽🧸

10月月更 监控可视化

跟着卷卷龙一起学Camera--透镜组

卷卷龙

ISP camera 10月月更

数字化转型,目的是为了转型还是数字化?

雨果

数字化转型

【算法作业】实验五:神奇宝贝大军 & 到迷宫出口的最短路径

清风莫追

算法 10月月更

用OptaPlanner进行车辆路线优化

成长兔🐇

【C语言内功修炼】柔性数组的奥秘

Albert Edison

数组 C语言 10月月更 柔性数组

http协议简介

二哈侠

Cookie HTTP协议 Cookie反爬虫 10月月更

Zebec地平线节点运营计划,Web3流支付赛道或多一条全新公链

股市老人

Python基础(十三) | 机器学习sklearn库详解与应用

timerring

Python 机器学习 sklearn 10月月更

深入浅出Kubernetes 实践篇 (五):二分之一活的微服务_文化 & 方法_罗建龙(声东)_InfoQ精选文章