Kubernetes.Приоритетность подов - теория. часть15

Thank you for reading this post, don't forget to subscribe!

Поды (Pods) могут иметь при­о­ри­тет. При­о­ри­тет отоб­ра­жа­ет важ­ность пода отно­си­тель­но дру­гих подов в кла­сте­ре. Если под не может быть запу­щен на под­хо­дя­щем узле из-за нехват­ки ресур­сов, то пла­ни­ров­щик (scheduler) пыта­ет­ся “вытес­нить” поды с более низ­ким при­о­ри­те­том и пере­ме­стить их на дру­гие узлы кла­сте­ра, что­бы осво­бо­дить ресур­сы и запу­стить ожи­да­ю­щий под. Давай­те разберемся!

При­о­ри­тет подов (Pod Priority) и вытес­не­ние (Preemption) вклю­че­ны по умол­ча­нию начи­ная с вер­сии Kubernetes 1.11. Для их исполь­зо­ва­ния необходимо:

  • создать один или несколь­ко объ­ек­тов PriorityClass в кластере;
  • доба­вить в опи­са­ние пода поле priorityClassName с име­нем создан­но­го ранее класса.

PriorityClass - это объ­ект, опре­де­ля­ю­щий соот­вет­ствие меж­ду име­нем клас­са при­о­ри­те­та и его цело­чис­лен­ным зна­че­ни­ем. Имя зада­ет­ся в поле name объ­ек­та, зна­че­ние - в поле value. Чем боль­ше зна­че­ние, тем выше при­о­ри­тет запус­ка у пода с ука­зан­ным клас­сом. В поле value мож­но ука­зать цело­чис­лен­ное 32-бит­ное зна­че­ние мень­ше или рав­ное 1000000000 - более высо­кие зна­че­ния заре­зер­ви­ро­ва­ны для кри­ти­че­ски важ­ных систем­ных подов, кото­рые, как пра­ви­ло, не могут быть вытес­не­ны. Кро­ме того, PriorityClass име­ет два опци­о­наль­ных поля - globalDefault и description. Поле globalDefault со зна­че­ни­ем уста­нов­лен­ным в true ука­зы­ва­ет, что дан­ный класс при­о­ри­те­та будет исполь­зо­вать­ся по умол­ча­нию для всех подов кла­сте­ра, в опи­са­нии кото­рых нет поля priorityClassName. В систе­ме может суще­ство­вать один и толь­ко один класс при­о­ри­те­та, в кото­ром globalDefault уста­нов­ле­но в true; если же во всех клас­сах кла­сте­ра это зна­че­ние будет уста­нов­ле­но в false, то по умол­ча­нию при­о­ри­тет всех подов в спе­ци­фи­ка­ции кото­рых нет поля priorityClassName будет равен нулю (0).

При­ме­ча­ние. Если вы созда­е­те объект(ы) PriorityClass в кла­сте­ре Kubernetes, в кото­ром уже запу­ще­ны поды, то сле­ду­ет пом­нить, что для суще­ству­ю­щих подов при­о­ри­тет будет рав­нять­ся 0 (даже если вы доба­ви­ли класс с иным зна­че­ни­ем и полем globalDefault рав­ным true) - зна­че­ния опре­де­лен­ные в клас­се при­о­ри­те­тов будут при­ме­нять­ся толь­ко к подам, кото­рые были созда­ны после созда­ния объекта(ов) PriorityClass.

При­мер объ­ек­та PriorityClass выгля­дит так:

В вер­сии Kubernetes 1.15 (в каче­стве аль­фа-фичи) в объ­ект PriorityClass добав­ле­но поле PreemptionPolicy, в кото­ром по умол­ча­нию уста­нов­ле­но зна­че­ние PreemptLowerPriority, что поз­во­ля­ет подам дан­но­го клас­са при­о­ри­те­та “вытес­нять” поды с более низ­ким клас­сом при­о­ри­те­та для осво­бож­де­ния ресур­сов. В поле PreemptionPolicy так­же мож­но уста­но­вить зна­че­ние Never - в дан­ном слу­чае поды будут раз­ме­ще­ны в оче­ре­ди пла­ни­ров­щи­ка перед пода­ми с более низ­ким при­о­ри­те­том, но не смо­гут вытес­нять дру­гие поды, ожи­дая “есте­ствен­но­го” осво­бож­де­ния ресурсов.

При­мер объ­ек­та PriorityClass с отклю­чен­ным вытес­не­ни­ем будет таким:

После созда­ния одно­го или несколь­ких клас­сов при­о­ри­те­та необ­хо­ди­мо доба­вить поле priorityClassName с име­нем соот­вет­ству­ю­ще­го клас­са в спе­ци­фи­ка­цию пода (или ReplicaSet, или Deployments), напри­мер:

В резуль­та­те под с более высо­ким при­о­ри­те­том будет постав­лен в оче­редь и запу­щен рань­ше подов с при­о­ри­те­том ниже. Если же под с высо­ким при­о­ри­те­том не может быть запу­щен в дан­ный момент вре­ме­ни, то пла­ни­ров­щик (scheduler) попро­бу­ет запу­стить его позд­нее, таким обра­зом давая воз­мож­ность стар­то­нуть подам с более низ­ким приоритетом.

Итак, когда под (Pods) создан, он поме­ща­ет­ся в оче­редь и ожи­да­ет, пока пла­ни­ров­щик опре­де­лит ему под­хо­дя­щий узел кла­сте­ра для запус­ка. Если пла­ни­ров­щик не может най­ти ни одно­го под­хо­дя­ще­го узла, соот­вет­ству­ю­ще­го всем тре­бо­ва­ни­ям запус­ка­е­мо­го пода, то для ожи­да­ю­ще­го пода запус­ка­ет­ся про­цесс вытес­не­ния. Допу­стим, P - это под в оче­ре­ди, тогда логи­ка вытес­не­ния пыта­ет­ся най­ти под­хо­дя­щую ноду в кла­сте­ре, на кото­рой уда­ле­ние одно­го или несколь­ких подов p0,1,2-... с более низ­ким при­о­ри­те­том, чем у P, поз­во­ли­ло бы запу­стить под P на этом узле. Когда такая нода най­де­на, один или несколь­ко подов p0,1,2-... “высе­ля­ют­ся” с нее, осво­бож­дая необ­хо­ди­мые ресур­сы для запус­ка пода P из оче­ре­ди планировщика.

Когда под P “высе­ля­ет” один или несколь­ко подов p0,1,2-... с узла N, в поле nominatedNodeName пода P появ­ля­ет­ся имя узла N - имен­но это поле помо­га­ет пла­ни­ров­щи­ку отсле­жи­вать необ­хо­ди­мое коли­че­ство ресур­сов для пода P и дает поль­зо­ва­те­лю инфор­ма­цию о вытес­не­ни­ях подов в кластере.

Сто­ит отме­тить, что под P не обя­за­тель­но будет запу­щен на ноде, имя кото­рой ука­за­но в поле nominatedNodeName - дело в том, что все поды име­ют вре­мя для кор­рект­но­го завер­ше­ния рабо­ты (graceful termination period). Если в тече­ние это­го пери­о­да на какой-либо дру­гой ноде кла­сте­ра появит­ся доста­точ­ное коли­че­ство ресур­сов для запус­ка пода P, то он будет запу­щен на под­хо­дя­щей ноде, не дожи­да­ясь вытес­не­ния подов p0,1,2-... и осво­бож­де­ния ресур­сов с ноды из поля nominatedNodeName. Вто­рой слу­чай когда име­на узлов в полях nominatedNodeName и nodeName могут не сов­па­дать - если в про­цес­се осво­бож­де­ния ресур­сов для пода P в оче­ре­ди пла­ни­ров­щи­ка появит­ся под с еще более высо­ким при­о­ри­те­том, то имен­но он будет запу­щен раньше.