前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >【TKE团队】Kubernetes 服务部署最佳实践(二) 如何提高服务可用性

【TKE团队】Kubernetes 服务部署最佳实践(二) 如何提高服务可用性

原创
作者头像
imroc
发布于 2020-06-19 13:50:00
发布于 2020-06-19 13:50:00
1.2K0
举报

引言

上一篇 文章我们围绕如何合理利用资源的主题做了一些最佳实践的分享,这一次我们就如何提高服务可用性的主题来展开探讨。

怎样提高我们部署服务的可用性呢?K8S 设计本身就考虑到了各种故障的可能性,并提供了一些自愈机制以提高系统的容错性,但有些情况还是可能导致较长时间不可用,拉低服务可用性的指标。本文将结合生产实践经验,为大家提供一些最佳实践来最大化的提高服务可用性。

如何避免单点故障?

K8S 的设计就是假设节点是不可靠的。节点越多,发生软硬件故障导致节点不可用的几率就越高,所以我们通常需要给服务部署多个副本,根据实际情况调整 replicas 的值,如果值为 1 就必然存在单点故障,如果大于 1 但所有副本都调度到同一个节点了,那还是有单点故障,有时候还要考虑到灾难,比如整个机房不可用。

所以我们不仅要有合理的副本数量,还需要让这些不同副本调度到不同的拓扑域(节点、可用区),打散调度以避免单点故障,这个可以利用 Pod 反亲和性来做到,反亲和主要分强反亲和与弱反亲和两种。

先来看个强反亲和的示例,将 dns 服务强制打散调度到不同节点上:

代码语言:txt
AI代码解释
复制
affinity:
 podAntiAffinity:
   requiredDuringSchedulingIgnoredDuringExecution:
   - labelSelector:
       matchExpressions:
       - key: k8s-app
         operator: In
         values:
         - kube-dns
     topologyKey: kubernetes.io/hostname
  • labelSelector.matchExpressions 写该服务对应 pod 中 labels 的 key 与 value,因为 Pod 反亲和性是通过判断 replicas 的 pod label 来实现的。
  • topologyKey 指定反亲和的拓扑域,即节点 label 的 key。这里用的 kubernetes.io/hostname 表示避免 pod 调度到同一节点,如果你有更高的要求,比如避免调度到同一个可用区,实现异地多活,可以用 failure-domain.beta.kubernetes.io/zone。通常不会去避免调度到同一个地域,因为一般同一个集群的节点都在一个地域,如果跨地域,即使用专线时延也会很大,所以 topologyKey 一般不至于用 failure-domain.beta.kubernetes.io/region
  • requiredDuringSchedulingIgnoredDuringExecution 调度时必须满足该反亲和性条件,如果没有节点满足条件就不调度到任何节点 (Pending)。

如果不用这种硬性条件可以使用 preferredDuringSchedulingIgnoredDuringExecution 来指示调度器尽量满足反亲和性条件,即弱反亲和性,如果实在没有满足条件的,只要节点有足够资源,还是可以让其调度到某个节点,至少不会 Pending。

我们再来看个弱反亲和的示例:

代码语言:txt
AI代码解释
复制
affinity:
  podAntiAffinity:
    preferredDuringSchedulingIgnoredDuringExecution:
    - weight: 100
      podAffinityTerm:
        labelSelector:
          matchExpressions:
          - key: k8s-app
            operator: In
            values:
            - kube-dns
      topologyKey: kubernetes.io/hostname

注意到了吗?相比强反亲和有些不同哦,多了一个 weight,表示此匹配条件的权重,而匹配条件被挪到了 podAffinityTerm 下面。

如何避免节点维护或升级时导致服务不可用?

有时候我们需要对节点进行维护或进行版本升级等操作,操作之前需要对节点执行驱逐 (kubectl drain),驱逐时会将节点上的 Pod 进行删除,以便它们漂移到其它节点上,当驱逐完毕之后,节点上的 Pod 都漂移到其它节点了,这时我们就可以放心的对节点进行操作了。

有一个问题就是,驱逐节点是一种有损操作,驱逐的原理:

  1. 封锁节点 (设为不可调度,避免新的 Pod 调度上来)。
  2. 将该节点上的 Pod 删除。
  3. ReplicaSet 控制器检测到 Pod 减少,会重新创建一个 Pod,调度到新的节点上。

这个过程是先删除,再创建,并非是滚动更新,因此更新过程中,如果一个服务的所有副本都在被驱逐的节点上,则可能导致该服务不可用。

我们再来下什么情况下驱逐会导致服务不可用:

  1. 服务存在单点故障,所有副本都在同一个节点,驱逐该节点时,就可能造成服务不可用。
  2. 服务没有单点故障,但刚好这个服务涉及的 Pod 全部都部署在这一批被驱逐的节点上,所以这个服务的所有 Pod 同时被删,也会造成服务不可用。
  3. 服务没有单点故障,也没有全部部署到这一批被驱逐的节点上,但驱逐时造成这个服务的一部分 Pod 被删,短时间内服务的处理能力下降导致服务过载,部分请求无法处理,也就降低了服务可用性。

针对第一点,我们可以使用前面讲的反亲和性来避免单点故障。

针对第二和第三点,我们可以通过配置 PDB (PodDisruptionBudget) 来避免所有副本同时被删除,驱逐时 K8S 会 "观察" nginx 的当前可用与期望的副本数,根据定义的 PDB 来控制 Pod 删除速率,达到阀值时会等待 Pod 在其它节点上启动并就绪后再继续删除,以避免同时删除太多的 Pod 导致服务不可用或可用性降低,下面给出两个示例。

示例一 (保证驱逐时 nginx 至少有 90% 的副本可用):

代码语言:txt
AI代码解释
复制
apiVersion: policy/v1beta1
kind: PodDisruptionBudget
metadata:
  name: zk-pdb
spec:
  minAvailable: 90%
  selector:
    matchLabels:
      app: zookeeper

示例二 (保证驱逐时 zookeeper 最多有一个副本不可用,相当于逐个删除并等待在其它节点完成重建):

代码语言:txt
AI代码解释
复制
apiVersion: policy/v1beta1
kind: PodDisruptionBudget
metadata:
  name: zk-pdb
spec:
  maxUnavailable: 1
  selector:
    matchLabels:
      app: zookeeper

如何让服务进行平滑更新?

解决了服务单点故障和驱逐节点时导致的可用性降低问题后,我们还需要考虑一种可能导致可用性降低的场景,那就是滚动更新。为什么服务正常滚动更新也可能影响服务的可用性呢?别急,下面我来解释下原因。

假如集群内存在服务间调用:

当 server 端发生滚动更新时:

发生两种尴尬的情况:

  1. 旧的副本很快销毁,而 client 所在节点 kube-proxy 还没更新完转发规则,仍然将新连接调度给旧副本,造成连接异常,可能会报 "connection refused" (进程停止过程中,不再接受新请求) 或 "no route to host" (容器已经完全销毁,网卡和 IP 已不存在)。
  2. 新副本启动,client 所在节点 kube-proxy 很快 watch 到了新副本,更新了转发规则,并将新连接调度给新副本,但容器内的进程启动很慢 (比如 Tomcat 这种 java 进程),还在启动过程中,端口还未监听,无法处理连接,也造成连接异常,通常会报 "connection refused" 的错误。

针对第一种情况,可以给 container 加 preStop,让 Pod 真正销毁前先 sleep 等待一段时间,等待 client 所在节点 kube-proxy 更新转发规则,然后再真正去销毁容器。这样能保证在 Pod Terminating 后还能继续正常运行一段时间,这段时间如果因为 client 侧的转发规则更新不及时导致还有新请求转发过来,Pod 还是可以正常处理请求,避免了连接异常的发生。听起来感觉有点不优雅,但实际效果还是比较好的,分布式的世界没有银弹,我们只能尽量在当前设计现状下找到并实践能够解决问题的最优解。

针对第二种情况,可以给 container 加 ReadinessProbe (就绪检查),让容器内进程真正启动完成后才更新 Service 的 Endpoint,然后 client 所在节点 kube-proxy 再更新转发规则,让流量进来。这样能够保证等 Pod 完全就绪了才会被转发流量,也就避免了链接异常的发生。

最佳实践 yaml 示例:

代码语言:txt
AI代码解释
复制
        readinessProbe:
          httpGet:
            path: /healthz
            port: 80
            httpHeaders:
            - name: X-Custom-Header
              value: Awesome
          initialDelaySeconds: 10
          timeoutSeconds: 1
        lifecycle:
          preStop:
            exec:
              command: ["/bin/bash", "-c", "sleep 10"]

健康检查怎么配才好?

我们都知道,给 Pod 配置健康检查也是提高服务可用性的一种手段,配置 ReadinessProbe (就绪检查) 可以避免将流量转发给还没启动完全或出现异常的 Pod;配置 LivenessProbe (存活检查) 可以让存在 bug 导致死锁或 hang 住的应用重启来恢复。但是,如果配置配置不好,也可能引发其它问题,这里根据一些踩坑经验总结了一些指导性的建议:

  • 不要轻易使用 LivenessProbe,除非你了解后果并且明白为什么你需要它,参考 Liveness Probes are Dangerous
  • 如果使用 LivenessProbe,不要和 ReadinessProbe 设置成一样 (failureThreshold 更大)
  • 探测逻辑里不要有外部依赖 (db, 其它 pod 等),避免抖动导致级联故障
  • 业务程序应尽量暴露 HTTP 探测接口来适配健康检查,避免使用 TCP 探测,因为程序 hang 死时, TCP 探测仍然能通过 (TCP 的 SYN 包探测端口是否存活在内核态完成,应用层不感知)

参考资料

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
如何在 Kubernetes 上部署高可用应用程序
但使用 Kubernetes 不仅仅是设置它并向其部署 pod。Kubernetes 中许多使应用程序更具弹性和高可用性的丰富功能不仅仅是一件事,而是不同流程和配置的组合。从如何在不停机的情况下部署应用程序,到调度Pod 以确保它们在节点之间正确分布。这些是我们将在本文中讨论的配置和技术的要点:
DevOps云学堂
2024/05/11
4150
如何在 Kubernetes 上部署高可用应用程序
【K8S专栏】Kubernetes应用质量管理
在Kubernetes中,Pod是最小的调度单元,所以跟资源和调度相关的属性都是Pod对象的字段,而其中最重要的就是CPU和内存。如下所示:
没有故事的陈师傅
2022/09/15
6100
15个Kubernetes调度情景实用指南
Kubernetes调度是确保集群中的Pod在适当节点上运行的关键组件。通过灵活配置调度策略,可以提高资源利用率、负载平衡和高可用性。
云云众生s
2024/03/28
2340
研发工程师玩转Kubernetes——利用Pod反亲和性控制一个Node上只能有一个Pod
在《研发工程师玩转Kubernetes——使用污点(taint)驱逐Pod》、《研发工程师玩转Kubernetes——使用Node特性定向调度Pod》和《研发工程师玩转Kubernetes——Node亲和性requiredDuringSchedulingIgnoredDuringExecution几种边界实验》中,我们介绍了Node的亲和性。后面几节我们将介绍Pod的亲和性和反亲和性。 Pod的亲和性和反亲和性通过Pod的标签来识别,而不是通过Node的标签。比如标题中“利用Pod反亲和性控制一个Node上只能有一个Pod”可以翻译成:只能将Pod调度到不存在该Pod标签的Node上。
方亮
2023/07/31
4160
Kubernetes(k8s)-Pod亲和性(Affinity)和反亲和性(Anti-affinity)
我们上一章介绍了Docker基本情况,目前在规模较大的容器集群基本都是Kubernetes,但是Kubernetes涉及的东西和概念确实是太多了,而且随着版本迭代功能在还增加,笔者有些功能也确实没用过,所以只能按照我自己的理解来讲解。
运维小路
2025/02/10
1880
Kubernetes(k8s)-Pod亲和性(Affinity)和反亲和性(Anti-affinity)
Kubernetes之调度篇
这边肯定会有其他场景也会有对pod的调度有特殊要求,这边只是列举了其中几个情况,对于上述遇到的情况我们需要怎么处理,其实k8s给我们提供了丰富的调度策略来满足我们的需求。下面我们来一一说下这些调度策略。
聂伟星
2020/08/25
1.5K0
kubernetesr进阶之pod的亲和性与反亲和性
nodeSelector 提供了一个非常简单的方式,将 Pod 限定到包含特定标签的节点上。亲和性与反亲和性(affinity / anti-affinity)特性则极大地扩展了限定的表达方式。主要的增强点在于:
linus_lin
2024/09/06
1270
kubernetesr进阶之pod的亲和性与反亲和性
Kubernetes 微服务最佳实践
原文作者:ryan4yin,🔗: https://thiscute.world/posts/kubernetes-best-practices/ 本文主要介绍我个人在使用 Kubernetes 的过程中,总结出的一套「Kubernetes 配置」,是我个人的「最佳实践」。其中大部分内容都经历过线上环境的考验,但是也有少部分还只在我脑子里模拟过,请谨慎参考。 阅读前的几个注意事项: 这份文档比较长,囊括了很多内容,建议当成参考手册使用,先参照目录简单读一读,有需要再细读相关内容。 这份文档需要一定的 Kube
我的小碗汤
2023/03/19
1.2K0
Kubernetes 微服务最佳实践
Kubernetes 运维记录(5)
request 的值并不是指给容器实际分配的资源大小,它仅仅是给调度器看的,调度器会 “观察” 每个节点可以用于分配的资源有多少,也知道每个节点已经被分配了多少资源。被分配资源的大小就是节点上所有 Pod 中定义的容器 request 之和,它可以计算出节点剩余多少资源可以被分配(可分配资源减去已分配的 request 之和)。如果发现节点剩余可分配资源大小比当前要被调度的 Pod 的 reuqest 还小,那么就不会考虑调度到这个节点,反之,才可能调度。所以,如果不配置 request,那么调度器就不能知道节点大概被分配了多少资源出去,调度器得不到准确信息,也就无法做出合理的调度决策,很容易造成调度不合理,有些节点可能很闲,而有些节点可能很忙,甚至 NotReady。
后端云
2023/02/10
4870
Kubernetes 运维记录(5)
Kubernetes服务部署最佳实践|如何合理利用资源
作者陈鹏(roc),腾讯工程师,负责腾讯云TKE的售中、售后的技术支持,根据客户需求输出合理技术方案与最佳实践,为客户业务保驾护航。
CNCF
2020/06/17
1.3K0
云原生高可用与容灾系列(一): Pod 打散调度
将 Pod 打散调度到不同地方,可避免因软硬件故障、光纤故障、断电或自然灾害等因素导致服务不可用,以实现服务的高可用部署。
imroc
2021/07/14
2.1K0
【K8s】Kubernetes 服务调度详解
在 Kubernetes 中,服务调度是指 kube-scheduler 组件根据特定的调度算法和策略,将 Pod 分配到最合适的 Node 节点上,以满足应用程序的资源需求和 Kubernetes 集群的资源限制,实现集群资源充分、合理的利用。
行者Sun
2024/09/02
3690
【K8s】Kubernetes 服务调度详解
2025年K8s最新高频面试题,看看你能答对几个?
亲和性与反亲和性通过节点亲和性(NodeAffinity)和Pod亲和性(PodAffinity/PodAntiAffinity)实现。
没有故事的陈师傅
2025/03/11
2060
2025年K8s最新高频面试题,看看你能答对几个?
利用 K8S 的反亲和性构建高可用应用
K8S 支持多副本部署,但不代表应用的高可用,因为多个副本可能部署到同一个节点上。
SRE扫地僧
2024/02/26
4850
利用 K8S 的反亲和性构建高可用应用
如何为你的Kubernetes保驾护航?
随着Kubernetes的不断发展,技术不断成熟,越来越多的公司选择把自家的应用部署到Kubernetes中。但是把应用部署到Kubernetes中就完事了吗?显然不是,应用容器化只是万里长征的第一步,如何让应用安心、稳定的运行才是后续的所有工作。
极客运维圈
2021/06/30
3030
合理管理规划TKE,成为一个日理万机的人!
我在2018年中的时候开始接触 kubernetes ,并主导过传统应用向容器化方向的转换工作。
Zeusro
2021/02/07
1.4K0
合理管理规划TKE,成为一个日理万机的人!
Kubernetes 调度均衡器 Descheduler 使用
从 kube-scheduler 的角度来看,它是通过一系列算法计算出最佳节点运行 Pod,当出现新的 Pod 进行调度时,调度程序会根据其当时对 Kubernetes 集群的资源描述做出最佳调度决定,但是 Kubernetes 集群是非常动态的,由于整个集群范围内的变化,比如一个节点为了维护,我们先执行了驱逐操作,这个节点上的所有 Pod 会被驱逐到其他节点去,但是当我们维护完成后,之前的 Pod 并不会自动回到该节点上来,因为 Pod 一旦被绑定了节点是不会触发重新调度的,由于这些变化,Kubernetes 集群在一段时间内就可能会出现不均衡的状态,所以需要均衡器来重新平衡集群。
我是阳明
2022/02/11
1.2K0
Kubernetes 调度均衡器 Descheduler 使用
k8s容器的定向调度与亲和性
Kubernetes(k8s)是一个开源的容器编排工具,而容器调度是其非常重要的特性,所谓的调度是指将容器(Pod)分配到集群中的节点上运行的过程。为了更好地控制容器的调度,k8s提供了多种调度策略,其中包括定向调度和亲和性策略。在实际的k8s集群维护场景中,合理使用这些调度策略,对集群的稳定性至关重要。本文将通过分享实践案例,帮助你更好地理解和使用这些功能。
SRE运维手记
2024/10/14
1330
k8s容器的定向调度与亲和性
Kubernetes 的亲和性污点与容忍
我们在使用k8s过程中经常有这样的需求:我的k8s集群有多台服务器,配置不尽相同。我想把数据库部署到CPU、内存比较好的这几台机;我想把静态承载服务部署到有固态硬盘的机器等;而这些需求,就是我们今天要讲的k8s的调度:
乔达摩@嘿
2023/03/23
7620
Kubernetes 的亲和性污点与容忍
使用腾讯云容器服务(TKE)实现应用跨可用区高可用部署之二
在上一篇文章中(使用腾讯云容器服务(TKE)实现应用跨可用区高可用部署之一),我们介绍了如何使用腾讯云容器服务的亲和性实现业务跨可用区高可用部署。通过控制台的简单操作,即可实现业务高可用部署。 控制台上实现的亲和性和反亲和性是通过节点亲和性(nodeAffinity)来实现的。
杨泽华
2019/03/22
10.8K0
推荐阅读
相关推荐
如何在 Kubernetes 上部署高可用应用程序
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
查看详情【社区公告】 技术创作特训营有奖征文