k8s 自定义dns_关于K8S的一些整理 其一 (理论 & 一些配置)
pod的意义容器设计本身是一种 “单进程” 模型. 由于容器的应用等于进程. 所以只能去管理pid=1的进程, 其他再起来的应用属于托管状态. 应用本身一般是不具备 管理容器里其他进程的能力. 如果通过exec 或 ssh在容器中创建的其他进程 异常退出时 很容易变成孤儿进程pod 相当于一个进程组的概念pod内的资源共享设置: 共享网络 :k8s会在po...
pod的意义
容器设计本身是一种 “单进程” 模型. 由于容器的应用等于进程. 所以只能去管理pid=1的进程, 其他再起来的应用属于托管状态. 应用本身一般是不具备 管理容器里其他进程的能力. 如果通过exec 或 ssh在容器中创建的其他进程 异常退出时 很容易变成孤儿进程
pod 相当于一个进程组的概念
pod内的资源共享设置:
共享网络 :
k8s会在pod中 额外起一个 Infra container 容器来共享整个pod的network namespace .
Infra container 是永远处于 “pause”状态的容器
其他容器会通过 join namespace的方式加入到Infra容器的 network namespace中的 共享网络
所以 一个pod里面 多个容器看到的网络是一样的 (设备, ip地址, mac地址等)
共享存储:
volume 变成了 Pod level. 所有容器. 属于同一个pod的容器 共享所有的volume
静态pod:
不通过apiserver 管理。无法与 rc, deploy, daemonset 进行关联。直接由kubelete创建管理。kubelet无法对他们做健康检查
kubelet 参数中用 --config 指定配置文件目录。kubelet定期扫描 并根据 .yaml 或 .json 创建 配置文件的内容和普通pod yaml文件一样
在 master上尝试删除 pod会变成pending 在pod所在机器 删除对应配置文件即可
--manifest-url : kubelet 会定期从这个url下载配置文件创建
静态pod无法使用configmap
DNS
1.11 版本开始 使用 CoreDNS 提供 COREdns 的配置
POD级别dns配置 :
通过 spec.dnsplicy 字段设置dns策略.
default : 继承pod所在的宿主机的dns设置
clusterfirst : 优先使用k8s环境dns服务 (如 coredns) . 无法解析的域名 用宿主机的dns配置
clusterFirstWithHostNet : 与 ClusterFirst 相同. 对于以 hostNetwork模式运行的Pod . 明确指定使用该策略
None : 忽略k8s环境dns配置. 通过 spec.dnsConfig自定义dns配置. 1.14版本新特性
自定义配置 : 这三个自定义配置是可以同时使用的
nameservers : 一组dns服务器的列表. 最多可以设置三个
searches : 一组用于域名搜索的dns域名后缀. 最多可以设置6个
options : 配置其他可选dns参数. 如 ndots, timeout等. k/v形式存储
Init Container 初始化容器 在 spec.initcontainers 中定义
用于在启动应用容器之前启动一个初始化容器 完成所需要的预置条件 , 并且必须在成功执行完成后 才能执行下一个容器. 多个init container 按顺序逐个进行. init 也可以资源限制. volume 等
多个init 都定义资源限制时 取最大值
pod的资源限制 取 1.所有应用容器资源限制和 2. init的有效限制值 最大值
调度算法基于pod的资源限制进行计算. 即 init 可以作为初始化操作预留系统资源. 即使后续应用无需使用这些资源
pod的有效qos等级适用 init 和应用容器
资源配额限制将根据pod有效资源配额计算
init 不能设置readinessprobe探针.
pod重新启动时 init 将重新运行. 常见pod重启场景如下 :
1.init镜像被更新时. init 将会重新运行.导致pod重启. 仅更新应用容器镜像只会使得应用容器被重启
2.pod的 infrastructure 容器更新时 pod 重启. (疑问: 什么是infrastructure)
3. pod的所有应用容器全部终止. 并且 restartpolicy = always . 则pod会重启
POD 的生命周期和重启策略
Pending | apiserver 已经创建该pod . 但pod内还有一个或多个容器的镜像没有创建. 包括处于正在下载镜像的过程 |
Running | pod内容器均已创建. 且至少有一个容器处于运行状态. 正在启动或正在重启状态 |
Succeeded | pod内所有容器均成功执行后退出. 且不会再重启 |
Failed | pod内所有容器均已退出 . 但至少有一个容器退出为失败状态 |
Unknown | 由于某种原因无法获取该pod状态. 可能由于网络不通 |
POD的重启策略 :
Always | 当容器失效时 由kubelet 自动重启该容器 |
OnFailure | 当容器终止运行且退出码 不为0 时 由kubelet 重启 |
Never | 不论运行状态如何 都不会重启 |
kubelet 重启失效的容器的时间间隔以 sync-frequency乘以 2n来继续算. 最长延迟5min. 并且在成功重启后的10min后重置该时间
各控制器对Pod的重启策略要求
rc & daemonset | 必须设置为 always . 需要保证容器持续运行 |
job | onfailure 或never . 确保容器执行完成后不会重启 |
kubelet ? | 在pod失效时自动重启 不论将restartpolicy设置为什么值. 都不会对pod进行健康检查 |
POD 健康检查和服务可用性检查
liveness | 判断是否会触发重启策略 , 会触发重启策略 |
readiness | 就绪性指针 — faild 不会接流量 |
readinessGates | 将自定义的readinessprobe探测方式设置在pod上. k8s将在判断全部readinessGates条件为True时 才设置pod为服务可用状态 — 1.14 发布的新特性 |
探测结果
success | |
failure | 没有通过检查 |
Unknown | 当前的判断机制 没有执行完成. 如超时等. 此时 liveness / readiness 都不会做操作. 等待下次一判断 |
pod的调度策略
pod &pod的关系
PodAffinity 与 PodAntAffinity 亲和/反亲和
pod与Node的关系调度
NodeSelector
常规性调度, 无法满足优先调度的策略
NodeAffinity 支持 In, NotIn, Exists, DoesNotExist, Gt, Lt 等
设置pod的节点亲和性
RequiredDuringSchedulingIgnoredDuringExecution | 必须满足制定的规则才可以调度pod到node上 |
PreferredDuringSchedulingIgnoredDuringExecution | 强调有限满足制定规则. 不强求. 多个优先级可以设置权重. 以定义执行的先后顺序 |
IgnoredDuringExecution | 如果一个Pod所在的节点在pod运行期间标签发生了变化. 不再符合该Pod的节点亲和性需求. 则系统忽略label的变化. 该pod能继续在该节点运行 |
如果同时定义了nodeSelector 和 nodeAffinity 必须两个条件都能满足. Pod才能运行在node上
如果定义了多个nodeSelectorTerms. 其中一个能够匹配即可
如果在 nodeSelectorTerms中有多个matchExpressions. 一个节点必须满足所有matchExpressions 才能运行该pod
Taint & toleration
kubectl taint nodes =:NoSchedule
NoSchedule | |
PreferNoSchedule | 尽量不调度到该node节点 |
NoExecute | 如果pod已经在该节点运行. 则会被驱逐. 如果没有运行. 则不会再被调度 |
Toleration声明中的key和effect需要与Taint的设置保持一致. 且满足以下条件:
operator的值必须是Exists (无需制定 value)
operator的值是Equal 并且value相等. 如果不制定operator 默认为quial
特例:
空的key配合exists操作能够符合所有的k/v
effect 设为空 容忍所有污点
node 允许设置多个taint, pod允许设置多个Toleration, k8s处理多个taint的逻辑顺序为 :
1.列出所有的taint. 忽略pod的toleration能够匹配的部分. 剩下的没有忽略的taint 就是对pod的效果
tolerationSeconds : 表明pod可以在taint添加到node之后还能再该node上运行多久
pod的调度
扩展资源的限制 只能填 int 如GPU
基础调度
三个Qos级别
Guaranteed | CPU/Memory 必须 request == limit 其余资源可不相 |
Burstable | CPU/Memory request != limit |
BestEffort | 所有资源都不填写 request/limit |
三个级别的调度表现
调度器调度时只会根据 request的情况调度, limit的值不会影响调度行为
Cpu根据request进行调度
Memory 按 QoS划分 OOMScore
Guaranteed == -998
Burstable 2~999 根据内存设计的大小和节点关系来分配具体分数
BestEffort 1000
Eviction — 驱逐策略
优先BestEffort
OOM 计分系统
计分规则 : 系统发生OOM时, OOM killler you 先杀掉 OOM积分更高的进程
进程占用内存在系统中占的百分比 %10 作为基础分 +
进程的 OOM_SCORE_ADJ
如果进程的OOM_SOCRE_ADJ 为999 这类进程不会被 OOM
本地磁盘是 BestEffort 资源 :
有Imagefs情况
如果nodefs触发. kubelet根据nodefs使用情况对pod进行排序
如果imagefs触发 . kubelet 根据pod中所有容器消耗的可写入层的使用空间进行排序
没有imagefs情况
如果nodefs触发. kubelet对每个pod中所有容器的总体磁盘消耗 (本地卷+ 日志+容器写入层占用空间) 进行排序
nodefs文件系统达到了阈值. kubelet按照以下顺序清理空间
删除死掉的Pod, 容器
删除所有无用的镜像
kubelt的参数 cpu-manager-policy=static 时 对于Guaranteed, 如果CPUrequest整数? 会对该级别pod做cpu的绑定
高级调度 — 集群资源不够时的策略
默认开启的特性
通过以下维度定义 pod 重要程度
Priority : 优先级
Qos : 服务质量等级
系统定义的其他度量指标
优先级抢占调度策略的核心行为 分别是 驱逐 Eviction 与 抢占 Preemption . 使用场景不同 但效果相同
Eviction : kubelet 的行为. 当一个node发生资源不足时. 执行驱逐. 根据pod优先级, 资源申请量与实际使用量等信息计算那些pod需要被驱逐. 相同优先级pod需要被驱逐时. 实际使用资源量超过申请量最大倍数的首先驱逐.
Preemption : scheduler行为. 当一个pod因为资源无法满足不能被调度时, scheduler 选择驱逐部分低优先级pod满足pod的调度目标
优先级调度
Priority
Preemption
在Pod的待调度队列中 Schedule 会优先安排调度高优先级的Pod到Node上
优先级抢占
优先级抢占策略
Pod的驱逐机制 -- 资源紧缺时的pod的驱逐机制
kubelet 持续监控主机资源使用情况. 一旦出现资源紧缺现象. 会主动终止一个或多个Pod运行. 当Pod被终止时. 其中所有容器会全部停止. Pod的状态会被设置为Failed
驱逐信号 : kubelet以来这些信号作为决策依据来触发驱逐行为
驱逐方式
软驱逐 | 由驱逐阈值 和 设定的宽限期 共同定义 如果定义了 Pod的宽限期. kubelet 用 pod.Spec.TerminationGracePeriodSeconds 和 最大宽限期 的 最小值进行宽限 软阈值定义相关参数 —eviction-soft : 描述驱逐阈值 如 memory.available<1.5GiB —eviction-soft-grace-period : 驱逐宽限期 . 达到软阈值后持续时间超过多久才进行驱逐 —eviction-max-pod-grace-period : 到达软阈值后 终止Pod的最大宽限期线 |
硬驱逐 | 到达阈值 kubelet 立即杀掉Pod进行回收 --eviction-hard=memory.available<1.5GiB |
驱逐监控频率 : --housekeeping-interval
kubelet 对每种资源定义 minimum-reclaim . 一旦开始驱逐. 回收不少于这个参数定义的资源数量
—eviction-pressure-transition-period 默认为五分钟 . kubelete设置脱离压力状态之前需要等待的时间 . 防止 节点在软阈值上下抖动. 又没超过宽限期的情况
节点资源紧缺情况下的系统行为
node资源紧缺时. node向master报告情况. scheduler 不再继续向该节点调度新的pod
对应行为 : MemoryPressure , DiskPressure, Node 的 OOM 行为
目前的缺陷
kubelet无法及时观测到内存压力. kubelet 从 cAdvisor 定时获取内存期间内 发生快速增长. 且 kubelet无法观察到MemoryPressure . 则可能会出发OOMKiller
主动驱逐保护
PodDisruptionBudget 资源对象. 通过labelselector 作用于同一组被同一个控制器管理的Pod . 作用是保重被驱逐的Pod 在同一时间不会停掉太多Pod 保证服务的稳定性
主要使用场景
节点的维护或升级
对应用的自动缩容操作
使用方法
创建了 PodDisruptionBudget 后 通过 Pod的驱逐Api 手动curl 来完成 . 如果Pod数量少于这个资源指定的数量的时候再进行删除命令 会返回错误
Requests 和 Limits 的背后机制 :
spec.container[].resources.requests.cpu : 转化为 core 数 .* 1024 结果作为 --cpu-shares参数传递给docker run 命令
spec.container[].resources.limits.cpu : 转化为millicore数. *100000 再除以1000 作为 --cpu-quota传递给docker run.
run命令中另一个参数 --cpu-period 默认设置为 100000 .表示docker 重新计量和分配cpu的使用时间间隔为 100000μs (100ms)
--cpu-quota 和 --cpu-period 参数一起配合完成对 容器cpu的使用限制 :
例 : 如limits 为 0.1. --cpu-quota 为 10000, --cpu-period为 1000000. 那么 在 100ms内最多给该容器分配 10ms*core的计算资源用量
kubelet 参数 --cpu-cfs-quota 默认为true . 强制要求所有pod 必须配置 cpu limits . ( 如果pod没有配置. 则集群提供默认配置也可以 )
spec.container[].resources.requests.memory : 只提供给调度器作为管理和调度的依据 . 不作为任何参数传递给docker
spec.container[].reousrces.limits.memory : 转发为 bytes单位整数 作为 --memory 参数传递给docker
资源管理 :
没有为 pod 设定 cpu request 与memory request时 k8s会默认认为 该pod 所需要的资源很少. 并可以将其调度到任何可用的node上 . 并且pod 可以使用所有可用 cpu和内存
spec.container.reousrces. 资源限制 是针对容器级别
k8s提供了 对pod级别的 Requests / Limits 配置 . 定义为pod之下容器资源使用总和
如果 一个node上 运行的pod的实际使用资源很低. 但是 pod的Reuqests 总和很高. 再加上 待调度的Pod的Requests值. 如果超过node提供的资源上限 不会往这上面调度
资源配额管理
ResourceQuota — 限制 namespace的资源用量
如果某ns资源使用超过限制. 在 之后创建&更新的请求会返回 403 并携带详细的出错原因
资源配额 与 集群资源总量 是完全独立的关系 . 增加/减少节点 不影响资源配额的配置
配置 Resource Quotas时 . 必须设置 Requests 或 Limits. 否则配额系统可能会直接拒绝Pod创建. 也可以使用LimitRange机制来为没有配置资源的Pod 提供默认资源管理
使用注意事项 :
如果集群总资源小于各ns资源配额总和 造成资源竞争时 . k8s遵循先到先得
在 kube-apiserver 的 --admission-control参数值中添加ResourceQuota参数开启. 在某个ns定义中存在ResourceQuota. 对于该ns 资源配额就是开启的 . 一个ns 可以拥有多个资源配额配置项
资源配额配置项 :
计算资源类
如果配额指定了 {requests | limits}.{cpu | memory} 则 强制要求每个容器都制定配置自己的限制. 或使用 LimitRange 提供的默认值
存储类
对象数量配额
资源配额作用域
每项配额可单独配置作用于. 仅对作用于内的资源进行计量和限制
BestEffort 通过限定资源配额追踪pod资源使用
其他三种用于追踪以下几种资源使用 :
计算资源类 和 Pods的使用
Requests 和 Limits 针对不同资源类型的限制机制和差异
可压缩资源 : CPU
Pod 没有超过 Limits 限制. 但 Node 上宿主机资源不够时 :
剩余CPU资源 pod 按照设定比值 进行分配
Pod 超出了 Limits 配置 . cgroups 会对pod中的容器cpu进行限流. 如果没有Limits 会尝试抢占所有空闲CPU资源. k8s 默认开启 --cpu-cfs-quota . 因此默认情况下必须配置 Limits
不可压缩资源 : 内存
如果 Pod 内存用量超过了 Limits 设置. kernel 会杀掉 Pod中 占用内存最多的一个. 直到内存不会超过 Limits
Pod A 使用了超过 Requests 不到 Limits 的内存量. 同一个Node B目前使用远少于Requests的内存. 但是突增内存需求 . 向node申请时可能会杀掉PodA
Pod A 使用了超过 Requests 不到 Limits 的内存量. 新调度的Pod 需要内存时 也可能会杀掉Pod A 来释放内存资源
pod 的调度过程
创建pod的大致流程
yaml提交到apiser, apiserver 通过webhook 给 Controllers 进行校验, 生成pod, 此时生成的pod, nodename为空, phase是pending状态. Scheduler watch到 pod的nodename为空后, 通过一系列调度算法, 决定node后会更新pod的nodename, kubelet watch到pod是属于自己节点后 把pod拿到自己节点进行操作, 包括创建容器storage 及 network等, 且把pod状态更新为 Running
kubelet 创建 修改pod步骤
为pod创建数据目录
从apiserver读取该pod清单
为pod挂载外部卷
下载pod用到的secret
检查已经运行在节点的pod . 如果pod没有容器或pause容器没有启动. 则先停止pod所有容器的进程. 如果pod中有需要删除的容器. 则删除容器
用pause镜像为每个pod创建一个容器.
为pod中的每个容器做以下处理
计算容器hash值. 去查询对应docker容器hash值. 若值不一样. 停止容器进程.停止关联的pause容器进程
若容器终止. 且没指定restartPolicy 不做任何处理
调用docker client 下载容器镜像
Scheduler 打分标准
node要满足 pod的资源需求
满足pod的一些特殊关系要求
满足node的一些限制要求
整个集群资源的合理利用
调度算法流程
通过Informer去watch需要等待的pod数据, 放到队列, 通过调度算法流程, 循环从队列拿数据, 经过调度流水线
调度器从队列拿到Pod进入Schedule Thread流程, 通过 Pre Filter —Filter —Post Filter — Score — Reserve
基本流程结束后把任务提交给 Wait Thread 及 Bind Thread, 然后Schedule Theread 继续执行流程. 做下一个调度
调度完成后, 会去更新 Schedule Cache
如Pod数据缓存, 更新Node数据
调度流水线的组成
调度器的调度流程
wait流程
bind流程
更多推荐
所有评论(0)