11.2 控制器如何协作
11.2 控制器如何协作
现在你了解了Kubernetes集群包含哪些组件。为了强化对Kubernetes工作方式的理解,让我们看一下当一个pod资源被创建时会发生什么。因为一般不会直接创建pod,所以创建Deployment资源作为替代,然后观察启动pod的容器会发生什么。
11.2.1 了解涉及哪些组件
在你启动整个流程之前,控制器、调度器、Kubelet就已经通过API服务器监听它们各自资源类型的变化了。如图 11.11 所示。图中描画的每个组件在即将触发的流程中都起到一定的作用。图表中不包含etcd,因为它被隐藏在API服务器之后,可以想象成API服务器就是对象存储的地方。
img图11.11 Kubernetes组件通过API服务器监听API对象
11.2.2 事件链
准备包含Deployment清单的YAML文件,通过kubetctl提交到Kubernetes。kubectl通过HTTP POST请求发送清单到Kubernetes API服务器。API服务器检查Deployment定义,存储到etcd,返回响应给kubectl。现在事件链开始被揭示出来,如图 11.12 所示。
img图11.12 Deployment资源提交到API服务器的事件链
Deployment控制器生成ReplicaSet
当新创建Deployment资源时,所有通过API服务器监听机制监听Deployment列表的客户端马上会收到通知。其中有个客户端叫Deployment控制器,之前讨论过,该控制器是一个负责处理部署事务的活动组件。
回忆一下第9章的内容,一个Deployment由一个或多个Replicaset支持,ReplicaSet后面会创建实际的pod。当Deployment控制器检查到有一个新的Deployment对象时,会按照Deploymnet当前定义创建ReplicaSet。这包括通过Kubernetes API创建一个新的ReplicaSet资源。Deployment控制器完全不会去处理单个pod。
ReplicaSet控制器创建pod资源
新创建的ReplicaSet由ReplicaSet控制器(通过API服务器创建、修改、删除ReplicaSet资源)接收。控制器会考虑replica数量、ReplicaSet中定义的pod选择器,然后检查是否有足够的满足选择器的pod。
然后控制器会基于ReplicatSet的pod模板创建pod资源(当Deployment控制器创建ReplicaSet时,会从Deployment复制pod模板)。
调度器分配节点给新创建的pod
新创建的pod目前保存在etcd中,但是它们每个都缺少一个重要的东西——它们还没有任何关联节点。它们的nodeName属性还未被设置。调度器会监控像这样的pod,发现一个,就会为pod选择最佳节点,并将节点分配给pod。pod的定义现在就会包含它应该运行在哪个节点。
目前,所有的一切都发生在Kubernetes 控制平面中。参与这个全过程的控制器没有做其他具体的事情,除了通过API服务器更新资源。
Kubelet运行pod容器
目前,工作节点还没做任何事情,pod容器还没有被启动起来,pod容器的图片还没有下载。
随着pod目前分配给了特定的节点,节点上的Kubelet终于可以工作了。Kubelet通过API服务器监听pod变更,发现有新的pod分配到本节点后,会去检查pod定义,然后命令Docker或者任何使用的容器运行时来启动pod容器,容器运行时就会去运行容器。
11.2.3 观察集群事件
控制平面组件和Kubelet执行动作时,都会发送事件给API服务器。发送事件是通过创建事件资源来实现的,事件资源和其他的Kubernetes资源类似。每次使用kubectl describe来检查资源的时候,就能看到资源相关的事件,也可以直接用 kubectl get events
获取事件。
可能是个人的感受,使用 kubectl get
检查事件比较痛苦,因为不是以合适的时间顺序显示的。当一个事件发生了多次,该事件只会被显示一次,显示首次出现时间、最后一次出现时间以及发生次数。幸运的是,利用--watch选项监听事件肉眼看起来更简单,对于观察集群发生了什么也更有用。
下面的代码清单展示了前述过程中发出的事件(由于页面空间有限,有些列被删掉了,输出也做了改动)。
代码清单11.9 观察控制器发出的事件
$ kubectl get events --watch
LAST SEEN TYPE REASON OBJECT MESSAGE
1s Normal SuccessfulDelete statefulset/kubia delete Pod kubia-2 in StatefulSet kubia successful
1s Normal Killing pod/kubia-2 Stopping container kubia
如你所见,SOURCE列显示执行动作的控制器,NAME和KIND列显示控制器作用的资源。REASON列以及MESSAGE列(显示在每一项的第二行)提供控制器所做的更详细的信息。
11.3 了解运行中的pod是什么
当pod运行时,让我们仔细看一下,运行的pod到底是什么。如果pod包含单个容器,你认为Kubelet会只运行单个容器,还是更多?
读本书的过程中,你已经运行过多个pod了。如果你是个喜欢深究的人,那么你可能已经看过,当你创建一个pod时实际运行的Docker。如果没有,让笔者为你解释。
想象你运行单个容器的pod,假设创建了一个Nginx pod:
$ kubectl run nginx --image=nginx
此时,可以ssh到运行pod的工作节点,检查一系列运行的Docker容器。笔者用的是Minikube,所以使用minikube ssh来ssh到单个节点。如果你用GKE,可以通过gcloud compute ssh 来ssh到一个节点。
一旦进入节点内部,可以通过docker ps命令列出所有运行的容器,如下面的代码清单所示。
代码清单11.10 列出运行的Docker容器
$ minikube ssh
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
ca79c87d2e7d nginx "/docker-entrypoint.…" 7 minutes ago Up 7 minutes k8s_nginx_nginx
e1f8806b5a88 k8s.gcr.io/pause:3.4.1 "/pause" 8 minutes ago Up 8 minutes k8s_POD_nginx_custom
注意 笔者已经把不相关的信息(包含列和行)从前面的代码清单中删除了,也删除了所有其他运行的容器。如果你自己尝试该命令,注意几秒前创建的两个容器。
如你所望,你看到了Nginx容器,以及一个附加容器。从COMMAND列判断,附加容器没有做任何事情(容器命令是″pause″)。如果仔细观察,你会发现容器是在Nginx容器前几秒创建的。它的作用是什么?
被暂停的容器将一个pod所有的容器收纳到一起。还记得一个pod的所有容器是如何共享同一个网络和Linux命名空间的吗?暂停的容器是一个基础容器,它的唯一目的就是保存所有的命名空间。所有pod的其他用户定义容器使用pod的该基础容器的命名空间。
[图片上传失败...(image-2c10b2-1627868835968)]
图11.13 一个双容器pod有 3个运行的容器,共享同一个Linux命名空间
实际的应用容器可能会挂掉并重启。当容器重启,容器需要处于与之前相同的Linux命名空间中。基础容器使这成为可能,因为它的生命周期和pod绑定,基础容器pod被调度直到被删除一直会运行。如果基础pod在这期间被关闭,Kubelet会重新创建它,以及pod的所有容器。