kubernetes. services— теория. часть4

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

Поды в кла­сте­ре Kubernetes смерт­ны - они созда­ют­ся (рож­да­ют­ся), но когда под по какой-либо при­чине уми­ра­ет, то он не вос­кре­са­ет. И хотя каж­дый под при созда­нии полу­ча­ет свой соб­ствен­ный IP-адрес, этот адрес нель­зя назвать посто­ян­ным и ста­биль­ным вслед­ствие “смерт­но­сти” подов.

К при­ме­ру, ReplicaSet может дина­ми­че­ски изме­нять коли­че­ство подов в кла­сте­ре (путем мас­шта­би­ро­ва­ния), при этом новые поды могут быть запу­ще­ны на дру­гих узлах (нодах) кла­сте­ра - в этом слу­чае IP-адрес пода навер­ня­ка изменится.

Это порож­да­ет про­бле­му: если некий набор подов (назо­вем их backends) предо­став­ля­ют функ­ци­о­нал дру­го­му набо­ру подов (назо­вем их frontends) внут­ри кла­сте­ра Kubernetes, то как frontend-поды узна­ют адре­са backend-подов и вза­и­мо­дей­ству­ют с ними?

Сер­вис в Kubernetes - это абстрак­ция, опре­де­ля­ю­щая логи­че­ский набор подов и поли­ти­ку досту­па к ним (ино­гда такой набор подов еще назы­ва­ют мик­ро­сер­ви­сом). Как пра­ви­ло, этот набор подов опре­де­ля­ет­ся на осно­ве меток (при­сва­и­ва­ют­ся в момент созда­ния подов) и селекторов.

Напри­мер, backend - это 3 репли­ки подов, зани­ма­ю­щих­ся обра­бот­кой изоб­ра­же­ний. Все три пода абсо­лют­но иден­тич­ны с функ­ци­о­наль­ной точ­ки зре­ния (так как это репли­ки), поэто­му frontend не дол­жен бес­по­ко­ить­ся на какой имен­но backend-под попа­дет запрос - это рабо­та сервиса.

Про­стей­ший при­мер сер­ви­са может выгля­деть так:

Соглас­но дан­ной спе­ци­фи­ка­ции, будет создан объ­ект “сер­вис” (service) с име­нем my-service, пере­на­прав­ля­ю­щий запро­сы на порт 9376 каж­до­го пода, у кото­ро­го при­сут­ству­ет мет­ка app=MyApp. Это­му сер­ви­су так­же будет назна­чен отдель­ный IP-адрес.

Соглас­но ука­зан­но­му селек­то­ру, в кла­сте­ре непре­рыв­но будут про­ве­рять­ся поды на нали­чие мет­ки app=MyApp, а резуль­тат такой про­вер­ки будет пуб­ли­ко­вать­ся (с помо­щью POST) в объ­ект Endpoints с таким же име­нем - my-service.

Сто­ит отме­тить, что сер­вис может сопо­став­лять вхо­дя­щий порт (port:) с любым пор­том назна­че­ния (targetPort:). Если пара­метр targetPort не ука­зан, то по умол­ча­нию будет исполь­зо­вать­ся тот же порт, что и в пара­мет­ре port. Еще одна инте­рес­ная осо­бен­ность - targetPort может содер­жать стро­ку с име­нем пор­та в подах (а сам номер пор­та, при­сво­ен­ный это­му име­ни, может быть раз­ным для каж­до­го пода).

Сер­ви­сы чаще все­го исполь­зу­ют­ся для досту­па к подам в кла­сте­ре Kubernetes, но их так­же мож­но исполь­зо­вать и для досту­па к дру­гим типам бэкен­дов, например:

  • внеш­ний кла­стер баз дан­ных в production-окру­же­нии, локаль­ная БД для тесто­во­го окружения;
  • совер­шен­но иной сер­вис в дру­гом нейм­с­пей­се или кластере;
  • часть бекен­дов, запу­щен­ных вне кла­сте­ра Kubernetes (напри­мер, если вы толь­ко пере­но­си­те свою инфраструктуру).

В любом из пред­ло­жен­ных вари­ан­тов сле­ду­ет создать сер­вис без селек­то­ров, например:

и, так как селек­то­ров нет, соот­вест­ву­ю­щий объ­ект Endpoints не создаст­ся авто­ма­ти­че­ски. Его необ­хо­ди­мо создать вруч­ную, тем самым ука­зав связь меж­ду сер­ви­сом и конеч­ной точ­кой (бекен­дом), напри­мер так:

При­ме­ча­ние. IP-адрес в дан­ном слу­чае не может нахо­дить­ся в диа­па­зо­нах 127.0.0.0/8, 169.254.0.0/16 и 224.0.0.0/24.

Для мно­гих сер­ви­сов обыч­ным делом явля­ет­ся исполь­зо­ва­ние (откры­тие) несколь­ких пор­тов. В таком слу­чае, все что необ­хо­ди­мо - при­сво­ить име­на всем пор­там, например:

В Kubernetes суще­ству­ет несколь­ко вари­ан­тов предо­став­ле­ния досту­па к сер­ви­сам, кото­рые назы­ва­ют­ся типы (ServiceTypes):

  • ClusterIP: предо­став­ля­ет доступ к сер­ви­су на внут­рен­нем IP-адре­се кла­сте­ра (сер­вис досту­пен толь­ко внут­ри кла­сте­ра). Тип ClusterIP исполь­зу­ет­ся по умолчанию;
  • NodePort: предо­став­ля­ет доступ к сер­ви­су на IP-адре­се каж­до­го узла (ноды) кла­сте­ра, на ста­ти­че­ском пор­ту (из диа­па­зо­на 30000-32767). Авто­ма­ти­че­ски создаст­ся и сер­вис типа ClusterIP, на кото­рый будут марш­ру­ти­зи­ро­вать­ся запро­сы с NodePort. Вза­и­мо­дей­ство­вать с сер­ви­сом мож­но так­же из-за пре­де­лов кла­сте­ра, исполь­зуя в каче­стве адре­са <NodeIP>:<NodePort>;
  • LoadBalancer: предо­став­ля­ет доступ к сер­ви­су исполь­зуя балан­си­ров­щик (load balancer) облач­но­го про­вай­де­ра. При этом авто­ма­ти­че­ски созда­ют­ся сер­ви­сы типа NodePort и ClusterIP, на кото­рые будут марш­ру­ти­зи­ро­вать­ся запро­сы с балансировщика;
  • ExternalName: осо­бый слу­чай - сопо­став­ля­ет имя сер­ви­са с содер­жи­мым поля externalName(напри­мер, foo.bar.example.com), воз­вра­щая CNAME запись. Ника­ко­го прок­си­ро­ва­ния не происходит.