Nous avons une configuration multi-nœuds de notre produit où nous devons déployer plusieurs pods Elasticsearch. Tous ces éléments étant des nœuds de données et disposant de montages de volume pour le stockage persistant, nous ne souhaitons pas installer deux modules sur le même nœud. J'essaie d'utiliser la fonctionnalité anti-affinité de Kubernetes, mais en vain.
Le déploiement du cluster est effectué via Rancher. Nous avons 5 nœuds dans le cluster et trois nœuds (disons node-1
, node-2
and node-3
) ont le libellé test.service.es-master: "true"
. Ainsi, lorsque je déploie le diagramme de barre et que je l'adapte jusqu'à 3, les modules Elasticsearch sont opérationnels sur tous ces trois nœuds. mais si je l'adapte à 4, le 4ème nœud de données se trouve dans l'un des nœuds mentionnés ci-dessus. Est-ce un comportement correct? D'après ce que j'avais compris, imposer une stricte anti-affinité devrait empêcher les pods de monter sur le même noeud. J'ai fait référence à plusieurs blogs et forums (par exemple, this et this ), et ils suggèrent des modifications similaires à celles que j'ai eues. Je joins la section correspondante du tableau de bord.
La condition est la suivante: nous devons afficher les ES uniquement sur les nœuds portant une paire clé-valeur spécifique, comme indiqué ci-dessus, et chacun de ces nœuds ne doit contenir qu'un seul module. Tout commentaire est apprécié.
apiVersion: v1
kind: Service
metadata:
creationTimestamp: null
labels:
test.service.es-master: "true"
name: {{ .Values.service.name }}
namespace: default
spec:
clusterIP: None
ports:
...
selector:
test.service.es-master: "true"
---
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
creationTimestamp: null
labels:
test.service.es-master: "true"
name: {{ .Values.service.name }}
namespace: default
spec:
selector:
matchLabels:
test.service.es-master: "true"
serviceName: {{ .Values.service.name }}
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: test.service.es-master
operator: In
values:
- "true"
topologyKey: kubernetes.io/hostname
replicas: {{ .Values.replicaCount }}
template:
metadata:
creationTimestamp: null
labels:
test.service.es-master: "true"
spec:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: test.service.es-master
operator: In
values:
- "true"
topologyKey: kubernetes.io/hostname
securityContext:
...
volumes:
...
...
status: {}
Update-1
Conformément aux suggestions figurant dans les commentaires et les réponses, j'ai ajouté la section anti-affinité dans template.spec. Mais malheureusement, le problème demeure. Le yaml mis à jour ressemble à ceci:
apiVersion: v1
kind: Service
metadata:
creationTimestamp: null
labels:
test.service.es-master: "true"
name: {{ .Values.service.name }}
namespace: default
spec:
clusterIP: None
ports:
- name: {{ .Values.service.httpport | quote }}
port: {{ .Values.service.httpport }}
targetPort: {{ .Values.service.httpport }}
- name: {{ .Values.service.tcpport | quote }}
port: {{ .Values.service.tcpport }}
targetPort: {{ .Values.service.tcpport }}
selector:
test.service.es-master: "true"
---
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
creationTimestamp: null
labels:
test.service.es-master: "true"
name: {{ .Values.service.name }}
namespace: default
spec:
selector:
matchLabels:
test.service.es-master: "true"
serviceName: {{ .Values.service.name }}
replicas: {{ .Values.replicaCount }}
template:
metadata:
creationTimestamp: null
labels:
test.service.es-master: "true"
spec:
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: test.service.es-master
operator: In
values:
- "true"
topologyKey: kubernetes.io/hostname
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: test.service.es-master
operator: In
values:
- "true"
topologyKey: kubernetes.io/hostname
securityContext:
readOnlyRootFilesystem: false
volumes:
- name: elasticsearch-data-volume
hostPath:
path: /opt/ca/elasticsearch/data
initContainers:
- name: elasticsearch-data-volume
image: busybox
securityContext:
privileged: true
command: ["sh", "-c", "chown -R 1010:1010 /var/data/elasticsearch/nodes"]
volumeMounts:
- name: elasticsearch-data-volume
mountPath: /var/data/elasticsearch/nodes
containers:
- env:
{{- range $key, $val := .Values.data }}
- name: {{ $key }}
value: {{ $val | quote }}
{{- end}}
image: {{ .Values.image.registry }}/analytics/{{ .Values.image.repository }}:{{ .Values.image.tag }}
name: {{ .Values.service.name }}
ports:
- containerPort: {{ .Values.service.httpport }}
- containerPort: {{ .Values.service.tcpport }}
volumeMounts:
- name: elasticsearch-data-volume
mountPath: /var/data/elasticsearch/nodes
resources:
limits:
memory: {{ .Values.resources.limits.memory }}
requests:
memory: {{ .Values.resources.requests.memory }}
restartPolicy: Always
status: {}
Comme suggéré par Egor, vous avez besoin de podAntiAffinity:
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"
Donc, avec votre label actuel, cela pourrait ressembler à ceci:
spec:
affinity:
nodeAffinity:
# node affinity stuff here
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: "test.service.es-master"
operator: In
values:
- "true"
topologyKey: "kubernetes.io/hostname"
Assurez-vous de mettre cela à la bonne place dans votre yaml, sinon cela ne fonctionnera pas.
Tout d’abord, dans votre manifeste initial et même dans le manifeste mis à jour, vous utilisez topologyKey
pour nodeAffinity
qui vous donnera une erreur lors de la tentative de déploiement de ce manifeste en utilisant kubectl create
ou kubectl apply
car il n’existe pas de clé api appelée topologyKey
pour nodeAffinity
Réf doc
Deuxièmement, vous utilisez une clé appelée test.service.es-master
pour votre nodeAffinity, êtes-vous sûr que votre "nœud" a ces étiquettes? veuillez confirmer par cette commande kubectl get nodes --show-labels
Enfin, augmenter pour @Laszlo answer et votre @bitswazsky commentez-le pour le simplifier, vous pouvez utiliser le code ci-dessous:
Ici, j'ai utilisé une étiquette de noeud (en tant que clé) appelée role
pour identifier le noeud, vous pouvez l'ajouter au noeud de vos clusters existants en exécutant cette commande kubectl label nodes <node-name> role=platform
selector:
matchLabels:
component: nginx
template:
metadata:
labels:
component: nginx
spec:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: role
operator: In
values:
- platform
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: component
operator: In
values:
- nginx
topologyKey: kubernetes.io/hostname
Cela fonctionne pour moi avec Kubernetes 1.11.5:
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: nginx
spec:
replicas: 3
selector:
matchLabels:
test.service.es-master: "true"
template:
metadata:
labels:
test.service.es-master: "true"
spec:
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: test.service.es-master
operator: In
values:
- "true"
topologyKey: kubernetes.io/hostname
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: test.service.es-master
operator: In
values:
- "true"
containers:
- image: nginx:1.7.10
name: nginx
Je ne sais pas pourquoi vous avez choisi la même clé/valeur pour l'étiquette de sélecteur de déploiement du pod, comme pour le sélecteur de nœud. Ils sont déroutants au minimum ...