做者 | 子白(阿里云开发工程师)、溪恒(阿里云技术专家)html
<关注阿里巴巴云原生公众号,回复 排查 便可下载电子书>node
《深刻浅出 Kubernetes》一书共聚集 12 篇技术文章,帮助你一次搞懂 6 个核心原理,吃透基础理论,一次学会 6 个典型问题的华丽操做!nginx
Kubernetes 集群中,业务一般采用 Deployment + LoadBalancer 类型 Service 的方式对外提供服务,其典型部署架构如图 1 所示。这种架构部署和运维都十分简单方便,可是在应用更新或者升级时可能会存在服务中断,引起线上问题。今天咱们来详细分析下这种架构为什么在更新应用时会发生服务中断以及如何避免服务中断。git
图1 业务部署图github
Deployment 滚动更新时会先建立新 pod,等待新 pod running 后再删除旧 pod。后端
图 2 服务中断示意图api
中断缘由:Pod running 后被加入到 Endpoint 后端,容器服务监控到 Endpoint 变动后将 Node 加入到 SLB 后端。此时请求从 SLB 转发到 Pod 中,可是 Pod 业务代码还未初始化完毕,没法处理请求,致使服务中断,如图 2 所示。
解决方法:为 pod 配置就绪检测,等待业务代码初始化完毕后后再将 node 加入到 SLB 后端。bash
在删除旧 pod 过程当中须要对多个对象(如 Endpoint、ipvs/iptables、SLB)进行状态同步,而且这些同步操做是异步执行的,总体同步流程如图 3 所示。网络
图 3 Deployment 更新时序图架构
中断缘由:上述 一、二、三、4步骤同时进行,所以有可能存在 Pod 收到 SIGTERM 信号而且中止工做后,还未从 Endpoints 中移除的状况。此时,请求从 slb 转发到 pod 中,而 Pod 已经中止工做,所以会出现服务中断,如图 4 所示。
图 4 服务中断示意图
解决方法:为 pod 配置 preStop Hook,使 Pod 收到 SIGTERM 时 sleep 一段时间而不是马上中止工做,从而确保从 SLB 转发的流量还能够继续被 Pod 处理。
中断缘由:当 pod 变为 termintaing 状态时,会从全部 service 的 endpoint 中移除该 pod。kube-proxy 会清理对应的 iptables/ipvs 条目。而容器服务 watch 到 endpoint 变化后,会调用 slb openapi 移除后端,此操做会耗费几秒。因为这两个操做是同时进行,所以有可能存在节点上的 iptables/ipvs 条目已经被清理,可是节点还未从 slb 移除的状况。此时,流量从 slb 流入,而节点上已经没有对应的 iptables/ipvs 规则致使服务中断,如图 5 所示。
图 5 服务中断示意图
解决方法:
图 6 Cluster 模式请求转发示意图
图 7 Local 模式原地升级时请求转发示意图
图 8 ENI 模式请求转发示意图
图 9 服务中断示意图
中断缘由:容器服务监控到 Endpoints 变化后,会将 Node 从 slb 后端移除。当节点从 slb 后端移除后,SLB 对于继续发往该节点的长链接会直接断开,致使服务中断。
解决方法:为 SLB 设置长连接优雅中断(依赖具体云厂商)。
避免服务中断能够从 Pod 和 Service 两类资源入手,接下来将针对上述中断缘由介绍相应的配置方法。
apiVersion: v1 kind: Pod metadata: name: nginx namespace: default spec: containers: - name: nginx image: nginx # 存活检测 livenessProbe: failureThreshold: 3 initialDelaySeconds: 30 periodSeconds: 30 successThreshold: 1 tcpSocket: port: 5084 timeoutSeconds: 1 # 就绪检测 readinessProbe: failureThreshold: 3 initialDelaySeconds: 30 periodSeconds: 30 successThreshold: 1 tcpSocket: port: 5084 timeoutSeconds: 1 # 优雅退出 lifecycle: preStop: exec: command: - sleep - 30 terminationGracePeriodSeconds: 60
注意:须要合理设置就绪检测(readinessProbe)的探测频率、延时时间、不健康阈值等数据,部分应用启动时间自己较长,若是设置的时间太短,会致使 POD 反复重启。
apiVersion: v1 kind: Service metadata: name: nginx namespace: default spec: externalTrafficPolicy: Cluster ports: - port: 80 protocol: TCP targetPort: 80 selector: run: nginx type: LoadBalancer
容器服务会将集群中全部节点挂载到 SLB 的后端(使用 BackendLabel 标签配置后端的除外),所以会快速消耗 SLB quota。SLB 限制了每一个 ECS 上可以挂载的 SLB 的个数,默认值为 50,当 quota 消耗完后会致使没法建立新的监听及 SLB。
Cluster 模式下,若是当前节点没有业务 pod 会将请求转发给其余 Node。在跨节点转发时须要作 NAT,所以会丢失源 IP。
apiVersion: v1 kind: Service metadata: name: nginx namespace: default spec: externalTrafficPolicy: Local ports: - port: 80 protocol: TCP targetPort: 80 selector: run: nginx type: LoadBalancer # 须要尽量的让每一个节点在更新的过程当中有至少一个的Running的Pod # 经过修改UpdateStrategy和利用nodeAffinity尽量的保证在原地rolling update # * UpdateStrategy能够设置Max Unavailable为0,保证有新的Pod启动后才中止以前的pod # * 先对固定的几个节点打上label用来调度 # * 使用nodeAffinity+和超过相关node数量的replicas数量保证尽量在原地建新的Pod # 例如: apiVersion: apps/v1 kind: Deployment ...... strategy: rollingUpdate: maxSurge: 50% maxUnavailable: 0% type: RollingUpdate ...... affinity: nodeAffinity: preferredDuringSchedulingIgnoredDuringExecution: - weight: 1 preference: matchExpressions: - key: deploy operator: In values: - nginx
容器服务默认会将 Service 对应的 Pod 所在的节点加入到 SLB 后端,所以 SLB quota 消耗较慢。Local 模式下请求直接转发到 pod 所在 node,不存在跨节点转发,所以能够保留源 IP 地址。Local 模式下能够经过原地升级的方式避免服务中断,yaml 文件如上。
apiVersion: v1 kind: Service metadata: annotations: service.beta.kubernetes.io/backend-type: "eni" name: nginx spec: ports: - name: http port: 30080 protocol: TCP targetPort: 80 selector: app: nginx type: LoadBalancer
Terway 网络模式下,经过设置 service.beta.kubernetes.io/backend-type:
"eni" annotation 能够建立 ENI 模式的 SLB。ENI 模式下,pod会直接挂载到 SLB 后端,不通过 kube-proxy,所以不存在服务中断的问题。请求直接转发到 pod,所以能够保留源 IP 地址。
三种 svc 模式对好比下表所示。
图 10 Service 对比
选用 ENI 模式的 svc + 设定 Pod 优雅终止 + 就绪检测。
为了更多开发者可以享受到 Serverless 带来的红利,这一次,咱们集结了 10+ 位阿里巴巴 Serverless 领域技术专家,打造出最适合开发者入门的 Serverless 公开课,让你即学即用,轻松拥抱云计算的新范式——Serverless。
点击便可免费观看课程:https://developer.aliyun.com/learning/roadmap/serverless
“ 阿里巴巴云原生关注微服务、Serverless、容器、Service Mesh 等技术领域、聚焦云原生流行技术趋势、云原生大规模的落地实践,作最懂云原生开发者的公众号。”