K8S基础知识点整理
Kubernetes知识点整理
# 概述
# 基本概念
按Mater/Node来区分
Master: 是集群的网关和中枢,负责诸如为用户和客户端暴露API、跟踪其他服务器的健康状态、以最优方式调度工作负载,以及编排其他组件之间的通信等任务。通常包含Api-Server、Controller-manager、Scheduler、etcd(可外置)。
Node: 是k8s集群的工作节点,负责接收来自Master的工作指令并根据指令相应地创建或销毁Pod对象,以及调整网络规则以合理地路由和转发流量等。
按模块组件来区分
Pod:
是一组功能相关的Container的封装;共享存储和Network Namespace;是K8S调度和作业运行的基本单位(Scheduler调度,Kubelet运行);Pod容易"走失",需要workload和Service的保障。
Workloads(Pod控制器):
尽管Pod是k8s的最小调度单元,但是用户通常并不会直接部署及管理Pod对象,而是要借助于另一类抽象 ---控制器(Controller)对其进行管理。 有Deployment,Statefulset,DaemonSet,Job ....,实际上是一组功能相关的Pod的封装。使用控制器之后就不再需要手动管理Pod对象了,用户只需要声明应用的期望状态,控制器就会自动对其进行进程管理。
Service:
主要是为了让Pod "防失联",给一组Pod设置反向代理。建立在一组Pod对象之上的资源抽象,它通过标签选择器选定一组Pod对象,并为这组Pod对象定义一个统一的固定访问入口(通常是一个IP地址)。
Ingress:
k8s将Pod对象和外部网络环境进行了隔离,Pod和Service等对象间的通信都使用其内部专用地址进行,如若需要开放某些Pod对象提供给外部用户访问,则需要为其请求流量打开一个通往k8s集群内部的通道。除了Service之外,Ingress也是这类通道的实现方式之一。
Voumes:
存储卷(Volume)是独立于容器文件系统之外的存储空间,常用于扩展容器的存储空间并为它提供持久存储能力。k8s集群上的存储卷大体上可以分为临时卷、本地卷和网络卷。
Name和Namespace:
名称(Name)是k8s集群中资源对象的标识符,它们的作用域通常是名称空间(Namespace),因此名称空间是名称的额外的限定机制。在同一个名称空间中,同一类型资源对象的名称必须具有唯一性。名称空间通常用于实现租户或项目的资源隔离。
Annotation:
Annotation(注解)是另一种附加在对象之上的键值类型的数据,但它拥有更大的数据容量。Annotation常用于各种非标识型元数据(metadata)附加到对象上,但它不能用于标识和选择对象,通常也不会被k8s直接使用,其主要目的是方便工具或用户的阅读及查找等。
# Master组件
# API Server
API Server负责输出RESTful风格的k8s API,它是发往集群的所有REST操作命令的接入点,并负责接收、校验并响应所有的REST请求,结果状态被持久化存储于etcd中。因此,API Server是整个集群的网关。
# etcd集群
k8s集群的所有状态信息都需要持久存储于存储系统etcd中,不过,etcd是由CoreOS基于Raft协议开发的分布式键值存储,可用于服务发现、共享配置以及一致性保障(如数据库主节点选择、分布式锁等)。
etcd不仅能提供键值数据存储,而且还为其提供了监听(watch)机制,用于监听和推送变更。k8s集群系统中,etcd中的键值发生变化时会通知到API Server,并由其通过watch API向客户端输出。基于watch机制,k8s集群的各组件实现了高效协同。
# Controller Manger
控制器完成的功能主要包括生命周期功能和API业务逻辑 .
生命周期管理:包括Namespace创建和生命周期、Event垃圾回收、Pod终止相关的垃圾回收、级联垃圾回收及Node垃圾回收等。
API业务逻辑:例如,由ReplicaSet执行的Pod扩展等。
# 调度器Scheduler
k8s是用于部署和管理大规模容器应用的平台。API Server 确认Pod对象的创建请求之后,便需要由Scheduler根据集群内各节点的可用资源状态,以及要运行的容器的资源需求做出调度决策。另外,k8s还支持用户自定义调度器。
# Node组件
# kubelet
是Node的核心代理程序,是运行于工作节点之上的守护进程,它从API Server接收关于Pod对象的配置信息并确保它们处于期望的状态(desired state,"目标状态")。kubelete会在API Server上注册当前工作节点,定期向Master汇报节点资源的使用情况,并通过cAdvisor监控容器和节点的资源占用情况。
# Container Runtime
每个Node都要提供一个容器运行时(Container Runtime)环境,它负责下载镜像并运行容器。kubelet并未固定链接至某容器运行时环境,而是以插件的方式载入配置的容器环境。这种方式清晰地定义了各组件的边界。目前,k8s支持的容器运行环境至少包括Docker、RKT、cri-o和Fraki等。
# kube-proxy
每个工作节点都需要运行一个kube-proxy守护进程,它能够按需为Service资源对象生成iptables或ipvs规则,从而捕获访问当前Service的ClusterIP的流量并将其转发至正确的后端Pod对象。
# 核心附件
# CoreDNS
k8s从1.11版本开始默认使用CoreDNS项目为集群提供服务注册和服务发现的动态名称解析服务。
# Kubernetes Dashboard
K8s集群的全部功能都要基于Web的UI,俩管理集群中的应用甚至集群自身。
# Ingress Controller
Service 是一种工作与传统层的负载均衡器,而Ingress是在应用层实现的HTTPS(s)负载均衡机制。
不过,Ingress资源自身并不能进行"流量穿透",它仅是一组路由规则的集合,这些规则需要通过Ingress控制器(Ingress Controller)发挥作用。目前,此类的可用项目有Nginx、Trafik、Envory及HAProxy等。
# 基础架构
# 模块架构信息
如下是K8S的架构图,展示了K8S中的各个组件模块的信息。
整体来说,K8S的架构是Master、Node的模式,Master节点上通常部署有scheduler、controller-manager、api-server,以及etcd分布式数据库。Node节点上通常运行着kubelet、kube-proxy组件,以及真正运行docker实例的Pod容器。
用户通过kubectl,经过一些列的集群的安全认证后,将所需要执行的资源清单配置文件提交给api-server,api-server会将此信息写入etcd,写入完成后,etcd向api-server发起一系列的事件信息,通过list-watch的机制,先后由controller-manager执行副本配置,scheduler完成调度node,kubelet完成落实到各自node上启动相应的pod。而kube-proxy则用于相应的Server资源转发到所关联的一系列的Pod对象上。
# 高可用架构1
- etcd自身提供的分布式存储集群为K8S构建一个可靠的存储层。
- 将无状态的apiserver运行为多副本,并在其前端使用负载均衡器调度请求;需要注意的是,负载均衡器本身也需要是高可用。
- 多副本的控制器管理器,通过其自带的leader选举功能(--leader-election)选举出主角色,余下的副本在主角色发送故障时自动启动新一轮的选举操作。
- 多副本的调度器,通过其自带的leader选举功能(--leader-election)选举出主角色,余下的副本在主角色发生故障时自动启动新一轮的选举操作。
# 高可用架构2
如下是结合了kubease的工具的架构图示。
- Master节点只需要2台即可。
- 从原有的服务器端负载均衡转为了node客户端负载均衡。
- 无须申请VIP资源。
- 免去维护外部HA LB的问题。
kubeasz 项目地址:https://github.com/easzlab/kubeasz
# 工作原理
如下的图示是完整的工作原理的解析,其中核心的重点概念就是list-watch的机制。
上面所有的"0"示意图,都是表示的是从k8s环境部署完毕后,各个api-server的客户端(api-server、scheduler、kubelet)都对于各个所订阅的资源对象,进行watch。
list-watch的异步消息通信机制,有效的保证了消息的可靠性、及时性、顺序性、高性能等。
具体的list-watch机制,可以参考下面章节的内容整理。
# API对象
# 声明式和命令式
kubectl的命令由此可以分为三类:陈述式命令、陈述式对象配置和声明式对象配置。第一种方式即此前用到的run、expose、delete和get等命令,它们直接作用于k8s系统上的活动对象,简单易用。
声明式的使用通常要依赖于资源配置文件,声明式对象配置并不直接指明要进行的对象管理操作,而是提供配置清单文件给k8s系统,并委托系统跟踪活动对象的状态变动。
# API对象的基本构成
整体来说,在K8S中一个完整的API对象由四大部分组成。
typeMeta: 主要由apiVersion和kind两个构成。主要描述的是该类API资源对象的类别和版本。
objectMeta: metadata类别,涵盖该类型资源对象下的基本元数据的信息,包含一些名称、label、注解等。
spec: 这是关键的信息内容,涵盖定义的所有的期望状态信息。
status: 这个不是客户端所需要配置的,反映的是当前该类资源对象的实际的状态值。
# Pod
绝大多数场景中都应该于**一个容器中仅运行一个进程。
不过,分别运行于各自容器的进程之间无法实现基于IPC的通信机制,此时,容器间的隔离机制对于依赖于此类通信方式的进程来说却又成了阻碍。Pod资源抽象正是用来解决此类问题的组件。前面提到,Pod对象是一组容器的集合,这些容器共享Network、UTS及IPC名称空间,因此具有相同的域名、主机名和网络接口,并可通过IPC直接通信。
为一个Pod对象中的各容器提供网络名称空间等共享机制的是底层基础容器pause。如下图所示,一个由三个容器组成的Pod资源,各容器共享Network、IPC和UTS名称空间,但分别拥有各自的MNT、USR和PID名称空间。需要特别强调的是,一个Pod对象中的多个容器必须运行于同一工作节点之上。
# 多Container Pod模型
k8s系统的Pod资源对象用于运行单个容器化应用,此应用称为Pod对象的主容器,同时Pod也能容纳多个容器,不过额外的容器一般工作为sidecar模式,用于辅助主容器完成工作职能。
# sidercar pattern(边车模式)
即为Pod的主应用容器提供协同的辅助应用容器,每个应用独立运行,最为典型的代表是将主应用容器中的日志使用agent收集至日志服务器中时,可以将agent运行为辅助应用容器,即sidecar。另一个典型的应用是为主应用容器中的database server启用本地缓存。如下图:
# Ambassador pattern(大使模式)
即为远程服务创建一个本地代理,代理应用运行于容器中,主容器中的应用通过代理容器访问远程服务。如下图所示,一个典型的使用示例是主应用容器中的进程访问"一主多从"模型的远程Redis应用时,可在当前Pod容器中为Redis服务创建一个Ambassador container,主应用容器中的进程直接通过localhost接口访问Ambassador container即可。即便是Redis主从集群架构发生变动时,也仅需要将Ambassador container加以修改即可,主应用容器无须对此作出任何反应。
# Adapter pattern(适配器模型)
此种模型一般用于将主应用容器中的内容进行标准化输出,例如,日志数据或指标数据的输出,这有助于调用者统一接收数据的接口,如下图所示。另外,某应用滚动升级后的版本不兼容旧的版本时,其报告信息的格式也存在不兼容的可能性,使用Adapter container有助于避免那些调用此报告数据的应用发生错误。
# 容器探测
K8s支持三种处理器用于Pod探测,任何一种探测方式都可能存在三种结果:"Success"(成功)、"Failure"(失败)或"Unknown"(未知),只有第一种结果表示成功通过检测。
- ExecAction:在容器中执行一个命令,并根据其返回的状态码进行诊断的操作称为Exec探测,状态码为0表示成功,否则即为不健康状态。
- TCPSocketAction:通过与容器的某TCP端口尝试建立连接进行诊断,端口能够成功打开即为正常,否则为不健康状态。
- HTTPGetAction:通过向容器IP地址的某指定端口的指定path发起HTTP GET请求进行诊断,响应码为2xx或3xx时即为成功,否则为失败。
Kubelet可在活动容器上执行两种类型的检测:存活性检测(livenessProbe)和就绪性检测(readinessProbe)。
- 存活性检测:用于判断容器是否处于"运行"(Running)状态;一旦此类检测未通过,kubelet将杀死容器并根据其restartPolicy决定是否将其重启;未定义存活性检测的容器的默认状态为"Success"。
- 就绪性检测:用于判断容器是否准备就绪并可对外提供服务;未通过检测的容器意味着其尚未准备就绪,端点控制器(如Service对象)会将其IP从所有匹配到此Pod对象的Service对象的端点列表中移除;检测通过之后,会再次将其IP添加至端点列表中。
# 存活性探测
有不少应用程序长时间持续运行后会逐渐转为不可用状态,探测到容器的状态为不可用时,仅能通过重启操作恢复。
容器的存活性探测主要配置有如下:
这些属性可以通过"spec.containers.livenessProbe"的如下属性字段来给出。
- initialDelaySeconds <integer>:存活性探测延迟时长,及容器启动多久之后开始第一次探测操作,显示为delay属性;默认为0秒,及容器启动后立刻便开始进行探测。
- timeoutSeconds <integer>:存活性探测的超时时长,显示为timeout属性,默认为1s,最小值也是1s。
- periodSeconds <integer>:存活性探测的额度,显示为period属性,默认为10s,最小值为1s;过高的频率会对Pod对象带来较大的额外开销,而过低的频率又会使得对错误的反应不及时。
- successThreshold <integer>:处于失败状态时,探测操作至少连续多少次的成功才被认为是通过检测,显示为#success属性,默认值为1,最小值也为1。
- failureThreshold <integer>:处于成功状态时,探测操作至少连续多少次的失败才被视为是检测不通过,显示为#failure属性,默认值为3,最小值为1。
# 就绪性探测
Pod对象启动后,容器应用通常需要一段时间才能完成其初始化过程,例如加载配置或数据,甚至有些程序需要运行某类的预热过程,应该避免于Pod对象启动后立即让其处理客户端请求,而是等待容器初始化工作执行完成并转为"就绪"状态。
未定义就绪性探测的Pod对象在Pod进入"Running"状态后将立即就绪。
与存活性探测触发的操作不同的是,探测失败时,就绪性探测不会杀死或重启容器以保证其健康性,而是通知其尚未就绪,并触发依赖于其就绪状态的操作(例如,从Service对象中移除此Pod对象)以确保不会有客户端请求接入此Pod对象。
# limit和request
目前来说,资源隔离尚且属于容器级别,CPU和内存资源的配置需要在Pod中的容器上进行,每种资源均可由**"request"属性定义其请求的确保可用值**,即容器运行可能用不到的这些额度的资源,但用到时必须要确保有如此多的资源可用,而**"limits"属性则用于限制资源可用的最大值,即硬限制**。
只有当节点上可分配资源量>=容器资源请求数时才允许将容器调度到该节点上。
但Request参数不限制容器的最大可使用资源。Limits,是容器能使用的资源的最大值,设置为0表示使用资源无上限。
K8S系统中,CPU的分配支持分数计量方式,一个核心(1core)相当于1000个微核心(millicores),因此500m相当于是0.5个核心,即二分之一个核心。内存的计量方式于日常使用方式相同,默认单位是字节,也可以使用E、P、T、G、M和K作为单位后缀,或Ei、Pi、Ti、Mi和Ki形式的单位后缀。
# Deployment控制器
Deployment(简称为deploy)是k8s控制器的又一种实现,它构建于ReplicaSet控制器之上,可为Pod和ReplicaSet资源提供声明式更新。
主要是配置期望副本数、定义Pod模板、定义标签选择器、回滚、暂停和启动、滚动升级。
# 滚动更新
滚动升级是默认的更新策略,它在删除一部分旧版本Pod资源的同时,补充创建一部分新版本的Pod对象进行应用升级,其优势是升级期间,容器中应用提供的服务不会中断,但要求应用程序能够应对新旧版本同时工作的情形,例如新旧版本兼容同一个数据库方法等。
滚动升级的过程中,会创建另外一个ReplicaSet控制器来完成新版本的Pod升级,现有的Pod对象会处于两个不同的Replicaset之下,旧控制器的Pod对象数量不断减少的同时,新控制器的Pod对象数量不断增加,直到旧控制器不再拥有Pod对象,而新控制器的副本数量变得完全符合期望值为止。
# maxSurge和maxUnavailable
滚动更新时,应用升级期间还要确保可用的Pod对象数量不低于某阀值以确保可用持续处理客户端的服务请求,变动的方式和Pod对象的数量范围将通过spec.strategy.rollingUpdate.maxSurge和spec.strategy.rollingUpdate.maxUnavailable两个属性协同进行定义。
- maxSurge:指定升级期间存在的总Pod对象数量最多可超出期望值的个数,其值可用是0或正整数,也可以是一个期望值的百分比;例如,如果期望值为3,当前的属性值为1,则表示Pod对象的总数不能超过4个。
- maxUnavailable:升级期间正常可用的Pod副本数(包括新旧版本)最多不能低于期望数值的个数,其值可以是0或正整数,也可以是一个期望值的百分比;默认值为1,该值意味着如果期望值是3,则升级期间至少要有两个Pod对象处于正常提供服务的状态。
maxSurge和maxUnavailable属性的值不可同时为0,否则Pod对象的副本数量在符合用户期望的数量后无法做出合理变动以进行滚动更新操作。
# 扩容和缩容
通过修改spec.replicas既可以修改Deployment控制器中的Pod控制器的副本数量,它将实时作用于控制器并直接生效。
另外,"kubectl scale"是专用于扩展某些控制器类型的应用规模的命令。
# DaemonSet控制器
用于在集群中的全部节点上同时运行一份指定的Pod资源副本,后续新加入集群的工作节点也会自动创建一个相关的Pod对象,当从集群移除节点时,此类Pod对象也将被自动回收而无须重建。
也可以使用节点选择器及节点标签指定仅在部分具有特定特征的节点上运行指定的Pod对象。
DaemonSet是一种特殊的控制器,它有特定的应用场景,通常运行那些执行系统级操作任务的应用,其应用场景具体如下。
- 运行集群存储的守护进程,如在各个节点上运行的glusterd或ceph。
- 在各个节点上运行日志收集守护进程,如fluentd和logstash。
- 在各个节点上运行监控系统的代理守护进程,如Prometheus Node Exporter、collectd、Datadong agent、New Relic agent或Ganglia gmond等。
当然,既然是需要运行于集群内的每个节点或部分节点,于是很多场景中也可以把应用直接运行为工作节点上的系统守护进程,不过,这样一来就失去了运行k8s管理所带来的便捷性。
另外,也只有必须将Pod对象运行于固定的几个节点并且需要先于其他Pod启动时,才有必要使用DeamonSet控制器,否则就应该使用Deployment控制器。
# StatefulSet控制器
ReplicaSet控制器所创建的Pod从本质上都是一个模板出来的,这些Pod资源除了主机名和IP都没有本质上的区别。所管理的这些Pod资源启动也不需要考虑前后顺序,任何一个Pod资源都可以被ReplicaSet控制器重构出的新版本所替代,管理员更多关注的也是它们的群体特征,而无须过于关注任何一个个体。例如Tomcat、jetty、Nginx等。
而StatefulSet这类控制器管理的Pod对象可能有如下场景:应用程序在处理客户端请求时,对当前请求的处理需要以前一次或多次的请求为基础进行,新客户端发起的请求则会被其施加专用标识,以确保其后续的请求可以被识别。例如,RDBMS系统上处于同一个事务中的多个请求不但彼此之间存在关联性,而且还要以严格的顺序执行。这类应用一般需要记录请求连接的相关信息,即"状态",有的甚至还需要持久保存由请求生成的数据,尤其是存储服务类的应用,运行于k8s系统上时需要用到的持久存储卷。
ReplicaSet控制器管理的Pod无法为每个Pod单独指定不同的Volume挂载,而StatefulSet(有状态副本集)则是专门用来满足此类应用的控制器类型,由其管控的每个Pod对象都有着固定的主机名和专有存储卷,即便被重构后也能保持不变。支持每个Pod对象一个专有索引、有序部署、有序终止、固定的标识符及固定的存储卷等特性。
# Scheduler
K8S中默认的调度器的核心目标就是基于资源可用性将各Pod资源公平地分布于集群节点之上。目前默认的调度器通过三个步骤来完成调度操作:节点预选(Predicate)、节点优先级排序(Priority)以及节点择优(Select)。
下面有些调度内容,摘录自网上https://blog.csdn.net/qq_34857250/article/details/90259693
https://www.cnblogs.com/L-dongf/p/12327401.html
调度策略,分为预选策略和优选策略。预选策略,predicate是强制性规则,会遍历所有的node节点,依据具体的预选策略筛选出符合要求的node列表,如果没有node符合predicates策略规则,那么Pod就会被挂起,直到有node能够满足。优选策略,这一步会在第一步筛选的基础上,按照优选策略为待选node打分排序,获取最优者。
# 预选策略
必须完全满足
- CheckNodeConditon: 检查node是否正常。
- GeneralPredicates: 普通判断策略
- HostName: 检测Pod对象是否定义了pod.spec.hostname,并且检查节点中是否有同名的pod而冲突。
- PodFitHostPorts: 检查pod.spec.containers.ports.hostPort属性(绑定节点上的某个端口)是否定义,并且检查节点中的节点端口是否冲突。
- MatchNodeSelector: pods.spec.nodeSelector,检查节点选择器。
- PodFitsResources: 检查Pod的资源需求request是否能被节点所满足。
- NoDiskConflict: 检测pod依赖的存储卷是否能满足需求,默认不检查。
- PodToleratesNodeTaints: pods.spec.tolerations可容忍的污点,检查Pod是否能容忍节点上的污点。
- PodToleratesNodeExecuteTanits: pod.tolerations属性中是否能接纳容忍NoExecute级别的污点,默认没有启用。
- CheckNodeLablePresence: 检测node上的标签的存在与否,默认没有启用。
- CheckServiceAffinity: 根据Pod所属的service,将相同所属的service尽可能放在同一个节点,默认不检查。
- CheckVolumeBinding: 检查节点上已绑定和未绑定的PVC是否能够满足Pod对象的存储卷需求。
- NoVolumeZoneConflict: 如果给定了区域限制,检查在此节点上部署Pod对象是否存在存储卷冲突。
- CheckNodeMemoryPressure: 检测节点内存是否存在压力,如果节点内存压力过大,则检查当前Pod是否可以调度此节点上。
- CheckNodePIDPressure: 检查节点PID数量是否存在压力。
- CheckNodeDiskPressure: 检查节点磁盘资源的压力情况。
- MatchInterPodAffinity: 检查给定节点是否能够满足Pod对象的亲和性或反亲和性条件,以用于实现Pod亲和性调度或反亲和性调度。
# 优选策略
优选过程中,调度器向每个通过预选的节点传递一系列的优选函数,来计算每个节点上各个优选函数后得到的值,调度器会给每个优选函数设定一个权重,大多数优先级默认为1。将所有优选函数得分乘以权重,然后相加从而得出节点的最终优先级分值。finaSoreNode=(weight1*priorityFunc1)+(weight2*priorityFunc2)+ ...
下面是各个优选函数的相关说明:
- LeastRequested: 节点的资源空闲率高的优选,是节点空闲资源与节点总容量的比值计算而来的。即由CPU或内存资源的总容量减去节点上已有Pod对象需求的容量总和,再减去当前要创建的Pod对象的需求容量的结果除以总容量。计算公式是:(cpu((capacity-sum(requested))*10 / capacity)+memory((capacity-sum(requested))*10 / capacity)) / 2
- BalancedResourceAllocation: 计算节点上面的CPU和内存资源被占用的比率相近程度,越接近,比分越高,平衡节点的资源使用情况。计算公式:cpu=cpu((capacity-sum(requested))*10 / capacity) mem=memory((capacity-sum(requested))*10 / capacity)
- NodePreferAvoidPodsPriority: 如果node上不存在"scheduler.alpha.kubernetes.io/preferAvoidPods"这个注解,那么不管什么pod都没有影响;如果node上存在相关的注解,那么注解中关联的Pod对象名称正好是要去调度的Pod,那么此类node分值会很低,如果关联的Pod对象名称和要调度的Pod名称没有任何关系,那么和没有注解是一样的效果。需要注意的是,在这个优先级中,优先级最高,得分会非常高。
- NodeAffinityPriority: 节点的亲和性,亲和性高,得分高。基于节点亲和性调度偏好进行的优选级评估,根据Pod资源中的nodeSelector对给定节点进行匹配度检查,成功匹配到的条目越多则节点得分越高。
- TaintTolerationPriority: 将Pod对象的spec.tolertions与节点的taints列表项进行匹配度检测,匹配的条目越多,得分越低。
- SelectorSpreading: 尽可能的把Pod分散开,也就是没有启动这个Pod的node,得分会越高。
- InterPodAffinityPriority: 遍历Pod对象的亲和性条目,匹配项越多,得分就越多。
- MostRequestedPriority: 节点中空限量越少的,得分越高,与LeastRequested不能同时使用,集中各一个机器上面跑Pod,默认没有启用。
- NodeLabelPriority: 根据node上面是否拥有特定的标签来评估得分,有标签就有分,而无论其值为何。默认没有启用。
- ImageLocalityPriority: 一个node的得分高低,是根据node上面是否有镜像,有镜像就有得分,反之就没有(根据node上已有满足需求的image的size大小之和来计算),默认没有启用。
# 高级调度
# nodeSelector
将Pod调度到特定的Node上
首选要在node上定义相应的labels,然后在pod.spec.nodeSelector中定义需要调度到的相应标签的node。类似于RDBMS中通过select node.name from xxx where node.disktype=ssd and node-flavor=s3.large.2
,也就是说pod中nodeSelector要完全匹配node上的标签。
这个匹配调度的逻辑,是之前描述的MatchNodeSelector预选调度算法,预选调度算法是必须要满足项的node。
# nodeAffinity
节点亲和性,是调度程序用来确定pod对象调度到哪个node上的一组规则,和nodeselecotr一样,也是基于节点上的自定义标签和Pod对象上指定的标签选择器来进行定义的,这是nodeSelector的升级版本。
nodeAffinity属于优选函数算法中一种。
总体来说,节点亲和性调度可以分为硬亲和(required)和软亲和(preferred)。顾名思义,硬亲和性实现的是强制性规则,它是Pod调度时必须要满足的规则,如果不满足,则Pod对象会被置于Pending状态;而软亲和则是一种柔性调度限制,它倾向于将Pod对象运行于某类特定的节点之上,而调度器也将尽量满足此需求,但是在无法满足调度需求的时候,它将退而求其次地选择一个不匹配规则的节点。
# 硬亲和
硬亲和的策略在pod中位置于pod.spec.affinity.nodeAffinity.requireDuringSchedulingIgnoreDuringExecution。"IgnoreDuringExecution"表明了,该调度策略只会对当前的pod和node标签,以及相应规则,做个调度匹配。以后要是节点标签发生了变化了,那么已经调度到了该node上的Pod对象不会做出改变,只会对新建的Pod对象生效。老人老办法,新人新规定。
在上述map的中可以包含多个value(nodeSelectorTerms字段),多个nodeSelecorTerm之间是"逻辑或"的关系,意思是有节点满足其中的一个nodeSelecorTerm就算这个node满足了。
nodeSelectorTerms下面需要可以定义多个matchExpression,多个规则彼此之间为"逻辑与"的关系,也就是说只有该nodeSelecorTerm下面的所有matchExpression定义的规则都满足,这个node才算是满足了。
标签选择器表达式(matchExpression下定义的规则),支持使用的操作符号有In、NotIn、Exsits、DoesNotExists、Lt和Gt等。In表示的是只要满足后面的集合中的一个就算是满足了条件。
# 软亲和
软亲和的策略在pod中位置于pod.spec.affinity.nodeAffinity.preferredDuringSchedulingIgnoreDuringExecution。同理是"IgnoreDuringExecution"。
在上述的map中可以包含多个规则,每个规则都可以配置weight属性以便用于定义其优先级。对于相关的节点分别计算出多个规则的权重值,最后分值高的节点胜出。
# podAffinity
pod亲和性,顾名思义,调度和判断的主体是之前已经存在的pod,而非上面所说的node。是根据已调度或将要调度的pod的所位于的node的情况,来决定后续的pod将要部署在哪些node上,反映的是后一种pod对已存在pod的一种亲和性的关系的,调度管理。
podAffinity也分为亲和性和反亲和性,每种亲和策略下又分为硬(required)亲和、软(preferred)亲和。
K8S调度器通过内建的MatchInterPodAffinity预选策略为这种调度方式完成节点预选,并基于InterPodAffinityPriority优选函数进行各节点的优选级评估。
# 为什么要有pod亲和性
出于高效通信的需要,偶尔需要把一些pod对象组织在相近的位置(同一节点、机架、区域或地区等),如某业务的前端Pod和后端Pod(表现为pod亲和性)。或者说要出于安全性或分布式的原因,需要将一些Pod对象在其运行的位置上隔离开来(表现为pod反亲和性)。
# 什么是位置拓扑topologykey
Pod亲和性调度需要各相关的Pod对象运行于"同一位置",而topologykey就恰恰定义了这个一个什么样的类别,比如区域的类别、机架的类别、主机的类别等等。
在定义Pod对象的亲和性与反亲和性时,需要借助于标签选择器来选择被依赖的Pod对象,并根据选出的Pod对象所在节点的标签来判断"同一位置"的 具体意义。
# pod亲和性和node亲和性有什么区别
- 在pod.sepc.affinity存在podAffinity和podAntiAffinity,这两种配置都是对称的。
- pod亲和性调度中labelSelector的匹配对象是Pod,而node亲和性调度中匹配的是node。
- pod亲和性调度中匹配到的是根据topologykey定义的一组node,topologykey定义了分组是什么样的一个级别,相同topologykey中的key和value的值为一组。
- 在pod亲和性中硬亲和过滤规则中,条件间只有逻辑与运算。
# pod硬亲和调度
pod的硬亲和调度的API定义于pod.spec.affinity.podAffinity.requiredDuringSchedulingIgnoredDuringExecution下,通过labelSelector 这个map定义多个匹配表达式。条件间只有逻辑与的运算,这一点和node亲和性有所不同。
topologyKey: kubernetes.io/hostname是K8S中节点的内建标签,它的值就是当前节点的节点主机名称。同理还有region(地区)、zone(区域)、rack(机架)的拓扑位置的定义。
# pod软亲和调度
pod的软亲和调度的API定义于pod.spec.affinity.podAffinity.preferredDuringSchedulingIgnoredDuringExecution下,和node软亲和性调度类型,可以定义多个调度规则,每个规则都可以定义一个权重。
# podAntiAffinity反亲和性
与podAffinity匹配过程相同,只是最终的结果取反。
# 手动调度和DaemonSet
当遇到调度器不工作时,需要考虑到手动调度Pod。
我们只需要在pod.spec.nodeName中直接填上需要调度到的node的名称就可以了。
# DaemonSet调度
老的版本中DaemonSet中的pod调度都是由controller-manager直接指定pod的运行节点,不经过调度器。直到1.11版本开始,DaemonSet的pod才由scheduler引入调度。
DaemonSet实际上是要求每个节点都部署一个相同的pod,通常用于部署集群中的agent,例如网络插件等。在下图的Daemonset的配置中,可以看出类似于定义了一个Deployment的清单配置文件中,对于要求一个主机上不存在相同的pod label,也就是对于Pod的反亲和性。
# 污点和容忍度
污点(taints)是定义在节点之上的键值型属性数据,用于让节点拒绝将pod调度运行于其上,除非该pod对象具有容纳节点污点的容忍度。
而容忍度(tolerations)是定义在Pod对象上的键值型属性数据,用于配置其可容忍的节点污点,而且调度器仅能将Pod对象调度至其能够容忍该节点污点的节点之上。
K8S中使用PodToleratesNodeTaints预选策略和TaintTolerationPriority优选函数来完成此类高级调度机制。
# 污点容忍度/节点选择器/节点亲和性区别
上面描述的节点选择器(nodeSelector)和节点亲和性两种调度方式都是通过在Pod对象上添加标签选择器来完成对特定类型节点标签的匹配,实现的是由Pod选择节点的机制。
而污点和容忍度则是通过向节点添加污点信息来控制Pod对象的调度结果,从而赋予了节点控制何种Pod对象能够调度与其上的主控权。
节点亲和性是使得Pod对象被吸引到一类特定的节点,而污点则相反,它提供了让节点排斥特定Pod对象的能力。
# 污点
污点(taint)的定义在 node.spec.taints下,是键值型数据,但又额外支持一个效果(effect)标识,语法格式为"key=value:effect",其中key和value的用法及格式与资源注解信息相似,而effect则用于定义对pod对象的排斥等级,它主要包含以下三种类型。
- NoSchedule: 不能容忍此污点的新Pod对象,不可调度至当前节点,属于强制型约束关系,节点上现存的Pod对象不受影响。
- PreferNoSchedule: NoSchedule的柔性约束版本,即不能容忍此污点的新Pod对象尽量不要调度至当前节点,不过无其他节点可供调度时也允许接受相应的Pod对象。节点上现存的Pod对象不受影响。
- NoExecute: 不能容忍此污点的新Pod对象,不可调度至当前节点,属于强制性约束关系,而且节点上现存的Pod对象会因节点污点变动或Pod容忍度变动而不再满足匹配规则时,Pod对象将被驱逐。
给节点添加污点标识:
$ kubectl taint nodes xxx <key>=<value>:<effect>
例如:
$ kubectl taint nodes node1 node-type=production:NoSchedule
2
3
即便是同一个键值数据,若其效用标识不同,则也分属于不同的污点信息,也就是说会增加一条污点信息,和之前的区别只是在于效用标识不同。
$ kubectl taint nodes node1 node-type=production:PreferNoSchedule
删除节点上某特定键名,特定标识的污点信息。
$ kubectl taint nodes node1 node-type=NoSchedule-
删除节点上某特定键名的所有污点信息。(也就是省略效用标识)
$ kubectl taint nodes node1 node-type-
删除节点上所有的全部的污点信息,可以使用kubectl patch命令将节点属性spec.taints的值直接置为空即可。
$ kubectl patch nodes node1 -p '{"spec":{"taints":{}}}'
# 容忍度
Pod对象的容忍度可通过其pod.spec.tolerations字段进行添加,根据使用的操作符不同,主要有两种不同的形式:一种是与污点信息完全匹配的等值关系"Equal";另一种是判断污点信息存在性的匹配方式"Exists"。其中tolerationSeconds用于定义延迟驱逐当前Pod对象的时长。
需要注意的如下信息:
- 如果节点node上有多个污点信息,那么就必须该Pod对此节点上的所有污点信息都能容忍,才能调度上去。
- 匹配逻辑和之前的pod中的nodeselector正好相反,之前的逻辑是只要是node上的一个标签满足于pod中定义的node selector就进行匹配。
- pod中定义的operator为"Equal"时,就是需要在pod的toerations中完整填写所有的key、value、effect。
- pod中定义的operator为"Exists"时(key/effect/operator项是必填的,而value项留空),可以填写value为空,表示的是匹配容忍节点node中所有关于这个key中的相应的"effect"的,所有value的污点的信息。
- pod中定义的operator为"Exists"时(key/operator项是必填,value和effect为空),表示的是匹配容忍节点node中所有关于这个key值与之相同的,所有value的所有effect的污点信息。
# 污点和容忍度的调度逻辑
一个节点可以配置使用多个污点,一个Pod对象也可以有多个容忍度,不过二者在进行逻辑检查时会遵循如下逻辑。
首先处理每个有着与之匹配的容忍度的污点。
不能匹配到的污点上,如果存在一个污点使用NoSchedule效用标识,则拒绝调度Pod对象至此节点上。
不能匹配到的污点上,若没有任何一个使用NoSchedule效用标识,但至少有一个使用了PreferNoScheduler,则应尽量避免将Pod对象调度至此节点。
如果至少有一个不匹配的污点使用了NoExecute效用标识,则节点将立即驱逐Pod对象,或者不予调度至给给定节点;另外,即便容忍度可以匹配到使用了NoExecute效用标识的污点,若在定义容忍度时还同时使用了tolerationSeconds属性定义了容忍时限,则超出时限后其也将被节点驱逐。
# Service和Ingress
Service端口用于接收客户端请求并将其转发至其后端的Pod中应用的相应端口之上,因此,这种代理机制也称为**"端口代理"或四层代理**,它工作于TCP/IP协议栈的传输层。Service及Pod对象的IP地址都仅在k8s集群内可达,它们无法接入集群外部的访问流量。
解决此类问题的办法中,除了在单一节点上做端口暴露(hostPort)及让Pod资源共享使用工作节点的网络名称空间(hostNetwork)之外,更推荐用户使用的是NodePort或LoadBalancer类型的Service资源,或者是有着七层负载均衡能力的Ingress资源。
# Service
一个Service对象就是工作节点上的一些iptables或ipvs规则,用于将到达Service对象IP地址的流量调度转发至相应的Endpoints对象指向的IP地址和端口之上。工作于每个工作节点的kube-proxy组件通过API Server持续监控着各Service及与其关联的Pod对象,并将其创建或变动实时反映至当前工作节点上相应的iptables或ipvs规则上。
客户端、Service及其Pod对象的关系如下图所示:
Service IP事实上是用于生成iptables或ipvs规则时使用的IP地址,它仅用于实现k8s集群网络的内部通信,并且仅能够将规则中定义的转发服务的请求作为目标地址予以响应,这也是它被称为虚拟IP的原因之一。kube-proxy将请求代理至相应端点的方式有三种:userspace(用户空间)、iptables和ipvs。
# kube-proxy代理模式
- Iptables模式
iptables代理模型中,kube-proxy负责跟踪API Server上Service和Endpoints对象的变动(创建或移除),并据此做出Service资源定义的变动。
对于每个Endpoints对象,Service资源会为其创建iptables规则并关联至挑选的后端Pod资源,默认算法是随机调度(random)。
在创建Service资源时,集群中每个节点上的kube-proxy都会收到通知并将其定义为当前节点上的iptables规则,用于转发工作接口接收到的与此Service资源的ClusterIP和端口的相关流量。客户端发来的请求被相关的iptables规则进行调度和目标地址转换(DNAT)后再转发至集群内的Pod对象之上。
相对于用户空间模型来说,iptables模型无须将流量在用户空间和内核空间来回切换,因而更加高效和可靠。不过,其缺点是iptables代理模型不会在被挑中的后端Pod资源无响应时自动进行重定向,而userspace模型则可以。
- ipvs模式
kube-proxy跟踪API Server上Service和Endpoints对象的变动,据此来调用netlink接口创建ipvs规则,并确保与API Server中的变动保持同步。它与iptables规则的不同之处仅在于其请求流量的调度功能由ipvs实现,余下的其他功能仍由iptables完成。
类似于iptables模型,ipvs构建于netfilter的钩子函数之上,但它使用hash表作为底层数据结构并工作于内核空间,因此具有流量转发速度快、规则同步性能好的特性。另外,ipvs支持众多调度算法,例如rr、lc、dh、sh、sed和nq等。
# Service类型
K8s的Service共有四种类型:ClusterIP、NodePort、LoadBalancer和ExternalName。
# Ingress
Ingress是k8s API的标准资源类型之一,它其实就是一组基于DNS名称(host)或URL路径把请求转发至指定的Service资源的规则,用于将集群外部的请求流量转发至集群内部完成服务发布。
然而,Ingress资源自身并不能进行"流量穿透",它仅是一组路由规则的集合,这些规则要想真正发挥作用还需要其他功能的辅助,如监听某套接字,然后根据这些规则的匹配机制路由请求流量。这种能够为Ingress资源监听套接字并转发流量的组件称为Ingress控制器(Ingress Controller)。
不同于Pod控制器Deployment,Ingress控制器并不直接运行为kube-controller-manager的一部分,它是k8s集群的一个重要附件,类似于CoreDNS,需要在集群上单独部署。
Ingress控制器可以由任何具有反向代理(HTTP/HTTPS)功能的服务程序实现,如Nginx、Envoy、HAProxy、Vulcand和Traefik等。Ingress控制器自身也是运行于集群中的Pod资源对象,它与被代理的运行为Pod资源的应用运行于同一网络中。
如下图中,ingress-nginx与pod1、pod3等的关系所示:
# Volumes
k8s支持非常丰富的存储卷类型,包括本地存储(节点)和网络存储系统中的诸多存储机制,甚至还支持Secret和ConfigMap这样的特殊存储资源。目前,k8s支持的存储卷包含以下这些类型。
上述类型中,emptyDir和hostPath属于节点级别的卷类型,emptyDir的生命周期与Pod资源相同,而使用了hostPath卷的Pod一旦被重新调度至其他节点,那么它将无法再使用此前的数据。因此,这两种类型都不具有持久性。要想使用持久类型的存储卷,就得使用网络存储系统,如NFS、Ceph、GlusterFS等,或者云端存储,如gcePersistentDisk、awsElasticBlockStore等。
# PV/PVC
PersistentVolume(PV**)是指由集群管理员配置提供的某存储系统上的一段存储空间,它是对底层共享存储的抽象**.
PV是集群级别的资源,不属于任何名称空间,用户对PV资源的使用需要使用PersistentVolumeClaim(PVC)提出的使用申请(或称为声明)来完成绑定,是PV资源的消费者,它向PV申请特定大小的空间及访问模式(如rw或ro),从而创建出PVC存储卷,而后再由Pod资源通过PersistentVolumeClaim存储卷关联使用。如下图所示:
k8s自1.4版本起引入了一个新的资源对象StorageClass,可用于将存储资源定义为具有显著特征的类别(Class)而不是具体的PV,例如"fast" "slow" 或"glod" "silver" "bronze"等。用户通过PVC直接向意向的类别发出申请,匹配由管理员事先创建的PV,或者由其按需为用户动态创建PV,这样做甚至免去了需要事先创建PV的过程。
# ConfigMap和Secret
ConfigMap对象用于为容器中的应用提供配置数据以定制程序的行为,不过敏感的配置信息,例如密钥、证书等通常由Secret对象来进行配置。
它们将相应的配置信息保存于对象中,而后在Pod资源上以存储卷的形式将其挂载并获取相关的配置,以实现配置于镜像文件的解耦。也可以通过环境变量的方式进行挂载。
# 网络模型
# K8S网络模型
K8s的网络模型主要可用于解决四类通信需求:同一Pod内容器间的通信(Container to Container)、Pod间的通信(Pod to Pod)、Service到Pod间的通信(Service to Pod)以及集群外部与Service之间的通信(external to Service)。
目前K8S支持使用CNI插件来编排网络,以实现Pod及集群网络管理功能的自动化。每次Pod被初始化或删除时,kubelet都会调用默认的CNI插件创建一个虚拟设备接口附加到相关的底层网络,为其设置IP地址、路由信息并将其映射到Pod对象的网络名称空间。
# CNI插件
CNI本身只是规范,付诸生产还需要有特定的实现。常见的CNI网络插件包含以下这些主流的项目。
Flannel:
一个为k8s提供叠加网络的网络插件,它基于Linux TUN/TAP,使用UDP封装IP报文来创建叠加网络,并借助etcd维护网络的分配情况。
Calico:
一个基于BGP的三层网络插件,并且也支持网络策略来实现网络的访问控制;它在每台机器上运行一个vRouter,利用Linux内核来转发网络数据包,并借助iptables实现防火墙等功能。
Canal:
由Flannel和Calico联合发布的一个统一网络插件,提供CNI网络插件,并支持网络策略。
Weave Net:
Weave Net是一个多主机容器的网络方案,支持去中心化的控制平面,在各个host上的wRouter间建立Full Mesh的TCP连接,并通过Gossip来同步控制信息。数据平面上,Weave通过UDP封装实现L2 Overlay,封装支持两种模式,一种是运行在user space的sleeve mode,另一种是运行在kernel space的fastpath mode。
# Flannel/Calico的区别
- Flannel是一个大二层网络。
- Calico是一个三层的虚拟网络方案。支持丰富的网络策略,比如namespace网络隔离,Pod出入站流量控制。
# HPA
手动调整的Pod控制器副本方式依赖于用户深度参与监控容器应用的资源压力并且需要计算出合理的值进行调整,存在一定程度的滞后性。K8S提供了多种自动弹性伸缩(Auto Scaling)工具。
Horizontal Pod Autoscaler,一种支持控制器对象下Pod规模弹性伸缩的工具,目前有两个版本的实现,分别称为HPA和HPA(v2),前一种仅支持把CPU指标数据作为评估基准,而新版本支持可从资源指标API和自定义指标API中获取的指标数据。
# Helm
# 容器和虚拟机有什么区别联系
在解释这个问题的之前,先要了解一下什么是容器。
# 什么是容器
容器是一种轻量级、可移植、自包含的软件打包技术,它使得应用程序可以在几乎任何地方以相同的方式运行。
容器由应用程序本身和它的环境依赖(库和其他应用程序)两部分组成,并在宿主机(Host)操作系统的用户空间中运行,但与操作系统的其他进程互相隔离。
它们的实现机制有别于诸如Vmware、KVM和Xen等实现方案的传统虚拟化技术。
# 两者区别如下
由于同一个宿主机上的所有容器都共享其底层操作系统(内核空间),这就使得容器体积上要比传统的虚拟机小得多。
另外,启动容器无需启动整个操作系统,所以容器部署和启动的速度更快,开销更小,也更容易迁移。事实上,容器赋予了应用程序超强的可移植能力。
# Kubernetes特性有哪些
# 什么是K8S
Kubernets是一种用于在一组主机上运行和协同容器化应用程序的系统,旨在提供可预测性、可扩展性与高可用性的方法来完全管理容器化应用程序和服务的生命周期的平台。
用户可以定义应用程序的运行方式,以及与其他应用程序或外部世界交互的途径,并能实现服务的扩容和缩容,执行平滑滚动更新,以及在不同版本的应用程序之间调度流量以测试功能或回滚有问题的部署。k8s提供了接口和可组合的平台原语,使得用户能够以高度的灵活性和可靠性定义及管理应用程序。
# 如下重要特性
自动装箱
建构与容器之上,基于资源依赖及其他约束自动完成容器部署且不影响其可用性,并通过调度机制混合关键型应用和非关键型应用的工作负载与同一节点以提升资源利用率。
自我修复(自愈)
支持容器故障后自动重启、节点故障后重新调度容器,以及其他可用节点、健康状态检查失败后关闭容器并重新创建等自我修复机制。
水平扩展
支持通过简单命令或UI手动水平扩展,以及基于CPU等资源负载率的自动水平扩展机制。
服务发现和负载均衡
K8s通过其附加组件之一的KubeDNS(或CoreDNS)为系统内置了服务发现功能,它会为每个Service配置DNS名称,并允许集群内的客户端直接使用此名称发出访问请求,而Service则通过iptables或ipvs内建了负载均衡机制。
自动发布和回滚
K8S支持**"灰度"更新应用程序或其配置信息**,它会监控更新过程中应用程序的健康状态,以确保它不会在同一时刻杀掉所有实例,而此过程中一旦有故障发生,就会立即自动执行回滚操作。
密钥和配置管理
Kubernetes的ConfigMap实现了配置数据与Docker镜像解耦,需要时,仅对配置做出变更而无须重新构建Docker镜像,这为应用开发部署带来了很大的灵活性。此外,对于应用所依赖的一些敏感数据,如用户名和密码、令牌、密钥等信息,K8s专门提供了Secret对象为其解耦,既便利了应用的快速开发和交付,又提供了一定程度上的安全保障。
存储编排
K8s支持Pod对象按需自动挂载不同类型的存储系统,这包括节点本地存储、公有云服务商的云存储,以及网络存储系统(例如,NFS、iSCSI、GlusterFS、Ceph、Cinder和Flocker等)。
批量处理执行
除了服务型应用,K8s还支持批处理作业及CI(持续集成),如果需要,一样可以实现容器故障后恢复。
# K8S的list-watch
list-watch 是K8S设计中的精髓所在,理解list-watch对用来整体理解K8S有着很大的帮助。
下面所有做的该知识点的整理和总结,都是基于网络上如下URL做的整理。
https://istio.cn/t/topic/157
https://www.kubernetes.org.cn/174.html
# 为什么需要list-watch这种机制
K8S各个组件之间仅采用的是HTTP协议通信,没有依赖中间件。而我们在使用K8S的过程中,必然会有对资源处理实时性,完整性,可靠性的强烈需求的。
而List-Watch 就是基于HTTP协议开发的,是K8S重要的异步信息通知机制。它通过list获取全量数据,通过watch API来监听增量数据,保证了消息的可靠性、实时性,性能和顺序性。
说白了就是k8s为了满足于异步消息处理的系统。
# 什么是List-Watch
list-watch 有两部分组成,分别是 list和watch。list就是调用资源的list API罗列资源,基于HTTP短连接实现。watch则是调用资源的watch API监听资源变更事件,基于HTTP长连接实现,watch是一个典型的发布-订阅模式。
以pod资源为例,它的List API如下,返回值是PodList,即一组pod。
GET /api/v1/pods
它的Watch API如下,往往带上watch=true,表示采用HTTP长连接持续监听pod相关事件,每当有事件来临是,返回一个WatchEvent。
GET /api/v1/watch/pods
# 应用模块
K8S的informer模块中就封装了list-watch API,用户只需要指定资源,编写相应的事件处理函数,例如AddFunc,UpdateFunc和DeleteFunc等。
在下图中,通过对apiserver的list API罗列资源和watch API监听资源的变更事件后,将反馈的一系列结果放入到了一个FIFO队列中,队列的另一头有协程从中取出事件,并调用对应的注册函数处理事件。Informer还维护了一个只读的Map Store缓存,主要是为了提升查询的效率,降低apiserver的负载。
# 设计理念
List-Watch就是一个异步消息的系统,一般我们对消息系统有至少如下四点要求:消息可靠性、消息实时性、消息顺序性、高性能。
list-watch如何实现消息的可靠性:
list API可以查询到当前的资源及其对应的状态(即期望的状态),客户端通过拿期望的状态和实际的状态进行对比,纠正状态不一致的资源。Watch API保持和api server一个长链接,接收资源的状态变更事件并做相应处理。如果仅调用watch API,若某个时间点连接中断,就有可能导致消息丢失,所以需要通过list API解决消息丢失的问题。从另一个角度来看,list API获取全量数据,watch API获取增量数据。虽然仅仅通过轮询list API,也能达到同步资源状态的效果,但是存在开销大,实时性不足的问题。
list-watch如何实现消息的实时性:
每当apiserver的资源发生了状态变更事件,都会将事件及时的推送给客户端,从而保证了消息的实时性。
list-watch如何实现消息的顺序性:
在并发的场景下,客户端在短时间内可能会收到同一个资源的多个事件,对于关注最终一致性的K8S来说,它需要知道哪个是最近发生的事件,并保证资源的最终状态如同最近事件所表述的状态一样。K8S在每个资源的事件中都带一个resourceVersion的标签,这个标签是递增的数字,所以当客户端并发处理同一个资源的事件时,它就可以对比resourceVersion来保证最终的状态和最新的事件所期望的状态保持一致。
list-watch如何实现消息的高性能:
仅通过周期性调用list API也能达到资源最终一致性的效果,但是周期性频繁的轮询大大的增大了开销,增加了apiserver的压力。而watch作为异步消息通知机制,复用一条长链接,保证实时性的同时也保证了性能。
# Kube-proxy中的ipvs模式和iptables模式
每个工作节点都需要运行一个kube-proxy守护进程,能够按需为Service资源对象生成iptables或ipvs规则,从而捕获访问当前Service的ClusterIP的流量并将其转发至正确的后端Pod对象。
# iptables模式是什么
# ipvs模式是什么
LVS由两部分组成,ipvs和ipvsadm。其中ipvsadm是LVS的管理工具,管理员通过ipvsadm定义或管理集群规则。
# 两者的区别和联系
# K8S删除node
查看现有集群中有哪些节点
$ kubectl get nodes
1删除节点前,先驱赶掉上面的pod
$ kubectl drain node-06 --delete-local-data --force --ignore-daemonsets
1此时节点上面的pod开始迁移。
再次检查节点,被标记为不可调度节点
$ kubectl get nodes node-06 Ready,SchedulingDisabled <none> 2d18h v1.14.1
1
2最后删除节点
$ kubectl delete node node-06 $ kubectl get nodes
1
2
# 修改docker storage pool
# 查看storage pool配置
docker info
more /usr/lib/systemd/system/docker.service
2
# 清理docker 空间
docker system prune -a -f
# 修改空间大小步骤如下
- 设置节点不可调度 kubectl cordon xxx.xxx.xxx.xxx
- 驱逐节点pod kubectl drain xxx.xxx.xxx.xxx
- 停止docker systemctl stop docker
- 修改docker配置文件 /usr/lib/systemd/system/docker.service 在ExecStart这一行加一个参数 ExecStart=/usr/bin/dockerd --storage-opt dm.loopdatasize=500G -g /app/data/ips/docker \
- systemctl daemon-reload && systemctl start docker
- 取消不可调度 kubectl uncordon xxx.xxx.xxx.xxx
# 修改最大的pod限制
http://blog.schoolofdevops.com/how-to-increase-the-number-of-pods-limit-per-kubernetes-node/