Kubernetes 节点调度
一、Kubernetes nodeSelector
labels在k8s中是一个很重要的概念,作为一个标识,Service、Deployment和pods之间的关系都是通过label来实现的。而每个节点也都拥有label,通过设置label相关的策略可以使得pods关联到对应的label的节点上。
1、查看节点标签
# kubectl get nodes --show-labels #通过--show-labels可以查看当前节点nodes的labels
NAME LABELS
huoban-k8s-api-pod1 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=huoban-k8s-api-pod1,kubernetes.io/os=linux,type=API
huoban-k8s-api-pod2 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=huoban-k8s-api-pod2,kubernetes.io/os=linux,type=API
huoban-k8s-master01 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=huoban-k8s-master01,kubernetes.io/os=linux,node-role.kubernetes.io/master=
huoban-k8s-master02 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=huoban-k8s-master02,kubernetes.io/os=linux,kubernetes.io=nginx-ingress,node-role.kubernetes.io/master=
huoban-k8s-master03 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=huoban-k8s-master03,kubernetes.io/os=linux,kubernetes.io=nginx-ingress,node-role.kubernetes.io/master=
huoban-k8s-mq-pod1 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=huoban-k8s-mq-pod1,kubernetes.io/os=linux,type=MQ
huoban-k8s-node03 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=huoban-k8s-node03,kubernetes.io/os=linux
2、给节点打标签
# kubectl label node huoban-k8s-node03 type=HBUC
node/huoban-k8s-node03 labeled
# kubectl get node huoban-k8s-node03 --show-labels
NAME STATUS ROLES AGE VERSION LABELS
huoban-k8s-node03 Ready <none> 19d v1.15.3 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=huoban-k8s-node03,kubernetes.io/os=linux,type=HBUC
3、创建pod并指定nodeSelector选项绑定节点
# cat nginx.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
labels:
app: hbuc
spec:
selector:
matchLabels:
app: hbuc
replicas: 1
template:
metadata:
labels:
app: hbuc
spec:
containers:
- name: nginx
image: nginx
imagePullPolicy: IfNotPresent
nodeSelector:
type: HBUC
# kubectl apply -f nginx.yaml
deployment.apps/nginx created
# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE
nginx-6fbf49794-cljsd 1/1 Running 0 87s 10.244.6.14 huoban-k8s-node03
二、亲和和反亲和 Affinity and anti-affinity
nodeSelector的调度方式略显简单,通过亲和和反亲和配置,能够为调度提供更灵活的策略,主要有以下几点增强:
- 更多的表达式支持,不仅仅是ADD和精确匹配了
- 可以设置soft/preference的调度策略,而不是刚性的要求
- 可以通过Pod的标签进行调度约束,不仅仅是Node的标签
亲和性特性包含两种方式
1、节点亲和性 Node affinity
Node affinity 是 Kubernetes 1.2版本后引入的新特性,类似于nodeSelector,允许我们指定一些Pod在Node间调度的约束。支持两种形式:requiredDuringSchedulingIgnoredDuringExecution
和preferredDuringSchedulingIgnoredDuringExecution
,可以认为前一种是必须满足,如果不满足则不进行调度,后一种是倾向满足,不满足的情况下会调度的不符合条件的Node上。IgnoreDuringExecution
表示如果在Pod运行期间Node的标签发生变化,导致亲和性策略不能满足,则继续运行当前的Pod。
apiVersion: v1
kind: Pod
metadata:
name: nginx
spec:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/e2e-az-name
operator: In
values:
- e2e-az1
- e2e-az2
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 1 //取值范围1-100
preference:
matchExpressions:
- key: another-node-label-key
operator: In
values:
- another-node-label-value
containers:
- name: nginx
image: docker.io/nginx
标签判断的操作符除了使用In
之外,还可以使用NotIn
、Exists
、DoesNotExist
、Gt
、Lt
。如果指定多个nodeSelectorTerms
,则只要满足其中一个条件,就会被调度到相应的节点上。如果指定多个matchExpressions
,则所有的条件都必须满足才会调度到对应的节点。
2、Pod间的亲和性与反亲和性 inter-pod affinity/anti-affinity
这个特性是Kubernetes 1.4后增加的,允许用户通过已经运行的Pod上的标签来决定调度策略,用文字描述就是“如果Node X上运行了一个或多个满足Y条件的Pod,那么这个Pod在Node应该运行在Pod X”,因为Node没有命名空间,Pod有命名空间,这样就允许管理员在配置的时候指定这个亲和性策略适用于哪个命名空间,可以通过topologyKey
来指定。topology是一个范围的概念,可以是一个Node、一个机柜、一个机房或者是一个区域(如北美、亚洲)等,实际上对应的还是Node上的标签。
有两种类型
- requiredDuringSchedulingIgnoredDuringExecution,刚性要求,必须精确匹配
- preferredDuringSchedulingIgnoredDuringExecution,软性要求
apiVersion: v1
kind: Pod
metadata:
name: with-pod-affinity
spec:
affinity:
podAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: security
operator: In
values:
- S1
topologyKey: failure-domain.beta.kubernetes.io/zone
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchExpressions:
- key: security
operator: In
values:
- S2
topologyKey: kubernetes.io/hostname
containers:
- name: with-pod-affinity
image: k8s.gcr.io/pause:2.0
标签的判断操作支持In
、NotIn
、Exists
、DoesNotExist
。
原则上topologyKey
可以是节点的合法标签,但是有一些约束:
- 对于亲和性以及RequiredDuringScheduling的反亲和性,topologyKey需要指定
- 对于RequiredDuringScheduling的反亲和性,LimitPodHardAntiAffinityTopology的准入控制限制topologyKey为kubernetes.io/hostname,可以通过修改或者disable解除该约束
- 对于PreferredDuringScheduling的反亲和性,空的topologyKey表示kubernetes.io/hostname, failure-domain.beta.kubernetes.io/zone and failure-domain.beta.kubernetes.io/region的组合.
- topologyKey在遵循其他约束的基础上可以设置成其他的key.
Pod间的亲和性策略要求可观的计算量可能显著降低集群的性能,不建议在超过100台节点的范围内使用。
Pod间的反亲和策略要求所有的Node都有一致的标签,例如集群中所有节点都应有匹配topologyKey的标签,如果一些节点缺失这些标签可能导致异常行为。
常用场景
apiVersion: apps/v1
kind: Deployment
metadata:
name: redis-cache
spec:
selector:
matchLabels:
app: store
replicas: 3
template:
metadata:
labels:
app: store
spec:
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: app
operator: In
values:
- store
topologyKey: "kubernetes.io/hostname"
containers:
- name: redis-server
image: redis:3.2-alpine
上面的例子中,创建了一个具有三个实例的部署,采用了Pod间的反亲和策略,限制创建的实例的时候,如果节点上已经存在具有相同标签的实例,则不进行部署,避免了一个节点上部署多个相同的实例。
apiVersion: apps/v1
kind: Deployment
metadata:
name: web-server
spec:
selector:
matchLabels:
app: web-store
replicas: 3
template:
metadata:
labels:
app: web-store
spec:
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: app
operator: In
values:
- web-store
topologyKey: "kubernetes.io/hostname"
podAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: app
operator: In
values:
- store
topologyKey: "kubernetes.io/hostname"
containers:
- name: web-app
image: nginx:1.12-alpine
再创建3个Web服务的实例,同上面Redis的配置,首先确保两个Web不会部署到相同的节点,然后在应用Pod间亲和策略,优先在有Redis服务的节点上部署Web。
三、污点(Taints)与容忍(tolerations)
对于nodeAffinity
无论是硬策略还是软策略方式,都是调度 POD 到预期节点上,而Taints
恰好与之相反,如果一个节点标记为 Taints ,除非 POD 也被标识为可以容忍污点节点,否则该 Taints 节点不会被调度pod。
比如用户希望把 Master 节点保留给 Kubernetes 系统组件使用,或者把一组具有特殊资源预留给某些 POD,则污点就很有用了,POD 不会再被调度到 taint 标记过的节点。taint 标记节点举例如下:
$ kubectl taint nodes k8s-node-pods01 key=value:NoSchedule
node "k8s-node-pods01" tainted
如果仍然希望某个 POD 调度到 taint 节点上,则必须在 Spec 中做出Toleration
定义,才能调度到该节点,举例如下:
tolerations:
- key: "key"
operator: "Equal"
value: "value"
effect: "NoSchedule"
effect 共有三个可选项,可按实际需求进行设置:
NoSchedule
:POD 不会被调度到标记为 taints 节点。PreferNoSchedule
:NoSchedule 的软策略版本。NoExecute
:该选项意味着一旦 Taint 生效,如该节点内正在运行的 POD 没有对应 Tolerate 设置,会直接被逐出。