写点什么

深入浅出 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:141289

评论

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

极客欢聚,燃动夏日!开发者嘉年华等你来

飞桨PaddlePaddle

人工智能 百度 paddle 飞桨

使用GithubAction自动构建部署项目

EquatorCoco

前端 Github Action 框架模式

字节跳动开源 Kelemetry:面向 Kubernetes 控制面的全局追踪系统

字节跳动开源

Kubernetes 云原生 可观测 追踪系统

ABAQUS下载-ABAQUS软件官方版教程下载

思茂信息

abaqus abaqus软件 abaqus有限元仿真 有限元分析 有限元仿真

浅析JVM GC配置指南 | 京东云技术团队

京东科技开发者

JVM 垃圾回收 GC 企业号 7 月 PK 榜

掌握这些写简历投简历的“黑魔法”,告别简历已读不回!

王中阳Go

golang 简历优化 面试技巧 求职面试 后端面试

Python第三方库(包)的安装(windows系统)

MEImei

纯代码和低代码的本质区别

互联网工科生

软件开发 低代码 代码开发

经过半年的努力,我终于成为了谷歌开发者专家(GDE)

拭心

android 面试 谷歌 GDE

DHorse v1.2.1 发布,基于k8s的发布平台

tiandizhiguai

DevOps k8s

MobPush 工作台操作指南:查看推送数据

MobTech袤博科技

大数据 前端 后端

软件测试/测试开发丨Windows Appium环境搭建

测试人

程序员 软件测试 自动化测试 环境搭建 appium

【推荐】贵阳市等保测评机构看这里!

行云管家

贵阳 等保 等级保护 等保测评

苹果APP安装包ipa如何安装在手机上|社区征文

雪奈椰子

年中技术盘点

【IOS】教你如何在手机端轻松安装ipa文件-(安装器已失效21.10)|社区征文

雪奈椰子

龙蜥开发者说:参与开源要敢于担任不同角色 | 第 21 期

OpenAnolis小助手

开源 标准化 sig 龙蜥开发者说 T-one

我们搬家啦!新家园,新征程,新篇章

KaiwuDB

KaiwuDB 剪彩 揭牌

小红书2024届REDstar技术提前批招聘火热进行中,快喊上学弟学妹看过来!【附专属内推码】

小红书技术REDtech

技术 招聘 校招 小红书

实时社群技术专题(二):百万级成员实时社群技术实现(消息系统篇)

JackJiang

网络编程 即时通讯 IM

谈一谈LLM在推荐域的一些理解

阿里技术

大模型 AIGC

中字头企业数字化转型的挑战与机遇

用友BIP

国产替代

SUFS: 存储资源使用量预测服务

KaiwuDB

KaiwuDB 存储资源使用量预测

架构师日记-到底该如何搭建一个新系统 | 京东云技术团队

京东科技开发者

架构 架构设计 工程架构 企业号 7 月 PK 榜

手把手教你用 NebulaGraph AI 全家桶跑图算法

NebulaGraph

人工智能 图数据库

sharding-jdbc分库连接数优化 | 京东物流技术团队

京东科技开发者

分库分表 Sharding sharding-jdbc 企业号 7 月 PK 榜

【活动回顾】Data + AI 时代下的云数仓设计 @Qcon

Databend

衡量开发人员工作效率的五个技巧

高端章鱼哥

代码 代码开发 开发效率

用友BIP数智化底座使能行业创新发展

用友BIP

数智底座

性能测试的理解误区

老张

性能测试 稳定性保障

常规LED广告显示屏的运营成本怎么估算

Dylan

广告 项目 运营 成本 LED显示屏

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