kubernetes

打造云原生大型分布式监控系统(四): Kvass+Thanos 监控超大规模容器集群

目录 概述 有 Thanos 不够吗 ? 什么是 Kvass ? 部署实践 部署准备 部署 Kvass 部署 thanos-query 小结 概述 继上一篇 Thanos 部署与实践 发布半年多之后,随着技术的发展,本系列又迎来了一次更新。本文将介绍如何结合 Kvass 与 Thanos,来更好的实现大规模容器集群场景下的监控。

Nginx Ingress 高并发实践

目录 概述 内核参数调优 调大连接队列的大小 扩大源端口范围 TIME_WAIT 复用 调大最大文件句柄数 配置示例 全局配置调优 调高 keepalive 连接最大请求数 调高 keepalive 最大空闲连接数 调高单个 worker 最大连接数 配置示例 总结 参考资料 概述 Nginx Ingress Controller 基于 Nginx 实现了 Kubernetes Ingress API,Nginx 是公认的高性能网关,但如果不对其进行一些参数调优,就不能充分发挥出高性能的优势。之前我们在 Nginx Ingress on TKE 部署最佳实践 一文中讲了 Nginx Ingress 在 TKE 上部署最佳实践,涉及的部署 YAML 其实已经包含了一些性能方面的参数优化,只是没有提及,本文将继续展开介绍针对 Nginx Ingress 的一些全局配置与内核参数调优的建议,可用于支撑我们的高并发业务。

Nginx Ingress on TKE 部署最佳实践

目录 概述 什么是 Nginx Ingress ? 有哪些部署方案 ? 方案一: Deployment + LB 方案二:Daemonset + HostNetwork + LB 方案三:Deployment + LB 直通 Pod 如何选型? 如何支持内网 Ingress ? 如何复用已有 LB ?

Kubernetes 服务部署最佳实践(二) 如何提高服务可用性

目录 引言 如何避免单点故障? 如何避免节点维护或升级时导致服务不可用? 如何让服务进行平滑更新? 健康检查怎么配才好? 参考资料 引言 上一篇 文章我们围绕如何合理利用资源的主题做了一些最佳实践的分享,这一次我们就如何提高服务可用性的主题来展开探讨。 怎样提高我们部署服务的可用性呢?K8S 设计本身就考虑到了各种故障的可能性,并提供了一些自愈机制以提高系统的容错性,但有些情况还是可能导致较长时间不可用,拉低服务可用性的指标。本文将结合生产实践经验,为大家提供一些最佳实践来最大化的提高服务可用性。 如何避免单点故障? K8S 的设计就是假设节点是不可靠的。节点越多,发生软硬件故障导致节点不可用的几率就越高,所以我们通常需要给服务部署多个副本,根据实际情况调整 replicas 的值,如果值为 1 就必然存在单点故障,如果大于 1 但所有副本都调度到同一个节点了,那还是有单点故障,有时候还要考虑到灾难,比如整个机房不可用。 所以我们不仅要有合理的副本数量,还需要让这些不同副本调度到不同的拓扑域(节点、可用区),打散调度以避免单点故障,这个可以利用 Pod 反亲和性来做到,反亲和主要分强反亲和与弱反亲和两种。 先来看个强反亲和的示例,将 dns 服务强制打散调度到不同节点上:

Kubernetes 服务部署最佳实践(一) 如何合理利用资源

目录 引言 Request 与 Limit 怎么设置才好 所有容器都应该设置 request 老是忘记设置怎么办 重要的线上应用改如何设置 怎样设置才能提高资源利用率 尽量避免使用过大的 request 与 limit 避免测试 namespace 消耗过多资源影响生产业务 如何让资源得到更合理的分配 使用亲和性 使用污点与容忍 弹性伸缩 如何支持流量突发型业务 如何节约成本 无法水平扩容的服务怎么办 参考资料 引言 业务容器化后,如何将其部署在 K8S 上?如果仅仅是将它跑起来,很简单,但如果是上生产,我们有许多地方是需要结合业务场景和部署环境进行方案选型和配置调优的。比如,如何设置容器的 Request 与 Limit、如何让部署的服务做到高可用、如何配置健康检查、如何进行弹性伸缩、如何更好的进行资源调度、如何选择持久化存储、如何对外暴露服务等。

打造云原生大型分布式监控系统(三): Thanos 部署与实践

目录 视频 概述 部署方式 方案选型 Sidecar or Receiver 评估是否需要 Ruler 评估是否需要 Store Gateway 与 Compact 部署实践 准备对象存储配置 给 Prometheus 加上 Sidecar 安装 Query 安装 Store Gateway 安装 Ruler 安装 Compact 安装 Receiver 指定 Query 为数据源 总结 视频 附上本系列完整视频 打造云原生大型分布式监控系统(一): 大规模场景下 Prometheus 的优化手段 https://www.bilibili.com/video/BV17C4y1x7HE 打造云原生大型分布式监控系统(二): Thanos 架构详解 https://www.bilibili.com/video/BV1Vk4y1R7S9 打造云原生大型分布式监控系统(三): Thanos 部署与实践 https://www.bilibili.com/video/BV16g4y187HD 概述 上一篇 Thanos 架构详解 我们深入理解了 thanos 的架构设计与实现原理,现在我们来聊聊实战,分享一下如何部署和使用 Thanos。

打造云原生大型分布式监控系统(二): Thanos 架构详解

目录 概述 Thanos 架构 架构设计剖析 Query 与 Sidecar Store Gateway Ruler Compact 再看架构图 Sidecar 模式与 Receiver 模式 总结 概述 之前在 大规模场景下 Prometheus 的优化手段 中,我们想尽 “千方百计” 才好不容易把 Prometheus 优化到适配大规模场景,部署和后期维护麻烦且复杂不说,还有很多不完美的地方,并且还无法满足一些更高级的诉求,比如查看时间久远的监控数据,对于一些时间久远不常用的 “冷数据”,最理想的方式就是存到廉价的对象存储中,等需要查询的时候能够自动加载出来。 Thanos (没错,就是灭霸) 可以帮我们简化分布式 Prometheus 的部署与管理,并提供了一些的高级特性:全局视图,长期存储,高可用。下面我们来详细讲解一下。

打造云原生大型分布式监控系统(一): 大规模场景下 Prometheus 的优化手段

目录 概述 大规模场景下 Prometheus 的痛点 从服务维度拆分 Prometheus 对超大规模的服务做分片 拆分引入的新问题 集中数据存储 Prometheus 联邦 Prometheus 高可用 总结 概述 Prometheus 几乎已成为监控领域的事实标准,它自带高效的时序数据库存储,可以让单台 Prometheus 能够高效的处理大量的数据,还有友好并且强大的 PromQL 语法,可以用来灵活的查询各种监控数据以及配置告警规则,同时它的 pull 模型指标采集方式被广泛采纳,非常多的应用都实现了 Prometheus 的 metrics 接口以暴露自身各项数据指标让 Prometheus 去采集,很多没有适配的应用也会有第三方 exporter 帮它去适配 Prometheus,所以监控系统我们通常首选用 Prometheus,本系列文章也将基于 Prometheus 来打造云原生环境下的大型分布式监控系统。 大规模场景下 Prometheus 的痛点 Prometheus 本身只支持单机部署,没有自带支持集群部署,也就不支持高可用以及水平扩容,在大规模场景下,最让人关心的问题是它的存储空间也受限于单机磁盘容量,磁盘容量决定了单个 Prometheus 所能存储的数据量,数据量大小又取决于被采集服务的指标数量、服务数量、采集速率以及数据过期时间。在数据量大的情况下,我们可能就需要做很多取舍,比如丢弃不重要的指标、降低采集速率、设置较短的数据过期时间(默认只保留15天的数据,看不到比较久远的监控数据)。 这些痛点实际也是可以通过一些优化手段来改善的,下面我们来细讲一下。

Kubernetes 疑难杂症排查分享:神秘的溢出与丢包

目录 问题描述 猜测 抓包 syn queue 与 accept queue listen 与 accept Linux 的 backlog 队列溢出 回到问题上来 somaxconn 的默认值很小 方式一: 使用 k8s sysctls 特性直接给 pod 指定内核参数 方式二: 使用 initContainers 设置内核参数 方式三: 安装 tuning CNI 插件统一设置 sysctl nginx 的 backlog 参考资料 上一篇 Kubernetes 疑难杂症排查分享: 诡异的 No route to host 不小心又爆火,这次继续带来干货,看之前请提前泡好茶,避免口干。 问题描述 有用户反馈大量图片加载不出来。 图片下载走的 k8s ingress,这个 ingress 路径对应后端 service 是一个代理静态图片文件的 nginx deployment,这个 deployment 只有一个副本,静态文件存储在 nfs 上,nginx 通过挂载 nfs 来读取静态文件来提供图片下载服务,所以调用链是:client –> k8s ingress –> nginx –> nfs。 猜测 猜测: ingress 图片下载路径对应的后端服务出问题了。 验证:在 k8s 集群直接 curl nginx 的 pod ip,发现不通,果然是后端服务的问题! 抓包 继续抓包测试观察,登上 nginx pod 所在节点,进入容器的 netns 中: # 拿到 pod 中 nginx 的容器 id $ kubectl describe pod tcpbench-6484d4b457-847gl | grep -A10 "^Containers:" | grep -Eo 'docker://.*$' | head -n 1 | sed 's/docker:\/\/\(.*\)$/\1/' 49b4135534dae77ce5151c6c7db4d528f05b69b0c6f8b9dd037ec4e7043c113e # 通过容器 id 拿到 nginx 进程 pid $ docker inspect -f {{.State.Pid}} 49b4135534dae77ce5151c6c7db4d528f05b69b0c6f8b9dd037ec4e7043c113e 3985 # 进入 nginx 进程所在的 netns $ nsenter -n -t 3985 # 查看容器 netns 中的网卡信息,确认下 $ ip a 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 inet 127.0.0.1/8 scope host lo valid_lft forever preferred_lft forever 3: eth0@if11: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default link/ether 56:04:c7:28:b0:3c brd ff:ff:ff:ff:ff:ff link-netnsid 0 inet 172.26.0.8/26 scope global eth0 valid_lft forever preferred_lft forever 使用 tcpdump 指定端口 24568 抓容器 netns 中 eth0 网卡的包: tcpdump -i eth0 -nnnn -ttt port 24568 在其它节点准备使用 nc 指定源端口为 24568 向容器发包: nc -u 24568 172.16.1.21 80 观察抓包结果: 00:00:00.000000 IP 10.0.0.3.24568 > 172.16.1.21.80: Flags [S], seq 416500297, win 29200, options [mss 1424,sackOK,TS val 3000206334 ecr 0,nop,wscale 9], length 0 00:00:01.032218 IP 10.0.0.3.24568 > 172.16.1.21.80: Flags [S], seq 416500297, win 29200, options [mss 1424,sackOK,TS val 3000207366 ecr 0,nop,wscale 9], length 0 00:00:02.011962 IP 10.0.0.3.24568 > 172.16.1.21.80: Flags [S], seq 416500297, win 29200, options [mss 1424,sackOK,TS val 3000209378 ecr 0,nop,wscale 9], length 0 00:00:04.127943 IP 10.0.0.3.24568 > 172.16.1.21.80: Flags [S], seq 416500297, win 29200, options [mss 1424,sackOK,TS val 3000213506 ecr 0,nop,wscale 9], length 0 00:00:08.192056 IP 10.0.0.3.24568 > 172.16.1.21.80: Flags [S], seq 416500297, win 29200, options [mss 1424,sackOK,TS val 3000221698 ecr 0,nop,wscale 9], length 0 00:00:16.127983 IP 10.0.0.3.24568 > 172.16.1.21.80: Flags [S], seq 416500297, win 29200, options [mss 1424,sackOK,TS val 3000237826 ecr 0,nop,wscale 9], length 0 00:00:33.791988 IP 10.0.0.3.24568 > 172.16.1.21.80: Flags [S], seq 416500297, win 29200, options [mss 1424,sackOK,TS val 3000271618 ecr 0,nop,wscale 9], length 0 SYN 包到容器内网卡了,但容器没回 ACK,像是报文到达容器内的网卡后就被丢了。看样子跟防火墙应该也没什么关系,也检查了容器 netns 内的 iptables 规则,是空的,没问题。 排除是 iptables 规则问题,在容器 netns 中使用 netstat -s 检查下是否有丢包统计: $ netstat -s | grep -E 'overflow|drop' 12178939 times the listen queue of a socket overflowed 12247395 SYNs to LISTEN sockets dropped 果然有丢包,为了理解这里的丢包统计,我深入研究了一下,下面插播一些相关知识。

Kubernetes 疑难杂症排查分享: 诡异的 No route to host

目录 问题反馈 分析 问题没有解决 深入分析 总结 之前发过一篇干货满满的爆火文章 Kubernetes 网络疑难杂症排查分享,包含多个疑难杂症的排查案例分享,信息量巨大。这次我又带来了续集,只讲一个案例,但信息量也不小,Are you ready ? 问题反馈 有用户反馈 Deployment 滚动更新的时候,业务日志偶尔会报 “No route to host” 的错误。 分析 之前没遇到滚动更新会报 “No route to host” 的问题,我们先看下滚动更新导致连接异常有哪些常见的报错: Connection reset by peer: 连接被重置。通常是连接建立过,但 server 端发现 client 发的包不对劲就返回 RST,应用层就报错连接被重置。比如在 server 滚动更新过程中,client 给 server 发的请求还没完全结束,或者本身是一个类似 grpc 的多路复用长连接,当 server 对应的旧 Pod 删除(没有做优雅结束,停止时没有关闭连接),新 Pod 很快创建启动并且刚好有跟之前旧 Pod 一样的 IP,这时 kube-proxy 也没感知到这个 IP 其实已经被删除然后又被重建了,针对这个 IP 的规则就不会更新,旧的连接依然发往这个 IP,但旧 Pod 已经不在了,后面继续发包时依然转发给这个 Pod IP,最终会被转发到这个有相同 IP 的新 Pod 上,而新 Pod 收到此包时检查报文发现不对劲,就返回 RST 给 client 告知将连接重置。针对这种情况,建议应用自身处理好优雅结束:Pod 进入 Terminating 状态后会发送 SIGTERM 信号给业务进程,业务进程的代码需处理这个信号,在进程退出前关闭所有连接。 Connection refused: 连接被拒绝。通常是连接还没建立,client 正在发 SYN 包请求建立连接,但到了 server 之后发现端口没监听,内核就返回 RST 包,然后应用层就报错连接被拒绝。比如在 server 滚动更新过程中,旧的 Pod 中的进程很快就停止了(网卡还未完全销毁),但 client 所在节点的 iptables/ipvs 规则还没更新,包就可能会被转发到了这个停止的 Pod (由于 k8s 的 controller 模式,从 Pod 删除到 service 的 endpoint 更新,再到 kube-proxy watch 到更新并更新 节点上的 iptables/ipvs 规则,这个过程是异步的,中间存在一点时间差,所以有可能存在 Pod 中的进程已经没有监听,但 iptables/ipvs 规则还没更新的情况)。针对这种情况,建议给容器加一个 preStop,在真正销毁 Pod 之前等待一段时间,留时间给 kube-proxy 更新转发规则,更新完之后就不会再有新连接往这个旧 Pod 转发了,preStop 示例: lifecycle: preStop: exec: command: - /bin/bash - -c - sleep 30 另外,还可能是新的 Pod 启动比较慢,虽然状态已经 Ready,但实际上可能端口还没监听,新的请求被转发到这个还没完全启动的 Pod 就会报错连接被拒绝。针对这种情况,建议给容器加就绪检查 (readinessProbe),让容器真正启动完之后才将其状态置为 Ready,然后 kube-proxy 才会更新转发规则,这样就能保证新的请求只被转发到完全启动的 Pod,readinessProbe 示例: readinessProbe: httpGet: path: /healthz port: 80 httpHeaders: - name: X-Custom-Header value: Awesome initialDelaySeconds: 15 timeoutSeconds: 1 Connection timed out: 连接超时。通常是连接还没建立,client 发 SYN 请求建立连接一直等到超时时间都没有收到 ACK,然后就报错连接超时。这个可能场景跟前面 Connection refused 可能的场景类似,不同点在于端口有监听,但进程无法正常响应了: 转发规则还没更新,旧 Pod 的进程正在停止过程中,虽然端口有监听,但已经不响应了;或者转发规则更新了,新 Pod 端口也监听了,但还没有真正就绪,还没有能力处理新请求。针对这些情况的建议跟前面一样:加 preStop 和 readinessProbe。 下面我们来继续分析下滚动更新时发生 No route to host 的可能情况。 这个报错很明显,IP 无法路由,通常是将报文发到了一个已经彻底销毁的 Pod (网卡已经不在)。不可能发到一个网卡还没创建好的 Pod,因为即便不加存活检查,也是要等到 Pod 网络初始化完后才可能 Ready,然后 kube-proxy 才会更新转发规则。 什么情况下会转发到一个已经彻底销毁的 Pod? 借鉴前面几种滚动更新的报错分析,我们推测应该是 Pod 很快销毁了但转发规则还没更新,从而新的请求被转发了这个已经销毁的 Pod,最终报文到达这个 Pod 所在 PodCIDR 的 Node 上时,Node 发现本机已经没有这个 IP 的容器,然后 Node 就返回 ICMP 包告知 client 这个 IP 不可达,client 收到 ICMP 后,应用层就会报错 “No route to host”。 所以根据我们的分析,关键点在于 Pod 销毁太快,转发规则还没来得及更新,导致后来的请求被转发到已销毁的 Pod。针对这种情况,我们可以给容器加一个 preStop,留时间给 kube-proxy 更新转发规则来解决,参考 《Kubernetes实践指南》中的部分章节: https://k8s.imroc.io/best-practice/high-availability-deployment-of-applications#smooth-update-using-prestophook-and-readinessprobe