一、Kubernetes介绍
核心功能
-
Kubernetes
抽象了数据中心的硬件设施,使得对外暴露的只是一个巨大的资源池。它让我们在部署和运行组件时,不用关系底层的服务器。使用Kubernetes部署多组件应用时,它会为每个组件都选择一个合适的服务器,部署之后它能够保证每个组件可以轻易地发现其他组件,并彼此之间实现通信。 - Kubernetes可以被当作一个操作系统来看待。它让应用程序开发者能集中精力实现应用本身的功能而不用关心集成应用与一些基础设施。能够简化应用部署,更好地利用硬件,自动扩容,自修复等。
集群架构
- 一个Kubernetes集群由很多节点组成,这些节点被分为:
-
主节点
,它承载着Kubernetes控制和管理整个集群的控制面版
-
工作节点
,它运行用户实际部署的应用。
-
控制面板
包含多个组件,组件可以运行在单个主节点上或者通过副本分别部署在多个主节点以确保高可用性。组件包括:
-
Kubernetes API服务
,你和其他控制面板组件都要和它通信 -
Scheculer
,调度你的应用(为应用的每个可部署组件分配一个工作节点) -
Controller Manager
,执行集群级别的功能,如复制组件、持续跟踪节点、处理节点失败等 -
etcd
,一个可靠的分布式存储,它能持久化存储集群配置。
-
工作节点
是运行容器化应用的机器。运行、监控和管理服务是由以下组件完成的:
- Docker、rtk或其他的容器类型
-
Kubelet
,它与API服务器通信,并管理它所在节点的容器 -
Kubernetes Service Proxy
(kube-proxy),它负责组件之间的负载均衡网络流量。
三、pod:运行于Kubernetes中的容器
介绍
- 每个
pod
就像一个独立的逻辑机器,拥有自己的IP、主机名、进程等,包含一个或多个容器。当一个pod包含多个容器时,这些容器总是运行于同一个工作节点上。 - 在包含容器的 pod 下,我们可以同时运行一些密切相关的进程,并为它们提供(几 乎)相同的环境,此时这些进程就好像全部运行于单个容器中一样,同时又保持着 一定的隔离。
- Kubemetes 通过配置 Docker 来让一个 pod 内的 所有容器共享相同的 Linux 命名空间,而不是每个容器都有自己的一组命名空间。达到隔离容器组,而不是单个容器,并让每个容器组内的容器共享一些资源的目的。
- 因此,每个pod应只包含紧密相关的组件或进程。
- pod也是扩容的基本单位,对于Kubernetes来说,它不能横向扩容单个容器,只能扩缩整个pod。
四、副本机制和其他控制器:部署托管的pod
存活探针
- 只要进程还在运行,Kubernetes会认为容器是健康的,但无法知道应用是否还在正常运行。
- Kubernetes可以通过
存活探针
检查容器是否还在运行。可以为pod中的每个容器单独指定存活探针。如果探测失败,Kubernetes将定期执行探针并重新启动容器。探针的失败阈值是可配置的。 - Kubernetes有以下三种探测容器的机制:
1)HTTP GET探针
对容器的IP地址执行HTTP GET请求;
2)TCP套接字探针
尝试与容器指定端口建立TCP连接;
3)Exec探针
在容器内执行任意命令,并检查命令的退出状态码。
Replication Controller、ReplicaSet和DaemonSet
Replication Controller
-
Replication Controller
是一种Kubernetes资源。他会持续监控正在运行的pod列表,并保证相应标签的pod的数目于期望值相符。如果运行的pod数量少于预期,它会根据pod模板创建新的副本。如果多余预期,它会删除多余副本。 - Replication Controller的副本数目、标签选择器和pod模板都可以随时更改,只有副本数目的变更会影响现有的pod,其它的只影响之后创建的pod。
ReplicaSet
-
ReplicaSet
的行为和ReplicationController完全相同,但pod选择器的表达能力更强。
DaemonSet
-
DaemonSet
保证集群中的每个节点都运行一个pod。如果节点下线,DaemonSet不会在其他地方重新创建pod。但是,当将一个新节点添加到集群中时,DaemonSet会立刻部署一个新的pod实例。如果有人无意中删除了一个pod,那么它也会重新创建一个新的pod。 - DaemonSet的一个目的是运行系统服务。如守护进程
- DaemonSet可以通过pod模板中的nodeSelector属性指定这些pod只在部分节点上运行。
- DaemonSet可以将pod部署到被设置为不可调度的,因为DaemonSet管理的podk可以完全绕过调度器。
Job
-
Job资源
允许你运行一种pod,该pod在内部进程成功结束时,不重启容器,一旦任务完成,pod就被认为处于完成状态。 - 在发生节点故障时,该节点上由Job管理的pod将按照ReplicaSet的pod方式,重新安排到其他节点。如果进程本身异常退出(进程返回错误退出代码时),可以将Job配置为重新启动容器。
- 作业可以配置为创建多个pod实例,并以并行或串行方式允许它们。这是通过
completions
和parallelism
属性来完成的。 - 通过在pod配置中设置
activeDeadlineSeconds
属性,可以限制pod的时间。如果pod运行时间超过此时间,系统将尝试终止pod,并将Job标记为失败。 - 可以通过
CronJob资源
配置cron任务。任务运行时间以知名的cron格式指定。
五、服务:让客户端发现pod并与之通信
介绍
- Kubernetes
服务
是一种为一组功能相同的pod提供单一不变的接入点的资源。当服务存在时,它的IP地址和端口不会改变。 - 客户端通过IP地址和端口号建立连接,这些连接会被路由到提供该服务的任意一个pod上。服务的连接对所有的后端pod是负载均衡的。
- 通过yaml文件创建服务后,服务会被分配一个内部集群IP。这个
IP只能在集群内部被访问
。 - 如果希望特定客户端产生的所有请求每次都指向同一个pod,可以设置服务的
sessionAffinity属性
为ClientIP
(默认值为None) - Kubernetes服务不是在HTTP层面上工作。服务处理
TCP和UDP包
,并不关心其中的载荷内容。因此Kubernetes的会话亲和性不能基于cookie。 - 同一服务可以暴露多个端口。
服务发现
- pod上的进程通过环境变量获得服务的IP地址和端口号。
- kube-system的命名空间包含了一个叫
kube-dns
的pod。这个pod运行DNS服务
,在集群中的其他pod都被配置成使用其作为dns。运行在pod上的进程DNS查询都会被kubernetes自身的DNS服务器相应,该服务器知道系统中运行的所有服务。 - 集群IP是无法ping通的,因为服务的集群IP是一个虚拟IP,只有在与服务端口结合时才有意义。
服务endpoint
- 服务endpoint可以让服务
将连接重定向到外部IP和端口
。 - 服务并不是和pod直接相连的。有一种资源介于两者之间,叫
Endpoint资源
。 - 选择器用于构建IP和端口列表,然后存储在Endpoint资源中。当客户端连接到服务时,服务代理选择这些IP和端口对中的一个,并将传入连接重定向到该位置监听的服务器。
- 如果创建了不包含pod选择器的服务,Kubernetes将不会自动创建Endpoint资源。这样就需要手动创建Endpoint资源来指定该服务的endpoint列表。
- Endpoint对象需要与服务具有相同的名称,并包含服务的目标IP地址和端口列表。
- 通过将服务的目标IP地址和端口设置为外部server的IP和端口,可以将服务连接到集群外部的server上。
- 除了手动设置Endpoint,也可以通过
FQDN
访问外部服务。需要把type设置为ExternalName
。 - ExternalName服务仅在
DNS级别
实施——为服务创建了简单的CNAME DNS记录。因此,连接到服务的客户端将直接连接到外部服务,完全绕过服务代理。
将服务暴露给外部客户端
NodePort
- 将一组pod公开给外部客户端的第一种方法是创建一个服务并将其类型设置为NodePort。
- 通过创建
NodePort服务
,可以让Kubernetes在其所有节点上保留一个端口
(所有节点上都使用相同的端口号),并将传入的连接重定向到服务的pod上。 - 不仅可以通过服务的内部集群IP访问NodePort服务,还可以通过任何节点的IP和预留节点端口访问NodePort服务。
- 需要更改防火墙规则来让外部客户端访问NodePort服务。
负载均衡器
-
负载均衡器
拥有自己独一无二的可公开访问的IP地址,并将所有连接重定向到服务。可以通过负载均衡器的IP地址访问服务。 - 使用该方法不需要像NodePort服务一样关闭防火墙。
- LoadBalancer类型的服务是一个具有额外的基础设施提供的负载平衡器NodePort服务。
外部连接特性
- 可以在服务的spec部分中
设置externalTrafficPolicy为local
,则服务代理将选择本地运行的pod。如果没有本地pod存在,则连接将挂起。 - 通过节点端口接收到连接时,由于对数据包执行了源网络地址转换(SNAT),数据包的源IP将发生更改。因此后端的pod无法看到实际的客户端IP。
通过Ingress暴露服务
- 每个LoadBalancer服务都需要自己的负载均衡器,以及独有的公有IP地址,而
Ingress
只需要一个公网IP就能为许多服务提供访问。 - 当客户端向Ingress发送HTTP请求时,Ingress会根据请求的主机名和路径决定请求转发到的服务。
- Ingress在网络栈(HTTP)的
应用层
操作,并且可以提供一些服务不能实现的功能,如基于cookie的会话亲和性(session affinity)等功能。 - 客户端向Ingress控制器发送HTTP请求,并在Host头中指定URL 地址。控制器从该头部确定客户端尝试访问哪个服务,通过与该服务关联的Endpoint对象查看pod IP,并将服务转发给其中一个pod。
- Ingress控制器不会将请求转发给服务,只是用它来选择一个pod。
六、卷:将磁盘挂载到容器
卷介绍
- pod中的每个容器都有自己独立的文件系统,因为文件系统来自容器镜像。
- Kubernetes的
存储卷
是pod的一个组成部分。pod中的所有容器都可以使用卷,但必须将它挂载
在每个需要访问它的容器中。在每个容器中都可以在其文件系统的任意位置挂载卷。 - 卷被绑定到pod的
生命周期
中。只有在pod存在时才会存在,但取决于卷的类型。pod删除时卷消失。但卷文件可能保持不变,并可以挂载到新的卷中。 - 如果一个pod包含多个容器,那这个卷可以同时被所有的容器使用。通过将相同的卷挂载到多个容器,它们可以对相同的文件进行操作。
- 使容器能够访问卷,需要在pod规范中定义卷,还需要在容器的规范中定义一个
VolumeMount
。 - 有多种类型的卷可供选择,包括
emptyDir
,hostPath
,gitRepo
,nfs
, 云服务商提供的特定存储类型(gcePersistenctDisk, awsElastic BlockStore, azureDisk), 其他类型的网络存储(cinder
,cephfs
,iscsi
, flocker,glusterfs
, quobyte, rdb, flexVolume), 特殊类型卷(configMap
,secret
, downwardAPI),persistentVolumeClaim
emptyDir卷
-
emptyDir
卷从一个空目录开始,运行在pod内的应用程序可以写入它需要的任何文件。卷的生存周期与pod的生存周期相关联,所以当删除pod时,卷的内容就会丢失
。 -
emptyDir
可用于同一个pod中运行的容器之间共享文件。也可用于被单个容器将数据临时写入磁盘。 - 作为卷来使用的emptyDir,是在承载pod的工作节点的实际磁盘上创建的,因此其性能取决于节点的磁盘类型。但我们可以通过将emptyDir的
medium
设置为Memory
来通知Kubernetes在tmfs文件系统(存在内存而非硬盘)上创建emptyDir。
hostPath卷
-
hostPath卷
指向节点文件系统上的特定文件或目录。在同一个节点上运行并在其hostPath卷中使用相同路径的pod可用看到相同的文件。 - hostPath卷是一种持久性存储。如果删除了一个pod,下一个pod使用指向主机上相同路径的hostPath卷,则新Pod会发现上一个pod留下的数据。但前提是必须将其调度到与第一个pod相同的节点上。
- hostPath卷通常用于尝试单节点集群中的持久化存储。
持久化存储
- 要保证即使pod重新调度到另一个节点时也要求具有相同的数据可用,必须将其存储在某种类型的网络存储中。
- 如果集群是运行在自由的一组服务器上,那么就有大量其他可移植的选项用于在卷内挂载外部存储。如要挂载一个简单的NFS共享,只需指定
NFS服务器
和共享路径
。 - 其他支持选项包括iscsi, glusterfs, rdb, flexVolume, cinder, cephfs, flocker, fc等。
从底层技术解耦pod
- 需要指定NFS节点所在的服务器,违背了
Kubernetes的基本理念
,即向应用程序及其开发人员隐藏真实的基础设施,使他们不必担心基础设施的具体状态,并使应用程序可在大量云服务商和数据企业之间进行功能迁移。
持久卷和持久卷声明
- Kubernetes由此引入了
持久卷(PV)
和持久卷声明(PVC)
两个新的资源。 - 当集群用户需要在其pod中使用持久化存储时,他们首先创建
持久卷声明(PVC)
清单,指定所需要的最低容量要求和访问模式,然后用户将持久卷声明清单提交给Kubernetes API服务器,Kubernetes找到可匹配的持久卷(PV)
并将其绑定到持久卷声明。 - 持久卷声明可用当作pod中的一个卷来使用,其他用户不能使用相同的持久卷,除非先通过删除持久卷声明绑定来释放。

- 持久卷不属于任何命名空间,它和集群节点一样是集群层面的资源,区别于pod和持久卷声明。
- 声明一个持久卷和创建一个pod是相对独立的过程,因为即使pod被重新调度,我们也希望通过相同的持久卷声明来确保可用。
- pvc访问模式包括(RWO, ROX, RWX涉及可以同时使用卷的工作节点数量而非pod的数量):
-
RWO
, ReadWriteOnce, 仅允许单个节点挂载读写。 -
ROX
, ReadOnlyMany, 允许多个节点挂载只读。 -
RWX
, ReadWriteMany, 允许多个节点挂载读写这个卷。
- 持久卷不属于任何命名空间,但持久卷声明只能在特定的命名空间创建。所以持久卷和持久卷声明只能被同一命名空间内的pod创建使用。
- 要在pod中使用持久卷,需要在pod的卷中引用持久卷声明名称。
- 可用在不同的Kubernetes集群上使用相同的pod和持久卷声明清单,因为它们不涉及任何特定依赖于基础设施的内容。
回收持久卷
- 删除pod和持久卷声明,再次创建持久卷声明,它不会被立即绑定到持久卷。持久卷的状态变成Released,持久卷声明的状态变成Pending
- 通过使用相同的持久卷,新的pod可用读取由全一个pod存放的数据,即使声明和pod是在不同的命名空间中创建的。
- 回收策略包括:
Retain
,Recycle
和Delete
持久卷的动态卷配置
- 集群管理员可以创建一个持久卷配置,并定义一个或多个
StorageClass对象
,从而让用户选择他们想要的持久卷类型而不仅仅只是创建持久卷。用户可以在其持久卷声明中引用StorageClass。 - StorageClass资源指定当持久卷声明请求此StorageClass时应使用哪个
制备程序(provisioner)
来提供持久卷。StorageClass定义中定义的参数将传递给置备程序,并具体到每个供应器插件。 - 创建StorageClass资源后,用户可以在其持久卷声明(PVC)中按名称引用存储类。除了指定大小和访问模式,持久卷声明现在还会指定要使用的存储类别。在创建声明时,
持久卷
由StorageClass资源中引用的provisioner创建。 - StorageClass的好处在于,声明是通过名称引用它们的。因此,只要StorageClass名称在所有这些名称中相同,PVC定义便可跨不同集群移植。
- 如果持久卷声明没有明确指出要使用哪个存储类,则
默认存储类
会用于动态提供持久卷的内容。 - 将持久化存储附加到一个容器的最佳方案是仅创建PVC(如果需要,可以使用明确指定的storageClassName)和容器(其通过名称引用PVC2),其他所有内容都由动态持久卷置备程序处理。
持久卷的动态配置的完整图示

七. ConfigMap和Secret:配置应用程序
- 传递配置数据给运行在Kubernetes中的应用程序的方法有:
- 向容器传递命令行参数
- 为每个容器设置自定义环境变量
- 通过特殊类型的卷将配置文件挂载到容器中。
利用ConfigMap解耦配置
- 应用配置的关键在于能够在多个环境中区分配置选项,将配置从应用程序源码中分离,可频繁变更配置值。
- 应用无需直接读取
ConfigMap
,甚至根本不需要知道其是否存在。映射的内容通过环境变量
或者卷
的形式传递给容器。 - 命令行参数的定义中可以通过
${ENV_VAR}
语法引用环境变量,因而可以达到将ConfigMap的条目当作命令行参数传递给进程的效果。 - pod是通过名称引用ConfigMap的,因此可以在多环境下使用相同的pod定义描述,同时保持不同的配置值以适应不同环境。
- 将ConfigMap暴露为卷可以达到
配置热更新
的效果,无需重新创建pod或者重启容器。ConfigMap被更新之后,卷中引用它的所有文件也会相应更新,进程发现文件被改变之后进行重载。 - Kubernetes通过
符号链接
做到热更新。每当ConfigMap被更新后,Kubernetes会创建一个文件夹,写入所有文件并重新将符号..data链接至新文件夹。通过这种方式可以一次性修改所有文件。 - 由于configMap卷中文件的更新行为对于所有运行中示例而言不是同步的,因此不同pod中的文件可能会在长达一分钟的时间内出现不一致的情况。
使用Secret给容器传递敏感数据
- Kubernetes提供了一种称为
Secret
的单独资源对象。用于存储一些敏感数据,如证书和私钥,需要确保其安全性。Secret结构与ConfigMap类似,均是键/值对的映射。其使用方法也类似。 - Kubernetes通过仅仅将Secret分发到需要访问Secret的pod所在的机器节点来保障其安全性。 另外,Secret只会存储在节点的
内存
中,永不写入物理存储。 - 从Kubernetes 1.7开始,etcd会以加密形式存储Secret,某种程度提高了系统安全性。
- 通过secret卷将Secret暴露给容器之后,Secret条目的值会被解码并以真实形式(纯文本或二进制)写入对应的文件。
九、Deployment:声明式地升级应用
-
Deployment
是一种用于部署应用程序并以声明的方式升级应用的高阶资源。 - 在使用Deployment时,实际的pod是由Deployment的Replicaset创建和管理的。
- 升级时,只需修改Deployment资源中定义的pod模板,Kubernetes会自动将实际的系统状态收敛为资源中定义的状态。
- Deloyment有
RollingUpdate
和Recreate
两种升级策略。 -
Recreate
会一次性删除所有旧版本的pod,然后创建新的pod。这会导致应用程序出现短暂的不可用。 -
RollingUpdate
会渐近地删除旧的pod,与此同时创建新的pod,使应用程序在整个升级过程中都处于可用状态。升级过程中pod数量可以在期望副本数的一定区间内浮动,而且其上下限是可配置的。如果应用能够支持多版本同时对外提供服务,则推荐使用该策略。 - 由deployment创建的所有ReplicaSet表示完整的修改版本历史。每个ReplicaSet都用特定的版本号来保存Deployment的完整信息。旧版本的ReplicaSet过多会导致ReplicaSet列表过于混乱,可以通过指定Deployment的
revisionHistoryLimit
属性来限制历史版本数量。默认值是2。 - 在Deployment的滚动升级期间,有两个属性会决定一次替换多少个pod:
maxSurge
和maxUnavailable
-
minReadySeconds
属性指定新创建的pod至少要成功运行多久之后,才能将其视为可用。在pod可用之前,滚动升级的过程不会继续。当所有容器的就绪探针
返回成功时,pod就被标记为就绪状态。
十、部署有状态的多副本应用
介绍
-
StatefulSet
保证了pod在重新调度后保留它们的标识和状态。当一个有状态的pod挂掉后,这个pod实例在别的节点上重建,但新的实例与被替换的实例拥有相同的名称、网络标识和状态。 - 与ReplicaSet或ReplicationController不同的是,Stateful创建的pod副本并不是完全一样的。每个pod都可以拥有一组独立的数据卷(持久化状态)。
提供稳定的网络标识
- 一个StatefulSet创建的每个pod都有一个从零开始的顺序索引,这个会体现在pod的名称和主机名上,同样还会体现在pod对应的固定存储上。
- 这些pod的名称则是可预知的,因为它是由Stateful的名称加该实例的顺序索引值组成的。
- 扩容一个StatefulSet会使用下一个还没用到的顺序索引值创建一个新的pod实例。
- 缩容一个StatefulSet将会最先删除最高索引值的实例,所以缩容的结果是可预知的。
- StatefulSet在有实例不健康的情况下是不允许做缩容操作的。
提供稳定的专属存储
- 像StatefulSet创建pod一样,StatefulSet也需要创建持久卷声明。所以一个StatefulSet可以拥有一个或多个卷声明模板,这些持久卷声明会在创建pod前创建出来,绑定到一个pod实例上。
- 扩容StatefulSet增加一个副本数时,会创建两个或更多的API对象(一个pod和与之关联的一个或多个持久卷声明)。但对于缩容来说,只会删除一个pod,而遗留下之前创建的声明。因为当一个声明被删除后,与之绑定的持久卷就会被回收或删除,则其上面的数据就会丢失。因此,当你需要释放持久卷时,需要手动删除对应的持久卷声明。
- 缩容后再扩容时,新的pod实例会使用绑定在持久卷上的相同声明和其上的数据。
- Kubernetes必须保证两个拥有相同标记和绑定相同持久卷声明的有状态的pod实例不会同时运行。也就是说一个StatefulSet必须在准确确认一个pod不再运行后,才会去创建它的替换pod。
- StatefulSet使用到新组件
volumeClaimTemplates
。其中仅仅定义了一个卷声明,会依据这个模板为每个pod都创建一个持久卷声明。在StatefulSet创建指定pod时,会自动将PersistentVolumeClaim卷添加到pod详述中,然后将这个卷关联到一个声明上。 - 生成的持久卷声明的名称由在volumeClaimTemplate字段中定义的名称和每个pod的名称组成。
- 使用StatefulSet配置创建pod时,后一个pod会在前一个pod运行并且处于就绪状态后创建。
在StatefulSet中发现伙伴节点
-
SRV
记录用来指向提供指定服务的服务器的主机名和端口号。Kubernetes通过一个headless service创建SRV记录来指向pod的主机名。 - 当一个pod要获取一个StatefulSet里的其他pod列表时,你需要做的就是触发一次
SRV DNS查询
。
十一、Kubernetes机理
了解架构

介绍
- 尽管一个
工作节点
上的组件都需要运行在同一节点上,控制平面
的组件可以被简单地分割在多台服务器上。 - 为了保证高可用性,控制平面的每个组件可以有多个实例。
etcd
和API服务器
的多个实例可以同时并行工作,但是,调度器
和控制器管理器
在给定时间内只能有一个实例起作用,其他实例处于待命模式。
etcd
- 上述的所有对象——pod、ReplicationController、服务和私密凭据等,需要以持久化方式存储到某个地方,这样它们的manifest在API服务器重启和失败的时候才不会丢失。Kubernetes使用了
etcd
-
etcd
是一个响应快、分布式、一致的key-value存储。 - 唯一能直接和etck通信的是Kubernetes的API服务器。所有其他组件通过API服务器间接地读取、写入数据到etcd。
- 使用这种方式更新集群状态总是一致的,因为API服务器实现了乐观锁机制。所有的Kubernetes包含一个
metadata.resourceVersion
字段,当更新对象时,客户端需要返回该值到API服务器。如果版本值与etcd中存储的不匹配,API服务器会拒绝该更新。API服务器确保写入存储的数据总是有效的,只有授权的客户端才能更改数据。 - etcd是Kubernetes存储集群状态和元数据的
唯一
的地方。 - API服务器将资源的完整JSON形式存储到etcd中。由于etcd的层级建空间,可以想象成把资源以JSON文件格式存储到etcd中。
- etcd使用RAFT一致性算法来保证一致性。确保任何时间点,每个节点的状态要么是大部分节点的当前状态,要么是之前确认过的状态。
API服务器
- Kubernetes API服务器作为中心组件,其他组件或者客户端(如kubectl)都会调用它。以RESTful API的形式提供了可以查询、修改集群状态的CRUD接口。它将状态存储到etcd中。
- 当以JSON文件创建一个资源,kubectl通过一个HTTP POST请求将文件内容发布到API服务器。
- 客户端通过创建到API服务器的HTTP连接来监听变更。通过此连接,客户端会接收到监听对象的一系列变更通知。每当更新对象,服务器把新版本对象发送至所有监听该对象的客户端。
调度器
- 调度器利用API服务器的监听机制等待新创建的pod,然后给每个新的、没有节点集的pod分配节点。
- 调度器通过API服务器更新pod的定义,然后API服务器再去通知Kubelet(通过之前描述的监听机制)该pod以及被调度过。当目标节点上的Kubelet发现该pod被调度到本节点,它就会创建并且运行pod的容器。
- 选择节点操作可以分解为两部分:
- 过滤所有节点,找出能分配给pod的可用节点列表
- 对可用节点按优先级排序,找出最优节点。如果多个节点都有最高的优先级分数,那么则循环分配,确保平均分类给pod。
- 可以在集群中运行多个调度器。然后,对每一个pod,可以通过在pod特性中设置schedulerName属性指定调度器来调度特定的pod。
- 可以实现自己的调度器,部署到集群,或者可以部署有不同配置项的额外Kubernetes调度器实例。
控制器管理器中运行的控制器
- 控制器管理器中的
控制器
负责确保系统真实状态朝API服务器定义的期望的状态收敛。然后将新的实际状态写入资源的status部分。 - 资源描述了集群中应该运行什么,而控制器就是活跃的Kubernetes组件,去做具体工作部署资源。
- 控制器通过API服务器监听资源(部署、服务等)变更,并对变更执行相应操作。
- 对于一个持久卷声明,
PersistentVolume控制器
为声明查找最佳匹配项,通过选择匹配声明中的访问模式,并且声明的容量大于需求的容量的最小持久卷。实现方式是保存一份有序的持久卷列表,对于每种访问模式按照容量升序排列,返回列表的第一个卷。
Kubelet
- Kubelet就是负责所有运行在工作节点上内容的组件。
- 它第一个任务是在API服务器中创建一个Node资源来注册该节点。然后需要持续监控API服务器是否把该节点分配给pod,然后启动pod容器。具体实现方式是告知配置好的容器运行时(Docker, CoreOS的Rkt,或者其他一些东西)来从特定容器镜像运行容器。Kubelet随后持续监控运行的容器,向API服务器报告它们的状态、事件和资源消耗。
Kubernetes Service Proxy
- 除了Kubelet,每个工作节点还会运行
kube-proxy
,用于确保客户端可以通过Kuberbetes API连接到你定义的服务 - kube-proxy确保对服务IP和端口的连接最终能到达支持服务的某个pod处。如果有多个pod支撑一个服务,那么代理会发挥对pod的负载均衡作用。
- kube-proxy当前仅仅通过iptables规则重定向数据包到一个
随机选择
的后端pod,而不会传递到一个实际的代理服务器。这个模式称为iptables代理模式
。
Kubernetes插件
-
kube-dns
pod利用API服务器的监控机制来订阅Service和Endpoint的变动,以及DNS记录的变更,使得其客户端(相对地)总是能够获取到最新地DNS信息。 -
Ingress控制器
运行一个反向代理服务器(例如类似nginx),根据集群中定义的Ingress、Service以及Endpoint资源来配置该控制器。所以需要订阅这些资源(通过监听机制),然后每次其中一个发生变化则更新代理服务器的配置。
控制器间的协作
-
事件链
-
控制平面组件和Kubelet执行动作时,都会发送事件给API服务器。发送事件是通过创建
事件资源
来实现的,事件资源和其它的Kubernetes资源类似。
运行中的pod
-
暂停的容器
是一个基础容器,它的唯一目的就是保存所有的命名空间。所有pod的其他用户定义容器使用pod的该基础容器的命名空间。
跨pod网络
- 每个pod有自己唯一的IP地址,可以通过一个扁平的、非NAT网络和其他pod通信。
- 网络是由系统管理员或者
Container Network Interface (CNI)
插件建立的,而非Kubernetes本身。 - pod用于通信的网络必须是:pod自己认为的IP地址一定和所有其他节点认为该pod拥有的IP地址一致。Kubernetes规定pod必须通过非NAT网络进行连接
同节点pod通信
- 基础设施容器启动之前,会为容器创建一个虚拟Ethernet接口对(
veth pair
)。一端放在主机的命名空间中,命名为veth***。一端放在新创建的容器命名空间中,命名为eth0。 - 主机网络命名空间的接口会绑定到容器运行时配置使用的
网络桥接
上。从网桥的地址段中取IP地址赋值给容器内的eth0接口。 - 应用的任何运行在容器内部的程序都会发送数据到eth0网络接口,数据从主机命名空间的另一个veth接口出来,然后发送到网桥。这意味着任何连接到网桥的网络接口都可以接收该数据。
- 节点上的所有容器都会连接到同一个网桥,意味着它们都能够互相通信。
不同节点上的pod通信
- 要让运行在不同节点上的容器之间能够通信,这些节点的网桥需要以某种方式连接起来。
- 有多种连接方式,包括overlay, underlay或常规的三层路由。
服务是如何实现的
- 和Service相关的任何事情都由每个节点上运行的kube-proxy进程处理。
- 当API服务器创建一个服务时,虚拟IP地址立刻会分配给它。之后很短时间内,kube-proxy会得到通知。每个kube-proxy都会让该服务在自己的运行节点上可寻址。原理是通过建立一些
iptables规则
,确保每个目的地为服务的IP/端口对的数据包被解析,目的地址被修改,这样数据包就会被重定向到支持服务的一个pod。
网友评论