做者 | 声东 阿里云售后技术专家nginx
**<关注阿里巴巴云原生公众号,回复 排查 便可下载电子书> **docker
《深刻浅出 Kubernetes》一书共聚集 12 篇技术文章,帮助你一次搞懂 6 个核心原理,吃透基础理论,一次学会 6 个典型问题的华丽操做!后端
Istio is the future!基本上,我相信对云原生技术趋势有些微判断的同窗,都会有这个觉悟。其背后的逻辑实际上是比较简单的:当容器集群,特别是 Kubernetes 成为事实上的标准以后,应用必然会不断的复杂化,服务治理确定会成为强需求。bash
**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 状况。less
更具体一点,kubelet 做为运行在每一个节点上的进程,以 tcp/http 的方式(节点网络命名空间到 Pod 网络命名空间)访问容器定义的接口,或者在容器的 namespace 里执行 exec 定义的命令,来肯定容器是否就绪。dom
这里的 2 说明这些 Pod 里都有两个容器,1/2 则表示,每一个 Pod 里只有一个容器是就绪的,即经过 Readiness 测试的。关于 2 这一点,咱们下一节会深刻讲,这里咱们先看一下,为何全部的 Pod 里,都有一个容器没有就绪。curl
使用 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 文章的第一篇,但愿在你们排查问题的时候,有所帮助。
第 3 期云原生网络研讨会邀您参加
5 月 28 日,阿里云技术专家将为你们带来《如何为云原生应用带来稳定高效的部署能力?》,届时将会介绍阿里经济体大规模应用上云过程当中遇到的核心部署问题、采起的对应解决方案,以及这些方案沉淀为通用化能力输出开源后,如何帮助阿里云上的用户提高应用部署发布的效率与稳定性。
听众可获取如下收益:
• 了解阿里经济体大规模应用上云的实践经验,如何解决原生 K8s workload 不知足场景需求的问题; • 做为外部用户,如何体验和使用上阿里经济体上云所沉淀下来的应用部署发布能力; • 演示阿里巴巴针对大规模 K8s 集群如何作到 DaemonSet 高可用的灰度升级(即将开源!)
点击连接便可预定直播:https://yq.aliyun.com/live/2898
“阿里巴巴云原生关注微服务、Serverless、容器、Service Mesh 等技术领域、聚焦云原生流行技术趋势、云原生大规模的落地实践,作最懂云原生开发者的公众号。”