K8S技术手册(三)
K8S技术手册
假设集群有 2 个 node 节点,其中一个有 pod,另一个则没有,那么新的 pod 会被调度到哪个节点上?
调度过程需要经过一系列处理阶段:
各个阶段的配置、以及 Pod 的配置(强制调度、亲和性、污点容忍等等)可能都会影响调度的结果。
然而,在不考虑各种配置、节点资源也相同的情况下,默认插件 NodeResourcesFit
的评分策略会起到比较关键的作用,NodeResourcesFit
有三种评分策略:LeastAllocated
(默认,优先选择资源使用率最低的节点)、MostAllocated
(优先选择资源使用率较高的节点)和 RequestedToCapacityRatio
(平衡节点的资源使用率)。这也就是说,在使用 MostAllocated
策略的时候,新的 pod 会被调度到已经有 pod 的节点上,而使用另外两种策略时,则调度到没有 pod 的节点。
应用程序通过容器的形式运行,如果 OOM(Out-of-Memory)了,是容器重启还是所在的 Pod 被重建?
当容器 OOM 了,一般情况下根据 Pod 的 RestartPolicy
配置(默认是 Always),容器会被重启,Pod 不会被重建。但在特殊情况下,节点内存压力很大时,可能会触发 Pod 驱逐,从而导致 Pod 被重建。
应用程序配置如环境变量或者 ConfigMap
可以不重建 Pod 实现动态更新吗?
环境变量无法动态更新,ConfigMap
通过挂载的方式可以动态更新,但要求挂载时不能使用 subPath
,挂载的同步延时受 kubelet 的配置 syncFrequency
(默认 1 分钟)和 configMapAndSecretChangeDetectionStrategy
影响。
Pod 被创建后是稳定的吗,即使用户不进行任何操作?
即使用户不操作,也可能发生节点资源不足或者网络异常导致 Pod 被驱逐。
使用 ClusterIP
类型的 Service
能保证 TCP 流量的负载均衡吗?
ClusterIP
类型的 Service
不论使用的是 iptables
还是 ipvs
,其都依赖于 Linux 内核的 Netfilter,而其内部的 connection tracking
机制会跟踪和记录每个连接的状态,进而让已经建立了 TCP 连接的双方一直使用这条连接。所以,针对长连接,是有可能出现负载不均衡的情况。
应用日志要怎么采集,会不会有丢失的情况?
应用日志一般输出为 stdout/stderr
,或者写入日志文件。针对前者,容器日志会被保存到节点上的特定位置,此时可以使用日志代理(如 Fluentd
、Filebeat
)并部署为 Daemonset
的方式进行采集,但是存在日志丢失的可能,因为一旦 pod 被删除,对应的容器日志文件也会被删除,而日志代理可能还未完成全部日志的采集。通过挂载持久化存储并写入日志文件的方式则可以避免丢失。
某个 HTTP Server Pod 的 livenessProbe 正常是否就一定没问题?
站在应用的角度看,livenessProbe
只检查应用是否存活,无法验证其功能是否正常,例如应用可能进入了非健康但仍存活的状态。站在网络的角度看,livenessProbe
(假设配置的是 httpGet)是由本机的 kubelet 发起的请求,无法保证跨节点的网络是正常的。
应用程序如何扩展以应对流量波动的情况?
Kubernetes 提供了水平扩展(HPA
)和垂直扩展(VPA
)两种机制,由于 VPA
不是原地资源扩展,会删除 Pod 再创建,使用场景往往受限,所以使用更多是 HPA
,可以根据指标(如 CPU 使用率、请求速率、其它自定义指标)动态调整 Pod 数量。当然,也可以在外部监测指标然后向 kube-apiserver 发起请求扩展 Pod 的数量。
当你执行 kubectl exec -it <pod> -- bash
之后是登录到了 pod 里吗?
首先,使用 kubectl exec
需要指定容器,当 Pod 只有一个容器时则可以忽略。其次,Pod 是一组独立的 Linux 命名空间,容器本质上是一个进程,Pod 内容器共享 Network、IPC、UTS namespace,而每个容器的 PID、Mount namespace 则是独立的。"登录"的说法并不准确,kubectl exec -it <pod> -- bash
既不是进入了 Pod,也不是进入了容器,而是在目标容器的隔离环境中创建了一个新的 bash 进程。
如果 Pod 里的容器反复退出并重启,如何排查?
如果 Pod 里的容器反复退出并重启,是无法使用 kubectl exec
的。此时,除了检查节点或容器状态和日志之外,还可以使用 kubectl debug
的方式在 Pod 上启动一个临时容器,用于检查环境和依赖。
K8S中pod节点指派
k8s中又四种方式实现pod定向调度:
- 与节点标签匹配的nodeSelector
- 亲和性和反亲和性
- nodeName字段
- POD拓扑分布约束
NodeSelector
nodeSelector是最简单的一种节点选择器,将nodeSelector
字段添加到 Pod 的规约中设置目标节点所具有的节点标签。 Kubernetes 只会将 Pod 调度到拥有你所指定的每个标签的节点上。
亲和性和反亲和性
亲和性功能由两种类型的亲和性组成:
- 节点亲和性功能类似于
nodeSelector
字段,但它的表达能力更强,并且允许你指定软规则。 - Pod 间亲和性/反亲和性允许你根据其他 Pod 的标签来约束 Pod。
节点亲和性
节点亲和性概念上类似于 nodeSelector
, 它使你可以根据节点上的标签来约束 Pod 可以调度到哪些节点上。 节点亲和性有两种:
requiredDuringSchedulingIgnoredDuringExecution
: 调度器只有在规则被满足的时候才能执行调度。此功能类似于nodeSelector
, 但其语法表达能力更强。preferredDuringSchedulingIgnoredDuringExecution
: 调度器会尝试寻找满足对应规则的节点。如果找不到匹配的节点,调度器仍然会调度该 Pod。
Pod 间亲和性与反亲和性
Pod 间亲和性与反亲和性的规则格式为“如果 X 上已经运行了一个或多个满足规则 Y 的 Pod, 则这个 Pod 应该(或者在反亲和性的情况下不应该)运行在 X 上”。 这里的 X 可以是节点、机架、云提供商可用区或地理区域或类似的拓扑域, Y 则是 Kubernetes 尝试满足的规则。
Pod 的亲和性与反亲和性也有两种类型:
requiredDuringSchedulingIgnoredDuringExecution
preferredDuringSchedulingIgnoredDuringExecution
Pod 间亲和性与反亲和性为其
operator
字段使用In
、NotIn
、Exists
、DoesNotExist
nodeName
nodeName
是比亲和性或者 nodeSelector
更为直接的形式。nodeName
是 Pod 规约中的一个字段。如果 nodeName
字段不为空,调度器会忽略该 Pod, 而指定节点上的 kubelet 会尝试将 Pod 放到该节点上。 使用 nodeName
规则的优先级会高于使用 nodeSelector
或亲和性与非亲和性的规则。
使用 nodeName
来选择节点的方式有一些局限性:
- 如果所指代的节点不存在,则 Pod 无法运行,而且在某些情况下可能会被自动删除。
- 如果所指代的节点无法提供用来运行 Pod 所需的资源,Pod 会失败, 而其失败原因中会给出是否因为内存或 CPU 不足而造成无法运行。
- 在云环境中的节点名称并不总是可预测的,也不总是稳定的。
Pod 拓扑分布约束
你可以使用 拓扑分布约束(Topology Spread Constraints) 来控制 Pod 在集群内故障域之间的分布, 故障域的示例有区域(Region)、可用区(Zone)、节点和其他用户自定义的拓扑域。 这样做有助于提升性能、实现高可用或提升资源利用率。
操作符
下面是你可以在上述 nodeAffinity
和 podAffinity
的 operator
字段中可以使用的所有逻辑运算符。
操作符 | 行为 |
---|---|
In | 标签值存在于提供的字符串集中 |
NotIn | 标签值不包含在提供的字符串集中 |
Exists | 对象上存在具有此键的标签 |
DoesNotExist | 对象上不存在具有此键的标签 |
节点亲和性和污点,容忍度区别
节点亲和性 是 Pod 的一种属性,它使 Pod 被吸引到一类特定的节点 。 污点(Taint) 则相反——它使节点能够排斥一类特定的Pod。
容忍度(Toleration) 是应用于 Pod 上的。容忍度允许调度器调度带有对应污点的 Pod。 容忍度允许调度但并不保证调度:作为其功能的一部分, 调度器也会评估其他参数。
污点和容忍度(Toleration)相互配合,可以用来避免 Pod 被分配到不合适的节点上。 每个节点上都可以应用一个或多个污点,这表示对于那些不能容忍这些污点的 Pod, 是不会被该节点接受的。
给节点添加一个污点
//给节点上添加污点
kubectl taint nodes node1 key1=value1:NoSchedule
//删除节点上的污点
kubectl taint nodes node1 key1=value1:NoSchedule-
给节点 node1
增加一个污点,它的键名是 key1
,键值是 value1
,效果是 NoSchedule
。 这表示只有拥有和这个污点相匹配的容忍度的 Pod 才能够被分配到 node1
这个节点。
默认的 Kubernetes 调度器在选择一个节点来运行特定的 Pod 时会考虑污点和容忍度。 然而,如果你手动为一个 Pod 指定了 .spec.nodeName
,那么选节点操作会绕过调度器; 这个 Pod 将会绑定到你指定的节点上,即使你选择的节点上有 NoSchedule
的污点。 如果这种情况发生,且节点上还设置了 NoExecute
的污点,kubelet 会将 Pod 驱逐出去,除非有适当的容忍度设置。
污点和容忍度作用:通过污点和容忍度,可以灵活地让 Pod 避开某些节点或者将 Pod 从某些节点驱逐。