# Распределение подов по узлам

LLMS index: [llms.txt](/llms.txt)

---

<!-- overview -->

Можно настроить <a class='glossary-tooltip' title='Самый маленький и простой объект в Kubernetes. Под — это набор запущенных контейнеров в кластере.' data-bs-toggle='tooltip' data-bs-placement='top' href='/ru/docs/concepts/workloads/pods/pod-overview/' target='_blank' aria-label='под'>под</a> так, чтобы тот мог запускаться _только_ на определенном(-ых) <a class='glossary-tooltip' title='Узел — рабочая машина в Kubernetes.' data-bs-toggle='tooltip' data-bs-placement='top' href='/ru/docs/concepts/architecture/nodes/' target='_blank' aria-label='узле(-ах)'>узле(-ах)</a> или _предпочитал_ определенную группу узлов.
Сделать это можно несколькими способами, при этом все рекомендуемые подходы используют [селекторы лейблов](/ru/docs/concepts/overview/working-with-objects/labels/) для выбора.
Зачастую нужды в подобных ограничениях нет; <a class='glossary-tooltip' title='Компонент управляющего слоя, который отслеживает недавно созданные поды без назначенного для них узла и выбирает узел, на котором они должны работать.' data-bs-toggle='tooltip' data-bs-placement='top' href='/ru/docs/reference/generated/kube-scheduler/' target='_blank' aria-label='планировщик'>планировщик</a> автоматически размещает поды оптимальным образом (например, распределяет их по узлам, чтобы они не оказались все на одном узле с дефицитом ресурсов).
Однако в некоторых обстоятельствах возможность контролировать, куда именно попадет под, может пригодиться. Например, она поможет запланировать под на узел с быстрым SSD-хранилищем или разместить два активно взаимодействующих друг с другом пода в одной зоне доступности.

<!-- body -->

Для планирования подов на определенные узлы можно использовать любой из методов:

  * Поле [nodeSelector](#nodeselector) и [лейблы узлов](#built-in-node-labels);
  * Правила [совместного/раздельного существования (affinity и anti-affinity)](#affinity-and-anti-affinity);
  * Поле [nodeName](#nodename);
  * [Ограничения на топологию распределения подов](#pod-topology-spread-constraints).

## Лейблы узлов {#built-in-node-labels}

Как и у многих других объектов Kubernetes, у узлов есть [лейблы](/ru/docs/concepts/overview/working-with-objects/labels/). Их можно [навешивать вручную](/docs/tasks/configure-pod-container/assign-pods-nodes/#add-a-label-to-a-node).
Kubernetes также навешивает [стандартный набор лейблов](/docs/reference/node/node-labels/) на все узлы кластера.


<div class="alert alert-info" role="note"><h4 class="alert-heading">Примечание:</h4>Значения этих лейблов зависят от облачного провайдера, поэтому на них нельзя полагаться.
Например, в одних окружениях значение <code>kubernetes.io/hostname</code> может совпадать с именем узла, в других — отличаться.</div>


### Изоляция узла/ограничение его использования

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

При использовании лейблов для изоляции узлов следует выбирать ключи лейблов, которые <a class='glossary-tooltip' title='Агент, работающий на каждом узле в кластере. Он следит за тем, чтобы контейнеры были запущены в поде.' data-bs-toggle='tooltip' data-bs-placement='top' href='/ru/docs/reference/command-line-tools-reference/kubelet' target='_blank' aria-label='kubelet'>kubelet</a> не может изменить. В этом случае взломанный узел не сможет навесить на себя эти лейблы в надежде, что планировщик разместит на него рабочие нагрузки.

[Admission-плагин NodeRestriction](/docs/reference/access-authn-authz/admission-controllers/#noderestriction) не позволяет kubelet'у устанавливать или изменять лейблы с префиксом `node-restriction.kubernetes.io/`.

Чтобы использовать этот префикс для изоляции узла:

1. Убедитесь, что используется [авторизатор узлов](/docs/reference/access-authn-authz/node/) и _включен_ admission-плагин `NodeRestriction`.
2. Добавьте лейблы с префиксом `node-restriction.kubernetes.io/` к узлам и используйте их в [селекторах узлов](#nodeselector). Например, `example.com.node-restriction.kubernetes.io/fips=true` или `example.com.node-restriction.kubernetes.io/pci-dss=true`.

## nodeSelector

`nodeSelector` — простейшая рекомендуемая форма настроек выбора узлов.
Можно добавить поле `nodeSelector` в спецификацию пода и перечислить в нем [лейблы узлов](#built-in-node-labels), которые подходят для развертывания пода. В этом случае Kubernetes будет планировать под только на узлы со всеми указанными лейблами.

Дополнительную информацию см. в разделе [Размещение подов на узлах](/docs/tasks/configure-pod-container/assign-pods-nodes).

## Правила совместного/раздельного существования (affinity и anti-affinity) {#affinity-and-anti-affinity}

`nodeSelector` — самый простой способ развернуть поды на узлах с определенными лейблами. Правила совместного/раздельного существования расширяют типы ограничений, которые можно накладывать. Вот некоторые из их преимуществ:

* Язык правил affinity/anti-affinity более выразителен. `nodeSelector` выбирает узлы только со всеми указанными лейблами. Правила affinity/anti-affinity расширяют логику выбора и делают ее более гибкой.
* Правило может быть *мягким* (soft) или *предпочтительным* (preferred). В этом случае планировщик все равно запланирует под, даже если подходящего узла для него не найдется.
* При планировании пода планировщик может учитывать лейблы других подов, запущенных на узле (или в иной топологической области), а не только лейблы самого узла. Это позволяет формулировать правила, определяющие сосуществование подов на узле.

Правила совместного существования (affinity) бывают двух типов:

* *Правила для узлов* (node affinity) работают подобно полю `nodeSelector`, но более выразительны. Кроме того, можно задавать мягкие правила. 
* *Правила для подов* (inter-pod affinity и anti-affinity) позволяют при планировании учитывать лейблы других подов.

### Правила совместного существования для узлов (node affinity) {#node-affinity}

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

  * `requiredDuringSchedulingIgnoredDuringExecution`: Планировщик не может запланировать под, если правило не выполнено. Работает как `nodeSelector`, но с более выразительным синтаксисом.
  * `preferredDuringSchedulingIgnoredDuringExecution`: Планировщик пытается найти узел, который соответствует правилу. Если подходящий узел не удается найти, планировщик все равно планирует под.


<div class="alert alert-info" role="note"><h4 class="alert-heading">Примечание:</h4>В типах выше <code>IgnoredDuringExecution</code> означает, что под продолжит свою работу, если лейблы узлов изменятся после того, как Kubernetes запланировал его.</div>


Задавать правила совместного существования для узлов можно с помощью поля `.spec.affinity.nodeAffinity` в спецификации пода.

В качестве примера рассмотрим следующую спецификацию пода:

















<div class="highlight code-sample">
    <div class="copy-code-icon">
    <a href="https://raw.githubusercontent.com/kubernetes/website/main/content/ru/examples/pods/pod-with-node-affinity.yaml" download="pods/pod-with-node-affinity.yaml"><code>pods/pod-with-node-affinity.yaml</code>
    </a><img src="/images/copycode.svg" class="icon-copycode" onclick="copyCode('pods-pod-with-node-affinity-yaml')" title="Copy pods/pod-with-node-affinity.yaml to clipboard"></img></div>
    <div class="includecode" id="pods-pod-with-node-affinity-yaml"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">apiVersion</span><span class="p">:</span><span class="w"> </span><span class="l">v1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">Pod</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">metadata</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">with-node-affinity</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">spec</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">affinity</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">nodeAffinity</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">requiredDuringSchedulingIgnoredDuringExecution</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">nodeSelectorTerms</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span>- <span class="nt">matchExpressions</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span>- <span class="nt">key</span><span class="p">:</span><span class="w"> </span><span class="l">topology.kubernetes.io/zone</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="nt">operator</span><span class="p">:</span><span class="w"> </span><span class="l">In</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="nt">values</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span>- <span class="l">antarctica-east1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span>- <span class="l">antarctica-west1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">preferredDuringSchedulingIgnoredDuringExecution</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="nt">weight</span><span class="p">:</span><span class="w"> </span><span class="m">1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">preference</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">matchExpressions</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span>- <span class="nt">key</span><span class="p">:</span><span class="w"> </span><span class="l">another-node-label-key</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="nt">operator</span><span class="p">:</span><span class="w"> </span><span class="l">In</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="nt">values</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span>- <span class="l">another-node-label-value</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">containers</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">with-node-affinity</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">image</span><span class="p">:</span><span class="w"> </span><span class="l">registry.k8s.io/pause:2.0</span></span></span></code></pre></div></div>
</div>

В этом примере применяются следующие правила:

  * У узла *должен* быть лейбл с ключом `topology.kubernetes.io/zone`, и значение этого лейбла *должно* быть либо `antarctica-east1`, либо `antarctica-west1`.
  * *Предпочтительно*, чтобы у узла был лейбл с ключом `another-node-label-key` и значением `another-node-label-value`.

Можно использовать поле `operator` для указания логического оператора, который Kubernetes будет применять при интерпретации правил. Доступны `In`, `NotIn`, `Exists`, `DoesNotExist`, `Gt` и `Lt`.

Узнать больше о том, как они работают, можно в подразделе [Операторы](/#operators).

`NotIn` и `DoesNotExist` позволяют задавать правила раздельного существования (anti-affinity) для узла.
Кроме того, можно использовать [taint'ы узлов](/docs/concepts/scheduling-eviction/taint-and-toleration/), чтобы "отвадить" поды от определенных узлов.


<div class="alert alert-info" role="note"><h4 class="alert-heading">Примечание:</h4><p>Когда указаны и <code>nodeSelector</code>, и <code>nodeAffinity</code>, под будет запланирован на узел только в том случае, если <em>оба</em> этих условия удовлетворены.</p>
<p>Если указать несколько условий <code>nodeSelectorTerms</code>, привязанных к типам <code>nodeAffinity</code>, то под может быть запланирован на узел, если удовлетворяется одно из указанных условий <code>nodeSelectorTerms</code>. К условиям применяется логическое ИЛИ.</p>
<p>Если задать несколько выражений в поле <code>matchExpressions</code> для одного условия <code>nodeSelectorTerms</code>, под будет запланирован на узел только если удовлетворены все выражения <code>matchExpressions</code>. К условиям применяется логическое И.</p>
</div>


Дополнительную информацию см. в разделе [Размещаем поды на узлы с помощью Node Affinity](/docs/tasks/configure-pod-container/assign-pods-nodes-using-node-affinity/).

#### Вес правил совместного существования

Для каждого правила типа `preferredDuringSchedulingIgnoredDuringExecution` можно указать вес `weight` в диапазоне от 1 до 100. Найдя несколько узлов, удовлетворяющих всем остальным требованиям для планирования пода, планировщик перебирает предпочтительные правила, которым удовлетворяет узел, и суммирует их веса.

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

В качестве примера рассмотрим следующую спецификацию пода:

















<div class="highlight code-sample">
    <div class="copy-code-icon">
    <a href="https://raw.githubusercontent.com/kubernetes/website/main/content/ru/examples/pods/pod-with-affinity-anti-affinity.yaml" download="pods/pod-with-affinity-anti-affinity.yaml"><code>pods/pod-with-affinity-anti-affinity.yaml</code>
    </a><img src="/images/copycode.svg" class="icon-copycode" onclick="copyCode('pods-pod-with-affinity-anti-affinity-yaml')" title="Copy pods/pod-with-affinity-anti-affinity.yaml to clipboard"></img></div>
    <div class="includecode" id="pods-pod-with-affinity-anti-affinity-yaml"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">apiVersion</span><span class="p">:</span><span class="w"> </span><span class="l">v1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">Pod</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">metadata</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">with-affinity-anti-affinity</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">spec</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">affinity</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">nodeAffinity</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">requiredDuringSchedulingIgnoredDuringExecution</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">nodeSelectorTerms</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span>- <span class="nt">matchExpressions</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span>- <span class="nt">key</span><span class="p">:</span><span class="w"> </span><span class="l">kubernetes.io/os</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="nt">operator</span><span class="p">:</span><span class="w"> </span><span class="l">In</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="nt">values</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span>- <span class="l">linux</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">preferredDuringSchedulingIgnoredDuringExecution</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="nt">weight</span><span class="p">:</span><span class="w"> </span><span class="m">1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">preference</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">matchExpressions</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span>- <span class="nt">key</span><span class="p">:</span><span class="w"> </span><span class="l">label-1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="nt">operator</span><span class="p">:</span><span class="w"> </span><span class="l">In</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="nt">values</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span>- <span class="l">key-1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="nt">weight</span><span class="p">:</span><span class="w"> </span><span class="m">50</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">preference</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">matchExpressions</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span>- <span class="nt">key</span><span class="p">:</span><span class="w"> </span><span class="l">label-2</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="nt">operator</span><span class="p">:</span><span class="w"> </span><span class="l">In</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="nt">values</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span>- <span class="l">key-2</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">containers</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">with-node-affinity</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">image</span><span class="p">:</span><span class="w"> </span><span class="l">registry.k8s.io/pause:2.0</span><span class="w">
</span></span></span></code></pre></div></div>
</div>

Если правилу `preferredDuringSchedulingIgnoredDuringExecution` соответствуют два узла (один — с лейблом `label-1:key-1`, другой — с `label-2:key-2`), планировщик считает вес `weight` каждого узла и добавляет его к другим оценкам для этого узла. Под планируется на узел с наивысшей итоговой оценкой.


<div class="alert alert-info" role="note"><h4 class="alert-heading">Примечание:</h4>Чтобы Kubernetes смог запланировать поды в этом примере, необходимо, чтобы существовали узлы с лейблом <code>kubernetes.io/os=linux</code>.</div>


#### Правила совместного существования и профили планирования








  <div class="feature-state-notice feature-beta">
      <span class="feature-state-name">СТАТУС ФИЧИ:</span>
      <code>Kubernetes v1.20 [beta]</code>
    </div>
  



При настройке нескольких [профилей планирования](/docs/reference/scheduling/config/#multiple-profiles) можно связать профиль с правилами совместного существования для узлов (это удобно, когда профиль применяется к определенному набору узлов). Для этого необходимо добавить `addedAffinity` в поле `args` [плагина `NodeAffinity`](/docs/reference/scheduling/config/#scheduling-plugins) в [конфигурации планировщика](/docs/reference/scheduling/config/). Например:

```yaml
apiVersion: kubescheduler.config.k8s.io/v1
kind: KubeSchedulerConfiguration

profiles:
  - schedulerName: default-scheduler
  - schedulerName: foo-scheduler
    pluginConfig:
      - name: NodeAffinity
        args:
          addedAffinity:
            requiredDuringSchedulingIgnoredDuringExecution:
              nodeSelectorTerms:
              - matchExpressions:
                - key: scheduler-profile
                  operator: In
                  values:
                  - foo
```

Правило `addedAffinity` применяется ко всем подам с полем `.spec.schedulerName`, имеющим значение `foo-scheduler`, в дополнение к NodeAffinity, заданному в PodSpec. Таким образом, подходящие для пода узлы должны удовлетворять параметрам `addedAffinity` и правилам `.spec.NodeAffinity` пода.

Поскольку конечные пользователи не видят `addedAffinity`, результат применения этих правил может быть для них неожиданным. Используйте лейблы узлов, которые однозначно соотносятся с именем профиля планировщика.


<div class="alert alert-info" role="note"><h4 class="alert-heading">Примечание:</h4>Контроллер DaemonSet, <a href="/ru/docs/concepts/workloads/controllers/daemonset/#scheduled-by-default-scheduler">создающий поды для DaemonSet'ов</a>, не поддерживает профили планирования. Контроллер DaemonSet создает поды, а штатный планировщик Kubernetes, размещая их, следит за соблюдением правил <code>nodeAffinity</code>.</div>


### Правила совместного/раздельного существования подов

Правила совместного/раздельного существования подов позволяют выбирать узлы для планирования в зависимости от лейблов **подов**, которые уже на этих узлах работают (вместо лейблов самих узлов). 

Алгоритм работы правил совместного/раздельного существования подов можно описать так: "данный под _должен_ (или _не должен_ в случае раздельного (anti-affinity) существования) размещаться на X, если на нем уже работает один или несколько подов, удовлетворяющих правилу Y", где X — топологический домен, например, узел, стойка, зона/регион облачного провайдера и т. п., а Y — правило, которое Kubernetes пытается удовлетворить.

Для задания правил Y используются [селекторы лейблов](/ru/docs/concepts/overview/working-with-objects/labels/#селекторы-меток) с необязательным связанным списком пространств имен. Для подов в Kubernetes указываются пространства имен, соответственно, их лейблы также оказываются неявно связаны с этими же пространствами имен. Любые селекторы лейблов для лейблов подов должны содержать пространства имен, в которых Kubernetes должен искать эти лейблы.

Топологический домен X задается с помощью `topologyKey` — ключа для лейбла узла, который система использует для обозначения домена. Примеры см. в разделе [Типичные лейблы, аннотации и taint'ы](/docs/reference/labels-annotations-taints/).


<div class="alert alert-info" role="note"><h4 class="alert-heading">Примечание:</h4>Обработка правил совместного/раздельного существования подов требует значительных ресурсов и может значительно замедлить планирование в больших кластерах. Их не рекомендуется использовать в кластерах, число узлов в которых превышает несколько сотен.</div>



<div class="alert alert-info" role="note"><h4 class="alert-heading">Примечание:</h4>Правила раздельного существования для подов требуют согласованности лейблов узлов. Другими словами, у каждого узла в кластере должен быть лейбл, относящий его к определенному ключу <code>topologyKey</code>. Отсутствие лейблов <code>topologyKey</code> у некоторых или всех узлов в кластере может привести к непредсказуемому поведению.</div>


#### Типы правил совместного/раздельного существования подов

По аналогии [c правилами для узлов](#node-affinity) существует два типа правил для подов:

  * `requiredDuringSchedulingIgnoredDuringExecution`
  * `preferredDuringSchedulingIgnoredDuringExecution`

Например, с помощью правила совместного существования `requiredDuringSchedulingIgnoredDuringExecution` можно заставить планировщик размещать поды, относящиеся к разным сервисам, в одной зоне облачного провайдера, поскольку они активно обмениваются данными друг с другом. 

Аналогичным образом можно использовать правило раздельного существования `preferredDuringSchedulingIgnoredDuringExecution` для распределения подов по нескольким зонам облачного провайдера.

Для задания правил совместного существования предназначено поле `affinity.podAffinity` в спецификации пода.

Для задания правил раздельного существования предназначено поле `affinity.podAntiAffinity` в спецификации пода.

#### Планирование группы подов, связанных правилами совместного существования

Если планируемый под — первый в серии подов, связанных правилами совместного существования,
он может быть запланирован, если удовлетворит всем остальным правилам совместного существования. Чтобы подтвердить, что этот под — действительно первый,
проводится проверка, которая должна показать, что пространство имен и селектор этого пода уникальны в кластере (то есть нет других таких подов). Кроме того,
под должен соответствовать своим собственным правилам, а выбранный узел — всем запрошенным топологиям.
Это предотвращает тупиковую ситуацию, когда поды не могут запланироваться из-за того, что все они связаны правилами совместного существования.

#### Пример правил совместного/раздельного существования для пода {#an-example-of-a-pod-that-uses-pod-affinity}

Рассмотрим следующую спецификацию пода:

















<div class="highlight code-sample">
    <div class="copy-code-icon">
    <a href="https://raw.githubusercontent.com/kubernetes/website/main/content/ru/examples/pods/pod-with-pod-affinity.yaml" download="pods/pod-with-pod-affinity.yaml"><code>pods/pod-with-pod-affinity.yaml</code>
    </a><img src="/images/copycode.svg" class="icon-copycode" onclick="copyCode('pods-pod-with-pod-affinity-yaml')" title="Copy pods/pod-with-pod-affinity.yaml to clipboard"></img></div>
    <div class="includecode" id="pods-pod-with-pod-affinity-yaml"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">apiVersion</span><span class="p">:</span><span class="w"> </span><span class="l">v1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">Pod</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">metadata</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">with-pod-affinity</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">spec</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">affinity</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">podAffinity</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">requiredDuringSchedulingIgnoredDuringExecution</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="nt">labelSelector</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">matchExpressions</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span>- <span class="nt">key</span><span class="p">:</span><span class="w"> </span><span class="l">security</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="nt">operator</span><span class="p">:</span><span class="w"> </span><span class="l">In</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="nt">values</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span>- <span class="l">S1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">topologyKey</span><span class="p">:</span><span class="w"> </span><span class="l">topology.kubernetes.io/zone</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">podAntiAffinity</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">preferredDuringSchedulingIgnoredDuringExecution</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="nt">weight</span><span class="p">:</span><span class="w"> </span><span class="m">100</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">podAffinityTerm</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">labelSelector</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="nt">matchExpressions</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span>- <span class="nt">key</span><span class="p">:</span><span class="w"> </span><span class="l">security</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">              </span><span class="nt">operator</span><span class="p">:</span><span class="w"> </span><span class="l">In</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">              </span><span class="nt">values</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">              </span>- <span class="l">S2</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">topologyKey</span><span class="p">:</span><span class="w"> </span><span class="l">topology.kubernetes.io/zone</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">containers</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">with-pod-affinity</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">image</span><span class="p">:</span><span class="w"> </span><span class="l">registry.k8s.io/pause:2.0</span><span class="w">
</span></span></span></code></pre></div></div>
</div>

В примере задается одно правило совместного существования подов, и одно — раздельного. Для совместного правила используется жесткий тип `requiredDuringSchedulingIgnoredDuringExecution`, для раздельного — мягкий `preferredDuringSchedulingIgnoredDuringExecution`.

Правило совместного существования гласит, что планировщик может разместить под на узел, только если тот находится в зоне с одним или более подами с лейблом `security=S1`. 
Например, если есть кластер с выделенной зоной, назовем ее "V",
состоящей из узлов с лейблом `topology.kubernetes.io/zone=V`, планировщик может
назначить под на любой узел зоны V только если в этой зоне уже есть хотя бы один под
с лейблом `security=S1`. И наоборот, если в зоне V нет подов с лейблом `security=S1`,
планировщик не сможет назначить под на какой-либо из узлов в этой зоне.

Правило раздельного существования гласит, что планировщик при возможности не должен размещать под на узел, если тот находится в зоне с одним или более подами с лейблом `security=S2`. 
Например, если есть кластер с выделенной зоной, назовем ее "R",
состоящей из узлов с лейблами `topology.kubernetes.io/zone=R`. Планировщик должен избегать
назначать поды на узлы из зоны R, если в ней уже есть по крайней мере один под
с лейблом `security=S2`. Соответственно, если в зоне R нет подов с лейблами `security=S2`,
правило раздельного существования не будет влиять на планирование подов в эту зону.

Больше примеров правил совместного/раздельного существования для подов можно найти в [рабочей документации](https://git.k8s.io/design-proposals-archive/scheduling/podaffinity.md).

Поле `operator` пода поддерживает значения `In`, `NotIn`, `Exists` и `DoesNotExist` при задании правил совместного/раздельного существования.

Узнать больше о том, как они работают, можно в подразделе [Операторы](/#operators).

В принципе, `topologyKey` может быть любым разрешенным лейблом-ключом со следующими исключениями по соображениям производительности и безопасности:

* При задании правил совместного/раздельного существования для подов пустое поле `topologyKey` не допускается как для `requiredDuringSchedulingIgnoredDuringExecution`, так и для `preferredDuringSchedulingIgnoredDuringExecution`.
* Для правил раздельного существования типа `requiredDuringSchedulingIgnoredDuringExecution` admission-контроллер разрешает использовать только `kubernetes.io/hostname` в качестве `topologyKey`. Для работы с кастомными топологиями admission-контроллер можно дополнить или совсем отключить его.

В дополнение к `labelSelector` и `topologyKey` можно опционально указать список пространств имен, которые должен выбирать `labelSelector`, с помощью поля `namespaces` на том же уровне, что `labelSelector` и `topologyKey`. Если поле `namespaces` опущено или пусто, по умолчанию выбирается пространство имен пода, в котором задаются правила совместного/раздельного существования.

#### Селектор пространств имен







  <div class="feature-state-notice feature-stable">
      <span class="feature-state-name">СТАТУС ФИЧИ:</span>
      <code>Kubernetes v1.24 [stable]</code>
    </div>
  



Подходящие пространства имен также можно выбрать с помощью `namespaceSelector`, который попытается найти лейбл в наборе пространств имен.
Условия совместного существования применяются к пространствам имен, выбранным как селектором `namespaceSelector`, так и полем `namespaces`.
Обратите внимание, что пустой селектор `namespaceSelector` ({}) выбирает все пространства имен, в то время как пустой или null-список `namespaces` и null-селектор `namespaceSelector` выбирает пространство имен пода, в котором правило задано.

#### matchLabelKeys








  <div class="feature-state-notice feature-stable" title="Feature Gate: MatchLabelKeysInPodAffinity">
              <span class="feature-state-name">СТАТУС ФИЧИ:</span> 
              <code>Kubernetes v1.33 [stable]</code>(enabled by default)</div>



<div class="alert alert-info" role="note"><h4 class="alert-heading">Примечание:</h4><!-- UPDATE THIS WHEN PROMOTING TO BETA -->
<p>Поле <code>matchLabelKeys</code> является полем альфа-уровня и по умолчанию отключено в
Kubernetes 1.36.
Включить его можно с помощью <a href="/docs/reference/command-line-tools-reference/feature-gates/">переключателя функциональности</a>
(feature gate) <code>MatchLabelKeysInPodAffinity</code>.</p></div>


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

Ключи используются для поиска значений в лейблах подов; эти ключи-лейблы объединяются (с помощью `AND`) 
с ограничениями, задаваемыми с помощью поля `labelSelector`. Такая комбинированная фильтрация позволяет отобрать 
набор существующих подов, которые приниматься в расчет при обработке правил совместного (раздельного) существования.

Обычно `matchLabelKeys` используется вместе с `pod-template-hash` (задается для подов,
которые управляются как часть деплоймента, где значение уникально для каждой ревизии).
Использование `pod-template-hash` в `matchLabelKeys` позволяет нацеливаться на поды, принадлежащие
к той же ревизии, что и входящий под. Таким образом, что скользящее обновление не нарушит правила совместного существования.

```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: application-server
...
spec:
  template:
    spec:
      affinity:
        podAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
          - labelSelector:
              matchExpressions:
              - key: app
                operator: In
                values:
                - database
            topologyKey: topology.kubernetes.io/zone
            # При расчете affinity подов учитываются только поды из определенного выката.
            # При обновлении Deployment'а новые поды будут следовать своим собственным правилам совместного существования
            # (если они заданы в новом шаблоне подов)
            matchLabelKeys: 
            - pod-template-hash
```

#### mismatchLabelKeys








  <div class="feature-state-notice feature-stable" title="Feature Gate: MatchLabelKeysInPodAffinity">
              <span class="feature-state-name">СТАТУС ФИЧИ:</span> 
              <code>Kubernetes v1.33 [stable]</code>(enabled by default)</div>



<div class="alert alert-info" role="note"><h4 class="alert-heading">Примечание:</h4><!-- UPDATE THIS WHEN PROMOTING TO BETA -->
<p>Поле <code>mismatchLabelKeys</code> является полем альфа-уровня и по умолчанию отключено в
Kubernetes 1.36.
Включить его можно с помощью <a href="/docs/reference/command-line-tools-reference/feature-gates/">переключателя функциональности</a>
(feature gate) <code>MatchLabelKeysInPodAffinity</code>.</p></div>


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

Один из примеров использования — размещение подов определенной группы пользователей (tenant'ов) или команды в конкретном топологическом домене (узле, зоне и т. д.).
То есть идея в том, чтобы избежать одновременного запуска подов от разных групп пользователей в одном топологическом домене.

```yaml
apiVersion: v1
kind: Pod
metadata:
  labels:
    # Assume that all relevant Pods have a "tenant" label set
    tenant: tenant-a
...
spec:
  affinity:
    podAffinity:      
      requiredDuringSchedulingIgnoredDuringExecution:
      # следим за тем, чтобы поды, связанные с этим тенантом, попадали на нужный пул узлов
      - matchLabelKeys:
          - tenant
        labelSelector: {}
        topologyKey: node-pool
    podAntiAffinity:  
      requiredDuringSchedulingIgnoredDuringExecution:
      # следим за тем, чтобы поды, связанные с этим тенантом, не смогли планироваться на узлы для другого тенанта
      - mismatchLabelKeys:
        - tenant # значение лейбла "tenant" этого пода будет предотвращать
                 # планирование на узлы в пулах, на которых работают поды
                 # другого тенанта
        labelSelector:
          # Должен быть labelSelector, который выбирает поды с лейблом tenant,
          # иначе этот под также будет "ненавидеть" поды из daemonset'ов, например, 
          # те, у которых нет лейбла tenant.
          matchExpressions:
          - key: tenant
            operator: Exists
        topologyKey: node-pool
```

#### Другие примеры использования

Правила совместного/раздельного существования для подов особенно удобны, когда используются совместно с абстракциями более высокого уровня, такими как ReplicaSet, StatefulSet, Deployment и т. д. Эти правила позволяют настроить размещение рабочих нагрузок с учетом имеющейся топологии; например, пара связанных подов будет планироваться на один и тот же узел.

Представьте кластер, состоящий из трех узлов. Он используется для запуска веб-приложения и как in-memory-кэш (например, Redis). Предположим, что задержка между веб-приложением и кэшем должна быть минимальной. Правила совместного/раздельного существования для подов позволяют настроить планировщик так, чтобы тот размещал веб-серверы как можно ближе к кэшу.

В приведенном ниже примере конфигурации деплоймента с Redis его реплики получают лейбл `app=store`. Правило `podAntiAffinity` запрещает планировщику размещать несколько реплик с лейблом `app=store` на одном узле. В результате каждый узел получает по отдельной кэш-реплике.

```yaml
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"
      containers:
      - name: redis-server
        image: redis:3.2-alpine
```

Конфигурация деплоймента, приведенная ниже, создает три реплики веб-сервера с лейблом `app=web-store`.
Правило совместного существования предписывает планировщику размещать каждую реплику на узле, на котором уже имеется под с лейблом `app=store`. В то же время правило раздельного существования запрещает планировщику размещать несколько серверов с лейблом `app=web-store` на одном узле.

```yaml
apiVersion: apps/v1
kind: Deployment
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.16-alpine
```

Развертывание ресурсов в соответствии с приведенными выше конфигурациями приведет к созданию кластера, в котором на каждом узле будет по одному веб-серверу и одной реплике Redis (всего три отдельных узла):

|       узел 1         |       узел 2        |       узел 3       |
|:--------------------:|:-------------------:|:------------------:|
| *webserver-1*        |   *webserver-2*     |    *webserver-3*   |
|  *cache-1*           |     *cache-2*       |     *cache-3*      |

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

Правила совместного/раздельного существования для подов можно использовать и в других случаях.

См., например, [руководство по ZooKeeper](/docs/tutorials/stateful-application/zookeeper/#tolerating-node-failure). В нем с помощью правил раздельного существования StatefulSet настраивается так, чтобы обеспечить высокую доступность (используется подход, аналогичный тому, что применен выше).

## nodeName

Поле `nodeName` в спецификации пода — более непосредственный способ выбора узлов по сравнению с правилами совместного существования или селектором `nodeSelector`. Если поле `nodeName` не пустое, планировщик игнорирует под, а kubelet на узле с соответствующим именем пытается разместить под на этом узле. Приоритет поля `nodeName` выше, чем селектора `nodeSelector` или правил совместного/раздельного существования.

Однако у `nodeName` имеются и некоторые недостатки:

-   Если узел с заданным именем не существует, под не будет запущен. Кроме того, в некоторых случаях он может быть автоматически удален.
-   Если на узле с заданным именем недостаточно ресурсов для работы пода, последний будет остановлен; соответствующая причина (например, OutOfmemory или OutOfcpu) будет указана.
-   В облачных окружениях имена узлов не всегда предсказуемы или стабильны.


<div class="alert alert-info" role="note"><h4 class="alert-heading">Примечание:</h4>Поле <code>nodeName</code> предназначено для использования в кастомных планировщиках или в продвинутых случаях,
когда необходимо обойти настроенные планировщики. Обход планировщиков может привести к тому, что поды не будут запущены,
если целевые узлы окажутся перегруженными. С помощью правила <a href="#node-affinity">совместного существования (affinity) узла</a> или поля <a href="#nodeselector"><code>nodeselector</code></a> под можно назначить на определенный узел, не обходя планировщики с помощью <code>nodeName</code>.</div>


Ниже приведен пример спецификации пода с полем `nodeName`:

```yaml
apiVersion: v1
kind: Pod
metadata:
  name: nginx
spec:
  containers:
  - name: nginx
    image: nginx
  nodeName: kube-01
```

Такой под будет работать только на узле с именем `kube-01`.

## Ограничения на топологию распределения подов

С помощью _ограничений на топологию распределения_ (_topology spread constraints_) можно настроить размещение <a class='glossary-tooltip' title='Самый маленький и простой объект в Kubernetes. Под — это набор запущенных контейнеров в кластере.' data-bs-toggle='tooltip' data-bs-placement='top' href='/ru/docs/concepts/workloads/pods/pod-overview/' target='_blank' aria-label='подов'>подов</a> в кластере по failure-доменам, таким как регионы, зоны, узлы, или любым другим заданным топологическим конфигурациям. Это позволяет повысить производительность, ожидаемую доступность или эффективность использования ресурсов.

Дополнительные сведения о принципах их работы читайте в разделе [Ограничения на топологию распределения подов](/docs/concepts/scheduling-eviction/topology-spread-constraints/).

## Операторы {#operators}

Ниже приведены все логические операторы, которые можно использовать в поле `operator` для `nodeAffinity` и `podAffinity`.

|    Оператор    |    Действие     |
| :------------: | :-------------: |
| `In` | Значение лейбла присутствует в предоставленном наборе строк |
|   `NotIn`   | Значение лейбла отсутствует в предоставленном наборе строк |
| `Exists` | Лейбл с таким ключом существует для объекта |
| `DoesNotExist` | У объекта не существует лейбла с таким ключом |

Следующие операторы могут использоваться только с `nodeAffinity`.

|    Оператор    |    Действие     |
| :------------: | :-------------: |
| `Gt` | Введенное значение будет обработано как целое число, и это целое число меньше, чем целое число, полученное в результате обработки значения лейбла, указанного этим селектором | 
| `Lt` | Введенное значение будет обработано как целое число, и это целое число больше, чем целое число, полученное в результате обработки значения лейбла, указанного этим селектором | 


<div class="alert alert-info" role="note"><h4 class="alert-heading">Примечание:</h4>Операторы <code>Gt</code> и <code>Lt</code> не работают с нецелыми значениями. Если заданное значение
не является целым числом, под не будет запланирован. Кроме того, <code>Gt</code> и <code>Lt</code>
недоступны для <code>podAffinity</code>.</div>


## Что дальше

* Дополнительная информация о [taint'ах и toleration'ах](/docs/concepts/scheduling-eviction/taint-and-toleration/).
* Рабочая документация по правилам [совместного существования для узлов](https://git.k8s.io/design-proposals-archive/scheduling/nodeaffinity.md) и по правилам [совместного/раздельного существования для подов](https://git.k8s.io/design-proposals-archive/scheduling/podaffinity.md).
* [Информация о том](/docs/tasks/administer-cluster/topology-manager/), как менеджер топологии участвует в принятии решений о распределении ресурсов на уровне узлов. 
* Подробнее об использовании [селектора nodeSelector](/docs/tasks/configure-pod-container/assign-pods-nodes/).
* Подробнее об использовании правил [совместного/раздельного существования](/docs/tasks/configure-pod-container/assign-pods-nodes-using-node-affinity/).
