Kubernetes:生命周期管理和服务发现
深入理解Pod的生命周期
Pod的生命周期
- create pending containerCreating running
-
delete terminating gone
Pod状态机
确保Pod高可用
- 设置合理的resources.memory.limits 防止容器进程被OOM Kill
- 设置合理的emptydir.sizeLimit并且确保数据写入部超过emptyDir的限制,防止Pod被驱逐
Pod的Qos分类
- Guaranteed
- pod的每个容器都设置了资源和CPU的需求
- Limits和requests的值要完全一致
- Burstable
- 至少一个容器制定了CPU或是内存request
- Pod中的资源需求不符合Gauranteed Qos的条件,也就是requests和limits不一致
- BestEffort:
- Pod中的所有容器都没有设置CPU或是内存资源需要
当计算节点检测到内存压力的时候,kubernetes会按照BestEffort -> Burstable -> Guaranteed的顺序依次驱逐Pod
kubectl get pod test-vfgg6 -oyaml | grep qosClass
- 定义guaranteed类型的资源来保证你最重要的pod
- 认真考量Pod需要的真实需要并设置limit和resource,这有利于将集群资源利用率控制在合理的范围并减少Pod被驱逐哦的现象
- 尽量避免将生产Pod设置为BestEffort,但是对测试环境来讲,BestEffort Pod能确保大多数的应该不会因为资源不足而一直处于pendiing状态
健康检查探针
- 健康探针类型分为
- livenessProbe
- 探活:当检测失败的时候,意味着该进程已经无法正常提供服务,kubelet会停止该容器进程并按照restartPolicy决定是否要重启readinessProbe:
- readinessProbe
- 就绪健康状态检测,当检测失败的时候,意味着应用进程正在运行,但因为某些原因不能提供服务,Pod状态会被标记为NotReady.
- startupProbe
- 在初始化阶段(Ready之前)进行健康检查,通常用来避免过于频繁的监测影响应用启动
- livenessProbe
- 探针方法
- ExecAction:容器内运行指定命令,当返回码为0的时候,探测结果为成功
- TCPSocketAction:由kubelet发起,通过TCP协议检查容器IP和端口,当端口可达后,探测结果为成功
- HTTP GetAction:由kubelet发起,对Pod的IP和端口以及路径进行HTTP Get操作。当返回码在200-400之间,探测结果为成功
- 探测属性
Parameters | Description |
---|---|
initialDelaySeconds | Default to 0 second |
periodSeconds | Default to 10 |
timeoutSeconds | Default to 1 |
sucssessThreshold | Default to 1 |
failureThreshold | Default to 3 |
- ReadinessGates
- 在Pod Condition之外引入自定义的继续条件
- 新引入的readinessGates condition需要为True状态后,加上内置的conditions ,Pod才可以为就绪状态
-
该状态应该由某种控制器控制
- Terminating Pod
- TerminationGracePeriodSeconds 默认死30秒
- 如果不关心Pod的终止时长,那么不用采取措施,如果需要加快终止应用程序
- 在preStop script中主动退出进程
- 在主进程中使用特定的初始化进程
- 优雅的初始化进程
- 正确处理信号量,讲信号量转发给子进程
- 在主进程退出之前,需要先等待并确保所有的子进程退出
- 监控并清理儿子进程
部署应用
-
资源规划
- 每个实例需要多少资源
- 每个实例需要多少存储资源
- 网络需求
-
存档的挑战
- emptyDIr需要控制size limit,否则无限扩张的应用会城堡主机
-
应用配置
- Environment variables
- volume mount
-
数据来源
- configMap
- Secret
- Download API
-
数据应该如何保存
| 存储卷类型 | 容器重启后数据是否在 | Pod重建后数据是否存在 | 是否有大小控制 | 注意 |
| -------------- | -------------------- | --------------------- | -------------- | -------------- |
| emptyDIr | 是 | 是 | 是 | |
| hostPath | 是 | 否 | 否 | 需要额外控制权 |
| LocalVolume | 是 | 否 | 是 | 5⃣️没有备份 |
| Network volume | 是 | 是 | 是 | |
| rootFS | 否 | 否 | 否 | 不要写入数据 |
- 容器应用可能面料的进程中断
类型 | 影响 | 建议 |
---|---|---|
Kubelet 升级 | - 不重建容器 | 无影响 |
- 重建容器 | ||
- pod进程会被重启,服务 | ||
主机操作系统升级,节点手工重启 | 节点重启,Pod进程会被终止数分钟 | |
节点下架,送修 | 节点会drain, pod进程会被终止数分钟 | |
节点崩溃 | pod进程会被终止15分钟 | |
-
高可用部署方式
- maxSurge
- maxUnavailable
服务发现
-
服务发布
需要把服务发布到集群内部或是外部,服务的类型不同
- clusterIP(Headless)
- NodePort
- LoadBalancer
- ExternalName
-
服务发布的挑战
- Kube-dns
- DNS TTL
- Service
- cluster ip只能对内
- Kube-proxy支持的iptables/ipvs规模有限
- IPVS的性能和生产化问题
- Kube-proxy的drift问题
- 频繁的Pod的变动导致LB频繁变更
- 对外发布的Service需要企业的LEB集成
- 不支持GRPC
- 不支持DNS和高级路由
- Ingress:
微服务架构下的高可用挑战
服务发现
- 微服务架构是由一系列职责单一的细粒度服务构成的分布式网络结构,服务之间通过轻量级机制进行通信,这时候必然会引入一个服务注册发现问题,也就是说服务提供房需要注册通过服务地址,服务的调用方要能发现目标服务。
- 同时服务提供方一般会以集群的方式提供服务,也就引入了负载均衡和健康检查的问题。
集中式LB服务发现
- 在服务消费者和服务提供者之间有一个独立的LB
- LB上所有的服务的地址的映射表,通常由运维配置注册
- 当服务消费方调用某个目标服务的时候,它向LB发起请求,由LB以某个策略做负载均衡后将请求转发到目标服务
- LB一般具有健康检查能力,能自动摘除不健康的实例
- 服务消费方通过DNS找到LB
进程内LB服务发现
- 进程内LB方案将LB的功能以库的形式即成到服务消费方的进程里
- 服务注册表配合支持服务的自注册和自发现,服务提供方启动时,首先将服务地址注册到服务注册表(同时定期报心跳以表明服务的存活状态)
- 服务消费方要访问某个服务的时候,它通过内置的LB组件向服务注册表查询目标服务地址列表,然后以某种负载策略选择目标服务地址
- 这一个方案对服务注册表的可用性要求很高,一般采用能满足高可用分布式一致组件(zookeeper,consul,etcd)来实现。
- 进程内LB是一种分布式模式,LB和服务发现能力被分割到每一个服务消费者内部,同时服务消费方和服务提供方之间是一直调用,没有额外开销,性能较好
- 一旦客户端跟随服务调用方发布到生产环境中,后续对相关库升级势必要求服务调用方升级库并重新发布。
独立LB进程服务发现
- 针对进程内LB模式不组的情况的一种折中方案
- 将LB和服务发现功能从进程内拆出来,变成主机上的一个独立进程,主机上的一个或是多个服务要访问目标服务的时候,他们都通过同一个主机上的独立的LB进程做服务发现或是负载均衡
- LB独立进程可以进一步同消费服务能力解耦,以独立集群的方式来提供负载均衡的能力
- 缺点是部署复杂,环节多
负载均衡
- 系统的扩展分为纵向扩展和横向扩展
- 横向:优雅终止,优雅启动
- 纵向:从单机角度增加硬件处理能力
- 解决问题
- 并发压力
- 故障转移
- 通过增加或是减少服务,提高服务的伸缩性
- 安全防护:白名单
- DNS负载均衡
链路层负载均衡
- 在通信协议的数据链路层修改MAC地址进行负载均衡
- 数据分发的时候,不修改IP地址,修改目标MAC地址,配置真实物理服务器集群所有机器虚拟IP和负载均衡服务器IP地址一致,达到不需改数据包的源地址和目标地址,进行数据分发的目的。
- 实际处理服务器IP和苏剧请求目的IP一致。不需要经过负载均衡服务器进行地址转换
隧道技术
负载均衡中常用的是隧道技术是IP over IP,其原理是保持原始数据包IP头不变,在IP头外层增加额外的IP包头后转发给上游服务器。
上游服务器接受IP数据包,解开外层IP包头后,剩下的是原始数据包
同样的,原始数据包中的目标iP地址要配置在上游服务器中,上游服务器处理完数据请求后,响应包通过网关直接返回给客户端。
- Kube-dns
Service
Service对象
- Service Selector
- kubernetes允许将POD对象通过标签(Label)进行标记,并通过Service Selector定义基于Pod标签的过滤规则,以方便选择服务器的上游应用实例。
- Ports:
- ports属性中的定义了服务的端口,协议目标端口等信息
apiVersion: v1
kind: Service
metadata:
name: nginx-service
spec:
selector:
app: nginx
ports:
- protocol: TCP
port: 80
targetPort: 80
EndPoint对象
- 当service的selector不为空的时候,kubernetes endpoint controller会监听服务创建事件,创建同service同名的endpoint对象
- selector能够选取的所有pod ip都会被分配到address中
- 如果此时selecor所对应的filter查询不到对应的pod,则address列表为空
- 默认配置下,如果此时对应的pod属于not ready下,则对应的podIP只会出现在subsets的notReadyAddress的属性中,这意味着对应的Pod还没准备好提供服务,不能作为流量转发
- 如果设置了PublishNotReadyAddress为true,则无论pod是否就绪都会加入到readyAddress list中。
EndPointSlice对象
- 当某个Service 对应的backend pod 较多的时候,endpoint对象就会因为保存的地址信息过多,而变的异常强大
- Pod状态的变更会引起endpoint的变更,endpoint的变更会推送到所有节点,从而导致持续占用大量网络带宽
- endpointSlice对象,用于对pod较多的endpoint进行切片,切片大小可以自己定义。
apiVersion:
- 用户创建service 但是不定义selecor
- endpoint controller 不会为该service 自动创建endpoint
- 用户可以手动创建endpoint 对象,并设置任意IP地址到address 属性
- 访问该服务的请求会被转发到该目标地址
- 通过该类型服务,可以为集群外的一组endpoint创建服务。
service 类型
apiVersion: v1
kind: Service
metadata:
name: nginx-basic
spec:
type: ClusterIP
ports:
- port: 80
protocol: TCP
name: http
selector:
app: nginx
- clusterIP
- Service的默认leasing,服务被发布到只有内部集群可见的虚拟IP上
- 在API server启动时候,需要通过service-cluster-ip-range参数配置虚拟IP的地址段,API server中由用于分配IP地址和端口的组件,当该组件捕获service对象并创建事件的时候,会从配置的虚拟IP地址段中取出一个有效的IP地址,分配给该service对象
- nodePort
- 在API Server启动的时候,需要通过node-port-range参数配置nodePort的范围,同样的 API Server组件会捕获service对象并创建事件,即从配置好的nodePort范围取一个有效的端口,分配给该Service
- 每个节点的kube-proxy会尝试在服务分配的nodePort上建立监听接受请求,并转发给服务对应的后端Pod实例。
- LoadBalancer
- 企业数据中心一般会采购一些负载均衡器,作为外网请求数据的统一的入口
- 针对不同的基础架构云平台,kubernetes的cloud manager提供支持不同供应商API的Service Controller;
- Headless Service
- Headless服务是用户将clusterIP显示定义为None的服务,无头服务表示kubernetes不会为该服务提供统一的入口
- ExternalName Service
- 为一个服务创建别名。
- Service Topology
- 一个网络调用的延迟手客户端和服务端所处的位置的影响,两者是否在同一个节点,同一个机架,同一个可用区,同一个数据中心,都会影响参与数据传输设备的数量
- 在分布式系统中,为了保证系统的高可用,往往需要控制应用的错误域,比如通过反亲和配置,将一个应用的多个副本部署在不同的机架,甚至不同的数据中心
- kubernetes提供通过标签来标记所出的物理位置
- service 引入了topologKeys的属性,可以通过如下的设置来控制流量
- 当topologyKeys设置为 kubernetes.io/hostname 时,调用服务的客户端所在的节点上如果有服务实例在运行,则该实例的处理请求,否则,调用失败
- 当topologyKeys 设置为kubernetes.io/hostname,topology.kubernetes.io/zone,topology.kubernetes.io/region 时,若果同一个节点有对应的服务实例,则请求会优先转发该实例,否做就按照书序向下查找
- 当topologyKeys 设置为topology.kubernetes.io/zone,* 时,若果同一个节点有对应的服务实例,则请求会优先转发该实例,否做就按照书序向下查找,如果都不存在,请求则会被转发到任意的实例。
Kube-Proxy
每台机器上都运行一个kube-proxy服务,监听API server中的service和endpoint的变化情况,通过iptables等来为服务配置负载均衡
Kube-proxy可以直接运行在物理机上,也可以以static pod 和dameonSet的方式进行
Kube-proxy当前支持以下几种实现
- Userspace:最早的负载均衡方案,它在用户空间监听一个端口,所有的服务通过iptables转发到这个端口,然后在其内部负载均衡到实际的pod,该方式最主要的是效率低下
- Iptables:目前推荐的方案,完全以iptables规则的方式来实现service负载均衡,该方式最主要的问题是在服务多的时候产生太多的iptables规则,非增量式更新会引入一定的延时,大规模下会有性能问题
- Ipvs:为了解决iptables模式的性能问题,v1.8新增ipvs模式,采用增量式更新,并可以保证service更新期间连接保持不变
- Winuserspace:工作在windows上
域名服务
- Kubernetes Service通过IP虚拟地址或者节点端口为用户提供访问入口
- 这些IP和端口的分配是动态的,如果用户重建一个服务,其分配的clusterIP 和nodePort 以及localBalanceIP 都会发生变化,我们无法把一个可变的入口发布出去
- kubernetes提供了内置的域名服务,用户定义的服务会自动获得域名,无论服务重建多少次,只要服务名不改变,其对应的域名就不会改变。
CoreDNS
- coreDNS包含了一个内存态DNS,以及一个cotroller控制器
- coreDNS实现原理是,控制器监听Service和Endpoint的变化并配置DNS,客户端Pod在进行域名解析的时候,从CoreDNS中查询对应的地址记录。
不同类型服务的DNS记录
- 普通Service:
- clusterIP nodePort LoadBalancer 类型的Service都拥有API Server分配clusterIP,CoreDNS会为这些Service创建FQDN格式为namespace.svc.$clusterdomain:clusterIP的A记录和PTR记录,并为端口创建SRV记录。
- Headless Service
- 故名思义,无头,是用户在Spec显示指定clusterIP为None的service,对于这类的Service API Server不会为其分配ClusterIP. CoreDNS为此类Service创建多条A类记录,并且目标为每个就绪的PodIP
- 每个Pod会拥有一个FQDN格式:svcname.clusterdomain的A记录指向PodIP
- ExternalName Service
- 此类Service用来引用一个已经存在的域名,CoreDNS会为该Service创建一个CName记录来指向目标域名。
Kubernetes 中的域名解析
- Kubernetes Pod有一个同DNS策略相关的属性DNSPolicy, 默认值是ClusterFirst
- Pod启动后/etc/resolv.conf会被改写,所有的地址解析优先发送至CoeDNS
cat /etc/resolv.conf
search ns1.svc.cluster.local srv.cluster.local cluster.local
nameserver 192.168.0.10
options ndots:4
- 当pod启动的时候,同一个namespace的所有的Service都会以环境变量的形式设置到容器内
DNS落地实践
kubernetes作为企业基础架构的一部分,需要定制企业DNS控制器
- 对于kubernetes中的服务,在企业DNS中同样创建A/PTR/SRV record
- 针对headless service,在PodIP可全局路由的前提下,按需创建DNS. records
- Headless service的DNS记录,需要按需要创建
kubernetes负载均衡技术
- 基于L4的服务
- 基于iptables/ipvs的分布式四层负载均衡
- 多种loadbalance Provider提供企业现有的ELB整合
- Kube-proxy基于iptables rules为kubernetes形成全局统一的distributed load balancer
- Kube-proxy是一种mesh,internal client无论通过podip,nodeport还是LB VIP 都是经过kube-proxy跳转到pod
- Kubernetes core
- 基于L7 Ingress
- 基于7层应用层,提供更多功能
- TLS
- L7
- URL/http header rewrite
Service中的Ingress的对比
- 基于L4的服务
- 每个应用独占用ELB,浪费资源
- 为每个服务动态创建DNS记录,频繁更新DNS更新
- 支持TCP和UDP,业务部门需要启动HTTPS服务,自己管理证书
- 基于L7的Ingress
- 多个应用共享ELB,节约资源
- 多个应用共享一个Domain
- TLStermination发生在Ingress层,可集中管理证书
Ingress
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: demo-ingress
spec:
tls:
- hosts:
- abcd
secretName: cncamp-tls
rules:
- host: abcd
http:
paths:
- path: "/"
pathType: Prefix
backend:
service:
name: nginx
port:
number: 80