Як атрымаць доступ да рэсурсаў Kubernetes Pod

Як атрымаць доступ да рэсурсаў Kubernetes PodThe Reward by Tohad

У пачатку працы з Kubernetes звычайна забываюць аб наладзе рэсурсаў кантэйнераў. На гэтым этапе дастаткова пераканацца, што выява Docker працуе і яго можна разгарнуць у кластары Kubernetes.

Але пазней прыкладанне патрабуецца разгарнуць у прадакшэн кластары разам з іншымі праграмамі. Для гэтага трэба вылучыць рэсурсы для кантэйнера і пераканацца, што іх дастаткова для запуску і працы прыкладання, а ў іншых запушчаных прыкладаннях не ўзнікне праблем.

Каманда Kubernetes aaS ад Mail.ru перавяла артыкул аб рэсурсах кантэйнераў (CPU & MEM), запытах і абмежаваннях рэсурсаў. Вы даведаецеся, якія перавагі даюць гэтыя наладкі і што адбудзецца, калі іх не ўсталяваць.

Вылічальныя рэсурсы

У нас ёсць два тыпы рэсурсаў з наступнымі адзінкамі:

  • Цэнтральны працэсар (CPU) - ядры;
  • Памяць (MEM) - байты.

Рэсурсы паказваюць для кожнага кантэйнера. У наступным YAML-файле Pod вы ўбачыце раздзел рэсурсаў, які змяшчае запытаныя і гранічныя рэсурсы:

  • Запытаныя рэсурсы Pod = сума запытаных рэсурсаў усіх кантэйнераў;
  • Гранічныя рэсурсы Pod = сума гранічных рэсурсаў усіх кантэйнераў.

apiVersion: v1
kind: Pod
metadata:
  name: backend-pod-name
  labels:
    application: backend
spec:
  containers:
    — name: main-container
      image: my-backend
      tag: v1
      ports:
      — containerPort: 8080
      resources:
        requests:
          cpu: 0.2 # REQUESTED CPU: 200m cores
          memory: "1Gi" # REQUESTED MEM: 1Gi
        limits:
          cpu: 1 # MAX CPU USAGE: 1 core
          memory: "1Gi" # MAX MEM USAGE:  1Gi
    — name: other-container
      image: other-app
      tag: v1
      ports:
      — containerPort: 8000
      resources:
        requests:
          cpu: "200m" # REQUESTED CPU: 200m cores
          memory: "0.5Gi" # REQUESTED MEM: 0.5Gi
        limits:
          cpu: 1 # MAX CPU USAGE: 1 core
          memory: "1Gi" # MAX MEM USAGE:  1Gi

Прыклад запытаных і лімітавых рэсурсаў

Поле resources.requested з спецыфікацыі Pod - адзін з элементаў, які выкарыстоўваюць для пошуку патрэбнай ноды. Ужо на яе можна запланаваць разгортванне Pod. Як жа шукаюць прыдатную ноду?

Kubernetes складаецца з некалькіх кампанентаў, у тым ліку ўтрымоўвае галоўны вузел ці майстар-ноду (Kubernetes Control Plane). У майстры-нодзе некалькі працэсаў: kube-apiserver, kube-controller-manager і kube-scheduler.

Працэс kube-scheduler адказвае за прагляд ізноў створаных модуляў і пошук магчымых працоўных вузлоў, якія адпавядаюць усім запытам модуляў, у тым ліку па колькасці запытаных рэсурсаў. Спіс вузлоў, знойдзеных kube-scheduler, ранжыруецца. Pod плануецца на вузле з найвышэйшымі баламі.

Як атрымаць доступ да рэсурсаў Kubernetes PodКуды будзе змешчаны фіялетавы Pod?

На малюнку відаць, што kube-scheduler павінен запланаваць новы фіялетавы Pod. Кластар Kubernetes утрымоўвае два вузла: A і B. Як можна заўважыць, kube-scheduler не можа запланаваць Pod на вузел A - даступныя (нязапытаныя) рэсурсы не адпавядаюць запытам фіялетавага Pod. Так, запытаны фіялетавым Pod 1 Гб памяці не змесціцца на вузле А, паколькі даступны аб'ём памяці – 0,5 Гб. Але ў вузла У дастаткова рэсурсаў. У выніку kube-scheduler вырашае, што месца прызначэння фіялетавага Pod - вузел B.

Цяпер мы ведаем, як запытаныя рэсурсы ўплываюць на выбар ноды для запуску Pod. Але як уплываюць гранічныя рэсурсы?

Гранічныя рэсурсы - мяжа, якую CPU/MEM не можа перасякаць. Тым не менш рэсурс CPU гнуткі, таму кантэйнеры, якія дасягнулі лімітавых значэнняў па CPU, не прывядуць да завяршэння працы Pod. Замест гэтага запусціцца тротлінг па CPU. Калі ж будзе дасягнуты мяжа па выкарыстанні MEM, то кантэйнер будзе спынены з прычыны OOM-Killer і перазапушчаны, калі гэта дазволена наладай RestartPolicy.

Запытаныя і гранічныя рэсурсы ў дэталях

Як атрымаць доступ да рэсурсаў Kubernetes PodСувязь рэсурсаў паміж Docker і Kubernetes

Лепшы спосаб растлумачыць, як працуюць запытаныя і гранічныя рэсурсы - прадставіць сувязь паміж Kubernetes і Docker. На малюнку вышэй вы можаце паглядзець, як звязаны палі Kubernetes і сцягі запуску Docker.

Памяць: запыт і абмежаванне

containers:
...
 resources:
   requests:
     memory: "0.5Gi"
   limits:
     memory: "1Gi"

Як згадвалася вышэй, памяць вымяраецца ў байтах. Грунтуючыся на дакументацыі Kubernetes, мы можам паказаць памяць у выглядзе ліку. Звычайна яно цэлае, напрыклад 2678 - гэта значыць 2678 байт. Можна таксама выкарыстоўваць суфіксы G и Gi, галоўнае - памятаць, што яны не раўназначныя. Першы - дзесятковы, а другі - двайковы. Як прыклад, згаданы ў дакументацыі k8s: 128974848, 129e6, 129M, 123Mi - Яны практычна эквівалентныя.

Параметр Kubernetes limits.memory адпавядае сцягу --memory з Docker. У выпадку з request.memory стрэлка для Docker адсутнічае, паколькі Docker не выкарыстоўвае гэтае поле. Вы можаце спытаць, ці трэба гэта ўвогуле? Так, трэба. Як я ўжо казаў, поле мае значэнне для Kubernetes. На аснове інфармацыі з яго kube-scheduler вырашае, на які вузел запланаваць Pod.

Што будзе, калі ўсталяваць для запыту нядосыць памяці?

Калі кантэйнер дасягнуў межаў запытанай памяці, то Pod змяшчаецца ў групу Pod, якія спыняюцца пры недахопе памяці ў нодзе.

Што адбудзецца, калі ўсталяваць занадта маленькае лімітавае значэнне памяці?

Калі кантэйнер перавышае лімітавае значэнне памяці, ён будзе завершаны па чынніку OOM-Killed. І будзе перазапушчаны, калі гэта магчыма на падставе RestartPolicy, дзе значэнне па змаўчанні Always.

Што будзе, калі не паказаць патрэбную памяць?

Kubernetes возьме лімітавае значэнне і ўсталюе яго як значэнне па змаўчанні.

Што можа адбыцца, калі не паказаць гранічную памяць?

У кантэйнера няма абмежаванняў, ён можа выкарыстоўваць гэтулькі памяці, колькі захоча. Калі ж ён пачне выкарыстоўваць усю даступную памяць ноды, тое яго заб'е OOM. Затым кантэйнер будзе перазапушчаны, калі гэта магчыма на аснове RestartPolicy.

Што будзе, калі не паказаць лімітаў памяці?

Гэта найгоршы сцэнар: планавальнік не ведае, колькі рэсурсаў патрабуецца кантэйнеру, і гэта можа выклікаць сур'ёзныя праблемы на нодзе. У гэтым выпадку добра б мець абмежаванні па змаўчанні ў прасторы імёнаў (усталёўваныя LimitRange). Абмежаванняў па змаўчанні няма у Pod няма абмежаванняў, ён можа выкарыстаць гэтулькі памяці, колькі захоча.

Калі запытаная памяць больш, чым можа прапанаваць нода - Pod не будзе запланаваны. Важна памятаць, што Requests.memory - Не мінімальнае значэнне. Гэта апісанне аб'ёму памяці, дастатковага для сталай працы кантэйнера.

Звычайна рэкамендуюць усталёўваць адно і тое ж значэнне для request.memory и limit.memory. Дзякуючы гэтаму Kubernetes не заплануе Pod на вузле, у якога дастаткова памяці для запуску Pod, але недастаткова для працы. Майце на ўвазе: пры планаванні Pod Kubernetes улічвае толькі requests.memory, А limits.memory не ўлічвае.

CPU: запыт і абмежаванне

containers:
...
 resources:
   requests:
     cpu: 1
   limits:
     cpu: "1200m"

C CPU усё крыху больш складана. Вяртаючыся да малюначка з узаемасувяззю паміж Kubernetes і Docker, можна заўважыць, што request.cpu адпавядае --cpu-shares, тады як limit.cpu адпавядае сцягу cpus у Docker.

CPU, які запытвае Kubernetes, памнажаецца на 1024 - прапорцыю цыклаў CPU. Калі вы хочаце запытаць 1 поўнае ядро, то павінны дадаць cpu: 1, як паказана вышэй.

Запыт поўнага ядра (прапорцыя = 1024) не азначае, што ваш кантэйнер яго атрымае. Калі ў вашага хост-кампутара толькі адно ядро, і вы выкарыстоўваеце больш аднаго кантэйнера, то ўсе кантэйнеры павінны сумесна выкарыстоўваць даступны CPU паміж сабой. Як гэта адбываецца? Давайце паглядзім на карцінку.

Як атрымаць доступ да рэсурсаў Kubernetes Pod
Запыт CPU - сістэма з адным ядром

Давайце ўявім, што ў вас ёсць хост-сістэма з адным ядром, на якой запушчаны кантэйнеры. Мама (Kubernetes) спякла пірог (CPU) і хоча падзяліць яго паміж дзецьмі (кантэйнерамі). Трое дзяцей жадаюць па цэлым пірогу (прапорцыя = 1024), яшчэ адно дзіця жадае палову пірага (512). Мама жадае быць справядлівай і праводзіць нескладаны разлік.

# Сколько пирогов хотят дети?
# 3 ребенка хотят по целому пирогу и еще один хочет половину пирога
cakesNumberKidsWant = (3 * 1) + (1 * 0.5) = 3.5
# Выражение получается так:
3 (ребенка/контейнера) * 1 (целый пирог/полное ядро) + 1 (ребенок/контейнер) * 0.5 (половина пирога/половина ядра)
# Сколько пирогов испечено?
availableCakesNumber = 1
# Сколько пирога (максимально) дети реально могут получить?
newMaxRequest = 1 / 3.5 =~ 28%

Зыходзячы з разліку, трое дзяцей атрымаюць па 28% ядра, а не па цэлым ядры. Чацвёртаму дзіцяці дастанецца 14% ад поўнага ядра, а не палова. Але ўсё будзе па-іншаму, калі ў вас мультыядзерная сістэма.

Як атрымаць доступ да рэсурсаў Kubernetes Pod
Запыт CPU - мультыядзерная (4) сістэма

На малюнку вышэй відаць, што трое дзяцей жадаюць па цэлым пірогу, а адзін - палову. Паколькі маці спякла чатыры пірага, кожны з яе дзяцей атрымае столькі, колькі захоча. У шмат'ядравай сістэме працэсарныя рэсурсы размеркаваны па ўсіх даступных ядрах працэсара. Калі кантэйнер абмежаваны менш, чым адным поўным ядром CPU, тое ўсё роўна можа выкарыстоўваць яго на 100%.

Прыведзеныя вышэй разлікі спрошчаны для разумення таго, як CPU размяркоўваецца паміж кантэйнерамі. Вядома, апроч саміх кантэйнераў, ёсць і іншыя працэсы, якія таксама выкарыстоўваюць рэсурсы CPU. Калі працэсы ў адным кантэйнеры прастойваюць, іншыя могуць выкарыстоўваць яго рэсурс. CPU: "200m" адпавядае CPU: 0,2, Што азначае прыкладна 20% аднаго ядра.

Цяпер давайце пагаворым аб limit.cpu. CPU, які абмяжоўвае Kubernetes, памнажаецца на 100. Вынік - колькасць часу, якое кантэйнер можа выкарыстоўваць кожныя 100 мкс (cpu-period).

limit.cpu адпавядае сцягу Docker --cpus. Гэта новая камбінацыя старых --cpu-period и --cpu-quota. Усталёўваючы яго, мы паказваем, колькі даступных рэсурсаў CPU кантэйнер можа максімальна выкарыстоўваць датуль, пакуль не пачнецца тротлінг:

  • працэсар - камбінацыя cpu-period и cpu-quota. cpus = 1.5 эквівалентна ўстаноўцы cpu-period = 100000 и cpu-quota = 150000;
  • cpu-period - перыяд планавальніка CPU CFS, па змаўчанні 100 мікрасекунд;
  • cpu-quota - Колькасць мікрасекунд ўнутры cpu-period, якім абмежаваны кантэйнер.

Што будзе, калі ўсталяваць нядосыць запытанага CPU?

Калі кантэйнеру трэба больш, чым устаноўлена, то ён украдзе CPU у іншых працэсаў.

Што адбудзецца, калі ўсталяваць недастатковы ліміт CPU?

Паколькі рэсурс CPU рэгуляваны, то ўключыцца тратлінг.

Што здарыцца, калі не пазначыць запыт CPU?

Як і ў выпадку з памяццю, значэнне запыту роўна ліміту.

Што будзе, калі не пазначыць ліміт CPU?

Кантэйнер будзе выкарыстоўваць гэтулькі CPU, колькі яму неабходна. Калі ў прасторы імёнаў вызначана палітыка CPU па змаўчанні (LimitRange), то гэты ліміт выкарыстоўваюць і для кантэйнера.

Што адбудзецца, калі не ўказаць ні запыт, ні ліміт CPU?

Як і ў выпадку з памяццю, гэта найгоршы сцэнар. Планавальнік не ведае, колькі рэсурсаў трэба вашаму кантэйнеру, і гэта можа выклікаць сур'ёзныя праблемы на нодзе. Каб гэтага пазбегнуць, трэба ўсталёўваць абмежаванні па змаўчанні для прастор імёнаў (LimitRange).

Памятайце: калі вы запытаеце больш CPU, чым могуць даць ноды, то Pod не будзе запланаваны. Requests.cpu - не мінімальнае значэнне, а значэнне, дастатковае для запуску Pod і працы без збояў. Калі прыкладанне не выконвае складаных вылічэнняў, лепшы варыянт - усталяваць request.cpu <= 1 і запусціць столькі рэплік, колькі неабходна.

Ідэальная колькасць запытаных рэсурсаў або ліміту рэсурсаў

Мы даведаліся аб абмежаванні вылічальных рэсурсаў. Цяпер прыйшоў час адказаць на пытанне: «Колькі рэсурсаў патрабуецца майму Pod для працы прыкладання без праблем? Якая колькасць ідэальная?».

Нажаль, на гэтыя пытанні няма адназначных адказаў. Калі вы не ведаеце, як працуе ваша дадатак, колькі CPU або памяці яму трэба, лепшы варыянт - даць з дадаткам шмат памяці і CPU, а затым запусціць тэсты прадукцыйнасці.

Апроч тэстаў прадукцыйнасці, на працягу тыдня назірайце за паводзінамі прыкладання ў маніторынгу. Калі з графікаў вынікае, што ваша прыкладанне спажывае менш рэсурсаў, чым вы запытвалі, то можна зменшыць колькасць запытанага CPU ці памяці.

У якасці прыкладу паглядзіце гэты дашборд Grafana. Ён адлюстроўвае розніцу паміж запытанымі рэсурсамі ці лімітам рэсурсаў і бягучым выкарыстаннем рэсурсаў.

Заключэнне

Запыт і абмежаванне рэсурсаў дапамагаюць падтрымліваць працаздольнасць кластара Kubernetes. Правільная канфігурацыя лімітаў зводзіць да мінімуму выдаткі і ўвесь час падтрымлівае прыкладанні ў працоўным стане.

Калі сцісла, то трэба памятаць аб некалькіх момантах:

  1. Запытаныя рэсурсы - канфігурацыя, якая ўлічваецца падчас запуску (калі Kubernetes плануе размяшчэнне прыкладання). Наадварот, абмежаванне рэсурсаў важна падчас працы - калі прыкладанне ўжо запушчана на вузле.
  2. У параўнанні з памяццю, CPU - рэгуляваны рэсурс. У выпадку недахопу CPU ваш Pod не завершыць працу, уключыцца механізм тротлінга.
  3. Запытаныя рэсурсы і ліміт рэсурсаў - гэта не мінімальныя і максімальныя значэння! Вызначаючы запытаныя рэсурсы, вы гарантуеце, што дадатак будзе працаваць без праблем.
  4. Добрая практыка - усталёўваць запыт памяці, роўны ліміту памяці.
  5. Добра ўсталёўваць запытаны CPU <=1, Калі прыкладанне не выконвае складаных вылічэнняў.
  6. Калі вы запытаеце больш рэсурсаў, чым есць на нодзе, то Pod ніколі не будзе запланаваны на гэтую ноду.
  7. Каб вызначыць правільную колькасць запытаных рэсурсаў/лімітаў рэсурсаў, выкарыстоўвайце нагрузачнае тэсціраванне і маніторынг.

Спадзяюся, гэты артыкул дапаможа вам зразумець асноўную канцэпцыю абмежавання рэсурсаў. І вы зможаце прымяніць гэтыя веды ў сваёй працы.

Поспехаў!

Што яшчэ пачытаць:

  1. Назіральнасць SRE: прасторы імёнаў і структура метрык.
  2. 90+ карысных інструментаў для Kubernetes: разгортванне, кіраванне, маніторынг, бяспека і не толькі.
  3. Наш канал Вакол Kubernetes у Тэлеграме.

Крыніца: habr.com

Дадаць каментар