Можно ли назначить модуль StatefulSet определенному узлу кластера Kubernetes?

У меня есть кластер из 5 узлов (1 мастер / 4 рабочих). Можно ли настроить StatefulSet, при котором я могу создать модуль (модули) для запуска на данном узле, зная, что у него достаточно мощности, а не планировщик Kubernetes, принимающий это решение?

Допустим, мой StatefulSet создает 4 модуля (реплик: 4) как myapp-0, myapp-1, myapp-2 и myapp-3. Теперь я ищу:

pod myapp-0 - перенести в расписание ---> worker-1

pod myapp-1-- перенести расписание ---> worker-2

myapp-2 pod-- перенести расписание ---> worker-3

pod myapp-3-- перенести расписание ---> worker-4

Пожалуйста, дайте мне знать, можно ли этого как-то достичь? Потому что, если я добавлю допуск к модулям StatefulSet, он будет одинаковым для всех модулей, и все они будут запланированы для одного узла, соответствующего заражению.

Спасибо, J


person Jaraws    schedule 15.02.2020    source источник
comment
вопрос был задан в 2015 году, но сегодня я нахожусь в такой же ситуации. Был ли у вас подход к этому? Если вы помните, дайте мне знать, пожалуйста?   -  person Nish    schedule 11.10.2020


Ответы (5)


Вы можете делегировать ответственность за планирование произвольных подмножеств подов своему собственному (ым) планировщику (ам), которые запускаются (ются) вместе с планировщиком Kubernetes по умолчанию или вместо него.

Вы можете написать свой собственный планировщик. Пользовательский планировщик может быть написан на любом языке и может быть настолько простым или сложным, насколько вам нужно. Ниже приведен очень простой пример настраиваемого планировщика, написанного на Bash, который назначает узел случайным образом. Обратите внимание, что вам нужно запустить это вместе с прокси-сервером kubectl, чтобы он работал.

SERVER='localhost:8001'

while true;

do

    for PODNAME in $(kubectl --server $SERVER get pods -o json | jq '.items[] | select(.spec.schedulerName == "my-scheduler") | select(.spec.nodeName == null) | .metadata.name' | tr -d '"')

;

    do

        NODES=($(kubectl --server $SERVER get nodes -o json | jq '.items[].metadata.name' | tr -d '"'))


        NUMNODES=${#NODES[@]}

        CHOSEN=${NODES[$[$RANDOM % $NUMNODES]]}

        curl --header "Content-Type:application/json" --request POST --data '{"apiVersion":"v1", "kind": "Binding", "metadata": {"name": "'$PODNAME'"}, "target": {"apiVersion": "v1", "kind"

: "Node", "name": "'$CHOSEN'"}}' http://$SERVER/api/v1/namespaces/default/pods/$PODNAME/binding/

        echo "Assigned $PODNAME to $CHOSEN"

    done

    sleep 1

done

Тогда просто в вашем файле конфигурации StatefulSet в разделе спецификации вам нужно будет добавить строку schedulerName: your-scheduler.

Вы также можете использовать привязка к пакету:.

Пример:

apiVersion: apps/v1
kind: StatefulSet
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

В приведенном ниже фрагменте yaml набора состояний веб-сервера настроены podAntiAffinity и podAffinity. Это сообщает планировщику, что все его реплики должны быть расположены вместе с модулями, имеющими метку селектора app = store. Это также гарантирует, что каждая реплика веб-сервера не будет размещена на одном узле.

apiVersion: apps/v1
kind: StatefulSet
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

Если мы создадим два вышеупомянутых развертывания, наш трехузловой кластер должен выглядеть, как показано ниже.

node-1              node-2           node-3
webserver-1     webserver-2          webserver-3
cache-1             cache-2          cache-3

В приведенном выше примере используется правило PodAntiAffinity с topologyKey: "kubernetes.io/hostname" для развертывания кластера Redis, чтобы на одном хосте не было двух экземпляров.

Вы можете просто определить три реплики конкретного модуля и определить конкретный файл конфигурации модуля, egg .: Есть метка: nodeName, который является простейшей формой ограничения выбора узла, но из-за его ограничений обычно не используется. nodeName - это поле PodSpec. Если он не пустой, планировщик игнорирует модуль, и кубелет, запущенный на указанном узле, пытается запустить модуль. Таким образом, если nodeName предоставляется в PodSpec, он имеет приоритет над вышеуказанными методами выбора узла.

Вот пример файла конфигурации модуля с использованием поля nodeName:

apiVersion: v1
kind: Pod
metadata:
  name: nginx
spec:
  containers:
  - name: nginx
    image: nginx
  nodeName: kube-worker-1

Дополнительная информация о планировщике: custom-scheduler.

Взгляните на эту статью: assigining-pods-kubernetes.

person Malgorzata    schedule 17.02.2020
comment
Помогает? Вы бы подумали о том, чтобы проголосовать / принять его, это будет полезно для сообщества. - person Malgorzata; 16.03.2020

Вы можете сделать это с помощью nodeSelector и node affinity (см. Это руководство https://kubernetes.io/docs/concepts/configuration/assign-pod-node/), любой может использоваться для запуска модулей на определенных узлах. Но если узел имеет порчи (ограничения), вам необходимо добавить допуски для этих узлов (больше можно найти здесь https://kubernetes.io/docs/concepts/configuration/taint-and-toleration/). Используя этот подход, вы можете указать список узлов, которые будут использоваться для планирования вашего модуля, уловка в том, если вы укажете для ex. 3 узла и у вас 5 модулей, то вы не можете контролировать, сколько модулей будет работать на каждом из этих узлов. Они распределяются по кубе-расписанию. Другой подходящий вариант использования: если вы хотите запустить по одному модулю на каждом из указанных узлов, вы можете создать набор демонов и выбрать узлы с помощью nodeSelector.

person anmol agrawal    schedule 16.02.2020
comment
Спасибо за ваш ответ. Я ищу исправить узел для отдельного модуля StatefulSet. Теперь, если я добавлю допуски к своим конфигурациям контейнеров в StatefulSet, if будет общим для всех модулей моего StatefulSet и будет планировать все модули на узле с соответствующим заражением. Я обновил свой вопрос, добавив более подробную информацию. Пожалуйста, проверьте. - person Jaraws; 16.02.2020
comment
Допуски предназначены для узлов, которые имеют порчи. А в nodeSelector или pod affinity вы указываете метку узла. Если вы добавите одну и ту же метку к своим рабочим узлам (от worker-1 до worker-4), тогда все поды будут распределены между ними. Добавлять допуски нужно только в том случае, если какой-либо из этих узлов имеет дефекты. - person anmol agrawal; 16.02.2020

ознакомьтесь с этим руководством https://kubernetes.io/docs/concepts/configuration/taint-and-toleration/ однако вы ищете директиву nodeSelector, которую следует поместить в спецификацию модуля.

person cperez08    schedule 15.02.2020
comment
Спасибо за ответ @ cperez08. Я ищу исправить узел для отдельного модуля StatefulSet. Теперь, если я добавлю допуски к своим конфигурациям контейнеров в StatefulSet, if будет общим для всех модулей моего StatefulSet и будет планировать все модули на узле с соответствующим заражением. Я обновил свой вопрос, добавив более подробную информацию. Пожалуйста, проверьте. - person Jaraws; 16.02.2020
comment
@Jaraws, в этом случае, я думаю, это невозможно, единственное, что вы можете сделать, - это запланировать разные Stafeulset или Deployments на разных узлах. - person cperez08; 16.02.2020

Вы можете использовать podAntiAffinity для распространения реплик на разные узлы.

apiVersion: v1
kind: Service
metadata:
  name: nginx
  labels:
    app: nginx
spec:
  ports:
  - port: 80
    name: web
  clusterIP: None
  selector:
    app: nginx
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: web
spec:
  serviceName: "nginx"
  replicas: 4
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: k8s.gcr.io/nginx-slim:0.8
        ports:
        - containerPort: 80
          name: web
      affinity:
        podAntiAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
          - labelSelector:
              matchExpressions:
              - key: app
                operator: In
                values:
                - nginx
            topologyKey: "kubernetes.io/hostname"

Это развернет web-0 в worker1, web-1 в worker2, web-2 в worker3 и web-3 в worker4.

person Arghya Sadhu    schedule 16.02.2020

Вы можете использовать следующее KubeMod ModRule:

apiVersion: api.kubemod.io/v1beta1
kind: ModRule
metadata:
  name: statefulset-pod-node-affinity
spec:
  type: Patch

  match:
    # Select pods named myapp-xxx.
    - select: '$.kind'
      matchValue: Pod
    - select: '$.metadata.name'
      matchRegex: myapp-.*

  patch:
    # Patch the selected pods such that their node affinity matches nodes that contain a label with the name of the pod.
    - op: add
      path: /spec/affinity/nodeAffinity/requiredDuringSchedulingIgnoredDuringExecution
      value: |-
        nodeSelectorTerms:
          - matchExpressions:
            - key: accept-pod/{{ .Target.metadata.name }}
              operator: In
              values:
                - 'true'

Вышеупомянутый ModRule будет отслеживать создание подов с именем myapp-* и вставит раздел nodeAffinity в их манифест ресурсов перед их развертыванием. Это даст указание планировщику назначить модуль для узла, для метки accept-pod/<pod-name> которого установлено значение true.

Затем вы можете назначить будущие модули узлам, добавив метки к узлам:

kubectl label node worker-1 accept-pod/myapp-0=true
kubectl label node worker-2 accept-pod/myapp-1=true
kubectl label node worker-3 accept-pod/myapp-2=true
...

После того, как указанное выше ModRule развернуто в вашем целевом пространстве имен и вы пометили свои узлы, развертывание StatefulSet запустит создание его модулей, которые, в свою очередь, запустят ModRule, который будет динамически вводить раздел nodeAffinity, используя имя модуля.

Удаление StatefulSet и последующее его создание приведет к тому, что поды будут запланированы на тех же точных узлах, что и раньше.

person vassilvk    schedule 01.06.2021