在DaemonSet中讲到使用nodeSelector选择Pod要部署的节点,其实Kubernetes还支持更精细、更灵活的调度机制,那就是亲和(affinity)与反亲和(anti-affinity)调G M i 9 n / w度。6 a R
Kubernetes支持节点和Pod两个层级的亲和与反亲和。通过配置亲和与反亲u 4 P和规则,可以允F Y l a { ( E许您指定硬性限制或者偏好,例如将前台Pod和后台Pod部署在一起、某类应用部署到某些特定的节点、不同应用部署到不同的节点等等。
Node Affinity(节点亲和)
您肯定也猜到了亲和性规则的基础肯定也是标签,先来看一下CCE集群中节点上有些什么标签。
$ kubectl describe node 192.168.0.212
Name: 192.168.0.212
Roles: <none>
Labels: beta.kubernetes.io/arch=amd64
beta.kuberg X ]netes.io/os=linux
failure` x ) + & x z-domain.N c h 1 Gbeta.kuberne, H k Qtes.io/is-b9 W w 7 % o y Naremetal=f; % E * 5 s ` $alse
failure-domain.beta.kubernet ! w X & B , }es.io/region=cn-east-3
failure-domain.beta.kubernetes.l 8 { ^ Jio/zone=cn-east-3a0 1 K ^ z u *
kubernetes.io/arch=amd64
kubernetes.io/availablezone=cn-east-3a
kubernetes.io/eniquota=12
kub[ $ T ` Eernetes.io/hostname=192.168.0.21; 4 9 c O G g i {2
kubernetes.io/os=linux
node.kubernetes.io/subnetid=fd43acad-33e7-48b2-a85a-24833f362e0e
os.architecture=amd64
os.name=EulerOS_2.0_d = x h L $SP5
os.version=3.10.0-862.14.1.5.h328.eulerosv2r7.x86_64
这些标签都是在创建节点的时候CCE会自动添加上的,下面介绍几个在调度中会用到比+ ] ^ Y 4 3 l c较多的标签。
- failure-domain.beta.kubernetes.i| ! )o/region:表示节点所在的区域,如果上面这个节点标0 @ U签值为cn-east-3,表示节点在上海一区域。
- failure-domain.beta.kubernetes.io/zone:表示节点所在的可用区(availability zone)。
- kubernetes.io/hostname:节点的hostname。
另外在Label:组织Pod的利器章节还介绍自定义标签,通常情况下,对于一个大型Kubernetes集群,肯定会根据业务需要定义很多标签。
在DaemN . 7 + ionSet中b V % g m 4 i = 2介绍了nodeSelectoS ~ 4 ,r,通过nodeSelector可以让Pod只部署在具m k 有特定标签的! } l P i @ ] C X节点上。如下所示,` Y S _ LPod只会部署在拥有gpu=true这个标签的节点上。
apiVersion: v1
kind: Pod
metadata:
name: nginxP & t S ] + E _ 0
spea P R Y @ b s V qc:
nod1 i 0 m v m P l ieSe4 D P j r 6 $lector: # 节点选择,当节点拥有gpu=true时才在节点上创建Pod
gpu: true
...
通过节点亲和性规则配置,也7 _ f .可以做到同样的事情,如下所示。
apiVersion: apps/v1
kind: Deployment
metadata:
name: gpu
labels:x j e r J k J 3 ?
app: gpu
sq 4 R X e Y m ^ *pec:
selector:
matchLabels:
app: gpu
replicas: 3
template:
metadata:
lab8 _ ? #els:
app: gpu
spec:
containers:
- image: nginx:alpine
name: gpu
resources:
requests:
cpu: 100m
memory: 200Mi
limits:
cpu: 100m
memory: 200Mi
imaq ^ X V : LgePullSecrets:
- name: default-se. + *cret
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: gpu
operator: In
valu[ [ V | f w Nes:
- "true"
看起来这要: 2 S ! &复杂很多,但这种方式可以得到更强的表达能力,后面会进一步介绍。
这里affinity表示亲和,nodeAf= $ k } 5 L finity表示节点亲和,requiredDuringSchedulingIgnoredDuringExecutionp p ~ *非常长,不过可以将这个分作两段来看:
- 前半段requiredDuringScheduling表示下面定义的规则必须强制满足(require)。
- 后半段IgnoredDuringExecution表示不会影响已经在节点上运行的Pod,目前Kubernetes提供的规则都是以IgnoredDuringExecution结尾的,因为当前的节点亲缘性规则只会影响正在被调度的pod,最终,kube N U z r 3 ornetes也会支持RequiredDuringExecution,即去除节点上的某个标签,那些需要节点包含该标签的pod将会被剔除。
- 另f u K外操作符operator的值为In,表示标签值需要在values的列表中,其他operator取值如下。
- NotIn:标签的值不在某个列表中
- Exists:某个标签存在
- DoesNotExist:某M + Q * l o c个标签不存在
- Gt:标签的值大于某个值(字符串比较)
- Lt:标签i l ) 7 ? Y *的值小于某个值(字符A z X _串比较)
需要说明的是并没有nodeAntiAffinity(节点反亲和),因为NotIn和DoeR L % G ? + asNotExise k K { t 4t可以提供相同的功能。
下面来验q q _ u F X Q证这段规则是? W R O } $ R否生效,首先给192.168.0.212这个节点P I $ h y @ 7打上gpu=true的标签。
$6 5 , 4 kubectl label node 192.168.0.212 gpu=true
node/192.168.0.212 labeled
$ kubectl get node -L gpu
NAME STATUS ROLES AGE VERSION GPU
192.168.0.212 Ready <none> 13m v1.15.6-r1-20.3.0.2.B001-15.30.2 true
192.168.0.94 Ready <none> 13m v1.15.6-r1-20.3.0.2.B001-15.30.2
192.168.0.97 Ready <none> 13m v1.15.6-r1-20.3.0.2.B001-15.30.2
创建这个Deployment,可以发现所有的Pod都部署在了192.168.0.212这个节点上。
$ kubectl create -f affinity.yaml
deA J Q t / E % d Nployment.apps/gpu created
$ kubectl get pod -owide
NAME READY STATUS RESTARTS AGE IP NODE
gpu-6df65c44cf-42xw4 1/1 Running 0 15s 172.16.0.37 192.16t y a a ;8.0.212
gpu-a Y f6df65c44cf-jzjvs 1/1 Running 0 15s 172.16.0.36 192.168.0.212
gpu-6df65c44cf-zv5cl 1/1 Runnin8 7 )g 0 15s 172.16.0.38 192.168.0.212
节点优先选择规则
上面讲的requiredDuringSchedulingIgnoredDuringEq } ) ~ Y J [ 8 pxecution是一种强制选择的规则,节点亲和还有一种优X c w + K o | h |先选择规则,即p? 4 H + Lrefe+ b r c | # orredDuringSchedulingIgnoredDuringExe3 d j 1 ( 0 &cution,表示会根据规则优先选择哪些节点。
为演示这个效果,先为上面的集群添加一个节点,且这个节点跟另外三个节点不在同一个可用区,创0 & : k 1 I建完之后查询节点的可用区标签,如下所示,新添加的节点在cn-east-3c这个可用区。
$ ku9 K X j Qbectl get node -L failur( g r 3 + I e-domain.beta.kubernetes.io/zone,gpu
NAME STATUS ROLES AGE VERSION ZONE GPU
192.168.0.100 Ready <none> 7h23m v1.15.: 8 h U 9 C & %6-r1-20.3.0.2.B001-15.30.2 cn-east-3c
192{ q ^ 2 _ x h . *._ 2 ( W * Y168.0.212 Ready <none> 8h v1.15.6-r1-20.3.0.2.B001-15.30.2 cn-east-3a true
192.168.0.94 Ready <none> 8h v1.15.6-r1-20.3.0.2.B001-15.30.2 cn-east-3a
192.168.0.97 Ready <none> 8hs | _ v1.15.6-r1-20.3.0.2.B001-15.30.2Y + u ( cn-east-3a
下面定义一个Deployment,要求Pod优先部署在可用区cn-east-3a的节点上,可以像下面这样定义,使用preferredDuringSchedulingIgnoredDuringExez P h I 1 rcution规则,给cn$ N z p z p-east-3a设置权重(weight)为80,而gpu=true权重为20,这样Pod就优先部署在cn-east-3a的节点上。
apiVersion: apps/v1
kind: Deployment
metadata:
name: gpu
labels:
appH ) ; z: gpu
spec:
selector:
matcn H w d ZhLabels:
app: gpu
replica. ! u Cs: 10
template:
metadag j i ` / j Jta:
labels:
app:S 8 = = gpu
spec:
containers:
- image: nginx:alpine
name: gpu
resources:
requests:
cpu: 100m
memory: 200Mi
lf b cimite ` e F Os:
cpu: 100m
memo( } - X Cry: 200Mi
imagePullSecrets:
- n- / ( % Z t t s vame: default-s+ i R B s gecret
affinity:
no9 D n o + }deAffinity:
preferredDuringSchedulingI] v { q N c 3gnoredDuringExecution:
- weight: 80
preference:
matchExpressions:
- key: failure-domain.v t , b l ) ~ 5 Kbeta.kubernetes.io/zone
operator] h { j * u: In
values:
- cn-east-3a
- weight: 20
preference:i N b I D b ) u :
matchExpressionk S H ~s:
-X z v ] X N ` O key: gpu
operator: Ih 9 } B # 1n
values:
- "true"
来看n & r _ q O w ! *实际部署后的情况,可以看到部署到192.168.0.212这个节点上的Pod有5个,而192.168.0.100上只有2个。
$ kubectl cri Z % aeate -f affinity2.yaml
deN M N 1ployment.apps/gpu created
$ kubectlu = _ x get po -o wide
NAME READY STATUS RESTARTSD k 6 V z Q g x 9 AGE IP NODE
gpu-585455d466-5bmcz 1/1 Running 0 2m2J q X R9s 172.+ 4 !16.0.44 192.168.0.24 I # k z *12
gpu-585455d466-cg2l6 1/1 RunnY I C 3 o k K ^ Hing 0 2m29s 172.16.0.63 192.168.0.97
gpu-585455d4f j p66-f2bt2 1/1 Running 0 2m29s 172.16.0.79 192.168.0.100
gpu-585455d466-hdb5n 1/1 Running 0 2m29s 172.16.0.42 192.168.0.212
gpu-d v X q ! / s B585455d466-h; = R vkgvz 1/1 Rg / @ L ! d F &unning 0 2m29s 172.16.0.43 192.168.0.212
gpu-585455d466-mngvn 1/1 Runni W fng 0 2m29s 172.16.0.48 192.168.0.97
gpu-585455d466-s26qs 1/1 Running 0 2m29s 172.16r ! L G o 2 4.0.62 192.168.0.97
gpu-585455d4d s 7 * Y ~ _ N n66-sxtzm 1/1 Running 0 2m29s 172.16.0.45 192.168.0.21F s c # ;2
gpu-585455d466-t56cm 1/1 Running 0 2m29s 17b G J ` 6 } K 2 ?2.16.0.64 192.168.0.100
gpu-585455d466-t5w5x 1/1 Running 0 2m29s 172.16.0.41 192.168.0.212
上面这个例子/ 5 7 * R ` 3 o中,对于节点排序优先级如下所示,有个两个标R A R V X签的节点排序最高,只有cnN @ j x * + f ]-t t 0 q Y + aeast-3a标签的节x x c 1 d点排序第二(权重为86 D w y0),只有gpu=true的节点排序第三,没有的节点排序最低。
图1 优先级排序顺序
这里您看到Pod并没有调度到192.168.0.94这个节点上,这是因! ] # ) X t z # &为这个节点上部署了很多其他Pod,资源使用较多,所以并没有往这个节点上调度,这也侧面说明preferredDuringSchedulingIgnoredDuringExecution是优先规则,而不是强制规则。
Pod Affinity(Pod亲和)
节点亲和的规则只能影响Pod和节点之间的亲和,Kubernetes还支持Pod和Pod之间的亲和,例如将应用的前端和后端部署在一起,从而减少访问延迟。Pod亲和同样有requiredDuringSchedulingIgnoredDuringf o g 9 ] d , c NExecutioj 4 W z rn和pre: 5 m j b v :ferredDuringSchedulingIgnoredDuringExecution两种规则。
来看下面这个例子,假设有个应用的后端已经创建,且带有app=backend的标签。
$ kubectl get po -o wide
NAME READY STATUS RESTARTS AGE IP NODE
backend-658f6cb858-dlrz8 1/1 Running 0 2m36sy q 5 172.16.0.67 192.168.0.100
将前端frontend的pn R tod部署在backend一起时,可以做如下Pod亲和规则配置。
apiVes _ 7 &rsion: apps/v1
kind: Deployment
metadata:
name: frontend
labels:
ae ( r C 7 K W qpp: frontend
spec:
selector:
matchLabels:
app: frontend
replicas: 3
template:
mes H D _ (tadata:
labels:
app: f$ - E x V / H x yront3 C , cend
spec2 # * -:
containers:
- image: ng7 R / 6 1inx:alpine
name: frontend
resources:
requests:
cpu: 100m
memor} @ B v vy: 200Mi
limits:
cpu: 100m
m4 | B o 4 y V Gemory: 200Mi
imagePullSecrets:
- name: default-secret? p y @ n
affinity:
podAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- topologyKey: kubernetes.io/h^ - Zostname
labelSelector:
matchLabels:
app: backend
创建frontend然后查看,可以看到frontend都创建到跟bacZ q 1kend一样的节点上了。
$ kubectl create -f affinity3.yaml
deploymen} v 7 x A t.apps/frontend created
$ kubectl get po -o wide
NAME READY STATU8 o ?S RESTARTS AGE IP NODE
backend-658f6c~ 7 6 I 9 hb858-s 3 @ ; A { 4dlrzH J =8 1/1 Running 0 5m38s 172.16^ F $ G.0.67 192.168.0.100
frontend-67ff9b7b97-dsqzn 1/1 Running 0 6s 172.16.0.70 192.168.0.100
frontend-67ff9b7b97-hxm5t 1/1 Running 0 6s 172.16.0.71 192.168.0.100
frontend-67ff9b7b97-z8pdb 1/1 Running 0 6s 172.16.0.72 192.168.0.100
这里有个tv Y e d + F w RopologyKey字段,意思是先圈定topologyKey指定的范围,然后再选择下面规则定义的内容。这里每个节点上都有kuc p 0 6bernetes.io/hostname,所以看不出topologyKey起到的作用。
如果b| * } N o S y 5ackend有两个Pod,分别在不同的节点上。= p ^ |
$ kubectl get po -o wide
NAME READY STAu ] l 7 P B a XTUS RESTARTS AGE IP NODE
backend8 j m ^ u | D 0-658f6cb858-5bpd6 1/1 Running 0 23m 172.16.0.40 192.168.0.97
ba7 . ] ! @ zckend-658f6cb858y [ M * M k S 7 R-dlrz8 1/1 Running 0 2m36s 172.16.0.67 192.168.0.100
给192.168.0.97和192.168.0.94打一个p% $ S * { M e ~ herfer=true的标签。
$ kubectl label node 192.168.0.97 perfer=tru/ 7 & k - Y .e
node/192.168.0.97 labeled
$ kubectl label node 192.168.0.94 perfer=true
nol 9 A ; j 0 E + Pde/192.168.0.94 labeled
$C ~ D | r kubectl get node -L p0 M W G , ; b N uerfh ] [ Jer
N1 X a 2 C AMEs z k 1 i O ! X STATUS ROLES AGE VERSION PERFER
19F E g g R $ b l2.168.0.100 Ready <none> 4 5 H `4m v1.15.6-r1-20.3.0.2.B001-15.30.2
192.168.0.212 Ready <none> 91m v1.15.6-r1-20.3.0.2.B001-15.30.2
192.16{ s B t D x8.0.94 Ready <none> 91m v1.15.6-r1-20.3.0.2.B001-15.30.2 tr* f @ue
192.168.0.97 Read@ I d { F 7 0y <none> 91m v1.15.6-r1-20.3.0.2.B001-15.30.2 true
将podAffinity的topologyKey定义为perfer。
affin] - 5 x O * $ @ity:
podAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- topologyKey: perfer
labelSelector:
matchLabels:
app: backend
调度时,先圈定拥有perfer标签的节点,这里也就是192.168.0.97和192.168.0.94,然后再匹配app=backend标签的Pod,从而fr6 y $ g e ; U Jontend就会全部部署在192.168.0.97上。
$ kubectl cre+ o k 5ate -f affinity3.yaml
deployment.apps/fron/ ^ 9 r J ; 0 ` -tend created
$ kubectl get po -o wide
NAME READY STATUS RESTARTS AGE IP NODE
backend-658f6cb858-5bpd6 1/1 Running 0 26m 172.16.0.40 1922 v u r p M.168.0.97
backend-658f6cb858-dlrz8 1/1 Running 0 5m38s 172.1M Z , . $ #6.0.67 192.168.0.100
frontend-67ff9b7b97-dsqzn 1/1 Running 0 6s 172.16.0.70 192.168.0.97
frontend-67ff9b7b97-hxm5t 1/1 Running 0 6s 172.16.0.71 192.168.0.97
frontend-67ff9b7b97-z8pdb 1/1- @ J 3 K Q Running 0 6s$ d T A o 172.16.0.72 192.168.0.97
Pod AntiAffinix Q + { 5 5ty(Pod反亲和)
前面讲a p d了Pod的亲和,通过亲和将Pod部署在一起,有时候需求却恰恰相反,需要将Pod分开部署,例如Pod之间部署在一起会影响性} / w 1 ~能的情况。
下面例子中定义了反亲h l [ $ 0 P 和规则,这个规则表示Pod不能调度到拥有app=frontend标签Pod的节点上,也就是下面将frontend分别调度到不同的节点上(每个/ : m T节点只有一个Pod)。
apiVersion: apps/v1
kind: Deployment
metadata:
name: frontenK p ` T + O 9d
labels:
app: frontend
spec:4 # = i A X S , b
selector:
matchLabels:
app: frontend
replicas: 5
template:
metadata:
labels:
app: frontend
spec:
containers:
- image: nginx:alpine
name: frontend
resources:
requests:
cpu: 100m
memory: 200Mi
limits:
cpu: 100m
memory: 200Mi
imagePullSecrets:
- name: default-secret
affinity:
podAntiAffu k |inity:
requiredDuringSchedulingIgnoredDuric M k & ;ngExecution:B B o W i ,
- topologyKey: kubernetes.io/hostname
labelSelector:
matchLabels:
app: frontend
创建并查看,可以看到每个节点上只有一个frontend的PodC f D ; 7 W ( & },还有一个在Pending,因为在部署第5个时4个节点上都有了app=frontend的Pod,所以第5个一直是Pending。
$ kubectl create -f af u j ffinity4.yaml
dD ! % # ? S reployment.apps/frontend created
$ kubectl get po -o wide
NAME READY STATUS RG . N ` zESTARTS AGE IP NODE
frontend-6f686d8d87-8dlsc 1/1 Running 0 18s 172.16.0.76 192.168.0.100
frontend-6f686d8d87-d6l8p 0/1 Pending 0 18s <none> <nong K oe>
fronteZ T -nd-6f686d8d87-hgcq2 1/1 Running 0 18s 172.16.0.54 192.168.0.97
frontendy h 8 2 -6f686d8d87-q7cfq 1/1 Running 0 18s 172.16.0.47 192.168.0.212
frontE Y Gend-6f686d8d87-xl8hx 1/1 Rd 2 v 9unning 0 18s 172.16.0.23 192.168.0.94
发表评论