web-dev-qa-db-fra.com

Laisser un seul pod elasticsearch apparaître sur un nœud de Kubernetes

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-2and 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: {}
5
Bitswazsky

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"

Source: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#always-co-located-in-the-same-node

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.

3
John

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
1
garlicFrancium

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 ...

1
Laszlo Valko