在K8S实战笔记–4中,我们提到控制器会在节点失联后将节点上的Pod进行删除操作,这个控制器就是节点控制器。那么节点控制器的作用究竟是什么呢?
1. 节点控制器
节点控制器作为K8S集群中的重要组件,在节点的生命周期中主要起如下作用:
- 在节点注册时为其分配CIDR地址块;
- 监控节点的健康状态;
- 通过调用云供应商接口检查云主机的可用性。
在检查节点健康状况时,节点控制器使用心跳机制判断主机是否可用,在无法接收到心跳时,将该节点状态置为Unknow
,并在一个pod-eviction-timeout
后将其所有Pod进行驱逐。在通常情况下,节点控制器每次检查节点状态所间隔的时间称为--node-monitor-period
,而在40秒未接收到来自节点的心跳后,就会更改NodeStatus
。
从K8Sv1.13
开始,node lease
特性进入alpha阶段,在启用此特性后,节点上会存在一个kubectl-node-lease
名称空间,在此空间下的Lease
对象与NodeStatus
会被一同用于记录节点状态,节点控制器会周期性地更新Lease
对象,但并没有NodeStatus
频繁。通常节点在发出心跳信号后,NodeStatus
都会被更新,在此情况下,Lease
才会被更新,或是在一段大于节点心跳失联的时间(40秒)之后更新,通常为1分钟。在现如今的版本,若集群中的某一高可用区出现了故障,节点在执行Pod的驱逐之前,会检查集群中其他Pod的状态。通常节点控制器会限制Pod的驱逐速率为--node-eviction-rate
= 0.1/s(即10秒驱逐一个Pod),但在实际中,考虑到集群中的某一高可用区出现了故障而其他的高可用区仍然可用,可以将工作负载迁移到仍然可用的高可用区中。因此,在辨别集群规模后,若故障节点的比例不低于--unhealthy-zone-threshold
= 0.55,节点控制器驱除Pod的速率会适当降低或停止驱除Pod,辨别集群规模的参数为--large-cluster-size-threshold
,默认值为50。当然,若集群中某个高可用区完全挂掉,即该区所有节点都故障时,节点控制器驱逐Pod的速率依然不变。
在将kubelet启动参数--register-node
设为true
(默认也是true)后,kubelet会尝试将自己注册到APIServer中,当设置为false
时,管理员可以手动创建和修改API对象。
2. 集群通信
2.1 集群访问主节点
所有从集群对主节点的访问,都是针对APIServer的。通常在安装K8S时,APIServer监听HTTPS端口,并且配置了一种或多种客户端认证方式,并且至少需要配置一种形式的授权方式。在节点上必须配置APIServer的公钥根证书(public root certificate
),在进行身份认证之后,节点可以安全访问APIServer,对于需要调用APIServer接口的Pod,则需要为其关联Service Account。
2.2 主节点访问集群
从主节点访问集群其实就是APIServer访问集群的过程,主要有两种方式:
- APIServer访问节点的kubelet进程;
- 使用APIServer的proxy,访问node、Pod、Service。
通常情况下,APIServer访问kubelet时,无非以下几种情况:抓取Pod日志、使用容器终端操纵容器(kubelet exec -it
)、提供kubectl port-forward
功能。APIServer监听HTTPS接口,在一般情况下,APIServer不会校验kubelet的HTTPS证书,这是不安全的。如果需要校验证书,可以通过配置--kubelet-certificate-authority
为APIServer提供校验kubelet证书的根证书完成,但如果无法提供证书,但又需要通过不受信的网络将节点加入集群,则需要使用SSH链接APIServer与kubelet。在APIServer连接node、Pod、Service时,使用HTTP连接,这个过程没有提供安全措施,因此这类连接是无法保证一致性的,如果运行在不受信网络上,则这类连接的安全性是无法保证的。但K8S支持通过SSH隧道保护主节点到集群的访问路径,目前,SSH隧道已不推荐使用,K8S正在设计新的替代通信方式。
3. 如何管理K8S对象
在之前的学习中,我们已经对通过YAML部署发布应用有了一定的了解。kubectl命令行工具支持多种途径以创建和管理K8S对象,其中典型的有以下三种:
- 指令性的对象配置;
- 指令性的命令行方式;
- 声明式的对象配置。
之前在学习应用部署发布时通过使用YAML文件来使K8S集群创建对应Pod的方式,属于指令性的对象配置。常见的操作有:create
、replace
、apply
、delete
等,这些操作后需要跟可选参数以及至少一个的配置。这种操作对象的方式,通过调用单个配置文件,向kubectl传入完整的对象定义,这些配置文件可以是YAML类型,也可以是JSON格式。需要注意的是:在使用replace
操作时,会使用新的spec
内容替换原有的该内容,但这种方式并不能应用于spec
独立于配置文件进行更新的情况,例如,LoadBalancer
类型的Service,其spec
字段的externalIPs
将由集群进行更新。使用这种方式操作管理K8S对象,是较为便捷且较完善的一种。
指令性的命令行是最简单的对象管理方式,用户只需向kubectl提供操作参数,就可以直接操作集群中的对象,此时,无需用到YAML文件。
例如,使用命令行创建一个Deployment,运行一个Nginx容器:
kubectl create deployment nginx --image nginx
这种命令行的好处是简单方便,不需要书写YAML文件。但是,由于其是通过命令行实现的,没有配置审计日志,也没有办法查看之前的版本,是一次性任务。在希望集群集中执行一次任务时,可以使用这种简便的方法。
使用声明式的对象配置时,需要提供对象本地存储的对象配置文件,与指令性的对象配置方式不同的是,在将配置传递给kubectl时,并不指定操作,而是由kubectl自动检测每个对象的状态并决定该对其采取哪种操作。使用这种操作时,可以将一个文件夹下的多个文件交与kubectl处理。
例如,递归处理conf目录下的对象:
kubectl diff -R -f conf/
kubectl apply -R -f conf/
其中,diff
是用来查看即将进行的具体变更的,再使用apply
应用这些变更。
4. 名称空间
在同一个名称空间下,同一类型的资源可以通过name
唯一性确定。通常有以下三种使用最多的资源名称限制条例:
DNS Subdomain Names
:这个规则下的名称必须符合以下几个条件:- 最长不超过253个字符;
- 由小写字母、数字、
-
、.
组成; - 由字母开始,由字母结束。
DNS Label Names
:这个规则下的名称必须符合以下几个条件:- 最长不超过63个字符;
- 由小写字母、数字、
-
、.
组成; - 由字母开始,由字母结束。
Path Segment Names
:这个规则下的资源要求其名称可以被编码到路径中,即不能包含.
、..
、/
、%
。
UID用于在集群中唯一标识一个对象,是由K8S系统生成的,在集群中,某一对象被删除后,还可再次创建一个同名对象,但两次创建的对象UID不同。UID可以用于区分多次创建的同名对象,且是全局的唯一标识符。
名称空间在K8S集群中的作用是为了区分各个项目,或是划分不同的工作区。在集群中,差异不大的对象不必使用名称空间来区分,例如同一软件的不同版本,使用labels
区分即可。在同一名称空间下,相同类型的资源不能拥有相同的名称,但在跨名称空间下可以有同名同类型的对象。名称空间之间是独立的,任何一个对象不能同时属于多个名称空间,名称空间之间也不能有嵌套关系。在集群中,默认拥有三个名称空间:
default
:默认名称空间,通常在元数据定义中没有指定名称空间(metadata.namespace
)的情况下,创建的对象将存放在此处;kube-system
:用于存放K8S系统对象;kube-public
:这个名称空间是所有用户都可以访问的,作为集群的预留名称空间。
在我们执行请求时,可以对kubectl命令行工具传入--namespace
参数来指定名称空间,或是通过set-context
命令来改变kubectl上下文的名称空间,也就是设置名称空间偏好,设置偏好后,所有的命令都默认在偏好的名称空间下执行,格式如下:
kubectl config set-context --current --namespace=NEW NAMESPACE
但是并非所有的对象都存在于名称空间下。我们可以使用如下命令来查看具体哪些对象在名称空间中,哪些不在。
kubectl api-resources --namespaced=true #在名称空间下
kubectl api-resources --namespaced=false #不在名称空间下
使用describe
命令可以查看名称空间的详细信息。当我们想创建一个名称空间时,可以通过YAML或是命令行的方式完成:
- 使用命令行:
kubectl create namespace NAME
- 创建YAML文件并使用它:
apiVersion: v1 kind: Namespace metadata: name: NAME
kubectl create -f namespace.yaml
需要注意的是,名称空间的名称必须与DNS兼容,即不含.
、_
且不能使用数字,小写字母和-
组成的字符串。使用delete
命令删除名称空间,但这个操作是异步的,名称空间的状态会在Terminating
停留一段时间。
那么在企业中,如何使用名称空间切分集群使其达到互不干扰的效果呢?
- 创建名称空间:
apiVersion: v1 #dev.yaml kind: Namespace metadata: name: dev labels: lab: dev
使用命令查看结果:apiVersion: v1 #pro.yaml kind: Namespace metadata: name: pro labels: lab: pro
kubectl get namespace --show-labels
可以看到带有标签的两个名称空间已经创建完毕。NAME STATUS AGE LABELS default Active 16d <none> dev Active 5m28s lab=dev kube-node-lease Active 16d <none> kube-public Active 16d <none> kube-system Active 16d <none> kuboard Active 16d <none> nginx-ingress Active 16d <none> pro Active 5m15s lab=pro
- 查看当前kubectl上下文:
输出如下:kubectl config view
执行命令:apiVersion: v1 clusters: - cluster: certificate-authority-data: DATA+OMITTED server: https://testk8s:6443 name: kubernetes contexts: - context: cluster: kubernetes user: kubernetes-admin name: kubernetes-admin@kubernetes current-context: kubernetes-admin@kubernetes kind: Config preferences: {} users: - name: kubernetes-admin user: client-certificate-data: REDACTED client-key-data: REDACTED
输出:kubectl config current-context
创建新的上下文,其中kubernetes-admin@kubernetes
cluster
与user
的取值为current-context的输出:kubectl config set-context dev --cluster=kubernetes-admin@kubernetes --user=kubernetes-admin@kubernetes
kubectl config set-context pro --cluster=kubernetes-admin@kubernetes --user=kubernetes-admin@kubernetes
由此,我们创建好了两个名称空间,并为其建立了上下文,使用use-context
命令可以切换名称空间,不同名称空间下的内容互不影响。
总结
终于在熬夜后完成了对本次学习的记录,在学习Pod驱逐策略与集群通信时,仍然存在许多悬而未解的问题,在后续的学习中,仍然会继续完善此部分的相关内容。