company_banner

Создание дополнительного kube-scheduler’a с кастомным набором правил планирования


    Kube-scheduler является неотъемлемым компонентом Kubernetes, который отвечает за планирование подов по нодам в соответствии с заданными политиками. Зачастую, в процессе эксплуатации Kubernetes-кластера нам не приходится задумываться о том, по каким именно политикам происходит планирование подов, так как набор политик дефолтного kube-scheduler’a подходит для большинства повседневных задач. Однако встречаются ситуации, когда нам важно тонко управлять процессом распределения подов, и для выполнения этой задачи есть два пути:


    1. Создать kube-scheduler с кастомным набором правил
    2. Написать свой собственный scheduler и научить его работать с запросами API-сервера

    В рамках данной статьи я опишу реализацию именно первого пункта для решения проблемы неравномерного планирования подов на одном из наших проектов.


    Краткая вводная о работе kube-scheduler’a


    Стоит особо отметить тот факт, что kube-scheduler не отвечает за непосредственное планирование подов — он отвечает только за определение ноды, на которую нужно разместить под. Иначе говоря, результат работы kube-scheduler’a — это имя ноды, которое он возвращает API-серверу на запрос о планировании и на этом его работа заканчивается.


    Сначала kube-scheduler составляет список нод, на которые может быть запланирован под в соответствии с политиками predicates. Далее каждая нода из этого списка получает определённое количество очков в соответствии с политиками priorites. В результате выбирается нода, набравшая максимальное количество очков. Если есть ноды, набравшие одинаковый максимальный балл, выбирается случайная. Со списком и описанием политик predicates (filtering) и priorites (scoring) можно ознакомиться в документации.


    Описание тела проблемы


    Несмотря на большое количество разных Kubernetes кластеров на обслуживании в Nixys, впервые с проблемой планирования подов мы столкнулись только недавно, когда для одного из наших проектов появилась необходимость запуска большого количества периодических задач (~100 сущностей CronJob). Чтобы максимально упростить описание проблемы, в качестве примера возьмём один микросервис, в рамках которого раз в минуту запускается cron-задача, создающая некоторую нагрузку на CPU. Для работы cron-задачи были выделены три абсолютно одинаковые по характеристикам ноды (24 vCPU на каждой).


    При этом нельзя с точностью сказать сколько времени будет выполняться CronJob, так как объём входных данных постоянно меняется. В среднем, при нормальной работе kube-scheduler’a, на каждой ноде работает 3-4 экземпляра задания, которые создают ~20-30% нагрузки на CPU каждой ноды:



    Сама проблема заключается в том, что иногда поды cron-задачи переставали планироваться на одну из трёх нод. То есть, в какой-то момент времени на одну из нод не планировалось ни одного пода, тогда как на двух других нодах работало по 6-8 экземпляров задания создавая ~40-60% нагрузки на CPU:



    Проблема повторялась с абсолютно случайной периодичностью и изредка коррелировала с моментом выкатки новой версии кода.


    Повысив уровень логирования kube-scheduler’a до 10 уровня (-v=10) мы начали фиксировать, сколько набирает очков в процессе оценки каждая из нод. При нормальной работе планирования в логах можно было увидеть следующую информацию:


    resource_allocation.go:78] cronjob-1574828880-mn7m4 -> Node03: BalancedResourceAllocation, capacity 23900 millicores 67167186944 memory bytes, total request 1387 millicores 4161694720 memory bytes, score 9
    resource_allocation.go:78] cronjob-1574828880-mn7m4 -> Node02: BalancedResourceAllocation, capacity 23900 millicores 67167186944 memory bytes, total request 1347 millicores 4444810240 memory bytes, score 9
    resource_allocation.go:78] cronjob-1574828880-mn7m4 -> Node03: LeastResourceAllocation, capacity 23900 millicores 67167186944 memory bytes, total request 1387 millicores 4161694720 memory bytes, score 9
    resource_allocation.go:78] cronjob-1574828880-mn7m4 -> Node01: BalancedResourceAllocation, capacity 23900 millicores 67167186944 memory bytes, total request 1687 millicores 4790840320 memory bytes, score 9
    resource_allocation.go:78] cronjob-1574828880-mn7m4 -> Node02: LeastResourceAllocation, capacity 23900 millicores 67167186944 memory bytes, total request 1347 millicores 4444810240 memory bytes, score 9
    resource_allocation.go:78] cronjob-1574828880-mn7m4 -> Node01: LeastResourceAllocation, capacity 23900 millicores 67167186944 memory bytes, total request 1687 millicores 4790840320 memory bytes, score 9
    generic_scheduler.go:726] cronjob-1574828880-mn7m4_project-stage -> Node01: NodeAffinityPriority, Score: (0)                                                                                       
    generic_scheduler.go:726] cronjob-1574828880-mn7m4_project-stage -> Node02: NodeAffinityPriority, Score: (0)                                                                                       
    generic_scheduler.go:726] cronjob-1574828880-mn7m4_project-stage -> Node03: NodeAffinityPriority, Score: (0)                                                                                       
    interpod_affinity.go:237] cronjob-1574828880-mn7m4 -> Node01: InterPodAffinityPriority, Score: (0)                                                                                                        
    generic_scheduler.go:726] cronjob-1574828880-mn7m4_project-stage -> Node01: TaintTolerationPriority, Score: (10)                                                                                   
    interpod_affinity.go:237] cronjob-1574828880-mn7m4 -> Node02: InterPodAffinityPriority, Score: (0)                                                                                                        
    generic_scheduler.go:726] cronjob-1574828880-mn7m4_project-stage -> Node02: TaintTolerationPriority, Score: (10)                                                                                   
    selector_spreading.go:146] cronjob-1574828880-mn7m4 -> Node01: SelectorSpreadPriority, Score: (10)                                                                                                        
    interpod_affinity.go:237] cronjob-1574828880-mn7m4 -> Node03: InterPodAffinityPriority, Score: (0)                                                                                                        
    generic_scheduler.go:726] cronjob-1574828880-mn7m4_project-stage -> Node03: TaintTolerationPriority, Score: (10)                                                                                   
    selector_spreading.go:146] cronjob-1574828880-mn7m4 -> Node02: SelectorSpreadPriority, Score: (10)                                                                                                        
    selector_spreading.go:146] cronjob-1574828880-mn7m4 -> Node03: SelectorSpreadPriority, Score: (10)                                                                                                        
    generic_scheduler.go:726] cronjob-1574828880-mn7m4_project-stage -> Node01: SelectorSpreadPriority, Score: (10)                                                                                    
    generic_scheduler.go:726] cronjob-1574828880-mn7m4_project-stage -> Node02: SelectorSpreadPriority, Score: (10)                                                                                    
    generic_scheduler.go:726] cronjob-1574828880-mn7m4_project-stage -> Node03: SelectorSpreadPriority, Score: (10)                                                                                    
    generic_scheduler.go:781] Host Node01 => Score 100043                                                                                                                                                                        
    generic_scheduler.go:781] Host Node02 => Score 100043                                                                                                                                                                        
    generic_scheduler.go:781] Host Node03 => Score 100043

    Т.е. судя по информации, полученной из логов, каждая из нод набирала равное количество итоговых очков и для планирования выбиралась случайная. В момент проблемного планирования логи выглядели следующим образом:


    resource_allocation.go:78] cronjob-1574211360-bzfkr -> Node02: BalancedResourceAllocation, capacity 23900 millicores 67167186944 memory bytes, total request 1587 millicores 4581125120 memory bytes, score 9
    resource_allocation.go:78] cronjob-1574211360-bzfkr -> Node03: BalancedResourceAllocation, capacity 23900 millicores 67167186944 memory bytes, total request 1087 millicores 3532549120 memory bytes, score 9
    resource_allocation.go:78] cronjob-1574211360-bzfkr -> Node02: LeastResourceAllocation, capacity 23900 millicores 67167186944 memory bytes, total request 1587 millicores 4581125120 memory bytes, score 9
    resource_allocation.go:78] cronjob-1574211360-bzfkr -> Node01: BalancedResourceAllocation, capacity 23900 millicores 67167186944 memory bytes, total request 987 millicores 3322833920 memory bytes, score 9
    resource_allocation.go:78] cronjob-1574211360-bzfkr -> Node01: LeastResourceAllocation, capacity 23900 millicores 67167186944 memory bytes, total request 987 millicores 3322833920 memory bytes, score 9 
    resource_allocation.go:78] cronjob-1574211360-bzfkr -> Node03: LeastResourceAllocation, capacity 23900 millicores 67167186944 memory bytes, total request 1087 millicores 3532549120 memory bytes, score 9
    interpod_affinity.go:237] cronjob-1574211360-bzfkr -> Node03: InterPodAffinityPriority, Score: (0)                                                                                                        
    interpod_affinity.go:237] cronjob-1574211360-bzfkr -> Node02: InterPodAffinityPriority, Score: (0)                                                                                                        
    interpod_affinity.go:237] cronjob-1574211360-bzfkr -> Node01: InterPodAffinityPriority, Score: (0)                                                                                                        
    generic_scheduler.go:726] cronjob-1574211360-bzfkr_project-stage -> Node03: TaintTolerationPriority, Score: (10)                                                                                   
    selector_spreading.go:146] cronjob-1574211360-bzfkr -> Node03: SelectorSpreadPriority, Score: (10)                                                                                                        
    selector_spreading.go:146] cronjob-1574211360-bzfkr -> Node02: SelectorSpreadPriority, Score: (10)                                                                                                        
    generic_scheduler.go:726] cronjob-1574211360-bzfkr_project-stage -> Node02: TaintTolerationPriority, Score: (10)                                                                                   
    selector_spreading.go:146] cronjob-1574211360-bzfkr -> Node01: SelectorSpreadPriority, Score: (10)                                                                                                        
    generic_scheduler.go:726] cronjob-1574211360-bzfkr_project-stage -> Node03: NodeAffinityPriority, Score: (0)                                                                                       
    generic_scheduler.go:726] cronjob-1574211360-bzfkr_project-stage -> Node03: SelectorSpreadPriority, Score: (10)                                                                                    
    generic_scheduler.go:726] cronjob-1574211360-bzfkr_project-stage -> Node02: SelectorSpreadPriority, Score: (10)                                                                                    
    generic_scheduler.go:726] cronjob-1574211360-bzfkr_project-stage -> Node01: TaintTolerationPriority, Score: (10)                                                                                   
    generic_scheduler.go:726] cronjob-1574211360-bzfkr_project-stage -> Node02: NodeAffinityPriority, Score: (0)                                                                                       
    generic_scheduler.go:726] cronjob-1574211360-bzfkr_project-stage -> Node01: NodeAffinityPriority, Score: (0)                                                                                       
    generic_scheduler.go:726] cronjob-1574211360-bzfkr_project-stage -> Node01: SelectorSpreadPriority, Score: (10)                                                                                    
    generic_scheduler.go:781] Host Node03 => Score 100041                                                                                                                                                                        
    generic_scheduler.go:781] Host Node02 => Score 100041                                                                                                                                                                        
    generic_scheduler.go:781] Host Node01 => Score 100038

    Из которых видно, что одна из нод набирала меньше итоговых очков чем остальные, и поэтому планирование выполнялось только на две ноды, набравшие максимальный балл. Таким образом мы точно убедились, что проблема заключается именно в планировании подов.


    Дальнейший алгоритм решения проблемы был для нас очевиден — проанализировать логи, понять по какому именно приоритету нода не добрала очков и, при необходимости, скорректировать политики дефолтного kube-scheduler’а. Однако здесь мы столкнулись с двумя существенными сложностями:


    1. На максимальном уровне логирования (10) отражается набор очков только по некоторым приоритетам. В приведённом выше отрывке логов можно заметить, что по всем приоритетам, отражённым в логах, ноды набирают одинаковое количество очков при нормальном и проблемном планировании, однако финальный результат в случае проблемного планирования отличается. Таким образом, можно сделать вывод, что по каким-то приоритетам подсчёт очков происходит “за кадром”, и у нас нет никакой возможности понять по какому именно приоритету нода не добрала очки. Данную проблему мы подробно описали в issue репозитория Kubernetes на Github. На момент написания статьи был получен ответ от разработчиков, что поддержка логирования будет добавлена в обновлениях Kubernetes v1.15,1.16 и 1.17.
    2. Нет простого способа понять с каким конкретно набором политик в данный момент работает kube-scheduler. Да, в документации этот список перечислен, но в нём нет информации какие конкретно веса выставлены каждой из политик priorites. Увидеть веса или отредактировать политики дефолтного kube-scheduler’a можно только в исходниках.

    Стоит отметить, один раз нам удалось зафиксировать, что нода не добирала очки по политике ImageLocalityPriority, которая начисляет очки ноде, если на ней уже есть образ, необходимый для запуска приложения. Т. е. в момент выкатки новой версии приложения cron-задача успевала запускаться на двух нодах, выкачивая на них новый образ из docker registry, и таким образом две ноды получали бОльший итоговый балл относительно третьей.


    Как я уже писал выше, в логах мы не видим информации об оценке политики ImageLocalityPriority, поэтому, чтобы проверить своё предположение, мы спулили образ с новой версией приложения на третью ноду, после чего планирование заработало корректно. Именно из-за политики ImageLocalityPriority проблема планирования наблюдалась достаточно редко, чаще она была связана с чем-то другим. Из-за того, что мы не могли полноценно дебажить каждую из политик в списке priorites дефолтного kube-scheduler’a, у нас появилась необходимость в гибком управлении политиками планирования подов.


    Постановка задачи


    Мы хотели, чтобы решение проблемы было максимально точечным, то есть основные сущности Kubernetes (тут имеется ввиду дефолтный kube-scheduler) должны оставаться неизменными. Нам не хотелось решать проблему в одном месте и создавать её в другом. Таким образом, мы пришли к двум вариантам решения проблемы, которые были озвучены во введении к статье — создание дополнительного scheduler’a или написание своего. Основное требование к планированию cron-задач — равномерное распределение нагрузки по трём нодам. Это требование можно удовлетворить уже существующими политиками kube-scheduler’a, поэтому для решения нашей задачи нет смысла писать свой собственный scheduler.


    Инструкция создания и Deployment дополнительного kube-scheduler’a описаны в документации. Однако, нам показалось, что сущности Deployment недостаточно для обеспечения отказоустойчивости в работе такого критичного сервиса как kube-scheduler, поэтому мы решили развернуть новый kube-scheduler как Static Pod, за которым будет следить непосредственно Kubelet. Таким образом, у нас сложились следующие требования к новому kube-scheduler’у:


    1. Сервис должен быть развёрнут как Static Pod на всех мастерах кластера
    2. Должна быть предусмотрена отказоустойчивость на случай недоступности активного пода с kube-scheduler’ом
    3. Основным приоритетом при планировании должно быть количество доступных ресурсов на ноде (LeastRequestedPriority)

    Реализация решения


    Стоит сразу отметить, что все работы мы будем проводить в Kubernetes v1.14.7, т.к. именно эта версия использовалась в проекте. Начнём с написания манифеста для нашего нового kube-scheduler’a. За основу возьмём манифест дефолтного (/etc/kubernetes/manifests/kube-scheduler.yaml) и приведём его к следующему виду:


    kind: Pod
    metadata:
      labels:
        component: scheduler
        tier: control-plane
      name: kube-scheduler-cron
      namespace: kube-system
    spec:
          containers:
          - command:
            - /usr/local/bin/kube-scheduler
            - --address=0.0.0.0
            - --port=10151
            - --secure-port=10159
            - --config=/etc/kubernetes/scheduler-custom.conf
            - --authentication-kubeconfig=/etc/kubernetes/scheduler.conf
            - --authorization-kubeconfig=/etc/kubernetes/scheduler.conf
            - --v=2
            image: gcr.io/google-containers/kube-scheduler:v1.14.7
            imagePullPolicy: IfNotPresent
            livenessProbe:
              failureThreshold: 8
              httpGet:
                host: 127.0.0.1
                path: /healthz
                port: 10151
                scheme: HTTP
              initialDelaySeconds: 15
              timeoutSeconds: 15
            name: kube-scheduler-cron-container
            resources:
              requests:
                cpu: '0.1'
            volumeMounts:
            - mountPath: /etc/kubernetes/scheduler.conf
              name: kube-config
              readOnly: true
            - mountPath: /etc/localtime
              name: localtime
              readOnly: true
            - mountPath: /etc/kubernetes/scheduler-custom.conf
              name: scheduler-config
              readOnly: true
            - mountPath: /etc/kubernetes/scheduler-custom-policy-config.json
              name: policy-config
              readOnly: true
          hostNetwork: true
          priorityClassName: system-cluster-critical
          volumes:
          - hostPath:
              path: /etc/kubernetes/scheduler.conf
              type: FileOrCreate
            name: kube-config
          - hostPath:
              path: /etc/localtime
            name: localtime
          - hostPath:
              path: /etc/kubernetes/scheduler-custom.conf
              type: FileOrCreate
            name: scheduler-config
          - hostPath:
              path: /etc/kubernetes/scheduler-custom-policy-config.json
              type: FileOrCreate
            name: policy-config

    Кратко по основным изменениям:


    1. Изменили имя пода и контейнера на kube-scheduler-cron
    2. Указали использование портов 10151 и 10159 так как определена опция hostNetwork: true и мы не можем использовать те же порты, что и дефолтный kube-scheduler (10251 и 10259)
    3. С помощью параметра --config указали файл конфигурации с которой должен запускаться сервис
    4. Настроили монтирование файла конфигурации (scheduler-custom.conf) и файла политик планирования (scheduler-custom-policy-config.json) с хоста

    Не забываем, что нашему kube-scheduler’у потребуются права, аналогичные дефолтному. Редактируем его кластерную роль:


    kubectl edit clusterrole system:kube-scheduler

    ...
       resourceNames:
        - kube-scheduler
        - kube-scheduler-cron
    ...

    Теперь поговорим о том, что должно содержаться в файле конфигурации и файле с политиками планирования:


    • Файл конфигурации (scheduler-custom.conf)
      Для получения конфигурации дефолтного kube-scheduler’a необходимо воспользоваться параметром --write-config-to из документации. Полученную конфигурацию разместим в файле /etc/kubernetes/scheduler-custom.conf и приведём к следующему виду:

    apiVersion: kubescheduler.config.k8s.io/v1alpha1
    kind: KubeSchedulerConfiguration
    schedulerName: kube-scheduler-cron
    bindTimeoutSeconds: 600
    clientConnection:
      acceptContentTypes: ""
      burst: 100
      contentType: application/vnd.kubernetes.protobuf
      kubeconfig: /etc/kubernetes/scheduler.conf
      qps: 50
    disablePreemption: false
    enableContentionProfiling: false
    enableProfiling: false
    failureDomains: kubernetes.io/hostname,failure-domain.beta.kubernetes.io/zone,failure-domain.beta.kubernetes.io/region
    hardPodAffinitySymmetricWeight: 1
    healthzBindAddress: 0.0.0.0:10151
    leaderElection:
      leaderElect: true
      leaseDuration: 15s
      lockObjectName: kube-scheduler-cron
      lockObjectNamespace: kube-system
      renewDeadline: 10s
      resourceLock: endpoints
      retryPeriod: 2s
    metricsBindAddress: 0.0.0.0:10151
    percentageOfNodesToScore: 0
    algorithmSource:
       policy:
         file:
           path: "/etc/kubernetes/scheduler-custom-policy-config.json"

    Кратко по основным изменениям:


    1. Задали в schedulerName имя нашего сервиса kube-scheduler-cron.
    2. В параметре lockObjectName также нужно задать имя нашего сервиса и убедиться, что параметр leaderElect выставлен в значение true (в случае, если у вас одна мастер-нода, можно выставить значение false).
    3. Указали путь к файлу с описанием политик планирования в параметре algorithmSource.

    Стоит более подробно остановиться на втором пункте, где мы редактируем параметры для ключа leaderElection. Для обеспечения отказоустойчивости мы активировали (leaderElect) процесс выбора ведущего (мастера) между подами нашего kube-scheduler’a с помощью использования единого для них endpoint (resourceLock) с именем kube-scheduler-cron (lockObjectName) в пространстве имён kube-system (lockObjectNamespace). О том как в Kubernetes обеспечивается высокая доступность основных компонентов (в том числе kube-scheduler) можно ознакомиться в статье.


    • Файл политик планирования (scheduler-custom-policy-config.json)
      Как я уже писал ранее — узнать с какими конкретно политиками работает дефолтный kube-scheduler мы можем только анализируя его код. То есть мы не можем получить файл с политиками планирования дефолтного kube-scheduler’а по аналогии с файлом конфигурации. Опишем интересующие нас политики планирования в файле /etc/kubernetes/scheduler-custom-policy-config.json следующим образом:

    {
      "kind": "Policy",
      "apiVersion": "v1",
      "predicates": [
        {
          "name": "GeneralPredicates"
        }
      ],
      "priorities": [
        {
          "name": "ServiceSpreadingPriority",
          "weight": 1
        },
        {
          "name": "EqualPriority",
          "weight": 1
        },
        {
          "name": "LeastRequestedPriority",
          "weight": 1
        },
        {
          "name": "NodePreferAvoidPodsPriority",
          "weight": 10000
        },
        {
          "name": "NodeAffinityPriority",
          "weight": 1
        }
      ],
      "hardPodAffinitySymmetricWeight" : 10,
      "alwaysCheckAllPredicates" : false
    }

    Таким образом, kube-scheduler сначала составляет список нод, на которые может быть запланирован под в соответствии с политикой GeneralPredicates (которая включает в себя набор политик PodFitsResources, PodFitsHostPorts, HostName и MatchNodeSelector). И далее производится оценка каждой ноды в соответствии с набором политик в массиве priorities. Для выполнения условий нашей задачи мы посчитали, что такой набор политик будет оптимальным решением. Напомню, что набор политик с их подробным описанием доступен в документации. Для выполнения своей задачи вы можете просто изменить набор используемых политик и назначить им соответствующие веса.


    Манифест нового kube-scheduler’а, который мы создавали в начале главы, назовём kube-scheduler-custom.yaml и разместим по следующему пути /etc/kubernetes/manifests на трёх мастер-нодах. Если всё выполнено правильно, Kubelet на каждой ноде запустит под, а в логах нашего нового kube-scheduler’а мы увидим информацию о том, что наш файл с политиками успешно применился:


    Creating scheduler from configuration: {{ } [{GeneralPredicates <nil>}] [{ServiceSpreadingPriority 1 <nil>} {EqualPriority 1 <nil>} {LeastRequestedPriority 1 <nil>} {NodePreferAvoidPodsPriority 10000 <nil>} {NodeAffinityPriority 1 <nil>}] [] 10 false}
    Registering predicate: GeneralPredicates
    Predicate type GeneralPredicates already registered, reusing.
    Registering priority: ServiceSpreadingPriority
    Priority type ServiceSpreadingPriority already registered, reusing.
    Registering priority: EqualPriority
    Priority type EqualPriority already registered, reusing.
    Registering priority: LeastRequestedPriority
    Priority type LeastRequestedPriority already registered, reusing.
    Registering priority: NodePreferAvoidPodsPriority
    Priority type NodePreferAvoidPodsPriority already registered, reusing.
    Registering priority: NodeAffinityPriority
    Priority type NodeAffinityPriority already registered, reusing.
    Creating scheduler with fit predicates 'map[GeneralPredicates:{}]' and priority functions 'map[EqualPriority:{} LeastRequestedPriority:{} NodeAffinityPriority:{} NodePreferAvoidPodsPriority:{} ServiceSpreadingPriority:{}]'

    Теперь остаётся только указать в spec’е нашей CronJob’ы, что все запросы на планирование её pod’ов должен обрабатывать наш новый kube-scheduler:


    ...
     jobTemplate:
        spec:
          template:
            spec:
              schedulerName: kube-scheduler-cron
    ...

    Заключение


    В конечном итоге мы получили дополнительный kube-scheduler с уникальным набором политик планирования, за работой которого следит непосредственно kubelet. Кроме того мы настроили выборы нового лидера между подами нашего kube-scheduler’а в случае, если старый лидер по каким-то причинам становится недоступен.


    Обычные приложения и сервисы продолжают планироваться через дефолтный kube-scheduler, а все cron-задачи полностью переведены на новый. Нагрузка, создаваемая cron-задачами, теперь равномерно распределяется по всем нодам. Учитывая, что большая часть cron-задач выполняется на тех же нодах, что и основные приложения проекта, это позволило значительно снизить риск переезда подов из-за нехватки ресурсов. После внедрения дополнительного kube-scheduler’а, проблем с неравномерным планированием cron-задач больше не возникало.


    Также читайте другие статьи в нашем блоге:


    Nixys
    Эксперты в DevOps и Kubernetes

    Комментарии 2

      0
      Спасибо за отличную статью. Собирался как раз изучить вопрос, а тут такой подарок.
      Очень жаль, конечно, что политики дефолтного шедулера не описаны yaml'ом.

      Вопрос: а нет подозрений какие политики кастомного шедулера привели к нормальному поведению?

      Побежал накручивать мониторинг распределения подов в кластере :)
        +1
        Спасибо за отличную статью. Собирался как раз изучить вопрос, а тут такой подарок.

        Рад, что статья зашла! :)


        Очень жаль, конечно, что политики дефолтного шедулера не описаны yaml'ом.

        На эту тему, кстати, есть issue.


        Вопрос: а нет подозрений какие политики кастомного шедулера привели к нормальному поведению?

        В нашем примере описания файла с политиками, при нормальной работе кластера, очки существенно изменяются только у одной политики — LeastRequestedPriority. Эта политика начисляет больше очков наименее загруженной ноде.

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

      Самое читаемое