创建一个Pod的流程
Kubernetes基于list-watch机制的控制器架构,实现组件间交互的解耦。
其他组件监控自己负责的资源,当这些资源发生变化时,kube-apiserver会通知这些组件,这个过程类似于发布与订阅
创建一个pod的工作流程
1、创建pod:kubectl run web --image=nginx
2、apiserver收到创建pod的请求,将相关资源写入到etcd中
3、scheduler收到apiserver的通之,为未分配pod根据调度算法选择一台合适的节点进行绑定,相当于做一个记号,例如namenode=k8s-node1,在响应给apiserver、apiserver存储到etcd
4、kubelet收到apiserver的通知,将分配到我自己节点上的pod通过docker api创建,并汇报状态给apiserver
5、kubectl get pods查看创建的pod
当然还有两个组件,controller-manager和kube-proxy
controller-manager控制器在创建pod过程中会参与进来,比如说创建一个deployment时,控制器就会在Scheduler之前进行干预
kube-porxy负责网络,当创建service时就会干预进来
Pod中影响调度的主要属性
Pod调度策略有两个----资源调度依据和调度策略
我简单的例出一些来
调度类型 | 参数 |
---|---|
资源调度依据 | resources:{} |
调度策略 | schedulerName: default-scheduler(这个可以自己开发也可以用k8s自有的) nodeName: " " nodeSelector: {} affinity:{} tolerations: {} |
资源限制
容器资源限制:
- resources.limits.cpu
- resources.limits.memory
容器使用的最小资源需求,作为容器调度时资源分配的依据: - resources.requests.cpu
- resources.requests.memory
CPU单位:可以写成浮点数,例如0.5=500m,1=1000m
资源限制就是通过:limits,它限制了容器最大使用的资源
资源请求:requests,相当于预留资源(实际不占用),用于合理分配(所有的node相当于是一个资源池,资源池利用率就是根据requests计算的)
案例
# 创建好这个pod后,查看节点的资源分配
[root@k8s-master1 ~]# kubectl describe nodes k8s-node2
nodeSelector & nodeAffinity
nodeSelector:用于将Pod调度到匹配Label的Node上,如果没有匹配的标签会调度失败。
他可以约束Pod到特定节点运行,完全匹配节点标签
一般用于专用节点(根据业务线将Node分组管理),配备特殊的硬件,容器需要使用这些资源(部分Node配备GPU等其他资源)
# 在创建pod时根据匹配到的标签进行去分配,所以,对应的node节点也是需要打上标签的
# 那么第一步就是对指定节点打上标签(假如说第三台机器有GPU,为第三台机器打上标签)
# 标签的键值对为gpu和ok,当然这个可以是随便起的
[root@k8s-master ~]# kubectl label nodes k8s-node2 gpu=ok
# 查看标签,可以看到k8s-node2是有gpu=ok的标签的
[root@k8s-master ~]# kubectl get node --show-labels
NAME STATUS ROLES AGE VERSION LABELS
k8s-master Ready control-plane,master 27m v1.21.0 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=k8s-master,kubernetes.io/os=linux,node-role.kubernetes.io/control-plane=,node-role.kubernetes.io/master=,node.kubernetes.io/exclude-from-external-load-balancers=
k8s-node1 Ready <none> 14m v1.21.0 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=k8s-node1,kubernetes.io/os=linux
k8s-node2 Ready <none> 14m v1.21.0 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,gpu=ok,kubernetes.io/arch=amd64,kubernetes.io/hostname=k8s-node2,kubernetes.io/os=linux
测试
# 创建一个pod,并指定标签
[root@k8s-master ~]# vim pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-nodeselector
spec:
nodeSelector:
gpu: "ok"
containers:
- image: nginx
name: web
resources: {}
# 构建Pod
[root@k8s-master ~]# kubectl apply -f pod.yaml
# 查看状态,会发现他跑到了k8s-node2上,但是这样子不一定可以体现出nodeselector特性来
[root@k8s-master ~]# kubectl get pods -o wide
# 可以把k8s-node2的标签去除,看看pod会怎么样,去除后已存在的popd还是正常运行的
[root@k8s-master ~]# kubectl label nodes k8s-node2 gpu-
# 那么我们删除掉原来的pod重新创建下看看他还会被分配么
[root@k8s-master ~]# kubectl delete -f pod.yaml
[root@k8s-master ~]# kubectl apply -f pod.yaml
# 可以看到pod状态为pending状态,使用describe命令查看日志时也会发现该pod没有被分配
[root@k8s-master ~]# kubectl get pods -o wide
# 在来给某个节点打上gpu=ok的标签,这次给node1打标签
[root@k8s-master ~]# kubectl label node k8s-node1 gpu=ok
#查看状态时就会发现pod被分配并且创建到了node1上
nodeAffinity:节点情和性,与nodeSelector作用一样,但相比更灵活,满足更多条件
- 匹配有更多的逻辑组合,不只是字符串的完全相等,支持的操作符:In、Notln、Exists、DoesNotExist、Gt、Lt
- 调度分为软策略和硬策略,而不是硬性要求
- 硬(required):必须满足
- 软(preferred):尝试满足,但不保证
硬性策略测试测试
# 现在官网复制一下实例,地址如下
# https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/
# 修改其内容
apiVersion: v1
kind: Pod
metadata:
name: with-node-affinity
spec:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/e2e-az-name
operator: In
values:
- e2e-az1
- e2e-az2
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 1
preference:
matchExpressions:
- key: another-node-label-key
operator: In
values:
- another-node-label-value
containers:
- name: with-node-affinity
image: k8s.gcr.io/pause:2.0
-------------------------------------------------------------------------
# 修改为
apiVersion: v1
kind: Pod
metadata:
name: with-node-affinity
spec:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution: # 硬性策略
nodeSelectorTerms:
- matchExpressions: # 这三行是固定格式,不需要去管他
- key: gpu # 标签的键
operator: In # 动作,表示这个节点上有key=values的值
values: # 标签的值,可以有多个值
- ok
containers:
- name: with-node-affinity
image: nginx
# 创建
[root@k8s-master ~]# kubectl apply -f pod-nodeaffinity.yaml
软性策略测试测试
软性策略即使没有匹配到对应的键值,他也会去分配
# 修改内容如下,然后进行创建
apiVersion: v1
kind: Pod
metadata:
name: with-node-affinity
spec:
affinity:
nodeAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 1 # 权重,只是会去优先分配,软性的不会像硬性那样固定
preference:
matchExpressions:
- key: gpu
operator: In
values:
- ok2
containers:
- name: with-node-affinity
image: nginx
Taint(污点)与Tolerations(污点容忍)
基于节点标签分配是站在Pod的角度上,通过在Pod上添加属性,来确定Pod是否要调度到指定的Node上,其实我们也可以站在Node的角度上,通过在Node上添加污点属性,来避免Pod被分配到不合适的节点上
- Taints:避免Pod调度到特定Node上
- Tolerations:允许Pod调度到持有Taints的Node上
添加污点的格式
一、给节点添加污点
格式:kubectl taint node [node] key=value:[effect]
例如:kubectl taint node k8s-node1 gpu=yes:NoSchedule
验证:kubectl describe node k8s-node1 | grep Taint
其中[effect]可取值
- NoSchedule:一定不能被调度(新来的不要来,在这儿的就别动了)
- PreferNoSchedule:尽量不要调度(尽量不要来,除非没办法)
- NoExecute:不仅不会调度,还会驱逐Node上已有的Pod(新来的不要来,在这里的也走)
二、添加污点容忍(tolrations)字段到Pod配置中
如果没有配置污点容忍的话,那么所有的Pod都不会被分配到有污点的节点上
(注意:master自带一个污点)
# 添加污点
[root@k8s-master ~]# kubectl taint node k8s-node1 gpu=yes:NoSchedule
# 在pod的yaml文件中配置污点容忍,使其可以调度掉有污点的主机上去
# 添加污点容忍可以去官网搜索taint即可
apiVersion: v1
kind: Pod
metadata:
name: pod-taint
spec:
tolerations:
- key: "gpu"
operator: "equal" # 这个操作符也有多个参数可以支持
effect: "NoSchedule"
value: "yes"
containers:
- image: nginx
name: web
resources: {}
可以去看看calico.yaml中的污点容忍的,他的匹配范围极大,只要有"NoSchedule"的就去容忍
# calico.yaml污点容忍配置如下
tolerations:
# Mark the pod as a critical add-on for rescheduling.
- key: CriticalAddonsOnly
operator: Exists
- key: node-role.kubernetes.io/master
effect: NoSchedule
三、去掉污点
kubectl taint node [node] key:[effect]-
nodeName
nodeName:指定节点名称,用于将Pod调度到指定的Node上,不经过调度器
apiVersion: v1
kind: Pod
metadata:
name: pod-taint
spec:
# 指哪儿打哪儿,因为不经过调度器,所以在配置nodename时即使该节点有污点,也会创建成功
nodename: k8s-node1
containers:
- image: nginx
name: web
resources: {}