En DaemonSet, hablamos sobre el uso de nodeSelector para seleccionar los nodos que Podrá implementar. De hecho, Kubernetes también admite un mecanismo de programación más refinado y flexible, es decir, programación de afinidad y antiafinidad.
Kubernetes admite dos niveles de afinidad y antiafinidad de nodos y pods. Al configurar reglas de afinidad y antiafinidad, puede especificar restricciones o preferencias estrictas, como implementar un pod de front-end y un pod de back-end juntos, implementar ciertos tipos de aplicaciones en nodos específicos, implementar diferentes aplicaciones en diferentes nodos, etc. .
Afinidad de nodo (afinidad de nodo)
Debes haber adivinado que la base de las reglas de afinidad deben ser etiquetas. Echemos un vistazo a las etiquetas de los nodos del clúster CCE.
$ kubectl describe node 192.168.0.212
Name: 192.168.0.212
Roles: <none>
Labels: beta.kubernetes.io/arch=amd64
beta.kubernetes.io/os=linux
failure-domain.beta.kubernetes.io/is-baremetal=false
failure-domain.beta.kubernetes.io/region=cn-east-3
failure-domain.beta.kubernetes.io/zone=cn-east-3a
kubernetes.io/arch=amd64
kubernetes.io/availablezone=cn-east-3a
kubernetes.io/eniquota=12
kubernetes.io/hostname=192.168.0.212
kubernetes.io/os=linux
node.kubernetes.io/subnetid=fd43acad-33e7-48b2-a85a-24833f362e0e
os.architecture=amd64
os.name=EulerOS_2.0_SP5
os.version=3.10.0-862.14.1.5.h328.eulerosv2r7.x86_64
Estas etiquetas son agregadas automáticamente por el CCE cuando se crea el nodo. A continuación se presentan algunas etiquetas que se utilizarán más en la programación.
- failure-domain.beta.kubernetes.io/region: indica la región donde se encuentra el nodo. Si el valor de la etiqueta del nodo anterior es cn-east-3, significa que el nodo está en una región de Shanghai.
- failure-domain.beta.kubernetes.io/zone: indica la zona de disponibilidad (zona de disponibilidad) donde se encuentra el nodo.
- kubernetes.io/hostname: el nombre de host del nodo.
Además, en Label: The Weapon of Organizing Pods, también se introducen etiquetas personalizadas. Generalmente, para un clúster de Kubernetes grande, muchas etiquetas se definirán de acuerdo con las necesidades comerciales.
En DaemonSet, se introduce nodeSelector. A través de nodeSelector, Pod se puede implementar solo en nodos con etiquetas específicas. Como se muestra a continuación, el pod solo se implementará en los nodos con la etiqueta gpu = true.
apiVersion: v1
kind: Pod
metadata:
name: nginx
spec:
nodeSelector: # 节点选择,当节点拥有gpu=true时才在节点上创建Pod
gpu: true
...
Se puede hacer lo mismo mediante la configuración de la regla de afinidad de nodo, como se muestra a continuación.
apiVersion: apps/v1
kind: Deployment
metadata:
name: gpu
labels:
app: gpu
spec:
selector:
matchLabels:
app: gpu
replicas: 3
template:
metadata:
labels:
app: gpu
spec:
containers:
- image: nginx:alpine
name: gpu
resources:
requests:
cpu: 100m
memory: 200Mi
limits:
cpu: 100m
memory: 200Mi
imagePullSecrets:
- name: default-secret
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: gpu
operator: In
values:
- "true"
Parece que esto es mucho más complicado, pero de esta manera se puede conseguir una mayor capacidad expresiva, que se introducirá más adelante.
Aquí, afinidad significa afinidad, nodeAffinity significa afinidad de nodo, requiredDuringSchedulingIgnoredDuringExecution es muy largo, pero esto se puede dividir en dos párrafos:
- La primera mitad de requiredDuringScheduling indica que las reglas definidas a continuación deben cumplirse obligatoriamente (require).
- La segunda mitad de IgnoredDuringExecution dijo que no afectará a los pods que ya se están ejecutando en el nodo. Las reglas actuales proporcionadas por Kubernetes terminan con IgnoredDuringExecution, porque las reglas de afinidad de nodo actuales solo afectarán a los pods que se están programando. Por último, kubernetes también admitirá RequiredDuringExecution, es decir, elimine una determinada etiqueta en el nodo, y se eliminarán los pods que necesitan que el nodo contenga la etiqueta.
- Además, el valor del operador operador es In, lo que indica que el valor de la etiqueta debe estar en la lista de valores, y los valores de otros operadores son los siguientes.
- NotIn: el valor de la etiqueta no está en una lista
- Existe: existe una etiqueta
- DoesNotExist: una etiqueta no existe
- Gt: el valor de la etiqueta es mayor que cierto valor (comparación de cadenas)
- Lt: El valor de la etiqueta es menor que un cierto valor (comparación de cadenas)
Cabe señalar que no hay nodeAntiAffinity (antiafinidad de nodo), porque NotIn y DoesNotExist pueden proporcionar la misma función.
Verifiquemos si esta regla tiene efecto. Primero, etiquete el nodo 192.168.0.212 con gpu = true.
$ 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
Cree esta implementación, puede encontrar que todos los pods están implementados en el nodo 192.168.0.212.
$ kubectl create -f affinity.yaml
deployment.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.168.0.212
gpu-6df65c44cf-jzjvs 1/1 Running 0 15s 172.16.0.36 192.168.0.212
gpu-6df65c44cf-zv5cl 1/1 Running 0 15s 172.16.0.38 192.168.0.212
Reglas de selección de prioridad de nodo
La requiredDuringSchedulingIgnoredDuringExecution mencionada anteriormente es una regla de selección obligatoria. También hay una regla de selección de prioridad para la afinidad de nodos, es decir, favoriteDuringSchedulingIgnoredDuringExecution, que indica qué nodos se seleccionarán preferentemente de acuerdo con las reglas.
Para demostrar este efecto, primero agregue un nodo al clúster anterior, y este nodo no está en la misma zona de disponibilidad que los otros tres nodos. Después de la creación, consulte la etiqueta de la zona de disponibilidad del nodo, como se muestra a continuación, el nodo recién agregado está en cn-este -3c esta zona disponible.
$ kubectl get node -L failure-domain.beta.kubernetes.io/zone,gpu
NAME STATUS ROLES AGE VERSION ZONE GPU
192.168.0.100 Ready <none> 7h23m v1.15.6-r1-20.3.0.2.B001-15.30.2 cn-east-3c
192.168.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> 8h v1.15.6-r1-20.3.0.2.B001-15.30.2 cn-east-3a
A continuación, se define una implementación que requiere que el pod se implemente en los nodos de la zona de disponibilidad cn-east-3a primero. Se puede definir de la siguiente manera, utilizando la regla favoriteDuringSchedulingIgnoredDuringExecution, y establezca el peso de cn-east-3a en 80 y gpu = true weight Es 20, por lo que Pod se implementará primero en el nodo cn-east-3a.
apiVersion: apps/v1
kind: Deployment
metadata:
name: gpu
labels:
app: gpu
spec:
selector:
matchLabels:
app: gpu
replicas: 10
template:
metadata:
labels:
app: gpu
spec:
containers:
- image: nginx:alpine
name: gpu
resources:
requests:
cpu: 100m
memory: 200Mi
limits:
cpu: 100m
memory: 200Mi
imagePullSecrets:
- name: default-secret
affinity:
nodeAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 80
preference:
matchExpressions:
- key: failure-domain.beta.kubernetes.io/zone
operator: In
values:
- cn-east-3a
- weight: 20
preference:
matchExpressions:
- key: gpu
operator: In
values:
- "true"
Al observar la situación de implementación real, puede ver que hay 5 pods implementados en el nodo 192.168.0.212, mientras que solo hay 2 en 192.168.0.100.
$ kubectl create -f affinity2.yaml
deployment.apps/gpu created
$ kubectl get po -o wide
NAME READY STATUS RESTARTS AGE IP NODE
gpu-585455d466-5bmcz 1/1 Running 0 2m29s 172.16.0.44 192.168.0.212
gpu-585455d466-cg2l6 1/1 Running 0 2m29s 172.16.0.63 192.168.0.97
gpu-585455d466-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-585455d466-hkgvz 1/1 Running 0 2m29s 172.16.0.43 192.168.0.212
gpu-585455d466-mngvn 1/1 Running 0 2m29s 172.16.0.48 192.168.0.97
gpu-585455d466-s26qs 1/1 Running 0 2m29s 172.16.0.62 192.168.0.97
gpu-585455d466-sxtzm 1/1 Running 0 2m29s 172.16.0.45 192.168.0.212
gpu-585455d466-t56cm 1/1 Running 0 2m29s 172.16.0.64 192.168.0.100
gpu-585455d466-t5w5x 1/1 Running 0 2m29s 172.16.0.41 192.168.0.212
En el ejemplo anterior, la prioridad de la clasificación de nodos es la siguiente. Un nodo con dos etiquetas ocupa el primer lugar. Solo el nodo con la etiqueta cn-east-3a ocupa el segundo lugar (peso 80) y solo el nodo con gpu = true ocupa el primer lugar. En tercer lugar, los nodos que no tienen la clasificación más baja.
Figura 1 Orden de clasificación de prioridad
Aquí puede ver que el pod no está programado en el nodo 192.168.0.94. Esto se debe a que hay muchos otros pods implementados en este nodo, que usa más recursos, por lo que no está programado en este nodo. Esto también muestra que favoriteDuringSchedulingIgnoredDuringExecution es la regla de prioridad. , No reglas obligatorias.
Pod Affinity (Pod Affinity)
Las reglas de afinidad de nodo solo pueden afectar la afinidad entre el pod y el nodo. Kubernetes también admite la afinidad entre el pod y el pod, como implementar el front-end y el back-end de la aplicación juntos para reducir la latencia de acceso. La afinidad de pod también tiene dos reglas: requiredDuringSchedulingIgnoredDuringExecution y favoriteDuringSchedulingIgnoredDuringExecution.
Eche un vistazo al siguiente ejemplo, asumiendo que se ha creado un backend de aplicación y tiene la etiqueta app = backend.
$ kubectl get po -o wide
NAME READY STATUS RESTARTS AGE IP NODE
backend-658f6cb858-dlrz8 1/1 Running 0 2m36s 172.16.0.67 192.168.0.100
Al implementar el pod de frontend junto con el backend, puede realizar la siguiente configuración de reglas de afinidad de pod.
apiVersion: apps/v1
kind: Deployment
metadata:
name: frontend
labels:
app: frontend
spec:
selector:
matchLabels:
app: frontend
replicas: 3
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:
podAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- topologyKey: kubernetes.io/hostname
labelSelector:
matchLabels:
app: backend
Cree la interfaz y luego verifíquela, puede ver que la interfaz se crea en el mismo nodo que el backend.
$ kubectl create -f affinity3.yaml
deployment.apps/frontend created
$ kubectl get po -o wide
NAME READY STATUS RESTARTS AGE IP NODE
backend-658f6cb858-dlrz8 1/1 Running 0 5m38s 172.16.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
Hay un campo topologyKey, que significa primero delimitar el rango especificado por topologyKey, y luego seleccionar el contenido definido por las siguientes reglas. Hay kubernetes.io/hostname en cada nodo aquí, por lo que no se ve el rol de topologyKey.
Si el backend tiene dos pods, están en diferentes nodos.
$ kubectl get po -o wide
NAME READY STATUS RESTARTS AGE IP NODE
backend-658f6cb858-5bpd6 1/1 Running 0 23m 172.16.0.40 192.168.0.97
backend-658f6cb858-dlrz8 1/1 Running 0 2m36s 172.16.0.67 192.168.0.100
Etiqueta 192.168.0.97 y 192.168.0.94 con perfer = true.
$ kubectl label node 192.168.0.97 perfer=true
node/192.168.0.97 labeled
$ kubectl label node 192.168.0.94 perfer=true
node/192.168.0.94 labeled
$ kubectl get node -L perfer
NAME STATUS ROLES AGE VERSION PERFER
192.168.0.100 Ready <none> 44m 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.168.0.94 Ready <none> 91m v1.15.6-r1-20.3.0.2.B001-15.30.2 true
192.168.0.97 Ready <none> 91m v1.15.6-r1-20.3.0.2.B001-15.30.2 true
Defina la topologyKey de podAffinity como prefiera.
affinity:
podAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- topologyKey: perfer
labelSelector:
matchLabels:
app: backend
Al programar, primero encierre en un círculo los nodos con la etiqueta de preferencia, aquí están 192.168.0.97 y 192.168.0.94, y luego haga coincidir el Pod con la etiqueta app = backend, para que todas las interfaces se implementen en 192.168.0.97.
$ kubectl create -f affinity3.yaml
deployment.apps/frontend 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 192.168.0.97
backend-658f6cb858-dlrz8 1/1 Running 0 5m38s 172.16.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 Running 0 6s 172.16.0.72 192.168.0.97
Pod AntiAffinity (Pod Anti-Affinity)
Hablé sobre la afinidad de los pods. Los pods se implementan juntos a través de la afinidad. A veces, los requisitos son todo lo contrario. Los pods deben implementarse por separado. Por ejemplo, la implementación de pods juntos afectará el rendimiento.
El siguiente ejemplo define una regla de antiafinidad. Esta regla indica que el pod no se puede programar en el nodo con la etiqueta app = frontend Pod, es decir, el frontend está programado en diferentes nodos (cada nodo tiene solo un pod).
apiVersion: apps/v1
kind: Deployment
metadata:
name: frontend
labels:
app: frontend
spec:
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:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- topologyKey: kubernetes.io/hostname
labelSelector:
matchLabels:
app: frontend
Cree y visualice, puede ver que solo hay un pod de frontend en cada nodo y uno está pendiente. Porque cuando se implementa el quinto, los cuatro nodos tienen pods con app = frontend, por lo que el quinto siempre está pendiente .
$ kubectl create -f affinity4.yaml
deployment.apps/frontend created
$ kubectl get po -o wide
NAME READY STATUS RESTARTS 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> <none>
frontend-6f686d8d87-hgcq2 1/1 Running 0 18s 172.16.0.54 192.168.0.97
frontend-6f686d8d87-q7cfq 1/1 Running 0 18s 172.16.0.47 192.168.0.212
frontend-6f686d8d87-xl8hx 1/1 Running 0 18s 172.16.0.23 192.168.0.94