客户端提交 Pod 的配置信息(可以是 yaml 文件定义的信息)到 kube-apiserver.
Apiserver 收到指令后,通知 controllr-manager 创建一个资源对象
controller-manager 通过 apiserver 将 pod 的配置信息存储到 ETCD 数据中薪心中
kube-scheduler 检查到 pod 信息会开始调度预选,会先过滤不符合 Pod 资源配置要求的节点,然后开始调度调优,主要是挑选出更适合运行的 pod 节点,然后将 pod 的资源配置单发送到 node 节点上的 kubelet 组件上
kubelet 根据 scheduler 发来的资源配置单运行 pod,运行成功后,将 pod 的运行的信息返回 scheduler, scheduler 将返回的 pod 运行状况的信息存储到 etcd 数据中信
Pod 重启策略(RestartPolicy)应用于 Pod 内的所有容器,并且仅再 Pod 所处的 Node 上由 Kubelet 进行判断和重启操作.当某个容器异常退出或健康检查失败时,kubele 将根据 RestartPolicy 的设置来进行相应操作
pod 的重启策略包括 Always,OnFaliure 和 Never,默认值为 Always
Always: 当容器失效时由 kubelet 自动重启该容器
OnFailure:当容器终止运行且退出不为 0 时,yo 由 kubelet 自动重启该容器
Nerve: 不论容器运行状态如何,kubelet 都不会重启该容器
同时 pod 的容器策略与控制方式关联,当前可用于管理 Pod 的控制器包括 RelicatonController
LivenessProbe探针:用于判断容器是否存活(running状态),如果LivenessProbe探针探测到容器不健康,则kubelet将杀掉该容器,并根据容器的重启策略做相应处理。若一个容器不包含LivenessProbe探针,kubelet认为该容器的LivenessProbe探针返回值用于是“Success”。
ReadineeProbe探针:用于判断容器是否启动完成(ready状态)。如果ReadinessProbe探针探测到失败,则Pod的状态将被修改。Endpoint Controller将从Service的Endpoint中删除包含该容器所在Pod的Eenpoint。
startupProbe探针:启动检查机制,应用一些启动缓慢的业务,避免业务长时间启动而被上面两类探针kill掉。
ExecAction:在容器内执行一个命令,若返回码为0,则表明容器健康。
TCPSocketAction:通过容器的IP地址和端口号执行TCP检查,若能建立TCP连接,则表明容器健康。
HTTPGetAction:通过容器的IP地址、端口号及路径调用HTTP Get方法,若响应的状态码大于等于200且小于400,则表明容器健康。
Deployment或RC:该调度策略主要功能就是自动部署一个容器应用的多份副本,以及持续监控副本的数量,在集群内始终维持用户指定的副本数量。
NodeSelector:定向调度,当需要手动指定将Pod调度到特定Node上,可以通过Node的标签(Label)和Pod的nodeSelector属性相匹配。
NodeAffinity亲和性调度:亲和性调度机制极大的扩展了Pod的调度能力,目前有两种节点亲和力表达:
requiredDuringSchedulingIgnoredDuringExecution:硬规则,必须满足指定的规则,调度器才可以调度Pod至Node上(类似nodeSelector,语法不同)。
preferredDuringSchedulingIgnoredDuringExecution:软规则,优先调度至满足的Node的节点,但不强求,多个优先级规则还可以设置权重值。
Taints和Tolerations(污点和容忍):
Taint:使Node拒绝特定Pod运行;
Toleration:为Pod的属性,表示Pod能容忍(运行)标注了Taint的Node。
在Deployment的定义中,可以通过spec.strategy指定Pod更新的策略,目前支持两种策略:Recreate(重建)和RollingUpdate(滚动更新),默认值为RollingUpdate。
Recreate:设置spec.strategy.type=Recreate,表示Deployment在更新Pod时,会先杀掉所有正在运行的Pod,然后创建新的Pod。
RollingUpdate:设置spec.strategy.type=RollingUpdate,表示Deployment会以滚动更新的方式来逐个更新Pod。同时,可以通过设置spec.strategy.rollingUpdate下的两个参数(maxUnavailable和maxSurge)来控制滚动更新的过程
通过创建Service,可以为一组具有相同功能的容器应用提供一个统一的入口地址,并且将请求负载分发到后端的各个容器应用上。其主要类型有:
ClusterIP:虚拟的服务IP地址,该地址用于Kubernetes集群内部的Pod访问,在Node上kube-proxy通过设置的iptables规则进行转发;
NodePort:使用宿主机的端口,使能够访问各Node的外部客户端通过Node的IP地址和端口号就能访问服务;
LoadBalancer:使用外接负载均衡器完成到服务的负载分发,需要在spec.status.loadBalancer字段指定外部负载均衡器的IP地址,通常用于公有云。
Service负载分发的策略有:RoundRobin和SessionAffinity
RoundRobin:默认为轮询模式,即轮询将请求转发到后端的各个Pod上。
SessionAffinity:基于客户端IP地址进行会话保持的模式,即第1次将某个客户端发起的请求转发到后端的某个Pod上,之后从相同的客户端发起的请求都将被转发到后端相同的Pod上。
映射Pod到物理机:将Pod端口号映射到宿主机,即在Pod中采用hostPort方式,以使客户端应用能够通过物理机访问容器应用。
映射Service到物理机:将Service端口号映射到宿主机,即在Service中采用nodePort方式,以使客户端应用能够通过物理机访问容器应用。
映射Sercie到LoadBalancer:通过设置LoadBalancer映射到云服务商提供的LoadBalancer地址。这种用法仅用于在公有云服务提供商的云平台上设置Service的场景。
Kubernetes的Ingress资源对象,用于将不同URL的访问请求转发到后端不同的Service,以实现HTTP层的业务路由机制。
Kubernetes使用了Ingress策略和Ingress Controller,两者结合并实现了一个完整的Ingress负载均衡器。使用Ingress进行负载分发时,Ingress Controller基于Ingress规则将客户端请求直接转发到Service对应的后端Endpoint(Pod)上,从而跳过kube-proxy的转发功能,kube-proxy不再起作用,全过程为:ingress controller + ingress 规则 ----> services。
同时当Ingress Controller提供的是对外服务,则实际上实现的是边缘路由器的功能。
K8s的镜像下载策略有三种:Always、Never、IFNotPresent。
Always:镜像标签为latest时,总是从指定的仓库中获取镜像。
Never:禁止从仓库中下载镜像,也就是说只能使用本地镜像。
IfNotPresent:仅当本地没有对应镜像时,才从目标仓库中下载。
默认的镜像下载策略是:当镜像标签是latest时,默认策略是Always;当镜像标签是自定义时(也就是标签不是latest),那么默认策略是IfNotPresent。
在Kubernetes集群中,在每个Node(又称Worker)上都会启动一个kubelet服务进程。该进程用于处理Master下发到本节点的任务,管理Pod及Pod中的容器。每个kubelet进程都会在API Server上注册节点自身的信息,定期向Master汇报节点资源的使用情况,并通过cAdvisor监控容器和节点资源。
创建完secret之后,可通过如下三种方式使用:
在创建Pod时,通过为Pod指定Service Account来自动使用该Secret。
通过挂载该Secret到Pod来使用它。
在Docker镜像下载时使用,通过指定Pod的spc.ImagePullSecrets来引用它。
CNI提供了一种应用容器的插件化网络解决方案,定义对容器网络进行操作和配置的规范,通过插件的形式对CNI接口进行实现。CNI仅关注在创建容器时分配网络资源,和在销毁容器时删除网络资源。在CNI模型中只涉及两个概念:容器和网络。
容器(Container):是拥有独立Linux网络命名空间的环境,例如使用Docker或rkt创建的容器。容器需要拥有自己的Linux网络命名空间,这是加入网络的必要条件。
网络(Network):表示可以互连的一组实体,这些实体拥有各自独立、唯一的IP地址,可以是容器、物理机或者其他网络设备(比如路由器)等。
对容器网络的设置和操作都通过插件(Plugin)进行具体实现,CNI插件包括两种类型:CNI Plugin和IPAM(IP Address Management)Plugin。CNI Plugin负责为容器配置网络资源,IPAM Plugin负责对容器的IP地址进行分配和管理。IPAM Plugin作为CNI Plugin的一部分,与CNI Plugin协同工作。
PV是对底层网络共享存储的抽象,将共享存储定义为一种“资源”。
PVC则是用户对存储资源的一个“申请”。
某个PV在生命周期中可能处于以下4个阶段(Phaes)之一。
Available:可用状态,还未与某个PVC绑定。
Bound:已与某个PVC绑定。
Released:绑定的PVC已经删除,资源已释放,但没有被集群回收。
Failed:自动资源回收失败。
模式 | 数据包封包 | 优点 | 缺点 |
vxlan | 封包, 在vxlan设备上将pod发来的数据包源、目的mac替换为本机vxlan网卡和对端节点vxlan网卡的mac。外层udp目的ip地址根据路由和对端vxlan的mac查fdb表获取 | 只要k8s节点间三层互通, 可以跨网段, 对主机网关路由没有特殊要求。各个node节点通过vxlan设备实现基于三层的”二层”互通, 三层即vxlan包封装在udp数据包中, 要求udp在k8s节点间三层可达;二层即vxlan封包的源mac地址和目的mac地址是自己的vxlan设备mac和对端vxlan设备mac。 | 需要进行vxlan的数据包封包和解包会存在一定的性能损耗 |
ipip | 封包,在tunl0设备上将pod发来的数据包的mac层去掉,留下ip层封包。 外层数据包目的ip地址根据路由得到。 | 只要k8s节点间三层互通, 可以跨网段, 对主机网关路由没有特殊要求。 | 需要进行ipip的数据包封包和解包会存在一定的性能损耗 |
bgp | 不需要进行数据包封包 | 不用封包解包,通过bgp协议可实现pod网络在主机间的三层可达, k8s节点不跨网段时和flannel的host-gw相似; | |
支持跨网段, 满足复杂的网络架构 | 跨网段时,需要主机网关路由也充当BGP Speaker能够学习到pod子网路由并实现pod子网路由的转发 |
fannel三种模式 | 效率 | calico 模式 |
UDP | 性能较差,封包解包涉及到多次用户态和内核态交互 | 类似 IPIP |
VXLAN | 性能较好,封包解包在内核态实现,内核转发数据,flanneld负责动态配置ARP和FDB(转发数据库)表项更新 | 类似VXLAN |
host-gw | 性能最好,不需要再次封包,正常发包,目的容器所在的主机充当网关 | |
flanneld 负责主机上路由表的刷新 | 类似 BGP |
calico根据iptables规则进行路由转发,并没有进行封包,解包的过程,这和flannel比起来效率就会快多 calico包括如下重要组件:Felix,etcd,BGP Client,BGP Route Reflector。下面分别说明一下这些组件。
Felix:主要负责路由配置以及ACLS规则的配置以及下发,它存在在每个node节点上。 etcd:分布式键值存储,主要负责网络元数据一致性,确保Calico网络状态的准确性,可以与kubernetes共用; BGPClient(BIRD), 主要负责把 Felix写入 kernel的路由信息分发到当前 Calico网络,确保 workload间的通信的有效性; BGPRoute Reflector(BIRD), 大规模部署时使用,摒弃所有节点互联的mesh模式,通过一个或者多个 BGPRoute Reflector 来完成集中式的路由分发 通过将整个互联网的可扩展 IP网络原则压缩到数据中心级别,Calico在每一个计算节点利用 Linuxkernel 实现了一个高效的 vRouter来负责数据转发,而每个vRouter通过 BGP协议负责把自己上运行的 workload的路由信息向整个Calico网络内传播,小规模部署可以直接互联,大规模下可通过指定的BGProute reflector 来完成。这样保证最终所有的workload之间的数据流量都是通过 IP包的方式完成互联的。
Flannel的工作原理: Flannel实质上是一种“覆盖网络(overlay network)”,也就是将TCP数据包装在另一种网络包里面进行路由转发和通信,目前已经支持UDP、VxLAN、AWS VPC和GCE路由等数据转发方式。
默认的节点间数据通信方式是UDP转发。 工作原理: 数据从源容器中发出后,经由所在主机的docker0虚拟网卡转发到flannel0虚拟网卡(先可以不经过docker0网卡,使用cni模式),这是个P2P的虚拟网卡,flanneld服务监听在网卡的另外一端。 Flannel通过Etcd服务维护了一张节点间的路由表,详细记录了各节点子网网段 。 源主机的flanneld服务将原本的数据内容UDP封装后根据自己的路由表投递给目的节点的flanneld服务,数据到达以后被解包,然后直接进入目的节点的flannel0虚拟网卡,然后被转发到目的主机的docker0虚拟网卡,最后就像本机容器通信一下的有docker0路由到达目标容器。 flannel在进行路由转发的基础上进行了封包解包的操作,这样浪费了CPU的计算资源。
在 Kubernetes 集群中,当节点由于某些原因(网络、宕机等)不能正常工作时会被认定为不可用状态(Unknown 或者 False 状态),当时间超过了 pod-eviction-timeout 值时,那么节点上的所有 Pod 都会被节点控制器计划删除。
Kubernetes 集群中有一个节点生命周期控制器:node_lifecycle_controller.go。它会与每一个节点上的 kubelet 进行通信,以收集各个节点已经节点上容器的相关状态信息。当超出一定时间后不能与 kubelet 通信,那么就会标记该节点为 Unknown 状态。并且节点生命周期控制器会自动创建代表状况的污点,用于防止调度器调度 pod 到该节点。
那么 Unknown 状态的节点上已经运行的 pod 会怎么处理呢?节点上的所有 Pod 都会被污点管理器(taint_manager.go)计划删除。而在节点被认定为不可用状态到删除节点上的 Pod 之间是有一段时间的,这段时间被称为容忍度。如果在不配置的情况下,Kubernetes 会自动给 Pod 添加一个 key 为 node.kubernetes.io/not-ready 的容忍度 并配置 tolerationSeconds=300,同样,Kubernetes 会给 Pod 添加一个 key 为 node.kubernetes.io/unreachable 的容忍度 并配置 tolerationSeconds=300。
当到了删除 Pod 时,污点管理器会创建污点标记事件,然后驱逐 pod 。这里需要注意的是由于已经不能与 kubelet 通信,所以该节点上的 Pod 在管理后台看到的是处于灰色标记,但是此时如果去获取 pod 的状态其实还是处于 Running 状态。每种类型的资源都有相应的资源控制器(Controller),例如:deployment_controller.go、stateful_set_control.go。每种控制器都在监听资源变化,从而做出相应的动作执行。deployment 控制器在监听到 Pod 被驱逐后会创建一个新的 Pod 出来,但是 Statefulset 控制器并不会创建出新的 Pod,原因是因为它可能会违反 StatefulSet 固有的至多一个的语义,可能出现具有相同身份的多个成员,这将可能是灾难性的,并且可能导致数据丢失。
deployment:适合无状态的服务部署 适合部署无状态的应用服务,用来管理pod和replicaset,具有上线部署、副本设定、滚动更新、回滚等功能,还可提供声明式更新,例如只更新一个新的Image
编写yaml文件,并创建nginx服务pod资源。
StatefullSet:适合有状态的服务部署 适合部署有状态应用,解决Pod的独立生命周期,保持Pod启动顺序和唯一性。
稳定,唯一的网络标识符,持久存储(例如:etcd配置文件,节点地址发生变化,将无法使用)
有序,优雅的部署和扩展、删除和终止(例如:mysql主从关系,先启动主,再启动从)有序,滚动更新
应用场景:例如数据库
无状态服务的特点:
deployment 认为所有的pod都是一样的
不用考虑顺序的要求
不用考虑在哪个node节点上运行
可以随意扩容和缩容
有状态服务的特点:
实例之间有差别,每个实例都有自己的独特性,元数据不同,例如etcd,zookeeper
实例之间不对等的关系,以及依靠外部存储的应用
常规的service服务和无头服务的区别
service:一组Pod访问策略,提供cluster-IP群集之间通讯,还提供负载均衡和服务发现
Headless service 无头服务,不需要cluster-IP,直接绑定具体的Pod的IP,无头服务经常用于statefulset的有状态部署
创建无头服务的service资源和dns资源,由于有状态服务的IP地址是动态的,所以使用无头服务的时候要绑定dns服务
DaemonSet:一次部署,所有的node节点都会部署,例如一些典型的应用场景: 运行集群存储 daemon,例如在每个Node上运行 glusterd、ceph
• 在每个Node上运行日志收集 daemon,例如 fluentd、 logstash
• 在每个Node上运行监控 daemon,例如 Prometheus Node Exporter
• 在每一个Node上运行一个Pod
• 新加入的Node也同样会自动运行一个Pod
• 应用场景:监控,分布式存储,日志收集等
Job:一次性的执行任务
• 一次性执行任务,类似Linux中的job
• 应用场景:如离线数据处理,视频解码等业务
Cronjob:周期性的执行任务
• 周期性任务,像Linux的Crontab一样
• 应用场景:如通知,备份等
• 使用cronjob要慎重,用完之后要删掉,不然会占用很多资源
通常情况下,service和pod的IP仅可在集群内部访问
• k8s提供了service方式:NodePort 来提供对外的服务,外部的服务可以通过访问Node节点ip+NodePort端口来访问集群内部的资源,外部的请求先到达service所选中的节点上,然后负载均衡到每一个节点上。
NodePort虽然提供了对外的方式但也有很大弊端:
• 由于service的实现方式:user_space 、iptebles、 3 ipvs、方式这三种方式只支持在4层协议通信,不支持7层协议,因此NodePort不能代理https服务。
• NodePort 需要暴露service所属每个node节点上端口,当需求越来越多,端口数量过多,导致维护成本过高,并且集群不好管理。
原理
• Ingress也是Kubernetes API的标准资源类型之一,它其实就是一组基于DNS名称(host)或URL路径把请求转发到指定的Service资源的规则。用于将集群外部的请求流量转发到集群内部完成的服务发布。我们需要明白的是,Ingress资源自身不能进行“流量穿透”,仅仅是一组规则的集合,这些集合规则还需要其他功能的辅助,比如监听某套接字,然后根据这些规则的匹配进行路由转发,这些能够为Ingress资源监听套接字并将流量转发的组件就是Ingress Controller。
• Ingress 控制器不同于Deployment 等pod控制器的是,Ingress控制器不直接运行为kube-controller-manager的一部分,它仅仅是Kubernetes集群的一个附件,类似于CoreDNS,需要在集群上单独部署。
• ingress controller通过监视api server获取相关ingress、service、endpoint、secret、node、configmap对象,并在程序内部不断循环监视相关service是否有新的endpoints变化,一旦发生变化则自动更新nginx.conf模板配置并产生新的配置文件进行reload
Scheduler工作原理: 请求及Scheduler调度步骤:
• 节点预选(Predicate):排除完全不满足条件的节点,如内存大小,端口等条件不满足。
• 节点优先级排序(Priority):根据优先级选出最佳节点
• 节点择优(Select):根据优先级选定节点
具体步骤:
• 首先用户通过 Kubernetes 客户端 Kubectl 提交创建 Pod 的 Yaml 的文件,向Kubernetes 系统发起资源请求,该资源请求被提交到
• Kubernetes 系统中,用户通过命令行工具 Kubectl 向 Kubernetes 集群即 APIServer 用 的方式发送“POST”请求,即创建 Pod 的请求。
• APIServer 接收到请求后把创建 Pod 的信息存储到 Etcd 中,从集群运行那一刻起,资源调度系统 Scheduler 就会定时去监控 APIServer
• 通过 APIServer 得到创建 Pod 的信息,Scheduler 采用 watch 机制,一旦 Etcd 存储 Pod 信息成功便会立即通知APIServer,
• APIServer会立即把Pod创建的消息通知Scheduler,Scheduler发现 Pod 的属性中 Dest Node 为空时(Dest Node=””)便会立即触发调度流程进行调度。
• 而这一个创建Pod对象,在调度的过程当中有3个阶段:节点预选、节点优选、节点选定,从而筛选出最佳的节点
• 节点预选:基于一系列的预选规则对每个节点进行检查,将那些不符合条件的节点过滤,从而完成节点的预选
• 节点优选:对预选出的节点进行优先级排序,以便选出最合适运行Pod对象的节点
• 节点选定:从优先级排序结果中挑选出优先级最高的节点运行Pod,当这类节点多于1个时,则进行随机选择
k8s的调用工作方式
• Kubernetes调度器作为集群的大脑,在如何提高集群的资源利用率、保证集群中服务的稳定运行中也会变得越来越重要Kubernetes的资源分为两种属性。
• 可压缩资源(例如CPU循环,Disk I/O带宽)都是可以被限制和被回收的,对于一个Pod来说可以降低这些资源的使用量而不去杀掉Pod。
• 不可压缩资源(例如内存、硬盘空间)一般来说不杀掉Pod就没法回收。未来Kubernetes会加入更多资源,如网络带宽,存储IOPS的支持。
userspace 模式
• 该模式下kube-proxy会为每一个Service创建一个监听端口。发向Cluster IP的请求被Iptables规则重定向到Kube-proxy监听的端口上,Kube-proxy根据LB算法选择一个提供服务的Pod并和其建立链接,以将请求转发到Pod上。
• 该模式下,Kube-proxy充当了一个四层Load balancer的角色。由于kube-proxy运行在userspace中,在进行转发处理时会增加两次内核和用户空间之间的数据拷贝,效率较另外两种模式低一些;好处是当后端的Pod不可用时,kube-proxy可以重试其他Pod。
iptables 模式
• 为了避免增加内核和用户空间的数据拷贝操作,提高转发效率,Kube-proxy提供了iptables模式。在该模式下,Kube-proxy为service后端的每个Pod创建对应的iptables规则,直接将发向Cluster IP的请求重定向到一个Pod IP。
• 该模式下Kube-proxy不承担四层代理的角色,只负责创建iptables规则。该模式的优点是较userspace模式效率更高,但不能提供灵活的LB策略,当后端Pod不可用时也无法进行重试。
该模式和iptables类似,kube-proxy监控Pod的变化并创建相应的ipvs rules。ipvs也是在kernel模式下通过netfilter实现的,但采用了hash table来存储规则,因此在规则较多的情况下,Ipvs相对iptables转发效率更高。除此以外,ipvs支持更多的LB算法。如果要设置kube-proxy为ipvs模式,必须在操作系统中安装IPVS内核模块。